@hublo/sentinel 1.5.1-alpha.1 → 1.5.1-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,10 +11,10 @@ import {
11
11
  presetNotInstalledYet,
12
12
  registerAdapters,
13
13
  resolve
14
- } from "../chunk-64H5HBPH.js";
14
+ } from "../chunk-JHLNNIZC.js";
15
15
  import {
16
16
  resolveContext
17
- } from "../chunk-J2USE3KV.js";
17
+ } from "../chunk-P7NYSMLS.js";
18
18
  import {
19
19
  ensureWorkspacePrep,
20
20
  findWorkspaceRoot,
@@ -527,7 +527,7 @@ async function runValidate(ctx) {
527
527
  );
528
528
  }
529
529
  if (!ctx.targets.includes("test")) return 1;
530
- const { validateModuleTests } = await import("../validate-TIPIECG6.js");
530
+ const { validateModuleTests } = await import("../validate-5SD5CTTO.js");
531
531
  const outcomes = modules.flatMap(
532
532
  (module) => validateModuleTests(module.name, module.root)
533
533
  );
@@ -20,7 +20,9 @@ import {
20
20
  binFromOwnInstall,
21
21
  binSearchPath,
22
22
  canRunTypeAware,
23
+ canonicalRuleName,
23
24
  describeHolds,
25
+ describeRuleKey,
24
26
  extendsWithBaseline,
25
27
  extendsWithPreset,
26
28
  formatPresetFor,
@@ -57,7 +59,7 @@ import {
57
59
  splitToolArgs,
58
60
  walk,
59
61
  writtenFileFormatter
60
- } from "./chunk-J2USE3KV.js";
62
+ } from "./chunk-P7NYSMLS.js";
61
63
  import {
62
64
  DEFAULT_MAX_DIAGNOSTICS,
63
65
  WORKSPACE_ROOT_MARKER,
@@ -4587,6 +4589,19 @@ function validate(preset, rules) {
4587
4589
  }
4588
4590
  }
4589
4591
  }
