@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.js CHANGED
@@ -7503,20 +7503,9 @@ var no_vague_suppression_description_default = createRule({
7503
7503
  }
7504
7504
  });
7505
7505
 
7506
- // src/rules/no-generic-single-export-module.ts
7506
+ // src/rules/_runtime-exports.ts
7507
7507
  import { AST_NODE_TYPES as AST_NODE_TYPES40, ASTUtils as ASTUtils16 } from "@typescript-eslint/utils";
7508
- var NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION = {
7509
- summary: "Disallow generic module stems when one runtime export already names the responsibility.",
7510
- rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
7511
- remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
7512
- category: "maintainability",
7513
- limitations: ["Only the fixed generic-stem vocabulary with exactly one public runtime export is checked; exported destructuring patterns are excluded rather than undercounted."],
7514
- examples: [
7515
- { 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 },
7516
- { 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 }
7517
- ]
7518
- };
7519
- var GENERIC_STEMS = /* @__PURE__ */ new Set([
7508
+ var GENERIC_MODULE_STEMS = /* @__PURE__ */ new Set([
7520
7509
  "common",
7521
7510
  "helper",
7522
7511
  "helpers",
@@ -7527,139 +7516,152 @@ var GENERIC_STEMS = /* @__PURE__ */ new Set([
7527
7516
  "utils"
7528
7517
  ]);
7529
7518
  var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
7530
- function fileParts(filename) {
7531
- const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
7532
- const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
7533
- const [stem4 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
7534
- return { stem: stem4, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
7535
- }
7536
- function declaredNames(declaration) {
7537
- if (declaration.declare === true) return [];
7538
- if (declaration.type === AST_NODE_TYPES40.FunctionDeclaration || declaration.type === AST_NODE_TYPES40.ClassDeclaration || declaration.type === AST_NODE_TYPES40.TSEnumDeclaration) {
7539
- if (declaration.type === AST_NODE_TYPES40.TSEnumDeclaration && declaration.const) return [];
7540
- return declaration.id === null ? [] : [declaration.id.name];
7541
- }
7542
- if (declaration.type === AST_NODE_TYPES40.TSModuleDeclaration && declaration.id.type === AST_NODE_TYPES40.Identifier) {
7543
- return [declaration.id.name];
7544
- }
7545
- if (declaration.type !== AST_NODE_TYPES40.VariableDeclaration) return [];
7546
- return declaration.declarations.flatMap(
7547
- (item) => item.id.type === AST_NODE_TYPES40.Identifier ? [item.id.name] : []
7548
- );
7549
- }
7550
7519
  function runtimeExports(program) {
7551
- const exports = [];
7552
- const typeBindings = typeOnlyBindings(program);
7520
+ const bindings = localBindings(program);
7521
+ const exports = /* @__PURE__ */ new Map();
7553
7522
  let ambiguous = false;
7554
- function collectRuntimeExport(statement) {
7555
- if (statement.type === AST_NODE_TYPES40.ExportAllDeclaration) {
7556
- if (statement.exportKind !== "type") ambiguous = true;
7557
- return;
7558
- }
7559
- if (statement.type === AST_NODE_TYPES40.ExportDefaultDeclaration) {
7560
- collectDefaultExport(statement);
7561
- return;
7562
- }
7563
- if (statement.type !== AST_NODE_TYPES40.ExportNamedDeclaration || statement.exportKind === "type") return;
7523
+ function add(key, name, node, emission) {
7524
+ if (emission === "unknown") ambiguous = true;
7525
+ if (emission === "runtime") exports.set(key, { key, name, node });
7526
+ }
7527
+ function collectDefault(statement) {
7528
+ const declaration = statement.declaration;
7529
+ if (declaration.type === AST_NODE_TYPES40.Identifier) {
7530
+ add("default", declaration.name, statement, bindings.get(declaration.name) ?? "unknown");
7531
+ } else if ("id" in declaration && declaration.id?.type === AST_NODE_TYPES40.Identifier) {
7532
+ add("default", declaration.id.name, declaration, declarationEmission(declaration));
7533
+ } else ambiguous = true;
7534
+ }
7535
+ function collectNamed(statement) {
7536
+ if (statement.exportKind === "type") return;
7564
7537
  if (statement.source !== null) {
7565
7538
  if (statement.specifiers.some((specifier) => specifier.exportKind !== "type")) ambiguous = true;
7566
7539
  return;
7567
7540
  }
7568
- if (statement.declaration !== null) {
7569
- const declaration = statement.declaration;
7541
+ const declaration = statement.declaration;
7542
+ if (declaration !== null) {
7570
7543
  if (declaration.type === AST_NODE_TYPES40.VariableDeclaration && declaration.declarations.some((item) => item.id.type !== AST_NODE_TYPES40.Identifier)) ambiguous = true;
7571
- exports.push(...declaredNames(declaration).map((name) => ({ key: name, name, node: declaration })));
7544
+ const names = declarationNames(declaration);
7545
+ if (names.length === 0 && declarationEmission(declaration) !== "erased") ambiguous = true;
7546
+ for (const name of names) add(name, name, declaration, bindings.get(name) ?? "unknown");
7572
7547
  }
7573
- collectExportSpecifiers(statement);
7574
- }
7575
- function collectExportSpecifiers(statement) {
7576
7548
  for (const specifier of statement.specifiers) {
7577
- if (specifier.exportKind === "type" || typeBindings.has(specifier.local.name)) continue;
7578
- const exported = specifier.exported.type === AST_NODE_TYPES40.Identifier ? specifier.exported.name : specifier.exported.value;
7549
+ if (specifier.exportKind === "type") continue;
7550
+ const key = specifier.exported.type === AST_NODE_TYPES40.Identifier ? specifier.exported.name : specifier.exported.value;
7579
7551
  const local = specifier.local.name;
7580
- exports.push({ key: exported, name: exported === "default" ? local : exported, node: specifier });
7552
+ add(key, key === "default" ? local : key, specifier, bindings.get(local) ?? "unknown");
7581
7553
  }
7582
7554
  }
7583
- function collectDefaultExport(statement) {
7584
- const declaration = statement.declaration;
7585
- if (declaration.type === AST_NODE_TYPES40.Identifier) {
7586
- if (!typeBindings.has(declaration.name)) {
7587
- exports.push({ key: "default", name: declaration.name, node: statement });
7588
- }
7589
- } else if ((declaration.type === AST_NODE_TYPES40.FunctionDeclaration || declaration.type === AST_NODE_TYPES40.ClassDeclaration) && declaration.id !== null && declaration.declare !== true) exports.push({ key: "default", name: declaration.id.name, node: declaration });
7590
- else if (declaration.type !== AST_NODE_TYPES40.TSInterfaceDeclaration && declaration.type !== AST_NODE_TYPES40.TSTypeAliasDeclaration) ambiguous = true;
7591
- }
7592
7555
  for (const statement of program.body) {
7593
- collectRuntimeExport(statement);
7556
+ if (statement.type === AST_NODE_TYPES40.ExportAllDeclaration && statement.exportKind !== "type") ambiguous = true;
7557
+ if (statement.type === AST_NODE_TYPES40.TSExportAssignment) ambiguous = true;
7558
+ if (statement.type === AST_NODE_TYPES40.ExportDefaultDeclaration) collectDefault(statement);
7559
+ if (statement.type === AST_NODE_TYPES40.ExportNamedDeclaration) collectNamed(statement);
7594
7560
  }
7595
- const unique = new Map(exports.map((entry) => [entry.key, entry]));
7596
- return { exports: [...unique.values()], ambiguous };
7561
+ return { exports: [...exports.values()], ambiguous };
7597
7562
  }
7598
- function typeOnlyBindings(program) {
7599
- const names = /* @__PURE__ */ new Set();
7600
- const runtimeNames = /* @__PURE__ */ new Set();
7601
- function collectTypeBinding(statement) {
7602
- const declaration = statement.type === AST_NODE_TYPES40.ExportNamedDeclaration ? statement.declaration : statement;
7603
- if (declaration?.type === AST_NODE_TYPES40.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES40.TSTypeAliasDeclaration) {
7604
- names.add(declaration.id.name);
7605
- return;
7606
- }
7607
- if (declaration?.type === AST_NODE_TYPES40.TSEnumDeclaration && declaration.const) {
7608
- names.add(declaration.id.name);
7609
- return;
7610
- }
7611
- if (statement.type === AST_NODE_TYPES40.ImportDeclaration) {
7612
- collectImportBindings(statement);
7613
- return;
7614
- }
7615
- if (declaration?.type === AST_NODE_TYPES40.TSDeclareFunction && declaration.id !== null) {
7616
- names.add(declaration.id.name);
7617
- return;
7618
- }
7619
- if (declaration !== null && declaration.declare === true) {
7620
- collectDeclaredNames(declaration);
7621
- return;
7622
- }
7623
- if (declaration !== null && "type" in declaration) {
7624
- for (const name of declaredNames(declaration)) {
7625
- runtimeNames.add(name);
7626
- }
7627
- }
7563
+ function localBindings(program) {
7564
+ const bindings = /* @__PURE__ */ new Map();
7565
+ function register(name, emission) {
7566
+ const previous = bindings.get(name);
7567
+ if (previous === "runtime" || emission === "runtime") bindings.set(name, "runtime");
7568
+ else if (previous === "unknown" || emission === "unknown") bindings.set(name, "unknown");
7569
+ else bindings.set(name, "erased");
7628
7570
  }
7629
- function collectImportBindings(statement) {
7630
- for (const specifier of statement.specifiers) {
7631
- if (statement.importKind === "type" || specifier.type === AST_NODE_TYPES40.ImportSpecifier && specifier.importKind === "type") {
7632
- names.add(specifier.local.name);
7633
- } else {
7634
- runtimeNames.add(specifier.local.name);
7571
+ for (const statement of program.body) {
7572
+ if (statement.type === AST_NODE_TYPES40.ImportDeclaration) {
7573
+ for (const specifier of statement.specifiers) {
7574
+ const typeOnly = statement.importKind === "type" || specifier.type === AST_NODE_TYPES40.ImportSpecifier && specifier.importKind === "type";
7575
+ register(specifier.local.name, typeOnly ? "erased" : "unknown");
7635
7576
  }
7577
+ continue;
7636
7578
  }
7579
+ const declaration = statement.type === AST_NODE_TYPES40.ExportNamedDeclaration || statement.type === AST_NODE_TYPES40.ExportDefaultDeclaration ? statement.declaration : statement;
7580
+ if (declaration === null) continue;
7581
+ for (const name of declarationNames(declaration)) register(name, declarationEmission(declaration));
7637
7582
  }
7638
- function collectDeclaredNames(declaration) {
7639
- if ((declaration.type === AST_NODE_TYPES40.ClassDeclaration || declaration.type === AST_NODE_TYPES40.FunctionDeclaration || declaration.type === AST_NODE_TYPES40.TSEnumDeclaration || declaration.type === AST_NODE_TYPES40.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === AST_NODE_TYPES40.Identifier) names.add(declaration.id.name);
7640
- if (declaration.type === AST_NODE_TYPES40.VariableDeclaration) {
7641
- for (const item of declaration.declarations) {
7642
- if (item.id.type === AST_NODE_TYPES40.Identifier) names.add(item.id.name);
7643
- }
7644
- }
7583
+ return bindings;
7584
+ }
7585
+ function declarationNames(declaration) {
7586
+ if ("id" in declaration && declaration.id !== null) {
7587
+ let id = declaration.id;
7588
+ while (id.type === AST_NODE_TYPES40.TSQualifiedName) id = id.left;
7589
+ if (id.type === AST_NODE_TYPES40.Identifier) return [id.name];
7645
7590
  }
7646
- for (const statement of program.body) {
7647
- collectTypeBinding(statement);
7591
+ if (declaration.type !== AST_NODE_TYPES40.VariableDeclaration) return [];
7592
+ return declaration.declarations.flatMap((item) => item.id.type === AST_NODE_TYPES40.Identifier ? [item.id.name] : []);
7593
+ }
7594
+ function declarationEmission(declaration) {
7595
+ if ("declare" in declaration && declaration.declare === true) return "erased";
7596
+ switch (declaration.type) {
7597
+ case AST_NODE_TYPES40.TSInterfaceDeclaration:
7598
+ case AST_NODE_TYPES40.TSTypeAliasDeclaration:
7599
+ case AST_NODE_TYPES40.TSDeclareFunction:
7600
+ return "erased";
7601
+ case AST_NODE_TYPES40.ClassDeclaration:
7602
+ case AST_NODE_TYPES40.FunctionDeclaration:
7603
+ case AST_NODE_TYPES40.VariableDeclaration:
7604
+ return "runtime";
7605
+ case AST_NODE_TYPES40.TSEnumDeclaration:
7606
+ return declaration.const ? "unknown" : "runtime";
7607
+ case AST_NODE_TYPES40.TSModuleDeclaration:
7608
+ return namespaceEmission(declaration);
7609
+ default:
7610
+ return "unknown";
7648
7611
  }
7649
- return new Set([...names].filter((name) => !runtimeNames.has(name)));
7650
7612
  }
7651
- function isGlobalIdentifier2(context, node) {
7652
- const variable = ASTUtils16.findVariable(context.sourceCode.getScope(node), node.name);
7653
- return variable === null || variable.defs.length === 0;
7613
+ function namespaceEmission(declaration) {
7614
+ const { body: body2 } = declaration;
7615
+ if (body2 === void 0) return "unknown";
7616
+ if (body2.body.length === 0) return "unknown";
7617
+ let ambiguous = false;
7618
+ for (const statement of body2.body) {
7619
+ if (statement.type === AST_NODE_TYPES40.ExportNamedDeclaration && statement.exportKind === "type") continue;
7620
+ const inner = statement.type === AST_NODE_TYPES40.ExportNamedDeclaration ? statement.declaration : statement;
7621
+ const emission = inner === null ? "unknown" : declarationEmission(inner);
7622
+ if (emission === "runtime") return "runtime";
7623
+ if (emission === "unknown") ambiguous = true;
7624
+ }
7625
+ return ambiguous ? "unknown" : "erased";
7654
7626
  }
7655
- function isConventionalFrameworkUtility(filename, exported) {
7656
- const normalized = filename.replaceAll("\\", "/");
7657
- return exported === "cn" && /(?:^|\/)lib\/utils\.[cm]?[jt]sx?$/u.test(normalized);
7627
+ function isCommonJsExportReference(node, sourceCode) {
7628
+ function isGlobal(node2) {
7629
+ const variable = ASTUtils16.findVariable(sourceCode.getScope(node2), node2.name);
7630
+ return variable === null || variable.defs.length === 0;
7631
+ }
7632
+ if (node.type === AST_NODE_TYPES40.MemberExpression) {
7633
+ return node.object.type === AST_NODE_TYPES40.Identifier && isGlobal(node.object) && (node.object.name === "exports" || node.object.name === "module" && memberPropertyName(node) === "exports");
7634
+ }
7635
+ const first = node.arguments[0];
7636
+ return first?.type === AST_NODE_TYPES40.Identifier && first.name === "exports" && isGlobal(first) && node.callee.type === AST_NODE_TYPES40.MemberExpression && node.callee.object.type === AST_NODE_TYPES40.Identifier && node.callee.object.name === "Object" && isGlobal(node.callee.object) && CJS_OBJECT_EXPORT_METHODS.has(memberPropertyName(node.callee) ?? "");
7658
7637
  }
7659
7638
  function memberPropertyName(node) {
7660
7639
  if (!node.computed && node.property.type === AST_NODE_TYPES40.Identifier) return node.property.name;
7661
7640
  return node.computed && node.property.type === AST_NODE_TYPES40.Literal && typeof node.property.value === "string" ? node.property.value : null;
7662
7641
  }
7642
+
7643
+ // src/rules/no-generic-single-export-module.ts
7644
+ var NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION = {
7645
+ summary: "Disallow generic module stems when one runtime export already names the responsibility.",
7646
+ rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
7647
+ remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
7648
+ category: "maintainability",
7649
+ limitations: ["Only the fixed generic-stem vocabulary with exactly one public runtime export is checked; exported destructuring patterns are excluded rather than undercounted."],
7650
+ examples: [
7651
+ { 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 },
7652
+ { 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 }
7653
+ ]
7654
+ };
7655
+ function fileParts(filename) {
7656
+ const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
7657
+ const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
7658
+ const [stem4 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
7659
+ return { stem: stem4, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
7660
+ }
7661
+ function isConventionalFrameworkUtility(filename, exported) {
7662
+ const normalized = filename.replaceAll("\\", "/");
7663
+ return exported === "cn" && /(?:^|\/)lib\/utils\.[cm]?[jt]sx?$/u.test(normalized);
7664
+ }
7663
7665
  var no_generic_single_export_module_default = createRule({
7664
7666
  name: "no-generic-single-export-module",
7665
7667
  documentation: NO_GENERIC_SINGLE_EXPORT_MODULE_DOCUMENTATION,
@@ -7675,16 +7677,11 @@ var no_generic_single_export_module_default = createRule({
7675
7677
  create(context) {
7676
7678
  const file = fileParts(context.filename);
7677
7679
  const { stem: stem4 } = file;
7678
- if (!GENERIC_STEMS.has(stem4) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
7680
+ if (!GENERIC_MODULE_STEMS.has(stem4) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
7679
7681
  let hasCommonJsExport = false;
7680
7682
  return {
7681
- CallExpression(node) {
7682
- const first = node.arguments[0];
7683
- if (first?.type === AST_NODE_TYPES40.Identifier && first.name === "exports" && isGlobalIdentifier2(context, first) && node.callee.type === AST_NODE_TYPES40.MemberExpression && node.callee.object.type === AST_NODE_TYPES40.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;
7684
- },
7685
- MemberExpression(node) {
7686
- if (node.object.type === AST_NODE_TYPES40.Identifier && node.object.name === "exports" && isGlobalIdentifier2(context, node.object)) hasCommonJsExport = true;
7687
- if (node.object.type === AST_NODE_TYPES40.Identifier && node.object.name === "module" && isGlobalIdentifier2(context, node.object) && memberPropertyName(node) === "exports") hasCommonJsExport = true;
7683
+ "CallExpression, MemberExpression"(node) {
7684
+ if (isCommonJsExportReference(node, context.sourceCode)) hasCommonJsExport = true;
7688
7685
  },
7689
7686
  "Program:exit"(program) {
7690
7687
  if (hasCommonJsExport) return;
@@ -9225,19 +9222,19 @@ var no_restated_jsdoc_default = createRule({
9225
9222
  function enclosingDeclarationNames(node) {
9226
9223
  let declaration = null;
9227
9224
  while (node != null && node.type !== AST_NODE_TYPES47.Program) {
9228
- declaration = declarationNames(node);
9225
+ declaration = declarationNames2(node);
9229
9226
  if (declaration !== null) break;
9230
9227
  node = node.parent ?? null;
9231
9228
  }
9232
9229
  return declaration;
9233
9230
  }
9234
- function declarationNames(node) {
9231
+ function declarationNames2(node) {
9235
9232
  switch (node.type) {
9236
9233
  // `export function f()` — the JSDoc sits above the `export`, so the token
9237
9234
  // after it resolves to the wrapper, not to the thing being documented.
9238
9235
  case AST_NODE_TYPES47.ExportNamedDeclaration:
9239
9236
  case AST_NODE_TYPES47.ExportDefaultDeclaration:
9240
- return node.declaration == null ? null : declarationNames(node.declaration);
9237
+ return node.declaration == null ? null : declarationNames2(node.declaration);
9241
9238
  case AST_NODE_TYPES47.FunctionDeclaration:
9242
9239
  case AST_NODE_TYPES47.TSDeclareFunction:
9243
9240
  return node.id === null ? null : { name: node.id.name, params: paramNames(node.params) };
@@ -21961,9 +21958,9 @@ var STEPDOWN_DOCUMENTATION = {
21961
21958
  remediation: "Consider moving the private helper below its sole caller after reviewing initialization and reflection dependencies.",
21962
21959
  category: "maintainability",
21963
21960
  limitations: [
21964
- "Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
21961
+ "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.",
21965
21962
  "Class helpers must be private; their sole caller may be public, protected, or private.",
21966
- "Runtime class-field, static-block, computed-member, and decorator barriers are never crossed.",
21963
+ "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.",
21967
21964
  "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."
21968
21965
  ],
21969
21966
  examples: [
@@ -21975,7 +21972,7 @@ function isFunction(node) {
21975
21972
  return node.type === AST_NODE_TYPES100.ArrowFunctionExpression || node.type === AST_NODE_TYPES100.FunctionDeclaration || node.type === AST_NODE_TYPES100.FunctionExpression;
21976
21973
  }
21977
21974
  function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
21978
- const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
21975
+ const byName = new Map(scopeDefinitions.map((definition) => [definition.key, definition]));
21979
21976
  const cycles = cycleComponents(calls);
21980
21977
  const callers = /* @__PURE__ */ new Map();
21981
21978
  for (const [caller, callees] of calls) {
@@ -21987,16 +21984,16 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned,
21987
21984
  }
21988
21985
  }
21989
21986
  for (const helper of candidates) {
21990
- const helperCallers = [...callers.get(helper.name) ?? []];
21991
- if (pinned.has(helper.name) || helperCallers.length !== 1) continue;
21987
+ const helperCallers = [...callers.get(helper.key) ?? []];
21988
+ if (pinned.has(helper.key) || helperCallers.length !== 1) continue;
21992
21989
  const callerName2 = helperCallers[0];
21993
- if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
21990
+ if (callerName2 === void 0 || cycles.has(helper.key) && cycles.get(helper.key) === cycles.get(callerName2)) continue;
21994
21991
  const caller = byName.get(callerName2);
21995
21992
  if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
21996
21993
  context.report({
21997
21994
  node: helper.node,
21998
21995
  messageId: "helperAboveOnlyCaller",
21999
- data: { helper: helper.name, caller: callerName2 }
21996
+ data: { helper: helper.name, caller: caller.name }
22000
21997
  });
22001
21998
  }
22002
21999
  }
@@ -22067,6 +22064,43 @@ function cycleComponents(graph) {
22067
22064
  }
22068
22065
  return components;
22069
22066
  }
22067
+ function isErasedExpression(node) {
22068
+ return node.type === AST_NODE_TYPES100.TSAsExpression || node.type === AST_NODE_TYPES100.TSTypeAssertion || node.type === AST_NODE_TYPES100.TSSatisfiesExpression || node.type === AST_NODE_TYPES100.TSNonNullExpression || node.type === AST_NODE_TYPES100.TSInstantiationExpression;
22069
+ }
22070
+ function unwrapExpression7(node) {
22071
+ let current = node;
22072
+ while (isErasedExpression(current)) current = current.expression;
22073
+ return current;
22074
+ }
22075
+ function wrappedExpression(node) {
22076
+ let current = node;
22077
+ while (current.parent !== void 0 && isErasedExpression(current.parent) && current.parent.expression === current) current = current.parent;
22078
+ return current;
22079
+ }
22080
+ function enclosingFunction5(context, node) {
22081
+ return [...context.sourceCode.getAncestors(node)].reverse().find(isFunction);
22082
+ }
22083
+ function localCallCount(context, node, owner) {
22084
+ const seen = /* @__PURE__ */ new Set();
22085
+ const pending = [node];
22086
+ let count = 0;
22087
+ while (pending.length > 0) {
22088
+ const current = pending.pop();
22089
+ if (current === void 0 || enclosingFunction5(context, current) !== owner) return null;
22090
+ const expression = wrappedExpression(current);
22091
+ const parent = expression.parent;
22092
+ if (parent?.type === AST_NODE_TYPES100.CallExpression && parent.callee === expression) {
22093
+ count += 1;
22094
+ continue;
22095
+ }
22096
+ if (parent?.type !== AST_NODE_TYPES100.VariableDeclarator || parent.init !== expression || parent.id.type !== AST_NODE_TYPES100.Identifier || parent.parent.type !== AST_NODE_TYPES100.VariableDeclaration || parent.parent.kind !== "const") return null;
22097
+ const variable = context.sourceCode.getDeclaredVariables(parent)[0];
22098
+ if (variable === void 0 || seen.has(variable) || !stableVariable(variable)) return null;
22099
+ seen.add(variable);
22100
+ pending.push(...variable.references.filter((reference) => reference.isRead()).map((reference) => reference.identifier));
22101
+ }
22102
+ return count;
22103
+ }
22070
22104
  function moduleScope(context, program) {
22071
22105
  const declarations = moduleDefinitions(program);
22072
22106
  const counts = /* @__PURE__ */ new Map();
@@ -22082,6 +22116,26 @@ function moduleScope(context, program) {
22082
22116
  const definitions = scopeDefinitions.filter(
22083
22117
  (definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
22084
22118
  );
22119
+ const { calls, pinned } = moduleCallGraph(context, scopeDefinitions);
22120
+ const statementIndexes = new Map(program.body.map((statement, index) => [statement, index]));
22121
+ const runtimeBarrierPrefix = [0];
22122
+ for (const statement of program.body) {
22123
+ runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isDeferredModuleStatement(statement) ? 0 : 1));
22124
+ }
22125
+ const statementIndex = (definition) => {
22126
+ let current = definition.node;
22127
+ while (current.parent !== void 0 && current.parent.type !== AST_NODE_TYPES100.Program) current = current.parent;
22128
+ return statementIndexes.get(current);
22129
+ };
22130
+ const canMove = (helper, caller) => {
22131
+ if (helper.node.type === AST_NODE_TYPES100.FunctionDeclaration) return true;
22132
+ const helperIndex = statementIndex(helper);
22133
+ const callerIndex = statementIndex(caller);
22134
+ return helperIndex !== void 0 && callerIndex !== void 0 && runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
22135
+ };
22136
+ reportMisordered(context, definitions, scopeDefinitions, calls, pinned, canMove);
22137
+ }
22138
+ function moduleCallGraph(context, scopeDefinitions) {
22085
22139
  const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
22086
22140
  const calls = /* @__PURE__ */ new Map();
22087
22141
  const pinned = /* @__PURE__ */ new Set();
@@ -22089,26 +22143,33 @@ function moduleScope(context, program) {
22089
22143
  const variable = context.sourceCode.getDeclaredVariables(definition.bindingNode)[0];
22090
22144
  for (const reference of variable?.references ?? []) {
22091
22145
  if (reference.isWrite() && reference.init !== true) {
22092
- pinned.add(definition.name);
22146
+ pinned.add(definition.key);
22093
22147
  continue;
22094
22148
  }
22095
22149
  if (!reference.isRead()) continue;
22096
- const identifier = reference.identifier;
22097
- const ancestors = context.sourceCode.getAncestors(identifier);
22098
- const nearestFunction2 = [...ancestors].reverse().find(isFunction);
22099
- const parent = identifier.parent;
22100
- const callerDefinition = nearestFunction2 === void 0 ? void 0 : byFunction.get(nearestFunction2);
22101
- if (callerDefinition === void 0 || parent.type !== AST_NODE_TYPES100.CallExpression || parent.callee !== identifier) {
22102
- pinned.add(definition.name);
22150
+ const callerDefinition = byFunction.get(enclosingFunction5(context, reference.identifier));
22151
+ if (callerDefinition?.functionNode === void 0) {
22152
+ pinned.add(definition.key);
22103
22153
  continue;
22104
22154
  }
22105
- const caller = callerDefinition.name;
22106
- const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22107
- callees.add(definition.name);
22108
- calls.set(caller, callees);
22155
+ const count = localCallCount(context, reference.identifier, callerDefinition.functionNode);
22156
+ if (count === null) {
22157
+ pinned.add(definition.key);
22158
+ } else if (count > 0) {
22159
+ const callees = calls.get(callerDefinition.key) ?? /* @__PURE__ */ new Set();
22160
+ callees.add(definition.key);
22161
+ calls.set(callerDefinition.key, callees);
22162
+ }
22109
22163
  }
22110
22164
  }
22111
- reportMisordered(context, definitions, scopeDefinitions, calls, pinned);
22165
+ return { calls, pinned };
22166
+ }
22167
+ function isDeferredModuleStatement(statement) {
22168
+ const node = statement.type === AST_NODE_TYPES100.ExportNamedDeclaration || statement.type === AST_NODE_TYPES100.ExportDefaultDeclaration ? statement.declaration : statement;
22169
+ if (node === null || node.type === AST_NODE_TYPES100.FunctionDeclaration || node.type === AST_NODE_TYPES100.TSDeclareFunction || node.type === AST_NODE_TYPES100.TSInterfaceDeclaration || node.type === AST_NODE_TYPES100.TSTypeAliasDeclaration || node.type === AST_NODE_TYPES100.ImportDeclaration || node.type === AST_NODE_TYPES100.ExportAllDeclaration || node.type === AST_NODE_TYPES100.EmptyStatement) return true;
22170
+ return node.type === AST_NODE_TYPES100.VariableDeclaration && node.declarations.every(
22171
+ (declaration) => declaration.id.type === AST_NODE_TYPES100.Identifier && declaration.init !== null && isFunction(unwrapExpression7(declaration.init))
22172
+ );
22112
22173
  }
22113
22174
  function exportedNames(program) {
22114
22175
  const names = /* @__PURE__ */ new Set();
@@ -22142,16 +22203,18 @@ function moduleDefinitions(program) {
22142
22203
  for (const statement of program.body) {
22143
22204
  const node = statement.type === AST_NODE_TYPES100.ExportNamedDeclaration || statement.type === AST_NODE_TYPES100.ExportDefaultDeclaration ? statement.declaration : statement;
22144
22205
  if (node?.type === AST_NODE_TYPES100.FunctionDeclaration && node.id !== null && node.body !== null) {
22145
- definitions.push({ name: node.id.name, node, functionNode: node, bindingNode: node });
22206
+ definitions.push({ key: node.id.name, name: node.id.name, node, functionNode: node, bindingNode: node });
22146
22207
  continue;
22147
22208
  }
22148
- if (node?.type !== AST_NODE_TYPES100.VariableDeclaration || node.kind !== "const") continue;
22209
+ if (node?.type !== AST_NODE_TYPES100.VariableDeclaration || node.kind !== "const" || node.declarations.length !== 1) continue;
22149
22210
  for (const declarator of node.declarations) {
22150
- if (declarator.id.type === AST_NODE_TYPES100.Identifier && declarator.init !== null && isFunction(declarator.init)) {
22211
+ const initializer = declarator.init === null ? null : unwrapExpression7(declarator.init);
22212
+ if (declarator.id.type === AST_NODE_TYPES100.Identifier && initializer !== null && isFunction(initializer)) {
22151
22213
  definitions.push({
22214
+ key: declarator.id.name,
22152
22215
  name: declarator.id.name,
22153
22216
  node: declarator,
22154
- functionNode: declarator.init,
22217
+ functionNode: initializer,
22155
22218
  bindingNode: declarator
22156
22219
  });
22157
22220
  }
@@ -22161,15 +22224,21 @@ function moduleDefinitions(program) {
22161
22224
  }
22162
22225
  function methodName2(node) {
22163
22226
  if (node.key.type === AST_NODE_TYPES100.PrivateIdentifier) return `#${node.key.name}`;
22164
- return !node.computed && node.key.type === AST_NODE_TYPES100.Identifier ? node.key.name : null;
22227
+ if (!node.computed && node.key.type === AST_NODE_TYPES100.Identifier) return node.key.name;
22228
+ return node.key.type === AST_NODE_TYPES100.Literal && typeof node.key.value === "string" ? node.key.value : null;
22165
22229
  }
22166
- function referencedMethod(context, node, classVariables) {
22167
- const objectVariable = node.object.type === AST_NODE_TYPES100.Identifier ? ASTUtils51.findVariable(context.sourceCode.getScope(node.object), node.object.name) : null;
22168
- const isClassReference = objectVariable !== null && classVariables.has(objectVariable);
22169
- if (node.object.type !== AST_NODE_TYPES100.ThisExpression && !isClassReference) return null;
22170
- if (node.property.type === AST_NODE_TYPES100.PrivateIdentifier) return `#${node.property.name}`;
22171
- if (!node.computed && node.property.type === AST_NODE_TYPES100.Identifier) return node.property.name;
22172
- return node.computed && node.property.type === AST_NODE_TYPES100.Literal && typeof node.property.value === "string" ? node.property.value : null;
22230
+ function methodKey(name, isStatic2) {
22231
+ return `${isStatic2 ? "static" : "instance"}:${name}`;
22232
+ }
22233
+ function receiverDomain(context, object, receivers, thisIsStatic) {
22234
+ const unwrapped = unwrapExpression7(object);
22235
+ if (unwrapped.type === AST_NODE_TYPES100.ThisExpression) return thisIsStatic;
22236
+ if (unwrapped.type !== AST_NODE_TYPES100.Identifier) return null;
22237
+ const variable = ASTUtils51.findVariable(context.sourceCode.getScope(unwrapped), unwrapped.name);
22238
+ return variable === null ? null : receivers.get(variable) ?? null;
22239
+ }
22240
+ function stableVariable(variable) {
22241
+ return !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
22173
22242
  }
22174
22243
  function referencedPropertyName(node) {
22175
22244
  if (node.property.type === AST_NODE_TYPES100.PrivateIdentifier) return `#${node.property.name}`;
@@ -22178,7 +22247,7 @@ function referencedPropertyName(node) {
22178
22247
  }
22179
22248
  function walk(node, visitorKeys, visit, nestedFunction = false) {
22180
22249
  visit(node, nestedFunction);
22181
- const nested = nestedFunction || isFunction(node);
22250
+ const nested = nestedFunction || isFunction(node) || node.type === AST_NODE_TYPES100.ClassDeclaration || node.type === AST_NODE_TYPES100.ClassExpression;
22182
22251
  forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
22183
22252
  }
22184
22253
  function classScope(context, node, computedReferenceNames) {
@@ -22186,123 +22255,127 @@ function classScope(context, node, computedReferenceNames) {
22186
22255
  (member) => member.type === AST_NODE_TYPES100.MethodDefinition
22187
22256
  );
22188
22257
  const counts = classMethodCounts(node.body, methods);
22189
- const scopeDefinitions = methods.flatMap((method) => {
22258
+ const implementations = methods.filter((method) => isFunction(method.value));
22259
+ const implementationCounts = classMethodCounts(node.body, implementations);
22260
+ const scopeDefinitions = implementations.flatMap((method) => {
22190
22261
  const name = methodName2(method);
22191
- return name !== null && counts.get(name) === 1 ? [{ name, node: method }] : [];
22192
- });
22193
- const definitions = methods.flatMap((method) => {
22194
- const name = methodName2(method);
22195
- const isPrivate = method.accessibility === "private" || method.key.type === AST_NODE_TYPES100.PrivateIdentifier;
22196
- return name !== null && isPrivate && counts.get(name) === 1 && method.decorators.length === 0 ? [{ name, node: method }] : [];
22262
+ const key = name === null ? null : methodKey(name, method.static);
22263
+ return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
22197
22264
  });
22265
+ const definitions = scopeDefinitions.filter(
22266
+ ({ key, node: method }) => (method.accessibility === "private" || method.key.type === AST_NODE_TYPES100.PrivateIdentifier) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
22267
+ );
22198
22268
  if (definitions.length === 0) return;
22199
- const privateNames = new Set(definitions.map((definition) => definition.name));
22269
+ const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
22200
22270
  const calls = /* @__PURE__ */ new Map();
22201
22271
  const pinned = /* @__PURE__ */ new Set();
22202
- const classVariables = /* @__PURE__ */ new Set();
22203
- if (node.id !== null) {
22204
- const internal = ASTUtils51.findVariable(context.sourceCode.getScope(node), node.id.name);
22205
- if (internal !== null) classVariables.add(internal);
22206
- }
22207
- if (node.type === AST_NODE_TYPES100.ClassExpression && node.parent.type === AST_NODE_TYPES100.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES100.Identifier) {
22208
- const outer = ASTUtils51.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
22209
- if (outer !== null) classVariables.add(outer);
22272
+ const classReceivers = classReceiverBindings(context, node);
22273
+ const pinNames = (name, domain = null) => {
22274
+ for (const definition of definitions) {
22275
+ if ((name === null || definition.name === name) && (domain === null || definition.node.static === domain)) {
22276
+ pinned.add(definition.key);
22277
+ }
22278
+ }
22279
+ };
22280
+ function pinDestructuredMembers(binding, domain) {
22281
+ for (const property of binding.properties) {
22282
+ if (property.type === AST_NODE_TYPES100.RestElement || property.computed) {
22283
+ pinNames(null, domain);
22284
+ } else if (property.key.type === AST_NODE_TYPES100.Identifier) {
22285
+ pinNames(property.key.name, domain);
22286
+ } else if (property.key.type === AST_NODE_TYPES100.Literal && typeof property.key.value === "string") {
22287
+ pinNames(property.key.value, domain);
22288
+ }
22289
+ }
22210
22290
  }
22211
22291
  function collectMethodCalls(method) {
22212
- const caller = methodName2(method);
22213
- if (caller === null || method.value.body === null) return;
22214
- const methodClassVariables = new Set(classVariables);
22215
- const methodAliases = /* @__PURE__ */ new Set();
22292
+ const name = methodName2(method);
22293
+ if (name === null) {
22294
+ pinNames(null);
22295
+ return;
22296
+ }
22297
+ if (!isFunction(method.value)) return;
22298
+ const methodFunction = method.value;
22299
+ const caller = methodKey(name, method.static);
22300
+ const receivers = new Map(classReceivers);
22216
22301
  const parameterDecoratorNodes = /* @__PURE__ */ new Set();
22217
22302
  for (const parameter of method.value.params) {
22218
22303
  for (const decorator of parameter.decorators) {
22219
22304
  walk(decorator, context.sourceCode.visitorKeys, (current) => parameterDecoratorNodes.add(current));
22220
22305
  }
22221
22306
  }
22222
- const thisValue = (value) => {
22223
- let current = value;
22224
- while (current?.type === AST_NODE_TYPES100.TSAsExpression || current?.type === AST_NODE_TYPES100.TSSatisfiesExpression || current?.type === AST_NODE_TYPES100.TSNonNullExpression) current = current.expression;
22225
- return current?.type === AST_NODE_TYPES100.ThisExpression;
22226
- };
22227
22307
  const collectAlias = (current, nestedFunction) => {
22228
- if (nestedFunction || current.type !== AST_NODE_TYPES100.VariableDeclarator && current.type !== AST_NODE_TYPES100.AssignmentPattern) return;
22229
- if (current.type === AST_NODE_TYPES100.VariableDeclarator && (current.parent.type !== AST_NODE_TYPES100.VariableDeclaration || current.parent.kind !== "const")) return;
22230
- const binding = current.type === AST_NODE_TYPES100.VariableDeclarator ? current.id : current.left;
22231
- const value = current.type === AST_NODE_TYPES100.VariableDeclarator ? current.init : current.right;
22232
- if (!thisValue(value)) return;
22233
- if (binding.type === AST_NODE_TYPES100.ObjectPattern) {
22234
- pinDestructuredMembers(binding);
22308
+ if (nestedFunction || current.type !== AST_NODE_TYPES100.VariableDeclarator || current.init === null) return;
22309
+ const domain = receiverDomain(context, current.init, receivers, method.static);
22310
+ if (domain === null) return;
22311
+ if (current.id.type === AST_NODE_TYPES100.ObjectPattern) {
22312
+ pinDestructuredMembers(current.id, domain);
22235
22313
  return;
22236
22314
  }
22237
- if (binding.type !== AST_NODE_TYPES100.Identifier) return;
22238
- const variable = ASTUtils51.findVariable(context.sourceCode.getScope(binding), binding.name);
22239
- if (variable !== null) {
22240
- methodClassVariables.add(variable);
22241
- methodAliases.add(variable);
22315
+ if (current.id.type !== AST_NODE_TYPES100.Identifier) return;
22316
+ if (current.parent.type !== AST_NODE_TYPES100.VariableDeclaration || current.parent.kind !== "const") {
22317
+ pinNames(null, domain);
22318
+ return;
22242
22319
  }
22320
+ const variable = context.sourceCode.getDeclaredVariables(current)[0];
22321
+ if (variable === void 0 || !stableVariable(variable)) return;
22322
+ const local = variable.references.every((reference) => {
22323
+ if (!reference.isRead()) return true;
22324
+ const expression = wrappedExpression(reference.identifier);
22325
+ return enclosingFunction5(context, reference.identifier) === methodFunction && expression.parent?.type === AST_NODE_TYPES100.MemberExpression && expression.parent.object === expression;
22326
+ });
22327
+ if (local) receivers.set(variable, domain);
22328
+ else pinNames(null, domain);
22243
22329
  };
22244
- for (const parameter of method.value.params) {
22245
- walk(parameter, context.sourceCode.visitorKeys, collectAlias);
22246
- }
22247
- for (const statement of method.value.body.body) {
22248
- walk(statement, context.sourceCode.visitorKeys, collectAlias);
22249
- }
22250
- function pinDestructuredMembers(binding) {
22251
- for (const property of binding.properties) {
22252
- if (property.type === AST_NODE_TYPES100.RestElement) {
22253
- for (const name of privateNames) pinned.add(name);
22254
- } else if (property.key.type === AST_NODE_TYPES100.Identifier && privateNames.has(property.key.name)) {
22255
- pinned.add(property.key.name);
22256
- }
22257
- }
22258
- }
22330
+ for (const statement of method.value.body.body) walk(statement, context.sourceCode.visitorKeys, collectAlias);
22259
22331
  const visitCall = (current, nestedFunction) => {
22260
- if (current.type === AST_NODE_TYPES100.VariableDeclarator && current.id.type === AST_NODE_TYPES100.ObjectPattern && thisValue(current.init)) {
22261
- pinDestructuredMembers(current.id);
22332
+ const destructuring = receiverDestructuring(current);
22333
+ if (destructuring !== null) {
22334
+ const domain2 = receiverDomain(context, destructuring.value, receivers, method.static);
22335
+ pinDestructuredMembers(destructuring.binding, domain2);
22262
22336
  }
22263
22337
  if (current.type !== AST_NODE_TYPES100.MemberExpression) return;
22264
- const target = referencedMethod(context, current, methodClassVariables);
22265
- if (target === null) {
22266
- const possibleTarget = referencedPropertyName(current);
22267
- if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
22338
+ const domain = receiverDomain(context, current.object, receivers, method.static);
22339
+ const targetName = referencedPropertyName(current);
22340
+ if (domain === null) {
22341
+ pinNames(targetName);
22268
22342
  return;
22269
22343
  }
22270
- if (!privateNames.has(target)) return;
22271
- const objectVariable = current.object.type === AST_NODE_TYPES100.Identifier ? ASTUtils51.findVariable(context.sourceCode.getScope(current.object), current.object.name) : null;
22272
- if (objectVariable !== null && methodAliases.has(objectVariable)) {
22273
- pinned.add(target);
22344
+ if (targetName === null) {
22345
+ if (current.computed) pinNames(null, domain);
22274
22346
  return;
22275
22347
  }
22276
- if (current.computed || nestedFunction || parameterDecoratorNodes.has(current) || current.parent.type !== AST_NODE_TYPES100.CallExpression || current.parent.callee !== current) {
22348
+ const target = methodKey(targetName, domain);
22349
+ if (!methodKeys.has(target)) return;
22350
+ if (current.computed || nestedFunction || parameterDecoratorNodes.has(current)) {
22277
22351
  pinned.add(target);
22278
22352
  return;
22279
22353
  }
22280
- const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22281
- callees.add(target);
22282
- calls.set(caller, callees);
22354
+ const count = localCallCount(context, current, methodFunction);
22355
+ if (count === null) {
22356
+ pinned.add(target);
22357
+ } else if (count > 0) {
22358
+ const callees = calls.get(caller) ?? /* @__PURE__ */ new Set();
22359
+ callees.add(target);
22360
+ calls.set(caller, callees);
22361
+ }
22283
22362
  };
22284
- for (const decorator of method.decorators) {
22285
- walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
22286
- }
22363
+ for (const decorator of method.decorators) walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
22287
22364
  if (method.computed) walk(method.key, context.sourceCode.visitorKeys, visitCall, true);
22288
- for (const parameter of method.value.params) {
22289
- walk(parameter, context.sourceCode.visitorKeys, visitCall);
22290
- }
22291
- for (const statement of method.value.body.body) {
22292
- walk(statement, context.sourceCode.visitorKeys, visitCall);
22293
- }
22365
+ for (const parameter of method.value.params) walk(parameter, context.sourceCode.visitorKeys, visitCall);
22366
+ for (const statement of method.value.body.body) walk(statement, context.sourceCode.visitorKeys, visitCall);
22294
22367
  }
22295
22368
  for (const method of methods) collectMethodCalls(method);
22296
22369
  for (const member of node.body.body) {
22297
22370
  if (member.type === AST_NODE_TYPES100.MethodDefinition || member.type === AST_NODE_TYPES100.TSAbstractMethodDefinition) continue;
22298
22371
  walk(member, context.sourceCode.visitorKeys, (current) => {
22299
22372
  if (current.type !== AST_NODE_TYPES100.MemberExpression) return;
22300
- const target = referencedMethod(context, current, classVariables);
22301
- const possibleTarget = target ?? referencedPropertyName(current);
22302
- if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
22373
+ pinNames(referencedPropertyName(current));
22303
22374
  });
22304
22375
  }
22305
- for (const name of privateNames) if (computedReferenceNames.has(name)) pinned.add(name);
22376
+ for (const definition of definitions) {
22377
+ if (computedReferenceNames.has(definition.name)) pinned.add(definition.key);
22378
+ }
22306
22379
  const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
22307
22380
  const runtimeBarrierPrefix = [0];
22308
22381
  for (const member of node.body.body) {
@@ -22316,6 +22389,27 @@ function classScope(context, node, computedReferenceNames) {
22316
22389
  };
22317
22390
  reportMisordered(context, definitions, scopeDefinitions, calls, pinned, canMove);
22318
22391
  }
22392
+ function classReceiverBindings(context, node) {
22393
+ const classReceivers = /* @__PURE__ */ new Map();
22394
+ if (node.id !== null) {
22395
+ const internal = ASTUtils51.findVariable(context.sourceCode.getScope(node), node.id.name);
22396
+ if (internal !== null && stableVariable(internal)) classReceivers.set(internal, true);
22397
+ }
22398
+ if (node.type === AST_NODE_TYPES100.ClassExpression && node.parent.type === AST_NODE_TYPES100.VariableDeclarator && node.parent.id.type === AST_NODE_TYPES100.Identifier && node.parent.parent.type === AST_NODE_TYPES100.VariableDeclaration && node.parent.parent.kind === "const") {
22399
+ const outer = ASTUtils51.findVariable(context.sourceCode.getScope(node.parent), node.parent.id.name);
22400
+ if (outer !== null && stableVariable(outer)) classReceivers.set(outer, true);
22401
+ }
22402
+ return classReceivers;
22403
+ }
22404
+ function receiverDestructuring(node) {
22405
+ if (node.type === AST_NODE_TYPES100.VariableDeclarator && node.id.type === AST_NODE_TYPES100.ObjectPattern && node.init !== null) {
22406
+ return { binding: node.id, value: node.init };
22407
+ }
22408
+ if ((node.type === AST_NODE_TYPES100.AssignmentPattern || node.type === AST_NODE_TYPES100.AssignmentExpression) && node.left.type === AST_NODE_TYPES100.ObjectPattern) {
22409
+ return { binding: node.left, value: node.right };
22410
+ }
22411
+ return null;
22412
+ }
22319
22413
  function isClassRuntimeBarrier(member) {
22320
22414
  switch (member.type) {
22321
22415
  case AST_NODE_TYPES100.StaticBlock:
@@ -22366,12 +22460,18 @@ function classMethodCounts(body2, methods) {
22366
22460
  const counts = /* @__PURE__ */ new Map();
22367
22461
  for (const method of methods) {
22368
22462
  const name = methodName2(method);
22369
- if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
22463
+ if (name !== null) {
22464
+ const key = methodKey(name, method.static);
22465
+ counts.set(key, (counts.get(key) ?? 0) + 1);
22466
+ }
22370
22467
  }
22371
22468
  for (const member of body2.body) {
22372
22469
  if (member.type !== AST_NODE_TYPES100.TSAbstractMethodDefinition) continue;
22373
22470
  const name = !member.computed && member.key.type === AST_NODE_TYPES100.Identifier ? member.key.name : null;
22374
- if (name !== null) counts.set(name, (counts.get(name) ?? 0) + 1);
22471
+ if (name !== null) {
22472
+ const key = methodKey(name, member.static);
22473
+ counts.set(key, (counts.get(key) ?? 0) + 1);
22474
+ }
22375
22475
  }
22376
22476
  return counts;
22377
22477
  }
@@ -22704,14 +22804,14 @@ function importedAssertionKind(specifier) {
22704
22804
  // src/rules/sole-export-matches-filename.ts
22705
22805
  import { AST_NODE_TYPES as AST_NODE_TYPES102 } from "@typescript-eslint/utils";
22706
22806
  var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
22707
- defaultLevel: "warning",
22807
+ defaultLevel: "error",
22708
22808
  summary: "Make a module filename reflect its sole named public runtime export.",
22709
22809
  rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
22710
- 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.",
22810
+ remediation: "Name the module for the exported responsibility, using the full kebab-case export name; otherwise colocate genuinely related exports.",
22711
22811
  category: "maintainability",
22712
22812
  limitations: [
22713
- "Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS, and re-exports are excluded.",
22714
- "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.",
22813
+ "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.",
22814
+ "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.",
22715
22815
  "Exported destructuring patterns are excluded rather than undercounted as public exports."
22716
22816
  ],
22717
22817
  examples: [
@@ -22720,39 +22820,16 @@ var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
22720
22820
  ]
22721
22821
  };
22722
22822
  var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
22723
- "common",
22724
22823
  "global",
22725
- "helpers",
22726
22824
  "index",
22727
22825
  "layout",
22728
22826
  "loading",
22729
22827
  "middleware",
22730
- "misc",
22731
22828
  "not-found",
22732
22829
  "page",
22733
22830
  "route",
22734
- "shared",
22735
22831
  "template",
22736
- "types",
22737
- "util",
22738
- "utils"
22739
- ]);
22740
- var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
22741
- "adapter",
22742
- "client",
22743
- "config",
22744
- "controller",
22745
- "factory",
22746
- "handler",
22747
- "manager",
22748
- "provider",
22749
- "record",
22750
- "repository",
22751
- "router",
22752
- "schema",
22753
- "service",
22754
- "store",
22755
- "worker"
22832
+ "types"
22756
22833
  ]);
22757
22834
  function stem3(filename) {
22758
22835
  const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
@@ -22761,34 +22838,6 @@ function stem3(filename) {
22761
22838
  function kebabCase(name) {
22762
22839
  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();
22763
22840
  }
22764
- function reflectsExportName(fileStem, exportedStem) {
22765
- if (fileStem.startsWith("_")) return false;
22766
- const visibleFileStem = fileStem.toLowerCase();
22767
- const fileTokens = visibleFileStem.split("-").filter(Boolean);
22768
- const exportTokens = exportedStem.split("-").filter(Boolean);
22769
- if (fileTokens.length === 0 || exportTokens.length === 0) return false;
22770
- if (fileTokens.length === 1) {
22771
- const [token] = fileTokens;
22772
- return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
22773
- }
22774
- const compactFile = fileTokens.join("");
22775
- if (compactFile.length < 6) return false;
22776
- const boundaryPhrases = /* @__PURE__ */ new Set();
22777
- for (let index = 1; index <= exportTokens.length; index += 1) {
22778
- boundaryPhrases.add(exportTokens.slice(0, index).join(""));
22779
- boundaryPhrases.add(exportTokens.slice(-index).join(""));
22780
- }
22781
- return boundaryPhrases.has(compactFile);
22782
- }
22783
- function declarationExport(statement) {
22784
- const declaration = statement.declaration;
22785
- if (declaration === null || declaration.declare === true) return [];
22786
- if (declaration.type === AST_NODE_TYPES102.ClassDeclaration || declaration.type === AST_NODE_TYPES102.FunctionDeclaration || declaration.type === AST_NODE_TYPES102.TSEnumDeclaration) return declaration.id === null ? [] : [{ name: declaration.id.name, node: declaration }];
22787
- if (declaration.type !== AST_NODE_TYPES102.VariableDeclaration) return [];
22788
- return declaration.declarations.flatMap(
22789
- (item) => item.id.type === AST_NODE_TYPES102.Identifier ? [{ name: item.id.name, node: item }] : []
22790
- );
22791
- }
22792
22841
  var sole_export_matches_filename_default = createRule({
22793
22842
  name: "sole-export-matches-filename",
22794
22843
  documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
@@ -22804,64 +22853,24 @@ var sole_export_matches_filename_default = createRule({
22804
22853
  create(context) {
22805
22854
  const fileStem = stem3(context.filename);
22806
22855
  const normalizedFilename = context.filename.replaceAll("\\", "/");
22807
- 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 {};
22856
+ 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 {};
22857
+ let hasCommonJsExport = false;
22808
22858
  return {
22859
+ "CallExpression, MemberExpression"(node) {
22860
+ if (isCommonJsExportReference(node, context.sourceCode)) hasCommonJsExport = true;
22861
+ },
22809
22862
  "Program:exit"(program) {
22810
- const exports = [];
22811
- const publicExports = /* @__PURE__ */ new Set();
22812
- function collectPublicExport(statement) {
22813
- if (statement.type === AST_NODE_TYPES102.ExportAllDeclaration || statement.type === AST_NODE_TYPES102.ExportNamedDeclaration && statement.source !== null) return false;
22814
- if (statement.type === AST_NODE_TYPES102.ExportDefaultDeclaration) {
22815
- publicExports.add("default");
22816
- const declaration = statement.declaration;
22817
- if ((declaration.type === AST_NODE_TYPES102.ClassDeclaration || declaration.type === AST_NODE_TYPES102.FunctionDeclaration) && declaration.id !== null) exports.push({ name: declaration.id.name, node: declaration });
22818
- else return false;
22819
- }
22820
- if (statement.type !== AST_NODE_TYPES102.ExportNamedDeclaration) return true;
22821
- return collectNamedExport(statement);
22822
- }
22823
- function collectNamedExport(statement) {
22824
- const declaration = statement.declaration;
22825
- if (declaration !== null && "id" in declaration && declaration.id?.type === AST_NODE_TYPES102.Identifier) {
22826
- publicExports.add(declaration.id.name);
22827
- }
22828
- if (declaration?.type === AST_NODE_TYPES102.VariableDeclaration) {
22829
- if (declaration.declarations.some((item) => item.id.type !== AST_NODE_TYPES102.Identifier)) return false;
22830
- for (const item of declaration.declarations) {
22831
- if (item.id.type === AST_NODE_TYPES102.Identifier) publicExports.add(item.id.name);
22832
- }
22833
- }
22834
- for (const specifier of statement.specifiers) {
22835
- const exported = specifier.exported.type === AST_NODE_TYPES102.Identifier ? specifier.exported.name : specifier.exported.value;
22836
- publicExports.add(String(exported));
22837
- }
22838
- if (statement.exportKind === "type") return true;
22839
- exports.push(...declarationExport(statement));
22840
- collectRuntimeSpecifiers(statement);
22841
- return true;
22842
- }
22843
- function collectRuntimeSpecifiers(statement) {
22844
- for (const specifier of statement.specifiers) {
22845
- const exported = specifier.exported.type === AST_NODE_TYPES102.Identifier ? specifier.exported.name : specifier.exported.value;
22846
- publicExports.add(String(exported));
22847
- if (specifier.exportKind === "type") continue;
22848
- if (exported === "default") exports.push({ name: specifier.local.name, node: specifier });
22849
- else exports.push({ name: String(exported), node: specifier });
22850
- }
22851
- }
22852
- for (const statement of program.body) {
22853
- if (!collectPublicExport(statement)) return;
22854
- }
22855
- const unique = new Map(exports.map((item) => [item.name, item]));
22856
- if (unique.size !== 1 || publicExports.size !== 1) return;
22857
- const only = [...unique.values()][0];
22863
+ if (hasCommonJsExport) return;
22864
+ const exports = runtimeExports(program);
22865
+ if (exports.ambiguous || exports.exports.length !== 1) return;
22866
+ const only = exports.exports[0];
22858
22867
  if (only === void 0) return;
22859
22868
  if (only.name === "onRouterTransitionStart" && /(?:^|\/)instrumentation-client\.[jt]s$/u.test(normalizedFilename)) return;
22860
22869
  if (only.name === "collections" && /(?:^|\/)src\/content\.config\.(?:ts|js|mjs)$/u.test(normalizedFilename) && program.body.some((statement) => statement.type === AST_NODE_TYPES102.ImportDeclaration && statement.source.value === "astro:content")) return;
22861
22870
  const exportedStem = kebabCase(only.name);
22862
22871
  if (exportedStem === "") return;
22863
22872
  const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
22864
- if (expected === fileStem.toLowerCase() || reflectsExportName(fileStem, exportedStem)) return;
22873
+ if (expected === fileStem.toLowerCase()) return;
22865
22874
  context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
22866
22875
  }
22867
22876
  };
@@ -23079,7 +23088,7 @@ var chainMemberNames2 = (node) => {
23079
23088
  names.reverse();
23080
23089
  return names;
23081
23090
  };
23082
- var unwrapExpression7 = (node) => {
23091
+ var unwrapExpression8 = (node) => {
23083
23092
  let current = node;
23084
23093
  while (current.type === AST_NODE_TYPES103.TSAsExpression || current.type === AST_NODE_TYPES103.TSSatisfiesExpression || current.type === AST_NODE_TYPES103.TSNonNullExpression || current.type === AST_NODE_TYPES103.TSTypeAssertion) {
23085
23094
  current = current.expression;
@@ -23130,7 +23139,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
23130
23139
  return binding !== null && schemaBindings.has(binding);
23131
23140
  }
23132
23141
  function isConfirmedSchema(expression) {
23133
- const init = unwrapExpression7(expression);
23142
+ const init = unwrapExpression8(expression);
23134
23143
  if (init.type === AST_NODE_TYPES103.Identifier) return isSchemaBinding(init);
23135
23144
  if (init.type !== AST_NODE_TYPES103.CallExpression || init.callee.type !== AST_NODE_TYPES103.MemberExpression) {
23136
23145
  return false;
@@ -23175,7 +23184,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
23175
23184
  }
23176
23185
  });
23177
23186
 
23178
- // src/rules/_renames.ts
23187
+ // src/rules/_renamed-rules.ts
23179
23188
  var RENAMED_RULES = {
23180
23189
  "jsdoc-restates-signature": "no-restated-jsdoc",
23181
23190
  "zod-naming-convention": "require-pascal-case-zod-schema-name",
@@ -23184,7 +23193,7 @@ var RENAMED_RULES = {
23184
23193
  "trailing-value-narration": "no-trailing-value-narration"
23185
23194
  };
23186
23195
 
23187
- // src/rules/_retired.ts
23196
+ // src/rules/_retired-rules.ts
23188
23197
  var RETIRED_RULES = {
23189
23198
  "ban-loose-type-guards-in-tests": {
23190
23199
  removedIn: "5.0.0",
@@ -23376,7 +23385,7 @@ var RULES = {
23376
23385
  };
23377
23386
  var meta = {
23378
23387
  name: "@sarj/eslint-plugin",
23379
- version: "15.28.1"
23388
+ version: "16.0.0"
23380
23389
  };
23381
23390
  var APPLICATION_ONLY_RULES = [];
23382
23391
  var LIBRARY_IMPORT_POLICY = ["error", {
@@ -23401,8 +23410,7 @@ var ADVISORY_RULES = [
23401
23410
  "@sarj/prefer-whole-object-assertion",
23402
23411
  "@sarj/require-camelcase-properties",
23403
23412
  "@sarj/require-interface-for-exported-class",
23404
- "@sarj/require-sql-access-class",
23405
- "@sarj/sole-export-matches-filename"
23413
+ "@sarj/require-sql-access-class"
23406
23414
  ];
23407
23415
  var RECOMMENDED_RULES = {
23408
23416
  "@sarj/no-conditional-empty-object-spread": "error",
@@ -23514,7 +23522,7 @@ var RECOMMENDED_RULES = {
23514
23522
  "@sarj/store-insert-requires-on-conflict": "error",
23515
23523
  "@sarj/stepdown": "error",
23516
23524
  "@sarj/source-coupled-test": "error",
23517
- "@sarj/sole-export-matches-filename": "warn",
23525
+ "@sarj/sole-export-matches-filename": "error",
23518
23526
  "@sarj/test-phase-label-comment": "error",
23519
23527
  "@sarj/require-pascal-case-zod-schema-name": "error"
23520
23528
  };
@@ -23632,7 +23640,7 @@ var STRICT_RULES = {
23632
23640
  "@sarj/store-insert-requires-on-conflict": "error",
23633
23641
  "@sarj/stepdown": "error",
23634
23642
  "@sarj/source-coupled-test": "error",
23635
- "@sarj/sole-export-matches-filename": "warn",
23643
+ "@sarj/sole-export-matches-filename": "error",
23636
23644
  "@sarj/test-phase-label-comment": "error",
23637
23645
  "@sarj/require-pascal-case-zod-schema-name": "error"
23638
23646
  };
@@ -23655,7 +23663,7 @@ var PLUGIN = {
23655
23663
  };
23656
23664
  }
23657
23665
  };
23658
- var index_default = PLUGIN;
23666
+ var src_default = PLUGIN;
23659
23667
  export {
23660
23668
  ADVISORY_RULES,
23661
23669
  APPLICATION_ONLY_RULES,
@@ -23666,7 +23674,7 @@ export {
23666
23674
  STRICT_RULES,
23667
23675
  ADVISORY_RULES as advisoryRules,
23668
23676
  APPLICATION_ONLY_RULES as applicationOnlyRules,
23669
- index_default as default,
23677
+ src_default as default,
23670
23678
  publicDocumentation,
23671
23679
  RECOMMENDED_RULES as recommendedRules,
23672
23680
  RENAMED_RULES as renamedRules,