@sarj/eslint-plugin 15.28.1 → 16.0.0

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.
package/dist/index.cjs CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  ADVISORY_RULES: () => ADVISORY_RULES,
34
34
  APPLICATION_ONLY_RULES: () => APPLICATION_ONLY_RULES,
35
35
  RECOMMENDED_RULES: () => RECOMMENDED_RULES,
@@ -39,7 +39,7 @@ __export(index_exports, {
39
39
  STRICT_RULES: () => STRICT_RULES,
40
40
  advisoryRules: () => ADVISORY_RULES,
41
41
  applicationOnlyRules: () => APPLICATION_ONLY_RULES,
42
- default: () => index_default,
42
+ default: () => src_default,
43
43
  publicDocumentation: () => publicDocumentation,
44
44
  recommendedRules: () => RECOMMENDED_RULES,
45
45
  renamedRules: () => RENAMED_RULES,
@@ -47,7 +47,7 @@ __export(index_exports, {
47
47
  rules: () => RULES,
48
48
  strictRules: () => STRICT_RULES
49
49
  });
50
- module.exports = __toCommonJS(index_exports);
50
+ module.exports = __toCommonJS(src_exports);
51
51
 
52
52
  // src/rules/no-conditional-empty-object-spread.ts
53
53
  var import_utils2 = require("@typescript-eslint/utils");
@@ -7521,20 +7521,9 @@ var no_vague_suppression_description_default = createRule({
7521
7521
  }
7522
7522
  });
7523
7523
 
7524
- // src/rules/no-generic-single-export-module.ts
7524
+ // src/rules/_runtime-exports.ts
7525
7525
  var import_utils47 = require("@typescript-eslint/utils");
7526
- var NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION = {
7527
- summary: "Disallow generic module stems when one runtime export already names the responsibility.",
7528
- rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
7529
- remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
7530
- category: "maintainability",
7531
- limitations: ["Only the fixed generic-stem vocabulary with exactly one public runtime export is checked; exported destructuring patterns are excluded rather than undercounted."],
7532
- examples: [
7533
- { id: "responsibility-named-module", title: "Name the module after its export", outcome: "no-match", files: [{ path: "src/parse-order.ts", source: "export function parseOrder() { return {}; }" }], focusPath: "src/parse-order.ts", expectedCount: 0, public: true },
7534
- { id: "generic-module-name", title: "Do not hide one export in a generic module", outcome: "match", files: [{ path: "src/utils.ts", source: "export function parseOrder() { return {}; }" }], focusPath: "src/utils.ts", expectedCount: 1, public: true }
7535
- ]
7536
- };
7537
- var GENERIC_STEMS = /* @__PURE__ */ new Set([
7526
+ var GENERIC_MODULE_STEMS = /* @__PURE__ */ new Set([
7538
7527
  "common",
7539
7528
  "helper",
7540
7529
  "helpers",
@@ -7545,139 +7534,152 @@ var GENERIC_STEMS = /* @__PURE__ */ new Set([
7545
7534
  "utils"
7546
7535
  ]);
7547
7536
  var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
7548
- function fileParts(filename) {
7549
- const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
7550
- const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
7551
- const [stem4 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
7552
- return { stem: stem4, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
7553
- }
7554
- function declaredNames(declaration) {
7555
- if (declaration.declare === true) return [];
7556
- if (declaration.type === import_utils47.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.TSEnumDeclaration) {
7557
- if (declaration.type === import_utils47.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) return [];
7558
- return declaration.id === null ? [] : [declaration.id.name];
7559
- }
7560
- if (declaration.type === import_utils47.AST_NODE_TYPES.TSModuleDeclaration && declaration.id.type === import_utils47.AST_NODE_TYPES.Identifier) {
7561
- return [declaration.id.name];
7562
- }
7563
- if (declaration.type !== import_utils47.AST_NODE_TYPES.VariableDeclaration) return [];
7564
- return declaration.declarations.flatMap(
7565
- (item) => item.id.type === import_utils47.AST_NODE_TYPES.Identifier ? [item.id.name] : []
7566
- );
7567
- }
7568
7537
  function runtimeExports(program) {
7569
- const exports2 = [];
7570
- const typeBindings = typeOnlyBindings(program);
7538
+ const bindings = localBindings(program);
7539
+ const exports2 = /* @__PURE__ */ new Map();
7571
7540
  let ambiguous = false;
7572
- function collectRuntimeExport(statement) {
7573
- if (statement.type === import_utils47.AST_NODE_TYPES.ExportAllDeclaration) {
7574
- if (statement.exportKind !== "type") ambiguous = true;
7575
- return;
7576
- }
7577
- if (statement.type === import_utils47.AST_NODE_TYPES.ExportDefaultDeclaration) {
7578
- collectDefaultExport(statement);
7579
- return;
7580
- }
7581
- if (statement.type !== import_utils47.AST_NODE_TYPES.ExportNamedDeclaration || statement.exportKind === "type") return;
7541
+ function add(key, name, node, emission) {
7542
+ if (emission === "unknown") ambiguous = true;
7543
+ if (emission === "runtime") exports2.set(key, { key, name, node });
7544
+ }
7545
+ function collectDefault(statement) {
7546
+ const declaration = statement.declaration;
7547
+ if (declaration.type === import_utils47.AST_NODE_TYPES.Identifier) {
7548
+ add("default", declaration.name, statement, bindings.get(declaration.name) ?? "unknown");
7549
+ } else if ("id" in declaration && declaration.id?.type === import_utils47.AST_NODE_TYPES.Identifier) {
7550
+ add("default", declaration.id.name, declaration, declarationEmission(declaration));
7551
+ } else ambiguous = true;
7552
+ }
7553
+ function collectNamed(statement) {
7554
+ if (statement.exportKind === "type") return;
7582
7555
  if (statement.source !== null) {
7583
7556
  if (statement.specifiers.some((specifier) => specifier.exportKind !== "type")) ambiguous = true;
7584
7557
  return;
7585
7558
  }
7586
- if (statement.declaration !== null) {
7587
- const declaration = statement.declaration;
7559
+ const declaration = statement.declaration;
7560
+ if (declaration !== null) {
7588
7561
  if (declaration.type === import_utils47.AST_NODE_TYPES.VariableDeclaration && declaration.declarations.some((item) => item.id.type !== import_utils47.AST_NODE_TYPES.Identifier)) ambiguous = true;
7589
- exports2.push(...declaredNames(declaration).map((name) => ({ key: name, name, node: declaration })));
7562
+ const names = declarationNames(declaration);
7563
+ if (names.length === 0 && declarationEmission(declaration) !== "erased") ambiguous = true;
7564
+ for (const name of names) add(name, name, declaration, bindings.get(name) ?? "unknown");
7590
7565
  }
7591
- collectExportSpecifiers(statement);
7592
- }
7593
- function collectExportSpecifiers(statement) {
7594
7566
  for (const specifier of statement.specifiers) {
7595
- if (specifier.exportKind === "type" || typeBindings.has(specifier.local.name)) continue;
7596
- const exported = specifier.exported.type === import_utils47.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value;
7567
+ if (specifier.exportKind === "type") continue;
7568
+ const key = specifier.exported.type === import_utils47.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value;
7597
7569
  const local = specifier.local.name;
7598
- exports2.push({ key: exported, name: exported === "default" ? local : exported, node: specifier });
7570
+ add(key, key === "default" ? local : key, specifier, bindings.get(local) ?? "unknown");
7599
7571
  }
7600
7572
  }
7601
- function collectDefaultExport(statement) {
7602
- const declaration = statement.declaration;
7603
- if (declaration.type === import_utils47.AST_NODE_TYPES.Identifier) {
7604
- if (!typeBindings.has(declaration.name)) {
7605
- exports2.push({ key: "default", name: declaration.name, node: statement });
7606
- }
7607
- } else if ((declaration.type === import_utils47.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.ClassDeclaration) && declaration.id !== null && declaration.declare !== true) exports2.push({ key: "default", name: declaration.id.name, node: declaration });
7608
- else if (declaration.type !== import_utils47.AST_NODE_TYPES.TSInterfaceDeclaration && declaration.type !== import_utils47.AST_NODE_TYPES.TSTypeAliasDeclaration) ambiguous = true;
7609
- }
7610
7573
  for (const statement of program.body) {
7611
- collectRuntimeExport(statement);
7574
+ if (statement.type === import_utils47.AST_NODE_TYPES.ExportAllDeclaration && statement.exportKind !== "type") ambiguous = true;
7575
+ if (statement.type === import_utils47.AST_NODE_TYPES.TSExportAssignment) ambiguous = true;
7576
+ if (statement.type === import_utils47.AST_NODE_TYPES.ExportDefaultDeclaration) collectDefault(statement);
7577
+ if (statement.type === import_utils47.AST_NODE_TYPES.ExportNamedDeclaration) collectNamed(statement);
7612
7578
  }
7613
- const unique = new Map(exports2.map((entry) => [entry.key, entry]));
7614
- return { exports: [...unique.values()], ambiguous };
7579
+ return { exports: [...exports2.values()], ambiguous };
7615
7580
  }
7616
- function typeOnlyBindings(program) {
7617
- const names = /* @__PURE__ */ new Set();
7618
- const runtimeNames = /* @__PURE__ */ new Set();
7619
- function collectTypeBinding(statement) {
7620
- const declaration = statement.type === import_utils47.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
7621
- if (declaration?.type === import_utils47.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils47.AST_NODE_TYPES.TSTypeAliasDeclaration) {
7622
- names.add(declaration.id.name);
7623
- return;
7624
- }
7625
- if (declaration?.type === import_utils47.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) {
7626
- names.add(declaration.id.name);
7627
- return;
7628
- }
7629
- if (statement.type === import_utils47.AST_NODE_TYPES.ImportDeclaration) {
7630
- collectImportBindings(statement);
7631
- return;
7632
- }
7633
- if (declaration?.type === import_utils47.AST_NODE_TYPES.TSDeclareFunction && declaration.id !== null) {
7634
- names.add(declaration.id.name);
7635
- return;
7636
- }
7637
- if (declaration !== null && declaration.declare === true) {
7638
- collectDeclaredNames(declaration);
7639
- return;
7640
- }
7641
- if (declaration !== null && "type" in declaration) {
7642
- for (const name of declaredNames(declaration)) {
7643
- runtimeNames.add(name);
7644
- }
7645
- }
7581
+ function localBindings(program) {
7582
+ const bindings = /* @__PURE__ */ new Map();
7583
+ function register(name, emission) {
7584
+ const previous = bindings.get(name);
7585
+ if (previous === "runtime" || emission === "runtime") bindings.set(name, "runtime");
7586
+ else if (previous === "unknown" || emission === "unknown") bindings.set(name, "unknown");
7587
+ else bindings.set(name, "erased");
7646
7588
  }
7647
- function collectImportBindings(statement) {
7648
- for (const specifier of statement.specifiers) {
7649
- if (statement.importKind === "type" || specifier.type === import_utils47.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type") {
7650
- names.add(specifier.local.name);
7651
- } else {
7652
- runtimeNames.add(specifier.local.name);
7589
+ for (const statement of program.body) {
7590
+ if (statement.type === import_utils47.AST_NODE_TYPES.ImportDeclaration) {
7591
+ for (const specifier of statement.specifiers) {
7592
+ const typeOnly = statement.importKind === "type" || specifier.type === import_utils47.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type";
7593
+ register(specifier.local.name, typeOnly ? "erased" : "unknown");
7653
7594
  }
7595
+ continue;
7654
7596
  }
7597
+ const declaration = statement.type === import_utils47.AST_NODE_TYPES.ExportNamedDeclaration || statement.type === import_utils47.AST_NODE_TYPES.ExportDefaultDeclaration ? statement.declaration : statement;
7598
+ if (declaration === null) continue;
7599
+ for (const name of declarationNames(declaration)) register(name, declarationEmission(declaration));
7655
7600
  }
7656
- function collectDeclaredNames(declaration) {
7657
- if ((declaration.type === import_utils47.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.TSEnumDeclaration || declaration.type === import_utils47.AST_NODE_TYPES.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === import_utils47.AST_NODE_TYPES.Identifier) names.add(declaration.id.name);
7658
- if (declaration.type === import_utils47.AST_NODE_TYPES.VariableDeclaration) {
7659
- for (const item of declaration.declarations) {
7660
- if (item.id.type === import_utils47.AST_NODE_TYPES.Identifier) names.add(item.id.name);
7661
- }
7662
- }
7601
+ return bindings;
7602
+ }
7603
+ function declarationNames(declaration) {
7604
+ if ("id" in declaration && declaration.id !== null) {
7605
+ let id = declaration.id;
7606
+ while (id.type === import_utils47.AST_NODE_TYPES.TSQualifiedName) id = id.left;
7607
+ if (id.type === import_utils47.AST_NODE_TYPES.Identifier) return [id.name];
7663
7608
  }
7664
- for (const statement of program.body) {
7665
- collectTypeBinding(statement);
7609
+ if (declaration.type !== import_utils47.AST_NODE_TYPES.VariableDeclaration) return [];
7610
+ return declaration.declarations.flatMap((item) => item.id.type === import_utils47.AST_NODE_TYPES.Identifier ? [item.id.name] : []);
7611
+ }
7612
+ function declarationEmission(declaration) {
7613
+ if ("declare" in declaration && declaration.declare === true) return "erased";
7614
+ switch (declaration.type) {
7615
+ case import_utils47.AST_NODE_TYPES.TSInterfaceDeclaration:
7616
+ case import_utils47.AST_NODE_TYPES.TSTypeAliasDeclaration:
7617
+ case import_utils47.AST_NODE_TYPES.TSDeclareFunction:
7618
+ return "erased";
7619
+ case import_utils47.AST_NODE_TYPES.ClassDeclaration:
7620
+ case import_utils47.AST_NODE_TYPES.FunctionDeclaration:
7621
+ case import_utils47.AST_NODE_TYPES.VariableDeclaration:
7622
+ return "runtime";
7623
+ case import_utils47.AST_NODE_TYPES.TSEnumDeclaration:
7624
+ return declaration.const ? "unknown" : "runtime";
7625
+ case import_utils47.AST_NODE_TYPES.TSModuleDeclaration:
7626
+ return namespaceEmission(declaration);
7627
+ default:
7628
+ return "unknown";
7666
7629
  }
7667
- return new Set([...names].filter((name) => !runtimeNames.has(name)));
7668
7630
  }
7669
- function isGlobalIdentifier2(context, node) {
7670
- const variable = import_utils47.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
7671
- return variable === null || variable.defs.length === 0;
7631
+ function namespaceEmission(declaration) {
7632
+ const { body: body2 } = declaration;
7633
+ if (body2 === void 0) return "unknown";
7634
+ if (body2.body.length === 0) return "unknown";
7635
+ let ambiguous = false;
7636
+ for (const statement of body2.body) {
7637
+ if (statement.type === import_utils47.AST_NODE_TYPES.ExportNamedDeclaration && statement.exportKind === "type") continue;
7638
+ const inner = statement.type === import_utils47.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
7639
+ const emission = inner === null ? "unknown" : declarationEmission(inner);
7640
+ if (emission === "runtime") return "runtime";
7641
+ if (emission === "unknown") ambiguous = true;
7642
+ }
7643
+ return ambiguous ? "unknown" : "erased";
7672
7644
  }
7673
- function isConventionalFrameworkUtility(filename, exported) {
7674
- const normalized = filename.replaceAll("\\", "/");
7675
- return exported === "cn" && /(?:^|\/)lib\/utils\.[cm]?[jt]sx?$/u.test(normalized);
7645
+ function isCommonJsExportReference(node, sourceCode) {
7646
+ function isGlobal(node2) {
7647
+ const variable = import_utils47.ASTUtils.findVariable(sourceCode.getScope(node2), node2.name);
7648
+ return variable === null || variable.defs.length === 0;
7649
+ }
7650
+ if (node.type === import_utils47.AST_NODE_TYPES.MemberExpression) {
7651
+ return node.object.type === import_utils47.AST_NODE_TYPES.Identifier && isGlobal(node.object) && (node.object.name === "exports" || node.object.name === "module" && memberPropertyName(node) === "exports");
7652
+ }
7653
+ const first = node.arguments[0];
7654
+ return first?.type === import_utils47.AST_NODE_TYPES.Identifier && first.name === "exports" && isGlobal(first) && node.callee.type === import_utils47.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils47.AST_NODE_TYPES.Identifier && node.callee.object.name === "Object" && isGlobal(node.callee.object) && CJS_OBJECT_EXPORT_METHODS.has(memberPropertyName(node.callee) ?? "");
7676
7655
  }
7677
7656
  function memberPropertyName(node) {
7678
7657
  if (!node.computed && node.property.type === import_utils47.AST_NODE_TYPES.Identifier) return node.property.name;
7679
7658
  return node.computed && node.property.type === import_utils47.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
7680
7659
  }
7660
+
7661
+ // src/rules/no-generic-single-export-module.ts
7662
+ var NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION = {
7663
+ summary: "Disallow generic module stems when one runtime export already names the responsibility.",
7664
+ rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
7665
+ remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
7666
+ category: "maintainability",
7667
+ limitations: ["Only the fixed generic-stem vocabulary with exactly one public runtime export is checked; exported destructuring patterns are excluded rather than undercounted."],
7668
+ examples: [
7669
+ { id: "responsibility-named-module", title: "Name the module after its export", outcome: "no-match", files: [{ path: "src/parse-order.ts", source: "export function parseOrder() { return {}; }" }], focusPath: "src/parse-order.ts", expectedCount: 0, public: true },
7670
+ { id: "generic-module-name", title: "Do not hide one export in a generic module", outcome: "match", files: [{ path: "src/utils.ts", source: "export function parseOrder() { return {}; }" }], focusPath: "src/utils.ts", expectedCount: 1, public: true }
7671
+ ]
7672
+ };
7673
+ function fileParts(filename) {
7674
+ const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
7675
+ const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
7676
+ const [stem4 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
7677
+ return { stem: stem4, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
7678
+ }
7679
+ function isConventionalFrameworkUtility(filename, exported) {
7680
+ const normalized = filename.replaceAll("\\", "/");
7681
+ return exported === "cn" && /(?:^|\/)lib\/utils\.[cm]?[jt]sx?$/u.test(normalized);
7682
+ }
7681
7683
  var no_generic_single_export_module_default = createRule({
7682
7684
  name: "no-generic-single-export-module",
7683
7685
  documentation: NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION,
@@ -7693,16 +7695,11 @@ var no_generic_single_export_module_default = createRule({
7693
7695
  create(context) {
7694
7696
  const file = fileParts(context.filename);
7695
7697
  const { stem: stem4 } = file;
7696
- if (!GENERIC_STEMS.has(stem4) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
7698
+ if (!GENERIC_MODULE_STEMS.has(stem4) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
7697
7699
  let hasCommonJsExport = false;
7698
7700
  return {
7699
- CallExpression(node) {
7700
- const first = node.arguments[0];
7701
- if (first?.type === import_utils47.AST_NODE_TYPES.Identifier && first.name === "exports" && isGlobalIdentifier2(context, first) && node.callee.type === import_utils47.AST_NODE_TYPES.MemberExpression && node.callee.object.type === import_utils47.AST_NODE_TYPES.Identifier && node.callee.object.name === "Object" && isGlobalIdentifier2(context, node.callee.object) && memberPropertyName(node.callee) !== null && CJS_OBJECT_EXPORT_METHODS.has(memberPropertyName(node.callee))) hasCommonJsExport = true;
7702
- },
7703
- MemberExpression(node) {
7704
- if (node.object.type === import_utils47.AST_NODE_TYPES.Identifier && node.object.name === "exports" && isGlobalIdentifier2(context, node.object)) hasCommonJsExport = true;
7705
- if (node.object.type === import_utils47.AST_NODE_TYPES.Identifier && node.object.name === "module" && isGlobalIdentifier2(context, node.object) && memberPropertyName(node) === "exports") hasCommonJsExport = true;
7701
+ "CallExpression, MemberExpression"(node) {
7702
+ if (isCommonJsExportReference(node, context.sourceCode)) hasCommonJsExport = true;
7706
7703
  },
7707
7704
  "Program:exit"(program) {
7708
7705
  if (hasCommonJsExport) return;
@@ -9243,19 +9240,19 @@ var no_restated_jsdoc_default = createRule({
9243
9240
  function enclosingDeclarationNames(node) {
9244
9241
  let declaration = null;
9245
9242
  while (node != null && node.type !== import_utils57.AST_NODE_TYPES.Program) {
9246
- declaration = declarationNames(node);
9243
+ declaration = declarationNames2(node);
9247
9244
  if (declaration !== null) break;
9248
9245
  node = node.parent ?? null;
9249
9246
  }
9250
9247
  return declaration;
9251
9248
  }
9252
- function declarationNames(node) {
9249
+ function declarationNames2(node) {
9253
9250
  switch (node.type) {
9254
9251
  // `export function f()` — the JSDoc sits above the `export`, so the token
9255
9252
  // after it resolves to the wrapper, not to the thing being documented.
9256
9253
  case import_utils57.AST_NODE_TYPES.ExportNamedDeclaration:
9257
9254
  case import_utils57.AST_NODE_TYPES.ExportDefaultDeclaration:
9258
- return node.declaration == null ? null : declarationNames(node.declaration);
9255
+ return node.declaration == null ? null : declarationNames2(node.declaration);
9259
9256
  case import_utils57.AST_NODE_TYPES.FunctionDeclaration:
9260
9257
  case import_utils57.AST_NODE_TYPES.TSDeclareFunction:
9261
9258
  return node.id === null ? null : { name: node.id.name, params: paramNames(node.params) };
@@ -21943,9 +21940,9 @@ var STEPDOWN_DOCUMENTATION = {
21943
21940
  remediation: "Consider moving the private helper below its sole caller after reviewing initialization and reflection dependencies.",
21944
21941
  category: "maintainability",
21945
21942
  limitations: [
21946
- "Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
21943
+ "Generated and test files, cycles, dynamic or escaped references, overload targets, and helpers with multiple callers are excluded; immutable local callable aliases are followed only when every use stays in the caller.",
21947
21944
  "Class helpers must be private; their sole caller may be public, protected, or private.",
21948
- "Runtime class-field, static-block, computed-member, and decorator barriers are never crossed.",
21945
+ "Runtime class-field, static-block, computed-member, and decorator barriers are never crossed; non-hoisted module helpers also cannot cross eager execution or unrelated initialization.",
21949
21946
  "This rule is report-only: reordering methods can change reflective property order, and moving module declarations can change initialization behavior or introduce temporal-dead-zone failures. Review module cycles and eager callers manually."
21950
21947
  ],
21951
21948
  examples: [
@@ -21957,7 +21954,7 @@ function isFunction(node) {
21957
21954
  return node.type === import_utils119.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils119.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils119.AST_NODE_TYPES.FunctionExpression;
21958
21955
  }
21959
21956
  function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
21960
- const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
21957
+ const byName = new Map(scopeDefinitions.map((definition) => [definition.key, definition]));
21961
21958
  const cycles = cycleComponents(calls);
21962
21959
  const callers = /* @__PURE__ */ new Map();
21963
21960
  for (const [caller, callees] of calls) {
@@ -21969,16 +21966,16 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned,
21969
21966
  }
21970
21967
  }
21971
21968
  for (const helper of candidates) {
21972
- const helperCallers = [...callers.get(helper.name) ?? []];
21973
- if (pinned.has(helper.name) || helperCallers.length !== 1) continue;
21969
+ const helperCallers = [...callers.get(helper.key) ?? []];
21970
+ if (pinned.has(helper.key) || helperCallers.length !== 1) continue;
21974
21971
  const callerName2 = helperCallers[0];
21975
- if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
21972
+ if (callerName2 === void 0 || cycles.has(helper.key) && cycles.get(helper.key) === cycles.get(callerName2)) continue;
21976
21973
  const caller = byName.get(callerName2);
21977
21974
  if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
21978
21975
  context.report({
21979
21976
  node: helper.node,
21980
21977
  messageId: "helperAboveOnlyCaller",
21981
- data: { helper: helper.name, caller: callerName2 }
21978
+ data: { helper: helper.name, caller: caller.name }
21982
21979
  });
21983
21980
  }
21984
21981
  }
@@ -22049,6 +22046,43 @@ function cycleComponents(graph) {
22049
22046
  }
22050
22047
  return components;
22051
22048
  }
22049
+ function isErasedExpression(node) {
22050
+ return node.type === import_utils119.AST_NODE_TYPES.TSAsExpression || node.type === import_utils119.AST_NODE_TYPES.TSTypeAssertion || node.type === import_utils119.AST_NODE_TYPES.TSSatisfiesExpression || node.type === import_utils119.AST_NODE_TYPES.TSNonNullExpression || node.type === import_utils119.AST_NODE_TYPES.TSInstantiationExpression;
22051
+ }
22052
+ function unwrapExpression7(node) {
22053
+ let current = node;
22054
+ while (isErasedExpression(current)) current = current.expression;
22055
+ return current;
22056
+ }
22057
+ function wrappedExpression(node) {
22058
+ let current = node;
22059
+ while (current.parent !== void 0 && isErasedExpression(current.parent) && current.parent.expression === current) current = current.parent;
22060
+ return current;
22061
+ }
22062
+ function enclosingFunction5(context, node) {
22063
+ return [...context.sourceCode.getAncestors(node)].reverse().find(isFunction);
22064
+ }
22065
+ function localCallCount(context, node, owner) {
22066
+ const seen = /* @__PURE__ */ new Set();
22067
+ const pending = [node];
22068
+ let count = 0;
22069
+ while (pending.length > 0) {
22070
+ const current = pending.pop();
22071
+ if (current === void 0 || enclosingFunction5(context, current) !== owner) return null;
22072
+ const expression = wrappedExpression(current);
22073
+ const parent = expression.parent;
22074
+ if (parent?.type === import_utils119.AST_NODE_TYPES.CallExpression && parent.callee === expression) {
22075
+ count += 1;
22076
+ continue;
22077
+ }
22078
+ if (parent?.type !== import_utils119.AST_NODE_TYPES.VariableDeclarator || parent.init !== expression || parent.id.type !== import_utils119.AST_NODE_TYPES.Identifier || parent.parent.type !== import_utils119.AST_NODE_TYPES.VariableDeclaration || parent.parent.kind !== "const") return null;
22079
+ const variable = context.sourceCode.getDeclaredVariables(parent)[0];
22080
+ if (variable === void 0 || seen.has(variable) || !stableVariable(variable)) return null;
22081
+ seen.add(variable);
22082
+ pending.push(...variable.references.filter((reference) => reference.isRead()).map((reference) => reference.identifier));
22083
+ }
22084
+ return count;
22085
+ }
22052
22086
  function moduleScope(context, program) {
22053
22087
  const declarations = moduleDefinitions(program);
22054
22088
  const counts = /* @__PURE__ */ new Map();
@@ -22064,6 +22098,26 @@ function moduleScope(context, program) {
22064
22098
  const definitions = scopeDefinitions.filter(
22065
22099
  (definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
22066
22100
  );
22101
+ const { calls, pinned } = moduleCallGraph(context, scopeDefinitions);
22102
+ const statementIndexes = new Map(program.body.map((statement, index) => [statement, index]));
22103
+ const runtimeBarrierPrefix = [0];
22104
+ for (const statement of program.body) {
22105
+ runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isDeferredModuleStatement(statement) ? 0 : 1));
22106
+ }
22107
+ const statementIndex = (definition) => {
22108
+ let current = definition.node;
22109
+ while (current.parent !== void 0 && current.parent.type !== import_utils119.AST_NODE_TYPES.Program) current = current.parent;
22110
+ return statementIndexes.get(current);
22111
+ };
22112
+ const canMove = (helper, caller) => {
22113
+ if (helper.node.type === import_utils119.AST_NODE_TYPES.FunctionDeclaration) return true;
22114
+ const helperIndex = statementIndex(helper);
22115
+ const callerIndex = statementIndex(caller);
22116
+ return helperIndex !== void 0 && callerIndex !== void 0 && runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
22117
+ };
22118
+ reportMisordered(context, definitions, scopeDefinitions, calls, pinned, canMove);
22119
+ }
22120
+ function moduleCallGraph(context, scopeDefinitions) {
22067
22121
  const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
22068
22122
  const calls = /* @__PURE__ */ new Map();
22069
22123
  const pinned = /* @__PURE__ */ new Set();
@@ -22071,26 +22125,33 @@ function moduleScope(context, program) {
22071
22125
  const variable = context.sourceCode.getDeclaredVariables(definition.bindingNode)[0];
22072
22126
  for (const reference of variable?.references ?? []) {
22073
22127
  if (reference.isWrite() && reference.init !== true) {
22074
- pinned.add(definition.name);
22128
+ pinned.add(definition.key);
22075
22129
  continue;
22076
22130
  }
22077
22131
  if (!reference.isRead()) continue;
22078
- const identifier = reference.identifier;
22079
- const ancestors = context.sourceCode.getAncestors(identifier);
22080
- const nearestFunction2 = [...ancestors].reverse().find(isFunction);
22081
- const parent = identifier.parent;
22082
- const callerDefinition = nearestFunction2 === void 0 ? void 0 : byFunction.get(nearestFunction2);
22083
- if (callerDefinition === void 0 || parent.type !== import_utils119.AST_NODE_TYPES.CallExpression || parent.callee !== identifier) {
22084
- pinned.add(definition.name);
22132
+ const callerDefinition = byFunction.get(enclosingFunction5(context, reference.identifier));
22133
+ if (callerDefinition?.functionNode === void 0) {
22134
+ pinned.add(definition.key);
22085
22135
  continue;
22086
22136
  }
22087
- const caller = callerDefinition.name;
22088
- const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22089
- callees.add(definition.name);
22090
- calls.set(caller, callees);
22137
+ const count = localCallCount(context, reference.identifier, callerDefinition.functionNode);
22138
+ if (count === null) {
22139
+ pinned.add(definition.key);
22140
+ } else if (count > 0) {
22141
+ const callees = calls.get(callerDefinition.key) ?? /* @__PURE__ */ new Set();
22142
+ callees.add(definition.key);
22143
+ calls.set(callerDefinition.key, callees);
22144
+ }
22091
22145
  }
22092
22146
  }
22093
- reportMisordered(context, definitions, scopeDefinitions, calls, pinned);
22147
+ return { calls, pinned };
22148
+ }
22149
+ function isDeferredModuleStatement(statement) {
22150
+ const node = statement.type === import_utils119.AST_NODE_TYPES.ExportNamedDeclaration || statement.type === import_utils119.AST_NODE_TYPES.ExportDefaultDeclaration ? statement.declaration : statement;
22151
+ if (node === null || node.type === import_utils119.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils119.AST_NODE_TYPES.TSDeclareFunction || node.type === import_utils119.AST_NODE_TYPES.TSInterfaceDeclaration || node.type === import_utils119.AST_NODE_TYPES.TSTypeAliasDeclaration || node.type === import_utils119.AST_NODE_TYPES.ImportDeclaration || node.type === import_utils119.AST_NODE_TYPES.ExportAllDeclaration || node.type === import_utils119.AST_NODE_TYPES.EmptyStatement) return true;
22152
+ return node.type === import_utils119.AST_NODE_TYPES.VariableDeclaration && node.declarations.every(
22153
+ (declaration) => declaration.id.type === import_utils119.AST_NODE_TYPES.Identifier && declaration.init !== null && isFunction(unwrapExpression7(declaration.init))
22154
+ );
22094
22155
  }
22095
22156
  function exportedNames(program) {
22096
22157
  const names = /* @__PURE__ */ new Set();
@@ -22124,16 +22185,18 @@ function moduleDefinitions(program) {
22124
22185
  for (const statement of program.body) {
22125
22186
  const node = statement.type === import_utils119.AST_NODE_TYPES.ExportNamedDeclaration || statement.type === import_utils119.AST_NODE_TYPES.ExportDefaultDeclaration ? statement.declaration : statement;
22126
22187
  if (node?.type === import_utils119.AST_NODE_TYPES.FunctionDeclaration && node.id !== null && node.body !== null) {
22127
- definitions.push({ name: node.id.name, node, functionNode: node, bindingNode: node });
22188
+ definitions.push({ key: node.id.name, name: node.id.name, node, functionNode: node, bindingNode: node });
22128
22189
  continue;
22129
22190
  }
22130
- if (node?.type !== import_utils119.AST_NODE_TYPES.VariableDeclaration || node.kind !== "const") continue;
22191
+ if (node?.type !== import_utils119.AST_NODE_TYPES.VariableDeclaration || node.kind !== "const" || node.declarations.length !== 1) continue;
22131
22192
  for (const declarator of node.declarations) {
22132
- if (declarator.id.type === import_utils119.AST_NODE_TYPES.Identifier && declarator.init !== null && isFunction(declarator.init)) {
22193
+ const initializer = declarator.init === null ? null : unwrapExpression7(declarator.init);
22194
+ if (declarator.id.type === import_utils119.AST_NODE_TYPES.Identifier && initializer !== null && isFunction(initializer)) {
22133
22195
  definitions.push({
22196
+ key: declarator.id.name,
22134
22197
  name: declarator.id.name,
22135
22198
  node: declarator,
22136
- functionNode: declarator.init,
22199
+ functionNode: initializer,
22137
22200
  bindingNode: declarator
22138
22201
  });
22139
22202
  }
@@ -22143,15 +22206,21 @@ function moduleDefinitions(program) {
22143
22206
  }
22144
22207
  function methodName2(node) {
22145
22208
  if (node.key.type === import_utils119.AST_NODE_TYPES.PrivateIdentifier) return `#${node.key.name}`;
22146
- return !node.computed && node.key.type === import_utils119.AST_NODE_TYPES.Identifier ? node.key.name : null;
22209
+ if (!node.computed && node.key.type === import_utils119.AST_NODE_TYPES.Identifier) return node.key.name;
22210
+ return node.key.type === import_utils119.AST_NODE_TYPES.Literal && typeof node.key.value === "string" ? node.key.value : null;
22147
22211
  }
22148
- function referencedMethod(context, node, classVariables) {
22149
- const objectVariable = node.object.type === import_utils119.AST_NODE_TYPES.Identifier ? import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
22150
- const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
22151
- if (node.object.type !== import_utils119.AST_NODE_TYPES.ThisExpression && !isClassReference) return null;
22152
- if (node.property.type === import_utils119.AST_NODE_TYPES.PrivateIdentifier) return `#${node.property.name}`;
22153
- if (!node.computed && node.property.type === import_utils119.AST_NODE_TYPES.Identifier) return node.property.name;
22154
- return node.computed && node.property.type === import_utils119.AST_NODE_TYPES.Literal && typeof node.property.value === "string" ? node.property.value : null;
22212
+ function methodKey(name, isStatic2) {
22213
+ return `${isStatic2 ? "static" : "instance"}:${name}`;
22214
+ }
22215
+ function receiverDomain(context, object, receivers, thisIsStatic) {
22216
+ const unwrapped = unwrapExpression7(object);
22217
+ if (unwrapped.type === import_utils119.AST_NODE_TYPES.ThisExpression) return thisIsStatic;
22218
+ if (unwrapped.type !== import_utils119.AST_NODE_TYPES.Identifier) return null;
22219
+ const variable = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(unwrapped), unwrapped.name);
22220
+ return variable === null ? null : receivers.get(variable) ?? null;
22221
+ }
22222
+ function stableVariable(variable) {
22223
+ return !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
22155
22224
  }
22156
22225
  function referencedPropertyName(node) {
22157
22226
  if (node.property.type === import_utils119.AST_NODE_TYPES.PrivateIdentifier) return `#${node.property.name}`;
@@ -22160,7 +22229,7 @@ function referencedPropertyName(node) {
22160
22229
  }
22161
22230
  function walk(node, visitorKeys, visit, nestedFunction = false) {
22162
22231
  visit(node, nestedFunction);
22163
- const nested = nestedFunction || isFunction(node);
22232
+ const nested = nestedFunction || isFunction(node) || node.type === import_utils119.AST_NODE_TYPES.ClassDeclaration || node.type === import_utils119.AST_NODE_TYPES.ClassExpression;
22164
22233
  forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
22165
22234
  }
22166
22235
  function classScope(context, node, computedReferenceNames) {
@@ -22168,123 +22237,127 @@ function classScope(context, node, computedReferenceNames) {
22168
22237
  (member) => member.type === import_utils119.AST_NODE_TYPES.MethodDefinition
22169
22238
  );
22170
22239
  const counts = classMethodCounts(node.body, methods);
22171
- const scopeDefinitions = methods.flatMap((method) => {
22240
+ const implementations = methods.filter((method) => isFunction(method.value));
22241
+ const implementationCounts = classMethodCounts(node.body, implementations);
22242
+ const scopeDefinitions = implementations.flatMap((method) => {
22172
22243
  const name = methodName2(method);
22173
- return name !== null && counts.get(name) === 1 ? [{ name, node: method }] : [];
22174
- });
22175
- const definitions = methods.flatMap((method) => {
22176
- const name = methodName2(method);
22177
- const isPrivate = method.accessibility === "private" || method.key.type === import_utils119.AST_NODE_TYPES.PrivateIdentifier;
22178
- return name !== null && isPrivate && counts.get(name) === 1 && method.decorators.length === 0 ? [{ name, node: method }] : [];
22244
+ const key = name === null ? null : methodKey(name, method.static);
22245
+ return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
22179
22246
  });
22247
+ const definitions = scopeDefinitions.filter(
22248
+ ({ key, node: method }) => (method.accessibility === "private" || method.key.type === import_utils119.AST_NODE_TYPES.PrivateIdentifier) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
22249
+ );
22180
22250
  if (definitions.length === 0) return;
22181
- const privateNames = new Set(definitions.map((definition) => definition.name));
22251
+ const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
22182
22252
  const calls = /* @__PURE__ */ new Map();
22183
22253
  const pinned = /* @__PURE__ */ new Set();
22184
- const classVariables = /* @__PURE__ */ new Set();
22185
- if (node.id !== null) {
22186
- const internal = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(node), node.id.name);
22187
- if (internal !== null) classVariables.add(internal);
22188
- }
22189
- if (node.type === import_utils119.AST_NODE_TYPES.ClassExpression && node.parent.type === import_utils119.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === import_utils119.AST_NODE_TYPES.Identifier) {
22190
- const outer = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
22191
- if (outer !== null) classVariables.add(outer);
22254
+ const classReceivers = classReceiverBindings(context, node);
22255
+ const pinNames = (name, domain = null) => {
22256
+ for (const definition of definitions) {
22257
+ if ((name === null || definition.name === name) && (domain === null || definition.node.static === domain)) {
22258
+ pinned.add(definition.key);
22259
+ }
22260
+ }
22261
+ };
22262
+ function pinDestructuredMembers(binding, domain) {
22263
+ for (const property of binding.properties) {
22264
+ if (property.type === import_utils119.AST_NODE_TYPES.RestElement || property.computed) {
22265
+ pinNames(null, domain);
22266
+ } else if (property.key.type === import_utils119.AST_NODE_TYPES.Identifier) {
22267
+ pinNames(property.key.name, domain);
22268
+ } else if (property.key.type === import_utils119.AST_NODE_TYPES.Literal && typeof property.key.value === "string") {
22269
+ pinNames(property.key.value, domain);
22270
+ }
22271
+ }
22192
22272
  }
22193
22273
  function collectMethodCalls(method) {
22194
- const caller = methodName2(method);
22195
- if (caller === null || method.value.body === null) return;
22196
- const methodClassVariables = new Set(classVariables);
22197
- const methodAliases = /* @__PURE__ */ new Set();
22274
+ const name = methodName2(method);
22275
+ if (name === null) {
22276
+ pinNames(null);
22277
+ return;
22278
+ }
22279
+ if (!isFunction(method.value)) return;
22280
+ const methodFunction = method.value;
22281
+ const caller = methodKey(name, method.static);
22282
+ const receivers = new Map(classReceivers);
22198
22283
  const parameterDecoratorNodes = /* @__PURE__ */ new Set();
22199
22284
  for (const parameter of method.value.params) {
22200
22285
  for (const decorator of parameter.decorators) {
22201
22286
  walk(decorator, context.sourceCode.visitorKeys, (current) => parameterDecoratorNodes.add(current));
22202
22287
  }
22203
22288
  }
22204
- const thisValue = (value) => {
22205
- let current = value;
22206
- while (current?.type === import_utils119.AST_NODE_TYPES.TSAsExpression || current?.type === import_utils119.AST_NODE_TYPES.TSSatisfiesExpression || current?.type === import_utils119.AST_NODE_TYPES.TSNonNullExpression) current = current.expression;
22207
- return current?.type === import_utils119.AST_NODE_TYPES.ThisExpression;
22208
- };
22209
22289
  const collectAlias = (current, nestedFunction) => {
22210
- if (nestedFunction || current.type !== import_utils119.AST_NODE_TYPES.VariableDeclarator && current.type !== import_utils119.AST_NODE_TYPES.AssignmentPattern) return;
22211
- if (current.type === import_utils119.AST_NODE_TYPES.VariableDeclarator && (current.parent.type !== import_utils119.AST_NODE_TYPES.VariableDeclaration || current.parent.kind !== "const")) return;
22212
- const binding = current.type === import_utils119.AST_NODE_TYPES.VariableDeclarator ? current.id : current.left;
22213
- const value = current.type === import_utils119.AST_NODE_TYPES.VariableDeclarator ? current.init : current.right;
22214
- if (!thisValue(value)) return;
22215
- if (binding.type === import_utils119.AST_NODE_TYPES.ObjectPattern) {
22216
- pinDestructuredMembers(binding);
22290
+ if (nestedFunction || current.type !== import_utils119.AST_NODE_TYPES.VariableDeclarator || current.init === null) return;
22291
+ const domain = receiverDomain(context, current.init, receivers, method.static);
22292
+ if (domain === null) return;
22293
+ if (current.id.type === import_utils119.AST_NODE_TYPES.ObjectPattern) {
22294
+ pinDestructuredMembers(current.id, domain);
22217
22295
  return;
22218
22296
  }
22219
- if (binding.type !== import_utils119.AST_NODE_TYPES.Identifier) return;
22220
- const variable = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(binding), binding.name);
22221
- if (variable !== null) {
22222
- methodClassVariables.add(variable);
22223
- methodAliases.add(variable);
22297
+ if (current.id.type !== import_utils119.AST_NODE_TYPES.Identifier) return;
22298
+ if (current.parent.type !== import_utils119.AST_NODE_TYPES.VariableDeclaration || current.parent.kind !== "const") {
22299
+ pinNames(null, domain);
22300
+ return;
22224
22301
  }
22302
+ const variable = context.sourceCode.getDeclaredVariables(current)[0];
22303
+ if (variable === void 0 || !stableVariable(variable)) return;
22304
+ const local = variable.references.every((reference) => {
22305
+ if (!reference.isRead()) return true;
22306
+ const expression = wrappedExpression(reference.identifier);
22307
+ return enclosingFunction5(context, reference.identifier) === methodFunction && expression.parent?.type === import_utils119.AST_NODE_TYPES.MemberExpression && expression.parent.object === expression;
22308
+ });
22309
+ if (local) receivers.set(variable, domain);
22310
+ else pinNames(null, domain);
22225
22311
  };
22226
- for (const parameter of method.value.params) {
22227
- walk(parameter, context.sourceCode.visitorKeys, collectAlias);
22228
- }
22229
- for (const statement of method.value.body.body) {
22230
- walk(statement, context.sourceCode.visitorKeys, collectAlias);
22231
- }
22232
- function pinDestructuredMembers(binding) {
22233
- for (const property of binding.properties) {
22234
- if (property.type === import_utils119.AST_NODE_TYPES.RestElement) {
22235
- for (const name of privateNames) pinned.add(name);
22236
- } else if (property.key.type === import_utils119.AST_NODE_TYPES.Identifier && privateNames.has(property.key.name)) {
22237
- pinned.add(property.key.name);
22238
- }
22239
- }
22240
- }
22312
+ for (const statement of method.value.body.body) walk(statement, context.sourceCode.visitorKeys, collectAlias);
22241
22313
  const visitCall = (current, nestedFunction) => {
22242
- if (current.type === import_utils119.AST_NODE_TYPES.VariableDeclarator && current.id.type === import_utils119.AST_NODE_TYPES.ObjectPattern && thisValue(current.init)) {
22243
- pinDestructuredMembers(current.id);
22314
+ const destructuring = receiverDestructuring(current);
22315
+ if (destructuring !== null) {
22316
+ const domain2 = receiverDomain(context, destructuring.value, receivers, method.static);
22317
+ pinDestructuredMembers(destructuring.binding, domain2);
22244
22318
  }
22245
22319
  if (current.type !== import_utils119.AST_NODE_TYPES.MemberExpression) return;
22246
- const target = referencedMethod(context, current, methodClassVariables);
22247
- if (target === null) {
22248
- const possibleTarget = referencedPropertyName(current);
22249
- if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
22320
+ const domain = receiverDomain(context, current.object, receivers, method.static);
22321
+ const targetName = referencedPropertyName(current);
22322
+ if (domain === null) {
22323
+ pinNames(targetName);
22250
22324
  return;
22251
22325
  }
22252
- if (!privateNames.has(target)) return;
22253
- const objectVariable = current.object.type === import_utils119.AST_NODE_TYPES.Identifier ? import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
22254
- if (objectVariable !== null && methodAliases.has(objectVariable)) {
22255
- pinned.add(target);
22326
+ if (targetName === null) {
22327
+ if (current.computed) pinNames(null, domain);
22256
22328
  return;
22257
22329
  }
22258
- if (current.computed || nestedFunction || parameterDecoratorNodes.has(current) || current.parent.type !== import_utils119.AST_NODE_TYPES.CallExpression || current.parent.callee !== current) {
22330
+ const target = methodKey(targetName, domain);
22331
+ if (!methodKeys.has(target)) return;
22332
+ if (current.computed || nestedFunction || parameterDecoratorNodes.has(current)) {
22259
22333
  pinned.add(target);
22260
22334
  return;
22261
22335
  }
22262
- const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22263
- callees.add(target);
22264
- calls.set(caller, callees);
22336
+ const count = localCallCount(context, current, methodFunction);
22337
+ if (count === null) {
22338
+ pinned.add(target);
22339
+ } else if (count > 0) {
22340
+ const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22341
+ callees.add(target);
22342
+ calls.set(caller, callees);
22343
+ }
22265
22344
  };
22266
- for (const decorator of method.decorators) {
22267
- walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
22268
- }
22345
+ for (const decorator of method.decorators) walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
22269
22346
  if (method.computed) walk(method.key, context.sourceCode.visitorKeys, visitCall, true);
22270
- for (const parameter of method.value.params) {
22271
- walk(parameter, context.sourceCode.visitorKeys, visitCall);
22272
- }
22273
- for (const statement of method.value.body.body) {
22274
- walk(statement, context.sourceCode.visitorKeys, visitCall);
22275
- }
22347
+ for (const parameter of method.value.params) walk(parameter, context.sourceCode.visitorKeys, visitCall);
22348
+ for (const statement of method.value.body.body) walk(statement, context.sourceCode.visitorKeys, visitCall);
22276
22349
  }
22277
22350
  for (const method of methods) collectMethodCalls(method);
22278
22351
  for (const member of node.body.body) {
22279
22352
  if (member.type === import_utils119.AST_NODE_TYPES.MethodDefinition || member.type === import_utils119.AST_NODE_TYPES.TSAbstractMethodDefinition) continue;
22280
22353
  walk(member, context.sourceCode.visitorKeys, (current) => {
22281
22354
  if (current.type !== import_utils119.AST_NODE_TYPES.MemberExpression) return;
22282
- const target = referencedMethod(context, current, classVariables);
22283
- const possibleTarget = target ?? referencedPropertyName(current);
22284
- if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
22355
+ pinNames(referencedPropertyName(current));
22285
22356
  });
22286
22357
  }
22287
- for (const name of privateNames) if (computedReferenceNames.has(name)) pinned.add(name);
22358
+ for (const definition of definitions) {
22359
+ if (computedReferenceNames.has(definition.name)) pinned.add(definition.key);
22360
+ }
22288
22361
  const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
22289
22362
  const runtimeBarrierPrefix = [0];
22290
22363
  for (const member of node.body.body) {
@@ -22298,6 +22371,27 @@ function classScope(context, node, computedReferenceNames) {
22298
22371
  };
22299
22372
  reportMisordered(context, definitions, scopeDefinitions, calls, pinned, canMove);
22300
22373
  }
22374
+ function classReceiverBindings(context, node) {
22375
+ const classReceivers = /* @__PURE__ */ new Map();
22376
+ if (node.id !== null) {
22377
+ const internal = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(node), node.id.name);
22378
+ if (internal !== null && stableVariable(internal)) classReceivers.set(internal, true);
22379
+ }
22380
+ if (node.type === import_utils119.AST_NODE_TYPES.ClassExpression && node.parent.type === import_utils119.AST_NODE_TYPES.VariableDeclarator && node.parent.id.type === import_utils119.AST_NODE_TYPES.Identifier && node.parent.parent.type === import_utils119.AST_NODE_TYPES.VariableDeclaration && node.parent.parent.kind === "const") {
22381
+ const outer = import_utils119.ASTUtils.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
22382
+ if (outer !== null && stableVariable(outer)) classReceivers.set(outer, true);
22383
+ }
22384
+ return classReceivers;
22385
+ }
22386
+ function receiverDestructuring(node) {
22387
+ if (node.type === import_utils119.AST_NODE_TYPES.VariableDeclarator && node.id.type === import_utils119.AST_NODE_TYPES.ObjectPattern && node.init !== null) {
22388
+ return { binding: node.id, value: node.init };
22389
+ }
22390
+ if ((node.type === import_utils119.AST_NODE_TYPES.AssignmentPattern || node.type === import_utils119.AST_NODE_TYPES.AssignmentExpression) && node.left.type === import_utils119.AST_NODE_TYPES.ObjectPattern) {
22391
+ return { binding: node.left, value: node.right };
22392
+ }
22393
+ return null;
22394
+ }
22301
22395
  function isClassRuntimeBarrier(member) {
22302
22396
  switch (member.type) {
22303
22397
  case import_utils119.AST_NODE_TYPES.StaticBlock:
@@ -22348,12 +22442,18 @@ function classMethodCounts(body2, methods) {
22348
22442
  const counts = /* @__PURE__ */ new Map();
22349
22443
  for (const method of methods) {
22350
22444
  const name = methodName2(method);
22351
- if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
22445
+ if (name !== null) {
22446
+ const key = methodKey(name, method.static);
22447
+ counts.set(key, (counts.get(key) ?? 0) + 1);
22448
+ }
22352
22449
  }
22353
22450
  for (const member of body2.body) {
22354
22451
  if (member.type !== import_utils119.AST_NODE_TYPES.TSAbstractMethodDefinition) continue;
22355
22452
  const name = !member.computed && member.key.type === import_utils119.AST_NODE_TYPES.Identifier ? member.key.name : null;
22356
- if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
22453
+ if (name !== null) {
22454
+ const key = methodKey(name, member.static);
22455
+ counts.set(key, (counts.get(key) ?? 0) + 1);
22456
+ }
22357
22457
  }
22358
22458
  return counts;
22359
22459
  }
@@ -22686,14 +22786,14 @@ function importedAssertionKind(specifier) {
22686
22786
  // src/rules/sole-export-matches-filename.ts
22687
22787
  var import_utils121 = require("@typescript-eslint/utils");
22688
22788
  var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
22689
- defaultLevel: "warning",
22789
+ defaultLevel: "error",
22690
22790
  summary: "Make a module filename reflect its sole named public runtime export.",
22691
22791
  rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
22692
- remediation: "Name the module for the exported responsibility, using either the full export name or a clear leading or trailing domain phrase; otherwise colocate genuinely related exports.",
22792
+ remediation: "Name the module for the exported responsibility, using the full kebab-case export name; otherwise colocate genuinely related exports.",
22693
22793
  category: "maintainability",
22694
22794
  limitations: [
22695
- "Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, and re-exports are excluded.",
22696
- "The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker. A leading or trailing export-name phrase is accepted only at token boundaries; multi-token stems also tolerate established acronym spelling such as github versus GitHub.",
22795
+ "Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, unresolved re-exports, and emission-dependent exports are excluded.",
22796
+ "The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker. Supporting types do not exempt a sole runtime export; public runtime aliases count as distinct exports.",
22697
22797
  "Exported destructuring patterns are excluded rather than undercounted as public exports."
22698
22798
  ],
22699
22799
  examples: [
@@ -22702,39 +22802,16 @@ var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
22702
22802
  ]
22703
22803
  };
22704
22804
  var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
22705
- "common",
22706
22805
  "global",
22707
- "helpers",
22708
22806
  "index",
22709
22807
  "layout",
22710
22808
  "loading",
22711
22809
  "middleware",
22712
- "misc",
22713
22810
  "not-found",
22714
22811
  "page",
22715
22812
  "route",
22716
- "shared",
22717
22813
  "template",
22718
- "types",
22719
- "util",
22720
- "utils"
22721
- ]);
22722
- var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
22723
- "adapter",
22724
- "client",
22725
- "config",
22726
- "controller",
22727
- "factory",
22728
- "handler",
22729
- "manager",
22730
- "provider",
22731
- "record",
22732
- "repository",
22733
- "router",
22734
- "schema",
22735
- "service",
22736
- "store",
22737
- "worker"
22814
+ "types"
22738
22815
  ]);
22739
22816
  function stem3(filename) {
22740
22817
  const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
@@ -22743,34 +22820,6 @@ function stem3(filename) {
22743
22820
  function kebabCase(name) {
22744
22821
  return name.replace(/([A-Z]{2,})([A-Z][a-z])/gu, "$1-$2").replace(/([a-z0-9])([A-Z])/gu, "$1-$2").replaceAll(/[^a-z0-9]+/giu, "-").replaceAll(/^-+|-+$/gu, "").toLowerCase();
22745
22822
  }
22746
- function reflectsExportName(fileStem, exportedStem) {
22747
- if (fileStem.startsWith("_")) return false;
22748
- const visibleFileStem = fileStem.toLowerCase();
22749
- const fileTokens = visibleFileStem.split("-").filter(Boolean);
22750
- const exportTokens = exportedStem.split("-").filter(Boolean);
22751
- if (fileTokens.length === 0 || exportTokens.length === 0) return false;
22752
- if (fileTokens.length === 1) {
22753
- const [token] = fileTokens;
22754
- return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
22755
- }
22756
- const compactFile = fileTokens.join("");
22757
- if (compactFile.length < 6) return false;
22758
- const boundaryPhrases = /* @__PURE__ */ new Set();
22759
- for (let index = 1; index <= exportTokens.length; index += 1) {
22760
- boundaryPhrases.add(exportTokens.slice(0, index).join(""));
22761
- boundaryPhrases.add(exportTokens.slice(-index).join(""));
22762
- }
22763
- return boundaryPhrases.has(compactFile);
22764
- }
22765
- function declarationExport(statement) {
22766
- const declaration = statement.declaration;
22767
- if (declaration === null || declaration.declare === true) return [];
22768
- if (declaration.type === import_utils121.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils121.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils121.AST_NODE_TYPES.TSEnumDeclaration) return declaration.id === null ? [] : [{ name: declaration.id.name, node: declaration }];
22769
- if (declaration.type !== import_utils121.AST_NODE_TYPES.VariableDeclaration) return [];
22770
- return declaration.declarations.flatMap(
22771
- (item) => item.id.type === import_utils121.AST_NODE_TYPES.Identifier ? [{ name: item.id.name, node: item }] : []
22772
- );
22773
- }
22774
22823
  var sole_export_matches_filename_default = createRule({
22775
22824
  name: "sole-export-matches-filename",
22776
22825
  documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
@@ -22786,64 +22835,24 @@ var sole_export_matches_filename_default = createRule({
22786
22835
  create(context) {
22787
22836
  const fileStem = stem3(context.filename);
22788
22837
  const normalizedFilename = context.filename.replaceAll("\\", "/");
22789
- if (EXCLUDED_STEMS.has(fileStem) || /(?:^|\/)app\/(?:.*\/)?(?:global-)?error\.[jt]sx?$/u.test(normalizedFilename) || normalizedFilename.includes("/pages/") || context.filename.endsWith(".d.ts") || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
22838
+ if (EXCLUDED_STEMS.has(fileStem) || GENERIC_MODULE_STEMS.has(fileStem) || /(?:^|\/)app\/(?:.*\/)?(?:global-)?error\.[jt]sx?$/u.test(normalizedFilename) || normalizedFilename.includes("/pages/") || context.filename.endsWith(".d.ts") || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
22839
+ let hasCommonJsExport = false;
22790
22840
  return {
22841
+ "CallExpression, MemberExpression"(node) {
22842
+ if (isCommonJsExportReference(node, context.sourceCode)) hasCommonJsExport = true;
22843
+ },
22791
22844
  "Program:exit"(program) {
22792
- const exports2 = [];
22793
- const publicExports = /* @__PURE__ */ new Set();
22794
- function collectPublicExport(statement) {
22795
- if (statement.type === import_utils121.AST_NODE_TYPES.ExportAllDeclaration || statement.type === import_utils121.AST_NODE_TYPES.ExportNamedDeclaration && statement.source !== null) return false;
22796
- if (statement.type === import_utils121.AST_NODE_TYPES.ExportDefaultDeclaration) {
22797
- publicExports.add("default");
22798
- const declaration = statement.declaration;
22799
- if ((declaration.type === import_utils121.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils121.AST_NODE_TYPES.FunctionDeclaration) && declaration.id !== null) exports2.push({ name: declaration.id.name, node: declaration });
22800
- else return false;
22801
- }
22802
- if (statement.type !== import_utils121.AST_NODE_TYPES.ExportNamedDeclaration) return true;
22803
- return collectNamedExport(statement);
22804
- }
22805
- function collectNamedExport(statement) {
22806
- const declaration = statement.declaration;
22807
- if (declaration !== null && "id" in declaration && declaration.id?.type === import_utils121.AST_NODE_TYPES.Identifier) {
22808
- publicExports.add(declaration.id.name);
22809
- }
22810
- if (declaration?.type === import_utils121.AST_NODE_TYPES.VariableDeclaration) {
22811
- if (declaration.declarations.some((item) => item.id.type !== import_utils121.AST_NODE_TYPES.Identifier)) return false;
22812
- for (const item of declaration.declarations) {
22813
- if (item.id.type === import_utils121.AST_NODE_TYPES.Identifier) publicExports.add(item.id.name);
22814
- }
22815
- }
22816
- for (const specifier of statement.specifiers) {
22817
- const exported = specifier.exported.type === import_utils121.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value;
22818
- publicExports.add(String(exported));
22819
- }
22820
- if (statement.exportKind === "type") return true;
22821
- exports2.push(...declarationExport(statement));
22822
- collectRuntimeSpecifiers(statement);
22823
- return true;
22824
- }
22825
- function collectRuntimeSpecifiers(statement) {
22826
- for (const specifier of statement.specifiers) {
22827
- const exported = specifier.exported.type === import_utils121.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value;
22828
- publicExports.add(String(exported));
22829
- if (specifier.exportKind === "type") continue;
22830
- if (exported === "default") exports2.push({ name: specifier.local.name, node: specifier });
22831
- else exports2.push({ name: String(exported), node: specifier });
22832
- }
22833
- }
22834
- for (const statement of program.body) {
22835
- if (!collectPublicExport(statement)) return;
22836
- }
22837
- const unique = new Map(exports2.map((item) => [item.name, item]));
22838
- if (unique.size !== 1 || publicExports.size !== 1) return;
22839
- const only = [...unique.values()][0];
22845
+ if (hasCommonJsExport) return;
22846
+ const exports2 = runtimeExports(program);
22847
+ if (exports2.ambiguous || exports2.exports.length !== 1) return;
22848
+ const only = exports2.exports[0];
22840
22849
  if (only === void 0) return;
22841
22850
  if (only.name === "onRouterTransitionStart" && /(?:^|\/)instrumentation-client\.[jt]s$/u.test(normalizedFilename)) return;
22842
22851
  if (only.name === "collections" && /(?:^|\/)src\/content\.config\.(?:ts|js|mjs)$/u.test(normalizedFilename) && program.body.some((statement) => statement.type === import_utils121.AST_NODE_TYPES.ImportDeclaration && statement.source.value === "astro:content")) return;
22843
22852
  const exportedStem = kebabCase(only.name);
22844
22853
  if (exportedStem === "") return;
22845
22854
  const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
22846
- if (expected === fileStem.toLowerCase() || reflectsExportName(fileStem, exportedStem)) return;
22855
+ if (expected === fileStem.toLowerCase()) return;
22847
22856
  context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
22848
22857
  }
22849
22858
  };
@@ -23058,7 +23067,7 @@ var chainMemberNames2 = (node) => {
23058
23067
  names.reverse();
23059
23068
  return names;
23060
23069
  };
23061
- var unwrapExpression7 = (node) => {
23070
+ var unwrapExpression8 = (node) => {
23062
23071
  let current = node;
23063
23072
  while (current.type === import_utils122.AST_NODE_TYPES.TSAsExpression || current.type === import_utils122.AST_NODE_TYPES.TSSatisfiesExpression || current.type === import_utils122.AST_NODE_TYPES.TSNonNullExpression || current.type === import_utils122.AST_NODE_TYPES.TSTypeAssertion) {
23064
23073
  current = current.expression;
@@ -23109,7 +23118,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
23109
23118
  return binding !== null && schemaBindings.has(binding);
23110
23119
  }
23111
23120
  function isConfirmedSchema(expression) {
23112
- const init = unwrapExpression7(expression);
23121
+ const init = unwrapExpression8(expression);
23113
23122
  if (init.type === import_utils122.AST_NODE_TYPES.Identifier) return isSchemaBinding(init);
23114
23123
  if (init.type !== import_utils122.AST_NODE_TYPES.CallExpression || init.callee.type !== import_utils122.AST_NODE_TYPES.MemberExpression) {
23115
23124
  return false;
@@ -23154,7 +23163,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
23154
23163
  }
23155
23164
  });
23156
23165
 
23157
- // src/rules/_renames.ts
23166
+ // src/rules/_renamed-rules.ts
23158
23167
  var RENAMED_RULES = {
23159
23168
  "jsdoc-restates-signature": "no-restated-jsdoc",
23160
23169
  "zod-naming-convention": "require-pascal-case-zod-schema-name",
@@ -23163,7 +23172,7 @@ var RENAMED_RULES = {
23163
23172
  "trailing-value-narration": "no-trailing-value-narration"
23164
23173
  };
23165
23174
 
23166
- // src/rules/_retired.ts
23175
+ // src/rules/_retired-rules.ts
23167
23176
  var RETIRED_RULES = {
23168
23177
  "ban-loose-type-guards-in-tests": {
23169
23178
  removedIn: "5.0.0",
@@ -23355,7 +23364,7 @@ var RULES = {
23355
23364
  };
23356
23365
  var meta = {
23357
23366
  name: "@sarj/eslint-plugin",
23358
- version: "15.28.1"
23367
+ version: "16.0.0"
23359
23368
  };
23360
23369
  var APPLICATION_ONLY_RULES = [];
23361
23370
  var LIBRARY_IMPORT_POLICY = ["error", {
@@ -23380,8 +23389,7 @@ var ADVISORY_RULES = [
23380
23389
  "@sarj/prefer-whole-object-assertion",
23381
23390
  "@sarj/require-camelcase-properties",
23382
23391
  "@sarj/require-interface-for-exported-class",
23383
- "@sarj/require-sql-access-class",
23384
- "@sarj/sole-export-matches-filename"
23392
+ "@sarj/require-sql-access-class"
23385
23393
  ];
23386
23394
  var RECOMMENDED_RULES = {
23387
23395
  "@sarj/no-conditional-empty-object-spread": "error",
@@ -23493,7 +23501,7 @@ var RECOMMENDED_RULES = {
23493
23501
  "@sarj/store-insert-requires-on-conflict": "error",
23494
23502
  "@sarj/stepdown": "error",
23495
23503
  "@sarj/source-coupled-test": "error",
23496
- "@sarj/sole-export-matches-filename": "warn",
23504
+ "@sarj/sole-export-matches-filename": "error",
23497
23505
  "@sarj/test-phase-label-comment": "error",
23498
23506
  "@sarj/require-pascal-case-zod-schema-name": "error"
23499
23507
  };
@@ -23611,7 +23619,7 @@ var STRICT_RULES = {
23611
23619
  "@sarj/store-insert-requires-on-conflict": "error",
23612
23620
  "@sarj/stepdown": "error",
23613
23621
  "@sarj/source-coupled-test": "error",
23614
- "@sarj/sole-export-matches-filename": "warn",
23622
+ "@sarj/sole-export-matches-filename": "error",
23615
23623
  "@sarj/test-phase-label-comment": "error",
23616
23624
  "@sarj/require-pascal-case-zod-schema-name": "error"
23617
23625
  };
@@ -23634,7 +23642,7 @@ var PLUGIN = {
23634
23642
  };
23635
23643
  }
23636
23644
  };
23637
- var index_default = PLUGIN;
23645
+ var src_default = PLUGIN;
23638
23646
  // Annotate the CommonJS export names for ESM import in node:
23639
23647
  0 && (module.exports = {
23640
23648
  ADVISORY_RULES,