4592
+ function rejectTwoSpellings(label, rules) {
4593
+ const seen = /* @__PURE__ */ new Map();
4594
+ for (const rule of Object.keys(rules)) {
4595
+ const canonical = canonicalRuleName(rule);
4596
+ const other = seen.get(canonical);
4597
+ if (other !== void 0) {
4598
+ throw new Error(
4599
+ `${label}: "${other}" and "${rule}" are the same rule to oxlint (${canonical}). Configure it once, under one name: oxlint keeps both and picks one by hash order.`
4600
+ );
4601
+ }
4602
+ seen.set(canonical, rule);
4603
+ }
4604
+ }
4590
4605
  function layered(preset) {
4591
4606
  const base = sharedRules();
4592
4607
  const layer = presetRules(preset);
@@ -4596,6 +4611,7 @@ function layered(preset) {
4596
4611
  }
4597
4612
  function toRuleMap(label, rules) {
4598
4613
  validate(label, rules);
4614
+ rejectTwoSpellings(label, rules);
4599
4615
  const entries = Object.entries(rules).map(([rule, value]) => {
4600
4616
  if (!isDecision(value)) return [rule, value];
4601
4617
  const options = value.options ?? [];
@@ -4663,6 +4679,110 @@ function committedExtendsList(cwd, configFile) {
4663
4679
  }
4664
4680
  }
4665
4681
 
4682
+ // src/roles/lint/module-own-config.ts
4683
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4684
+ function pluginKey(entry) {
4685
+ if (typeof entry === "string") return entry;
4686
+ if (isRecord(entry) && typeof entry.name === "string") return entry.name;
4687
+ if (isRecord(entry) && typeof entry.specifier === "string") return entry.specifier;
4688
+ return void 0;
4689
+ }
4690
+ function governedRuleNames(preset) {
4691
+ return /* @__PURE__ */ new Set([
4692
+ ...Object.keys(preset.rules),
4693
+ ...(preset.overrides ?? []).flatMap((override) => Object.keys(override.rules))
4694
+ ]);
4695
+ }
4696
+ function governedRules(preset) {
4697
+ const governed = /* @__PURE__ */ new Map();
4698
+ for (const name of governedRuleNames(preset)) {
4699
+ const canonical = canonicalRuleName(name);
4700
+ if (!governed.has(canonical)) governed.set(canonical, name);
4701
+ }
4702
+ return governed;
4703
+ }
4704
+ function moduleOwnBlocks(committed, preset) {
4705
+ const blocks = {};
4706
+ const kept = [];
4707
+ const dropped = [];
4708
+ if (Array.isArray(committed.plugins)) {
4709
+ const own = committed.plugins.filter(
4710
+ (name) => typeof name === "string" && !preset.plugins.includes(name)
4711
+ );
4712
+ if (own.length > 0) {
4713
+ blocks.plugins = own;
4714
+ kept.push(
4715
+ `kept this module's own plugins (${own.join(", ")}): the preset does not enable them`
4716
+ );
4717
+ }
4718
+ }
4719
+ if (Array.isArray(committed.jsPlugins)) {
4720
+ const presetKeys = new Set(preset.jsPlugins.map((entry) => pluginKey(entry)));
4721
+ const own = [];
4722
+ for (const entry of committed.jsPlugins) {
4723
+ const key = pluginKey(entry);
4724
+ if (key !== void 0 && presetKeys.has(key)) dropped.push(`jsPlugins ${key}`);
4725
+ else own.push(entry);
4726
+ }
4727
+ if (own.length > 0) {
4728
+ blocks.jsPlugins = own;
4729
+ kept.push(
4730
+ `kept ${own.length} jsPlugin(s) this module added (${own.map((entry) => pluginKey(entry) ?? "?").join(", ")})`
4731
+ );
4732
+ }
4733
+ }
4734
+ if (isRecord(committed.settings)) {
4735
+ const presetSettings = preset.settings ?? {};
4736
+ const own = {};
4737
+ for (const [key, value] of Object.entries(committed.settings)) {
4738
+ if (key in presetSettings) dropped.push(`settings.${key}`);
4739
+ else own[key] = value;
4740
+ }
4741
+ if (Object.keys(own).length > 0) {
4742
+ blocks.settings = own;
4743
+ kept.push(`kept this module's own settings (${Object.keys(own).join(", ")})`);
4744
+ }
4745
+ }
4746
+ if (Array.isArray(committed.overrides)) {
4747
+ const governed = governedRules(preset);
4748
+ const byFiles = /* @__PURE__ */ new Map();
4749
+ for (const block of committed.overrides) {
4750
+ if (!isRecord(block) || !Array.isArray(block.files)) continue;
4751
+ const files = JSON.stringify([...block.files].map(String).sort());
4752
+ const rules = {};
4753
+ for (const [rule, value] of Object.entries(isRecord(block.rules) ? block.rules : {})) {
4754
+ const presetSpelling = governed.get(canonicalRuleName(rule));
4755
+ if (presetSpelling === void 0) rules[rule] = value;
4756
+ else
4757
+ dropped.push(
4758
+ `overrides[${block.files.join(", ")}] ${describeRuleKey(rule, presetSpelling)}`
4759
+ );
4760
+ }
4761
+ const { rules: _rules, ...rest } = block;
4762
+ const merged = byFiles.get(files);
4763
+ if (merged === void 0) {
4764
+ byFiles.set(files, { ...rest, ...Object.keys(rules).length > 0 ? { rules } : {} });
4765
+ continue;
4766
+ }
4767
+ const { files: _sameFiles, ...others } = rest;
4768
+ const mergedRules = { ...isRecord(merged.rules) ? merged.rules : {}, ...rules };
4769
+ Object.assign(
4770
+ merged,
4771
+ others,
4772
+ Object.keys(mergedRules).length > 0 ? { rules: mergedRules } : {}
4773
+ );
4774
+ }
4775
+ const own = [...byFiles.values()].filter((block) => Object.keys(block).length > 1);
4776
+ if (own.length > 0) {
4777
+ blocks.overrides = own;
4778
+ kept.push(
4779
+ `kept ${own.length} override block(s) this module set (${own.map((block) => block.files.join(", ")).join("; ")})`
4780
+ );
4781
+ }
4782
+ }
4783
+ return { blocks, kept, dropped };
4784
+ }
4785
+
4666
4786
  // src/roles/lint/parse-diagnostics.ts
4667
4787
  function ruleId(code) {
4668
4788
  const inner = /\(([^)]+)\)/.exec(code);
@@ -4712,6 +4832,26 @@ function errorCountsByConfigRule(stdout) {
4712
4832
  return counts;
4713
4833
  }
4714
4834
 
4835
+ // src/roles/lint/presets.ts
4836
+ var hasRuleData = (name) => GENERATED_LINT_PRESETS.includes(name);
4837
+ function lintPresetFor(preset) {
4838
+ const file = presetFile(preset);
4839
+ const resolved = hasRuleData(preset) ? preset : "node";
4840
+ return {
4841
+ plugins: file.plugins,
4842
+ jsPlugins: file.jsPlugins,
4843
+ ...file.settings === null ? {} : { settings: file.settings },
4844
+ ...file.renames ? { renames: file.renames } : {},
4845
+ ...file.overrides ? {
4846
+ overrides: file.overrides.map((override, index) => ({
4847
+ files: override.files,
4848
+ rules: toRuleMap(`${preset} override ${index}`, override.rules)
4849
+ }))
4850
+ } : {},
4851
+ rules: rulesFor(resolved)
4852
+ };
4853
+ }
4854
+
4715
4855
  // src/roles/lint/adapters/oxlint/plan.ts
4716
4856
  import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
4717
4857
  import { join as join16 } from "node:path";
@@ -4804,118 +4944,6 @@ function keepsOtherCommands3(script) {
4804
4944
  return keepsOtherCommands(script, SENTINEL_LINT_COMMAND);
4805
4945
  }
4806
4946
 
4807
- // src/roles/lint/module-own-config.ts
4808
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
4809
- function pluginKey(entry) {
4810
- if (typeof entry === "string") return entry;
4811
- if (isRecord(entry) && typeof entry.name === "string") return entry.name;
4812
- if (isRecord(entry) && typeof entry.specifier === "string") return entry.specifier;
4813
- return void 0;
4814
- }
4815
- function governedRuleNames(preset) {
4816
- return /* @__PURE__ */ new Set([
4817
- ...Object.keys(preset.rules),
4818
- ...(preset.overrides ?? []).flatMap((override) => Object.keys(override.rules))
4819
- ]);
4820
- }
4821
- function moduleOwnBlocks(committed, preset) {
4822
- const blocks = {};
4823
- const kept = [];
4824
- const dropped = [];
4825
- if (Array.isArray(committed.plugins)) {
4826
- const own = committed.plugins.filter(
4827
- (name) => typeof name === "string" && !preset.plugins.includes(name)
4828
- );
4829
- if (own.length > 0) {
4830
- blocks.plugins = own;
4831
- kept.push(
4832
- `kept this module's own plugins (${own.join(", ")}): the preset does not enable them`
4833
- );
4834
- }
4835
- }
4836
- if (Array.isArray(committed.jsPlugins)) {
4837
- const presetKeys = new Set(preset.jsPlugins.map((entry) => pluginKey(entry)));
4838
- const own = [];
4839
- for (const entry of committed.jsPlugins) {
4840
- const key = pluginKey(entry);
4841
- if (key !== void 0 && presetKeys.has(key)) dropped.push(`jsPlugins ${key}`);
4842
- else own.push(entry);
4843
- }
4844
- if (own.length > 0) {
4845
- blocks.jsPlugins = own;
4846
- kept.push(
4847
- `kept ${own.length} jsPlugin(s) this module added (${own.map((entry) => pluginKey(entry) ?? "?").join(", ")})`
4848
- );
4849
- }
4850
- }
4851
- if (isRecord(committed.settings)) {
4852
- const presetSettings = preset.settings ?? {};
4853
- const own = {};
4854
- for (const [key, value] of Object.entries(committed.settings)) {
4855
- if (key in presetSettings) dropped.push(`settings.${key}`);
4856
- else own[key] = value;
4857
- }
4858
- if (Object.keys(own).length > 0) {
4859
- blocks.settings = own;
4860
- kept.push(`kept this module's own settings (${Object.keys(own).join(", ")})`);
4861
- }
4862
- }
4863
- if (Array.isArray(committed.overrides)) {
4864
- const governed = governedRuleNames(preset);
4865
- const byFiles = /* @__PURE__ */ new Map();
4866
- for (const block of committed.overrides) {
4867
- if (!isRecord(block) || !Array.isArray(block.files)) continue;
4868
- const files = JSON.stringify([...block.files].map(String).sort());
4869
- const rules = {};
4870
- for (const [rule, value] of Object.entries(isRecord(block.rules) ? block.rules : {})) {
4871
- if (governed.has(rule)) dropped.push(`overrides[${block.files.join(", ")}] ${rule}`);
4872
- else rules[rule] = value;
4873
- }
4874
- const { rules: _rules, ...rest } = block;
4875
- const merged = byFiles.get(files);
4876
- if (merged === void 0) {
4877
- byFiles.set(files, { ...rest, ...Object.keys(rules).length > 0 ? { rules } : {} });
4878
- continue;
4879
- }
4880
- const { files: _sameFiles, ...others } = rest;
4881
- const mergedRules = { ...isRecord(merged.rules) ? merged.rules : {}, ...rules };
4882
- Object.assign(
4883
- merged,
4884
- others,
4885
- Object.keys(mergedRules).length > 0 ? { rules: mergedRules } : {}
4886
- );
4887
- }
4888
- const own = [...byFiles.values()].filter((block) => Object.keys(block).length > 1);
4889
- if (own.length > 0) {
4890
- blocks.overrides = own;
4891
- kept.push(
4892
- `kept ${own.length} override block(s) this module set (${own.map((block) => block.files.join(", ")).join("; ")})`
4893
- );
4894
- }
4895
- }
4896
- return { blocks, kept, dropped };
4897
- }
4898
-
4899
- // src/roles/lint/presets.ts
4900
- var hasRuleData = (name) => GENERATED_LINT_PRESETS.includes(name);
4901
- function lintPresetFor(preset) {
4902
- const file = presetFile(preset);
4903
- const resolved = hasRuleData(preset) ? preset : "node";
4904
- return {
4905
- plugins: file.plugins,
4906
- jsPlugins: file.jsPlugins,
4907
- ...file.settings === null ? {} : { settings: file.settings },
4908
- ...file.renames ? { renames: file.renames } : {},
4909
- ...file.overrides ? {
4910
- overrides: file.overrides.map((override, index) => ({
4911
- files: override.files,
4912
- rules: toRuleMap(`${preset} override ${index}`, override.rules)
4913
- }))
4914
- } : {},
4915
- rules: rulesFor(resolved)
4916
- };
4917
- }
4918
-
4919
4947
  // src/roles/lint/rename-suppressions.ts
4920
4948
  import { readdirSync as readdirSync2, readFileSync as readFileSync12, statSync as statSync3 } from "node:fs";
4921
4949
  import { join as join15, relative as relative3 } from "node:path";
@@ -5172,7 +5200,7 @@ function plan2(context2) {
5172
5200
  }
5173
5201
  if (droppedRules.length > 0) {
5174
5202
  notes.push(
5175
- `dropped ${droppedRules.length} rule(s) this module set over the preset (${droppedRules.join(", ")}): the preset governs them`
5203
+ `dropped ${droppedRules.length} rule(s) this module set over the preset (${droppedRules.map((rule) => describeGoverned(rule, variant)).join(", ")}): the preset governs them`
5176
5204
  );
5177
5205
  }
5178
5206
  notes.push(...teamBlocks.kept);
@@ -5266,15 +5294,18 @@ function committedConfig(cwd) {
5266
5294
  }
5267
5295
  }
5268
5296
  function moduleOwnRules(committed, preset) {
5269
- const governedNames = governedRuleNames(preset);
5297
+ const governedNames = governedRules(preset);
5270
5298
  const kept = {};
5271
5299
  const governed = [];
5272
5300
  for (const [rule, value] of Object.entries(committed)) {
5273
- if (governedNames.has(rule)) governed.push(rule);
5301
+ if (governedNames.has(canonicalRuleName(rule))) governed.push(rule);
5274
5302
  else kept[rule] = value;
5275
5303
  }
5276
5304
  return { kept, governed };
5277
5305
  }
5306
+ function describeGoverned(rule, variant) {
5307
+ return describeRuleKey(rule, governedRules(lintPresetFor(variant)).get(canonicalRuleName(rule)));
5308
+ }
5278
5309
  function committedIgnorePatterns(cwd) {
5279
5310
  try {
5280
5311
  const parsed = parseJsonc(
@@ -5516,7 +5547,10 @@ ${result.stderr ?? ""}`);
5516
5547
  );
5517
5548
  return;
5518
5549
  }
5519
- const holds = holdsFrom(errorCountsByConfigRule(measured.stdout ?? ""));
5550
+ const holds = holdsFrom(
5551
+ errorCountsByConfigRule(measured.stdout ?? ""),
5552
+ governedRules(lintPresetFor(presetVariant(ctx.preset, ctx.cwd)))
5553
+ );
5520
5554
  const next = extendsWithBaseline(current, holds.length > 0);
5521
5555
  if (holds.length > 0) {
5522
5556
  writeFileSync3(baselinePath, renderBaseline(holds, readOwnPackage().version));
@@ -5672,7 +5706,8 @@ ${result.stderr ?? ""}`);
5672
5706
  * exactly when something is broken.
5673
5707
  *
5674
5708
  * Counted as a SET of rule names, so the baseline re-stating a preset rule at `warn` is
5675
- * the same rule, not a second one.
5709
+ * the same rule, not a second one. The rule oxlint RUNS (`canonicalRuleName`), so a layer
5710
+ * spelling it `no-unused-vars` beside the preset's `typescript/no-unused-vars` is one rule too.
5676
5711
  */
5677
5712
  presetRuleCount(cwd) {
5678
5713
  const target = this.unresolvedPreset(cwd);
@@ -5693,7 +5728,7 @@ ${result.stderr ?? ""}`);
5693
5728
  readFileSync14(join17(cwd, entry), "utf8"),
5694
5729
  entry
5695
5730
  );
5696
- for (const rule of Object.keys(preset.rules ?? {})) names.add(rule);
5731
+ for (const rule of Object.keys(preset.rules ?? {})) names.add(canonicalRuleName(rule));
5697
5732
  } catch {
5698
5733
  continue;
5699
5734
  }
@@ -5940,11 +5940,682 @@ import { join as join30 } from "node:path";
5940
5940
  // src/roles/lint/module-baseline.ts
5941
5941
  import { existsSync as existsSync20, readFileSync as readFileSync22 } from "node:fs";
5942
5942
  import { join as join26 } from "node:path";
5943
+
5944
+ // src/roles/lint/oxlint-rule-owners.json
5945
+ var oxlint_rule_owners_default = {
5946
+ "adjacent-overload-signatures": "typescript",
5947
+ "alt-text": "jsx_a11y",
5948
+ "always-return": "promise",
5949
+ "anchor-ambiguous-text": "jsx_a11y",
5950
+ "anchor-has-content": "jsx_a11y",
5951
+ "anchor-is-valid": "jsx_a11y",
5952
+ "approx-constant": "oxc",
5953
+ "aria-activedescendant-has-tabindex": "jsx_a11y",
5954
+ "aria-props": "jsx_a11y",
5955
+ "aria-proptypes": "jsx_a11y",
5956
+ "aria-role": "jsx_a11y",
5957
+ "aria-unsupported-elements": "jsx_a11y",
5958
+ "array-type": "typescript",
5959
+ "autocomplete-valid": "jsx_a11y",
5960
+ "avoid-new": "promise",
5961
+ "await-thenable": "typescript",
5962
+ "bad-array-method-on-arguments": "oxc",
5963
+ "bad-bitwise-operator": "oxc",
5964
+ "bad-char-at-comparison": "oxc",
5965
+ "bad-comparison-sequence": "oxc",
5966
+ "bad-match-all-arg": "oxc",
5967
+ "bad-min-max-func": "oxc",
5968
+ "bad-object-literal-comparison": "oxc",
5969
+ "bad-replace-all-arg": "oxc",
5970
+ "ban-ts-comment": "typescript",
5971
+ "ban-tslint-comment": "typescript",
5972
+ "ban-types": "typescript",
5973
+ "branches-sharing-code": "oxc",
5974
+ "button-has-type": "react",
5975
+ "callback-return": "node",
5976
+ "catch-error-name": "unicorn",
5977
+ "catch-or-return": "promise",
5978
+ "check-access": "jsdoc",
5979
+ "check-property-names": "jsdoc",
5980
+ "check-tag-names": "jsdoc",
5981
+ "checked-requires-onchange-or-readonly": "react",
5982
+ "class-literal-property-style": "typescript",
5983
+ "click-events-have-key-events": "jsx_a11y",
5984
+ "component-definition-name-casing": "vue",
5985
+ "consistent-assert": "unicorn",
5986
+ "consistent-date-clone": "unicorn",
5987
+ "consistent-each-for": "vitest",
5988
+ "consistent-empty-array-spread": "unicorn",
5989
+ "consistent-existence-index-check": "unicorn",
5990
+ "consistent-function-scoping": "unicorn",
5991
+ "consistent-generic-constructors": "typescript",
5992
+ "consistent-indexed-object-style": "typescript",
5993
+ "consistent-return": "typescript",
5994
+ "consistent-template-literal-escape": "unicorn",
5995
+ "consistent-test-filename": "vitest",
5996
+ "consistent-test-it": "jest",
5997
+ "consistent-type-assertions": "typescript",
5998
+ "consistent-type-definitions": "typescript",
5999
+ "consistent-type-exports": "typescript",
6000
+ "consistent-type-imports": "typescript",
6001
+ "consistent-type-specifier-style": "import",
6002
+ "consistent-vitest-vi": "vitest",
6003
+ "const-comparisons": "oxc",
6004
+ "control-has-associated-label": "jsx_a11y",
6005
+ "custom-error-definition": "unicorn",
6006
+ default: "import",
6007
+ "define-emits-declaration": "vue",
6008
+ "define-props-declaration": "vue",
6009
+ "define-props-destructuring": "vue",
6010
+ "display-name": "react",
6011
+ "dot-notation": "typescript",
6012
+ "double-comparisons": "oxc",
6013
+ "empty-brace-spaces": "unicorn",
6014
+ "empty-tags": "jsdoc",
6015
+ "erasing-op": "oxc",
6016
+ "error-message": "unicorn",
6017
+ "escape-case": "unicorn",
6018
+ "exhaustive-deps": "react",
6019
+ "expect-expect": "jest",
6020
+ "explicit-function-return-type": "typescript",
6021
+ "explicit-length-check": "unicorn",
6022
+ "explicit-member-accessibility": "typescript",
6023
+ "explicit-module-boundary-types": "typescript",
6024
+ "explicit-timer-delay": "unicorn",
6025
+ export: "import",
6026
+ "exports-last": "import",
6027
+ "exports-style": "node",
6028
+ extensions: "import",
6029
+ "filename-case": "unicorn",
6030
+ first: "import",
6031
+ "forbid-component-props": "react",
6032
+ "forbid-dom-props": "react",
6033
+ "forbid-elements": "react",
6034
+ "forward-ref-uses-ref": "react",
6035
+ "function-component-definition": "react",
6036
+ "global-require": "node",
6037
+ "google-font-display": "nextjs",
6038
+ "google-font-preconnect": "nextjs",
6039
+ "group-exports": "import",
6040
+ "handle-callback-err": "node",
6041
+ "heading-has-content": "jsx_a11y",
6042
+ "hoisted-apis-on-top": "vitest",
6043
+ "hook-use-state": "react",
6044
+ "html-has-lang": "jsx_a11y",
6045
+ "iframe-has-title": "jsx_a11y",
6046
+ "iframe-missing-sandbox": "react",
6047
+ "img-redundant-alt": "jsx_a11y",
6048
+ "implements-on-classes": "jsdoc",
6049
+ "import-style": "unicorn",
6050
+ "inline-script-id": "nextjs",
6051
+ "interactive-supports-focus": "jsx_a11y",
6052
+ "jsx-boolean-value": "react",
6053
+ "jsx-curly-brace-presence": "react",
6054
+ "jsx-filename-extension": "react",
6055
+ "jsx-fragments": "react",
6056
+ "jsx-handler-names": "react",
6057
+ "jsx-key": "react",
6058
+ "jsx-max-depth": "react",
6059
+ "jsx-no-comment-textnodes": "react",
6060
+ "jsx-no-constructed-context-values": "react",
6061
+ "jsx-no-duplicate-props": "react",
6062
+ "jsx-no-jsx-as-prop": "react_perf",
6063
+ "jsx-no-literals": "react",
6064
+ "jsx-no-new-array-as-prop": "react_perf",
6065
+ "jsx-no-new-function-as-prop": "react_perf",
6066
+ "jsx-no-new-object-as-prop": "react_perf",
6067
+ "jsx-no-script-url": "react",
6068
+ "jsx-no-target-blank": "react",
6069
+ "jsx-no-undef": "react",
6070
+ "jsx-no-useless-fragment": "react",
6071
+ "jsx-pascal-case": "react",
6072
+ "jsx-props-no-spread-multi": "react",
6073
+ "jsx-props-no-spreading": "react",
6074
+ "label-has-associated-control": "jsx_a11y",
6075
+ lang: "jsx_a11y",
6076
+ "max-dependencies": "import",
6077
+ "max-expects": "jest",
6078
+ "max-nested-calls": "unicorn",
6079
+ "max-nested-describe": "jest",
6080
+ "max-props": "vue",
6081
+ "media-has-caption": "jsx_a11y",
6082
+ "method-signature-style": "typescript",
6083
+ "misrefactored-assign-op": "oxc",
6084
+ "missing-throw": "oxc",
6085
+ "mouse-events-have-key-events": "jsx_a11y",
6086
+ named: "import",
6087
+ namespace: "import",
6088
+ "new-for-builtins": "unicorn",
6089
+ "newline-after-import": "import",
6090
+ "next-script-for-ga": "nextjs",
6091
+ "next-tick-style": "vue",
6092
+ "no-absolute-path": "import",
6093
+ "no-abusive-eslint-disable": "unicorn",
6094
+ "no-access-key": "jsx_a11y",
6095
+ "no-accessor-recursion": "unicorn",
6096
+ "no-accumulating-spread": "oxc",
6097
+ "no-alias-methods": "jest",
6098
+ "no-amd": "import",
6099
+ "no-anonymous-default-export": "import",
6100
+ "no-aria-hidden-on-focusable": "jsx_a11y",
6101
+ "no-array-callback-reference": "unicorn",
6102
+ "no-array-delete": "typescript",
6103
+ "no-array-fill-with-reference-type": "unicorn",
6104
+ "no-array-for-each": "unicorn",
6105
+ "no-array-index-key": "react",
6106
+ "no-array-method-this-argument": "unicorn",
6107
+ "no-array-reduce": "unicorn",
6108
+ "no-array-reverse": "unicorn",
6109
+ "no-array-sort": "unicorn",
6110
+ "no-arrow-functions-in-watch": "vue",
6111
+ "no-assign-module-variable": "nextjs",
6112
+ "no-async-await": "oxc",
6113
+ "no-async-client-component": "nextjs",
6114
+ "no-async-endpoint-handlers": "oxc",
6115
+ "no-async-in-computed-properties": "vue",
6116
+ "no-autofocus": "jsx_a11y",
6117
+ "no-await-expression-member": "unicorn",
6118
+ "no-await-in-promise-methods": "unicorn",
6119
+ "no-barrel-file": "oxc",
6120
+ "no-base-to-string": "typescript",
6121
+ "no-before-interactive-script-outside-document": "nextjs",
6122
+ "no-callback-in-promise": "promise",
6123
+ "no-children-prop": "react",
6124
+ "no-clone-element": "react",
6125
+ "no-commented-out-tests": "jest",
6126
+ "no-commonjs": "import",
6127
+ "no-computed-properties-in-data": "vue",
6128
+ "no-conditional-expect": "jest",
6129
+ "no-conditional-in-test": "jest",
6130
+ "no-conditional-tests": "vitest",
6131
+ "no-confusing-array-with": "unicorn",
6132
+ "no-confusing-non-null-assertion": "typescript",
6133
+ "no-confusing-set-timeout": "jest",
6134
+ "no-confusing-void-expression": "typescript",
6135
+ "no-console-spaces": "unicorn",
6136
+ "no-const-enum": "oxc",
6137
+ "no-css-tags": "nextjs",
6138
+ "no-cycle": "import",
6139
+ "no-danger": "react",
6140
+ "no-danger-with-children": "react",
6141
+ "no-default-export": "import",
6142
+ "no-defaults": "jsdoc",
6143
+ "no-deprecated": "typescript",
6144
+ "no-deprecated-data-object-declaration": "vue",
6145
+ "no-deprecated-delete-set": "vue",
6146
+ "no-deprecated-destroyed-lifecycle": "vue",
6147
+ "no-deprecated-events-api": "vue",
6148
+ "no-deprecated-functions": "jest",
6149
+ "no-deprecated-model-definition": "vue",
6150
+ "no-deprecated-props-default-this": "vue",
6151
+ "no-deprecated-vue-config-keycodes": "vue",
6152
+ "no-did-mount-set-state": "react",
6153
+ "no-did-update-set-state": "react",
6154
+ "no-direct-mutation-state": "react",
6155
+ "no-disabled-tests": "jest",
6156
+ "no-distracting-elements": "jsx_a11y",
6157
+ "no-document-cookie": "unicorn",
6158
+ "no-document-import-in-page": "nextjs",
6159
+ "no-done-callback": "jest",
6160
+ "no-duplicate-enum-values": "typescript",
6161
+ "no-duplicate-head": "nextjs",
6162
+ "no-duplicate-hooks": "jest",
6163
+ "no-duplicate-type-constituents": "typescript",
6164
+ "no-duplicates": "import",
6165
+ "no-dynamic-delete": "typescript",
6166
+ "no-dynamic-require": "import",
6167
+ "no-empty-file": "unicorn",
6168
+ "no-empty-interface": "typescript",
6169
+ "no-empty-named-blocks": "import",
6170
+ "no-empty-object-type": "typescript",
6171
+ "no-explicit-any": "typescript",
6172
+ "no-export": "jest",
6173
+ "no-export-in-script-setup": "vue",
6174
+ "no-exports-assign": "node",
6175
+ "no-expose-after-await": "vue",
6176
+ "no-extra-non-null-assertion": "typescript",
6177
+ "no-extraneous-class": "typescript",
6178
+ "no-find-dom-node": "react",
6179
+ "no-floating-promises": "typescript",
6180
+ "no-focused-tests": "jest",
6181
+ "no-for-in-array": "typescript",
6182
+ "no-head-element": "nextjs",
6183
+ "no-head-import-in-document": "nextjs",
6184
+ "no-hex-escape": "unicorn",
6185
+ "no-hooks": "jest",
6186
+ "no-html-link-for-pages": "nextjs",
6187
+ "no-identical-title": "jest",
6188
+ "no-img-element": "nextjs",
6189
+ "no-immediate-mutation": "unicorn",
6190
+ "no-import-compiler-macros": "vue",
6191
+ "no-import-node-test": "vitest",
6192
+ "no-import-type-side-effects": "typescript",
6193
+ "no-importing-vitest-globals": "vitest",
6194
+ "no-inferrable-types": "typescript",
6195
+ "no-instanceof-array": "unicorn",
6196
+ "no-instanceof-builtins": "unicorn",
6197
+ "no-interactive-element-to-noninteractive-role": "jsx_a11y",
6198
+ "no-interpolation-in-snapshots": "jest",
6199
+ "no-invalid-fetch-options": "unicorn",
6200
+ "no-invalid-remove-event-listener": "unicorn",
6201
+ "no-invalid-void-type": "typescript",
6202
+ "no-is-mounted": "react",
6203
+ "no-jasmine-globals": "jest",
6204
+ "no-large-snapshots": "jest",
6205
+ "no-length-as-slice-end": "unicorn",
6206
+ "no-lifecycle-after-await": "vue",
6207
+ "no-magic-array-flat-depth": "unicorn",
6208
+ "no-map-spread": "oxc",
6209
+ "no-meaningless-void-operator": "typescript",
6210
+ "no-misused-new": "typescript",
6211
+ "no-misused-promises": "typescript",
6212
+ "no-misused-spread": "typescript",
6213
+ "no-mixed-enums": "typescript",
6214
+ "no-mixed-requires": "node",
6215
+ "no-mocks-import": "jest",
6216
+ "no-multi-comp": "react",
6217
+ "no-multiple-resolved": "promise",
6218
+ "no-multiple-slot-args": "vue",
6219
+ "no-mutable-exports": "import",
6220
+ "no-named-as-default": "import",
6221
+ "no-named-as-default-member": "import",
6222
+ "no-named-default": "import",
6223
+ "no-named-export": "import",
6224
+ "no-namespace": "import",
6225
+ "no-negation-in-equality-check": "unicorn",
6226
+ "no-nesting": "promise",
6227
+ "no-new-array": "unicorn",
6228
+ "no-new-buffer": "unicorn",
6229
+ "no-new-require": "node",
6230
+ "no-new-statics": "promise",
6231
+ "no-nodejs-modules": "import",
6232
+ "no-non-null-asserted-nullish-coalescing": "typescript",
6233
+ "no-non-null-asserted-optional-chain": "typescript",
6234
+ "no-non-null-assertion": "typescript",
6235
+ "no-noninteractive-element-interactions": "jsx_a11y",
6236
+ "no-noninteractive-element-to-interactive-role": "jsx_a11y",
6237
+ "no-noninteractive-tabindex": "jsx_a11y",
6238
+ "no-null": "unicorn",
6239
+ "no-object-as-default-parameter": "unicorn",
6240
+ "no-object-type-as-default-prop": "react",
6241
+ "no-optional-chaining": "oxc",
6242
+ "no-page-custom-font": "nextjs",
6243
+ "no-path-concat": "node",
6244
+ "no-process-env": "node",
6245
+ "no-process-exit": "unicorn",
6246
+ "no-promise-in-callback": "promise",
6247
+ "no-react-children": "react",
6248
+ "no-redundant-roles": "jsx_a11y",
6249
+ "no-redundant-should-component-update": "react",
6250
+ "no-redundant-type-constituents": "typescript",
6251
+ "no-relative-parent-imports": "import",
6252
+ "no-render-return-value": "react",
6253
+ "no-require-imports": "typescript",
6254
+ "no-required-prop-with-default": "vue",
6255
+ "no-reserved-component-names": "vue",
6256
+ "no-reserved-keys": "vue",
6257
+ "no-reserved-props": "vue",
6258
+ "no-rest-spread-properties": "oxc",
6259
+ "no-restricted-jest-methods": "jest",
6260
+ "no-restricted-matchers": "jest",
6261
+ "no-restricted-types": "typescript",
6262
+ "no-restricted-vi-methods": "vitest",
6263
+ "no-return-in-finally": "promise",
6264
+ "no-return-wrap": "promise",
6265
+ "no-script-component-in-head": "nextjs",
6266
+ "no-self-import": "import",
6267
+ "no-set-state": "react",
6268
+ "no-shared-component-data": "vue",
6269
+ "no-side-effects-in-computed-properties": "vue",
6270
+ "no-single-promise-in-promise-methods": "unicorn",
6271
+ "no-standalone-expect": "jest",
6272
+ "no-static-element-interactions": "jsx_a11y",
6273
+ "no-static-only-class": "unicorn",
6274
+ "no-string-refs": "react",
6275
+ "no-styled-jsx-in-document": "nextjs",
6276
+ "no-sync": "node",
6277
+ "no-sync-scripts": "nextjs",
6278
+ "no-test-prefixes": "jest",
6279
+ "no-test-return-statement": "jest",
6280
+ "no-thenable": "unicorn",
6281
+ "no-this-alias": "typescript",
6282
+ "no-this-assignment": "unicorn",
6283
+ "no-this-in-before-route-enter": "vue",
6284
+ "no-this-in-exported-function": "oxc",
6285
+ "no-this-in-sfc": "react",
6286
+ "no-title-in-document-head": "nextjs",
6287
+ "no-top-level-await": "node",
6288
+ "no-typeof-undefined": "unicorn",
6289
+ "no-typos": "nextjs",
6290
+ "no-unassigned-import": "import",
6291
+ "no-unescaped-entities": "react",
6292
+ "no-unknown-property": "react",
6293
+ "no-unnecessary-array-flat-depth": "unicorn",
6294
+ "no-unnecessary-array-splice-count": "unicorn",
6295
+ "no-unnecessary-await": "unicorn",
6296
+ "no-unnecessary-boolean-literal-compare": "typescript",
6297
+ "no-unnecessary-condition": "typescript",
6298
+ "no-unnecessary-parameter-property-assignment": "typescript",
6299
+ "no-unnecessary-qualifier": "typescript",
6300
+ "no-unnecessary-slice-end": "unicorn",
6301
+ "no-unnecessary-template-expression": "typescript",
6302
+ "no-unnecessary-type-arguments": "typescript",
6303
+ "no-unnecessary-type-assertion": "typescript",
6304
+ "no-unnecessary-type-constraint": "typescript",
6305
+ "no-unnecessary-type-conversion": "typescript",
6306
+ "no-unnecessary-type-parameters": "typescript",
6307
+ "no-unneeded-async-expect-function": "jest",
6308
+ "no-unreadable-array-destructuring": "unicorn",
6309
+ "no-unreadable-iife": "unicorn",
6310
+ "no-unsafe": "react",
6311
+ "no-unsafe-argument": "typescript",
6312
+ "no-unsafe-assignment": "typescript",
6313
+ "no-unsafe-call": "typescript",
6314
+ "no-unsafe-declaration-merging": "typescript",
6315
+ "no-unsafe-enum-comparison": "typescript",
6316
+ "no-unsafe-function-type": "typescript",
6317
+ "no-unsafe-member-access": "typescript",
6318
+ "no-unsafe-return": "typescript",
6319
+ "no-unsafe-type-assertion": "typescript",
6320
+ "no-unsafe-unary-minus": "typescript",
6321
+ "no-unstable-nested-components": "react",
6322
+ "no-untyped-mock-factory": "jest",
6323
+ "no-unwanted-polyfillio": "nextjs",
6324
+ "no-useless-collection-argument": "unicorn",
6325
+ "no-useless-default-assignment": "typescript",
6326
+ "no-useless-empty-export": "typescript",
6327
+ "no-useless-error-capture-stack-trace": "unicorn",
6328
+ "no-useless-fallback-in-spread": "unicorn",
6329
+ "no-useless-iterator-to-array": "unicorn",
6330
+ "no-useless-length-check": "unicorn",
6331
+ "no-useless-promise-resolve-reject": "unicorn",
6332
+ "no-useless-spread": "unicorn",
6333
+ "no-useless-switch-case": "unicorn",
6334
+ "no-useless-undefined": "unicorn",
6335
+ "no-var-requires": "typescript",
6336
+ "no-watch-after-await": "vue",
6337
+ "no-webpack-loader-syntax": "import",
6338
+ "no-will-update-set-state": "react",
6339
+ "no-wrapper-object-types": "typescript",
6340
+ "no-zero-fractions": "unicorn",
6341
+ "non-nullable-type-assertion-style": "typescript",
6342
+ "number-arg-out-of-range": "oxc",
6343
+ "number-literal-case": "unicorn",
6344
+ "numeric-separators-style": "unicorn",
6345
+ "only-export-components": "react",
6346
+ "only-throw-error": "typescript",
6347
+ "only-used-in-recursion": "oxc",
6348
+ "padding-around-after-all-blocks": "jest",
6349
+ "padding-around-test-blocks": "jest",
6350
+ "param-names": "promise",
6351
+ "parameter-properties": "typescript",
6352
+ "prefer-add-event-listener": "unicorn",
6353
+ "prefer-array-find": "unicorn",
6354
+ "prefer-array-flat": "unicorn",
6355
+ "prefer-array-flat-map": "unicorn",
6356
+ "prefer-array-index-of": "unicorn",
6357
+ "prefer-array-some": "unicorn",
6358
+ "prefer-as-const": "typescript",
6359
+ "prefer-at": "unicorn",
6360
+ "prefer-await-to-callbacks": "promise",
6361
+ "prefer-await-to-then": "promise",
6362
+ "prefer-bigint-literals": "unicorn",
6363
+ "prefer-blob-reading-methods": "unicorn",
6364
+ "prefer-called-exactly-once-with": "vitest",
6365
+ "prefer-called-once": "vitest",
6366
+ "prefer-called-times": "vitest",
6367
+ "prefer-called-with": "jest",
6368
+ "prefer-catch": "promise",
6369
+ "prefer-class-fields": "unicorn",
6370
+ "prefer-classlist-toggle": "unicorn",
6371
+ "prefer-code-point": "unicorn",
6372
+ "prefer-comparison-matcher": "jest",
6373
+ "prefer-date-now": "unicorn",
6374
+ "prefer-default-export": "import",
6375
+ "prefer-default-parameters": "unicorn",
6376
+ "prefer-describe-function-title": "vitest",
6377
+ "prefer-dom-node-append": "unicorn",
6378
+ "prefer-dom-node-dataset": "unicorn",
6379
+ "prefer-dom-node-remove": "unicorn",
6380
+ "prefer-dom-node-text-content": "unicorn",
6381
+ "prefer-each": "jest",
6382
+ "prefer-ending-with-an-expect": "jest",
6383
+ "prefer-enum-initializers": "typescript",
6384
+ "prefer-equality-matcher": "jest",
6385
+ "prefer-es6-class": "react",
6386
+ "prefer-event-target": "unicorn",
6387
+ "prefer-expect-assertions": "jest",
6388
+ "prefer-expect-resolves": "jest",
6389
+ "prefer-expect-type-of": "vitest",
6390
+ "prefer-export-from": "unicorn",
6391
+ "prefer-find": "typescript",
6392
+ "prefer-for-of": "typescript",
6393
+ "prefer-function-component": "react",
6394
+ "prefer-function-type": "typescript",
6395
+ "prefer-global-this": "unicorn",
6396
+ "prefer-hooks-in-order": "jest",
6397
+ "prefer-hooks-on-top": "jest",
6398
+ "prefer-import-from-vue": "vue",
6399
+ "prefer-import-in-mock": "vitest",
6400
+ "prefer-import-meta-properties": "unicorn",
6401
+ "prefer-importing-jest-globals": "jest",
6402
+ "prefer-importing-vitest-globals": "vitest",
6403
+ "prefer-includes": "typescript",
6404
+ "prefer-jest-mocked": "jest",
6405
+ "prefer-keyboard-event-key": "unicorn",
6406
+ "prefer-literal-enum-member": "typescript",
6407
+ "prefer-logical-operator-over-ternary": "unicorn",
6408
+ "prefer-lowercase-title": "jest",
6409
+ "prefer-math-min-max": "unicorn",
6410
+ "prefer-math-trunc": "unicorn",
6411
+ "prefer-mock-promise-shorthand": "jest",
6412
+ "prefer-mock-return-shorthand": "jest",
6413
+ "prefer-modern-dom-apis": "unicorn",
6414
+ "prefer-modern-math-apis": "unicorn",
6415
+ "prefer-module": "unicorn",
6416
+ "prefer-namespace-keyword": "typescript",
6417
+ "prefer-native-coercion-functions": "unicorn",
6418
+ "prefer-negative-index": "unicorn",
6419
+ "prefer-node-protocol": "unicorn",
6420
+ "prefer-nullish-coalescing": "typescript",
6421
+ "prefer-number-coercion": "unicorn",
6422
+ "prefer-number-properties": "unicorn",
6423
+ "prefer-object-from-entries": "unicorn",
6424
+ "prefer-optional-catch-binding": "unicorn",
6425
+ "prefer-optional-chain": "typescript",
6426
+ "prefer-prototype-methods": "unicorn",
6427
+ "prefer-query-selector": "unicorn",
6428
+ "prefer-readonly": "typescript",
6429
+ "prefer-readonly-parameter-types": "typescript",
6430
+ "prefer-reduce-type-parameter": "typescript",
6431
+ "prefer-reflect-apply": "unicorn",
6432
+ "prefer-regexp-exec": "typescript",
6433
+ "prefer-regexp-test": "unicorn",
6434
+ "prefer-response-static-json": "unicorn",
6435
+ "prefer-return-this-type": "typescript",
6436
+ "prefer-set-has": "unicorn",
6437
+ "prefer-set-size": "unicorn",
6438
+ "prefer-single-call": "unicorn",
6439
+ "prefer-snapshot-hint": "jest",
6440
+ "prefer-spy-on": "jest",
6441
+ "prefer-strict-boolean-matchers": "vitest",
6442
+ "prefer-strict-equal": "jest",
6443
+ "prefer-string-raw": "unicorn",
6444
+ "prefer-string-replace-all": "unicorn",
6445
+ "prefer-string-slice": "unicorn",
6446
+ "prefer-string-starts-ends-with": "typescript",
6447
+ "prefer-string-trim-start-end": "unicorn",
6448
+ "prefer-structured-clone": "unicorn",
6449
+ "prefer-tag-over-role": "jsx_a11y",
6450
+ "prefer-ternary": "unicorn",
6451
+ "prefer-to-be": "jest",
6452
+ "prefer-to-be-falsy": "vitest",
6453
+ "prefer-to-be-object": "vitest",
6454
+ "prefer-to-be-truthy": "vitest",
6455
+ "prefer-to-contain": "jest",
6456
+ "prefer-to-have-been-called": "jest",
6457
+ "prefer-to-have-been-called-times": "jest",
6458
+ "prefer-to-have-length": "jest",
6459
+ "prefer-todo": "jest",
6460
+ "prefer-top-level-await": "unicorn",
6461
+ "prefer-ts-expect-error": "typescript",
6462
+ "prefer-type-error": "unicorn",
6463
+ "promise-function-async": "typescript",
6464
+ "prop-name-casing": "vue",
6465
+ "react-compiler": "react",
6466
+ "react-in-jsx-scope": "react",
6467
+ "related-getter-setter-pairs": "typescript",
6468
+ "relative-url-style": "unicorn",
6469
+ "require-array-join-separator": "unicorn",
6470
+ "require-array-sort-compare": "typescript",
6471
+ "require-awaited-expect-poll": "vitest",
6472
+ "require-default-export": "vue",
6473
+ "require-default-prop": "vue",
6474
+ "require-direct-export": "vue",
6475
+ "require-hook": "jest",
6476
+ "require-local-test-context-for-concurrent-snapshots": "vitest",
6477
+ "require-mock-type-parameters": "vitest",
6478
+ "require-module-attributes": "unicorn",
6479
+ "require-module-specifiers": "unicorn",
6480
+ "require-number-to-fixed-digits-argument": "unicorn",
6481
+ "require-param": "jsdoc",
6482
+ "require-param-description": "jsdoc",
6483
+ "require-param-name": "jsdoc",
6484
+ "require-param-type": "jsdoc",
6485
+ "require-post-message-target-origin": "unicorn",
6486
+ "require-prop-type-constructor": "vue",
6487
+ "require-prop-types": "vue",
6488
+ "require-property": "jsdoc",
6489
+ "require-property-description": "jsdoc",
6490
+ "require-property-name": "jsdoc",
6491
+ "require-property-type": "jsdoc",
6492
+ "require-render-return": "react",
6493
+ "require-returns": "jsdoc",
6494
+ "require-returns-description": "jsdoc",
6495
+ "require-returns-type": "jsdoc",
6496
+ "require-slots-as-functions": "vue",
6497
+ "require-test-timeout": "vitest",
6498
+ "require-throws-description": "jsdoc",
6499
+ "require-throws-type": "jsdoc",
6500
+ "require-to-throw-message": "jest",
6501
+ "require-top-level-describe": "jest",
6502
+ "require-typed-ref": "vue",
6503
+ "require-yields": "jsdoc",
6504
+ "require-yields-description": "jsdoc",
6505
+ "require-yields-type": "jsdoc",
6506
+ "restrict-plus-operands": "typescript",
6507
+ "restrict-template-expressions": "typescript",
6508
+ "return-await": "typescript",
6509
+ "return-in-computed-property": "vue",
6510
+ "return-in-emits-validator": "vue",
6511
+ "role-has-required-aria-props": "jsx_a11y",
6512
+ "role-supports-aria-props": "jsx_a11y",
6513
+ "rules-of-hooks": "react",
6514
+ scope: "jsx_a11y",
6515
+ "self-closing-comp": "react",
6516
+ "spec-only": "promise",
6517
+ "state-in-constructor": "react",
6518
+ "strict-boolean-expressions": "typescript",
6519
+ "strict-void-return": "typescript",
6520
+ "style-prop-object": "react",
6521
+ "switch-case-braces": "unicorn",
6522
+ "switch-case-break-position": "unicorn",
6523
+ "switch-exhaustiveness-check": "typescript",
6524
+ "tabindex-no-positive": "jsx_a11y",
6525
+ "text-encoding-identifier-case": "unicorn",
6526
+ "throw-new-error": "unicorn",
6527
+ "triple-slash-reference": "typescript",
6528
+ unambiguous: "import",
6529
+ "unbound-method": "typescript",
6530
+ "unified-signatures": "typescript",
6531
+ "uninvoked-array-callback": "oxc",
6532
+ "use-unknown-in-catch-callback-variable": "typescript",
6533
+ "valid-define-emits": "vue",
6534
+ "valid-define-options": "vue",
6535
+ "valid-define-props": "vue",
6536
+ "valid-describe-callback": "jest",
6537
+ "valid-expect": "jest",
6538
+ "valid-expect-in-promise": "jest",
6539
+ "valid-next-tick": "vue",
6540
+ "valid-params": "promise",
6541
+ "valid-title": "jest",
6542
+ "void-dom-elements-no-children": "react",
6543
+ "warn-todo": "vitest"
6544
+ };
6545
+
6546
+ // src/roles/lint/rule-name.ts
6547
+ var PLUGIN_ALIASES = {
6548
+ "@typescript-eslint": "typescript",
6549
+ "import-x": "import",
6550
+ "jsx-a11y": "jsx_a11y",
6551
+ "jsx-a11y-x": "jsx_a11y",
6552
+ "jsx_a11y-x": "jsx_a11y",
6553
+ "react-perf": "react_perf",
6554
+ "@next": "nextjs",
6555
+ "@next/next": "nextjs",
6556
+ "react-hooks": "react",
6557
+ deepscan: "oxc"
6558
+ };
6559
+ var TYPESCRIPT_COMPATIBLE_ESLINT_RULES = /* @__PURE__ */ new Set([
6560
+ "class-methods-use-this",
6561
+ "default-param-last",
6562
+ "init-declarations",
6563
+ "max-params",
6564
+ "no-array-constructor",
6565
+ "no-dupe-class-members",
6566
+ "no-empty-function",
6567
+ "no-invalid-this",
6568
+ "no-loop-func",
6569
+ "no-loss-of-precision",
6570
+ "no-magic-numbers",
6571
+ "no-redeclare",
6572
+ "no-restricted-imports",
6573
+ "no-shadow",
6574
+ "no-unused-expressions",
6575
+ "no-unused-vars",
6576
+ "no-use-before-define",
6577
+ "no-useless-constructor"
6578
+ ]);
6579
+ var OWNERS = oxlint_rule_owners_default;
6580
+ var PLUGIN_PREFIXES = ["eslint-plugin", "oxlint-plugin"];
6581
+ function normalizePluginName(plugin) {
6582
+ const slash = plugin.indexOf("/");
6583
+ if (slash !== -1) {
6584
+ const scope = plugin.slice(0, slash);
6585
+ const rest = plugin.slice(slash + 1);
6586
+ for (const prefix of PLUGIN_PREFIXES) {
6587
+ if (rest === prefix) return scope;
6588
+ if (rest.startsWith(`${prefix}-`)) return `${scope}/${rest.slice(prefix.length + 1)}`;
6589
+ }
6590
+ }
6591
+ for (const prefix of PLUGIN_PREFIXES) {
6592
+ if (plugin.startsWith(`${prefix}-`)) return plugin.slice(prefix.length + 1);
6593
+ }
6594
+ return plugin;
6595
+ }
6596
+ function canonicalRuleName(key) {
6597
+ const at = key.startsWith("@") ? key.lastIndexOf("/") : key.indexOf("/");
6598
+ if (at === -1) return `${OWNERS[key] ?? "eslint"}/${key}`;
6599
+ const normalized = normalizePluginName(key.slice(0, at));
6600
+ const rule = key.slice(at + 1);
6601
+ const plugin = PLUGIN_ALIASES[normalized] ?? normalized;
6602
+ return plugin === "typescript" && TYPESCRIPT_COMPATIBLE_ESLINT_RULES.has(rule) ? `eslint/${rule}` : `${plugin}/${rule}`;
6603
+ }
6604
+ function describeRuleKey(key, presetSpelling) {
6605
+ return presetSpelling === void 0 || presetSpelling === key ? key : `${key} (oxlint runs it as ${presetSpelling})`;
6606
+ }
6607
+
6608
+ // src/roles/lint/module-baseline.ts
5943
6609
  var LINT_BASELINE_FILE = ".oxlintrc.baseline.json";
5944
6610
  var LINT_MEASURE_FILE = ".oxlintrc.measure.json";
5945
6611
  var LINT_BASELINE_SPECIFIER = `./${LINT_BASELINE_FILE}`;
5946
- function holdsFrom(errorCounts) {
5947
- return [...errorCounts].map(([rule, count]) => ({ rule, count })).sort((left, right) => right.count - left.count || left.rule.localeCompare(right.rule));
6612
+ function holdsFrom(errorCounts, presetSpellings = /* @__PURE__ */ new Map()) {
6613
+ const counts = /* @__PURE__ */ new Map();
6614
+ for (const [id, count] of errorCounts) {
6615
+ const rule = presetSpellings.get(canonicalRuleName(id)) ?? id;
6616
+ counts.set(rule, (counts.get(rule) ?? 0) + count);
6617
+ }
6618
+ return [...counts].map(([rule, count]) => ({ rule, count })).sort((left, right) => right.count - left.count || left.rule.localeCompare(right.rule));
5948
6619
  }
5949
6620
  function renderBaseline(holds, version) {
5950
6621
  const entries = holds.map(({ rule }) => ` ${JSON.stringify(rule)}: "warn"`).join(",\n");
@@ -6607,6 +7278,8 @@ export {
6607
7278
  rewriteToolchainImports,
6608
7279
  resolveOxfmt,
6609
7280
  oxfmtRejects,
7281
+ canonicalRuleName,
7282
+ describeRuleKey,
6610
7283
  LINT_BASELINE_FILE,
6611
7284
  LINT_MEASURE_FILE,
6612
7285
  LINT_BASELINE_SPECIFIER,
package/dist/index.js CHANGED
@@ -6,10 +6,10 @@ import {
6
6
  registerAdapters,
7
7
  resolve,
8
8
  setDefaultRunner
9
- } from "./chunk-64H5HBPH.js";
9
+ } from "./chunk-JHLNNIZC.js";
10
10
  import {
11
11
  BaseAdapter
12
- } from "./chunk-J2USE3KV.js";
12
+ } from "./chunk-P7NYSMLS.js";
13
13
  import "./chunk-JRZI5IIN.js";
14
14
  import "./chunk-WBDBRGLV.js";
15
15
  import "./chunk-O7REVMOC.js";
@@ -7,7 +7,7 @@ import {
7
7
  readTestAdoption,
8
8
  resolveVitest,
9
9
  suiteOf
10
- } from "./chunk-J2USE3KV.js";
10
+ } from "./chunk-P7NYSMLS.js";
11
11
  import "./chunk-JRZI5IIN.js";
12
12
  import "./chunk-O7REVMOC.js";
13
13
  import "./chunk-QXFCZON7.js";
@@ -215,6 +215,16 @@ Two limits, both the preset's governance:
215
215
  it and names it. That includes `no-console` and `no-restricted-imports` in the react preset. To
216
216
  set one of those for your module, put it in an `extends` layer after the preset (above): a
217
217
  layer is reviewed as yours and `--inspect` shows it.
218
+ - **"The same rule" is the rule oxlint runs, not the name you wrote** (since 1.5.1). Oxlint
219
+ accepts several names for one rule: `eslint/x` is `x`, `typescript/x` is the eslint rule `x`
220
+ for 18 rules that typescript-eslint extends (`no-restricted-imports`, `no-unused-vars`,
221
+ `no-unused-expressions`, ...), `@typescript-eslint/` is `typescript/`, `react-hooks/` is
222
+ `react/`, `jsx-a11y/` is `jsx_a11y/`, `import-x/` is `import/`, `eslint-plugin-` prefixes are
223
+ dropped, and a bare name belongs to the first built-in rule of that name. Measured on 1.77.0, a
224
+ react stub setting `typescript/no-restricted-imports` beside console's two rules linted with
225
+ its own patterns and none of the preset's, so `@nestjs/*` imports passed. A re-`init` now drops
226
+ an alias like the name it stands for, and says which preset rule it is:
227
+ `typescript/no-restricted-imports (oxlint runs it as no-restricted-imports)`.
218
228
  - **Your `overrides` blocks are kept as written**, with their `files`, and their rules go through
219
229
  the same filter. Two of your blocks with the same `files` are merged into one on re-`init`; a
220
230
  block is never merged into a preset override, which lives behind `extends` where the stub
@@ -283,7 +293,12 @@ things follow, and they are the point:
283
293
  - your own config stays `extends` + `ignorePatterns`, so a rule **you** change by hand is
284
294
  still reported as drift and is never confused with a hold sentinel made;
285
295
  - `--init` says what it held and how many violations each hold covers, so the debt is
286
- announced rather than discovered.
296
+ announced rather than discovered;
297
+ - a hold is written under the **preset's** name for the rule (since 1.5.1). Oxlint reports
298
+ `eslint(no-unused-expressions)` for a rule the nest preset configures as
299
+ `typescript/no-unused-expressions`, and a hold keyed `no-unused-expressions` did not hold:
300
+ measured on 1.77.0, the violation stayed an `error` on react and nest, because oxlint keeps the
301
+ two names side by side and picks one by hash order.
287
302
 
288
303
  Do not edit `.oxlintrc.baseline.json` by hand. Commit it: it is part of the adoption, and it
289
304
  is what makes the promise above true on the day you adopt rather than on the day the preset
@@ -137,7 +137,7 @@ majority of the migration, and only a sweep over every real module found it.
137
137
 
138
138
  ## The `--init` matrix for 1.5.1, 29 October 2026
139
139
 
140
- Six fixes, found by re-initializing the monorepo's 63 adopted modules with 1.5.1-alpha.0. Every
140
+ Six fixes, then a seventh (below) found by the alpha.1 validation. Found by re-initializing the monorepo's 63 adopted modules with 1.5.1-alpha.0. Every
141
141
  cell below is a test in this repo, against the built CLI unless it says unit, and every fix has a
142
142
  negative control: the fix reverted in the source, the cells run again, and the cells that must
143
143
  fail do. No existing cell changed: against the alpha, the test tree gains lines and loses none
@@ -240,6 +240,47 @@ Negative controls, one per block, each reverting one read-back: plugins 5 cells
240
240
  6, settings 5, overrides 6, extends 5, rules 6, sortImports 7, format `ignorePatterns` 1. Each
241
241
  fails its own cells plus the every-block cells, and nothing else.
242
242
 
243
+ ### 7. an alias of a governed rule is governed (`init-drops-governed-aliases`, unit `lint-governed-aliases`, `lint-rule-name`)
244
+
245
+ Oxlint 1.77.0 runs several names as one rule, and sentinel compared names. Proven first, outside
246
+ sentinel, react preset, a file importing `@nestjs/common` and one importing `lodash`:
247
+
248
+ | stub `rules` beside the preset | what fires |
249
+ | ------------------------------------------------------------------------------------- | ------------------------------ |
250
+ | five unrelated rules (control) | the preset's `@nestjs/*` ban |
251
+ | the same, plus `typescript/no-restricted-imports` with a lodash pattern | the lodash ban only |
252
+ | `typescript/no-restricted-imports` alone | the preset's ban only |
253
+ | `eslint/no-restricted-imports` alone | the lodash ban only, every run |
254
+ | baseline `no-unused-expressions: warn` over nest's `typescript/no-unused-expressions` | still an `error` |
255
+
256
+ The `typescript/` outcome moves with the rest of the rule set because oxlint keeps both names
257
+ through the `extends` merge and picks one by hash order; `eslint/` collapses with the canonical
258
+ name in the merge, where the stub wins. The canonical form is ported from oxc at
259
+ `oxlint_v1.77.0` (`parse_rule_key`, `normalize_plugin_name`, `unalias_plugin_name`,
260
+ `TYPESCRIPT_COMPATIBLE_ESLINT_RULES`), with the bare-name owners taken from `oxlint --rules -f json`.
261
+
262
+ | cell | result |
263
+ | ----------------------------------------------------------------------------------------- | ----------------------------------------------------- |
264
+ | re-init, react, console's rules + `typescript/no-restricted-imports` / `eslint/...` | alias dropped and named, the two team rules kept |
265
+ | the same, then a file importing `@nestjs/common`, `--run --lint` | fails on the preset's ban |
266
+ | control: the canonical `no-restricted-imports` | dropped, message unchanged |
267
+ | nest module violating `no-unused-expressions` at adoption | holds `typescript/no-unused-expressions`, lints green |
268
+ | team layer `no-unused-vars` over nest's `typescript/no-unused-vars`, `--inspect` | rule count unchanged |
269
+ | unit: `@typescript-eslint/`, bare `no-unused-vars`, `react/rules-of-hooks`, an override | dropped (override named with the preset rule) |
270
+ | unit: `typescript/require-await`, `unicorn/no-nested-ternary`, `typescript/no-namespace` | kept: different rules to oxlint too |
271
+ | every governed built-in rule of every preset, in every spelling, through `--print-config` | resolves where sentinel says |
272
+ | a preset rule set naming one rule twice | refused at build |
273
+
274
+ Negative control, `canonicalRuleName` made the identity: 31 of the 48 new cells fail, the 17 that
275
+ pass are the negative ones (the non-aliases, the preset-builds, the owners file, the canonical
276
+ control). With the source fix reverted (`git checkout` of the five files, new files kept), 6 of
277
+ the 7 e2e cells fail, the canonical control passes, and the `--run --lint` cell lints the
278
+ `@nestjs/common` import clean. No existing cell changed.
279
+
280
+ Checked in the monorepo at 7177eb86038: none of the 66 committed `.oxlintrc*.json` files and
281
+ team layers spells a governed rule under another name, so no adopted module carries the gap
282
+ today.
283
+
243
284
  ### against the consumer, the 63 adopted modules
244
285
 
245
286
  The packed branch, through a pnpm `file:` override, on a fresh worktree of the monorepo at
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hublo/sentinel",
3
- "version": "1.5.1-alpha.1",
3
+ "version": "1.5.1-alpha.2",
4
4
  "description": "One CLI that guards code health across Hublo repos: shared lint/typescript/build/test presets, static & dynamic analysis, and architecture checks.",
5
5
  "type": "module",
6
6
  "license": "MIT",