@sarj/eslint-plugin 15.28.0 → 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 +386 -377
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +383 -374
- package/dist/rule-examples.cjs +4 -4
- package/dist/rule-examples.js +1 -1
- package/package.json +1 -1
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/
|
|
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
|
|
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
|
|
7552
|
-
const
|
|
7520
|
+
const bindings = localBindings(program);
|
|
7521
|
+
const exports = /* @__PURE__ */ new Map();
|
|
7553
7522
|
let ambiguous = false;
|
|
7554
|
-
function
|
|
7555
|
-
if (
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
}
|
|
7563
|
-
|
|
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
|
-
|
|
7569
|
-
|
|
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
|
-
|
|
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"
|
|
7578
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
7596
|
-
return { exports: [...unique.values()], ambiguous };
|
|
7561
|
+
return { exports: [...exports.values()], ambiguous };
|
|
7597
7562
|
}
|
|
7598
|
-
function
|
|
7599
|
-
const
|
|
7600
|
-
|
|
7601
|
-
|
|
7602
|
-
|
|
7603
|
-
if (
|
|
7604
|
-
|
|
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
|
-
|
|
7630
|
-
|
|
7631
|
-
|
|
7632
|
-
|
|
7633
|
-
|
|
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
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
|
|
7642
|
-
|
|
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
|
-
|
|
7647
|
-
|
|
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
|
|
7652
|
-
const
|
|
7653
|
-
|
|
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
|
|
7656
|
-
|
|
7657
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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 :
|
|
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) };
|
|
@@ -20093,7 +20090,7 @@ var REQUIRE_FETCH_TIMEOUT_DOCUMENTATION = {
|
|
|
20093
20090
|
rationale: "An unbounded request can occupy work indefinitely when an upstream stalls.",
|
|
20094
20091
|
remediation: "Pass an abort signal, such as `AbortSignal.timeout(ms)`, in the fetch init.",
|
|
20095
20092
|
category: "correctness",
|
|
20096
|
-
limitations: ["Signal presence establishes an explicit cancellation path, not a guaranteed timeout. Forwarded Request objects can carry an existing signal."],
|
|
20093
|
+
limitations: ["Signal presence establishes an explicit cancellation path, not a guaranteed timeout. Literal null, global undefined, and void 0 do not provide a signal. Unknown spreads and dynamic values are not inferred. Forwarded Request objects can carry an existing signal."],
|
|
20097
20094
|
examples: [
|
|
20098
20095
|
{ id: "bounded-fetch", title: "Bound the request", outcome: "no-match", files: [{ path: "src/client.ts", source: "await fetch(url, { signal: AbortSignal.timeout(5000) });" }], focusPath: "src/client.ts", expectedCount: 0, public: true },
|
|
20099
20096
|
{ id: "unbounded-fetch", title: "Do not leave fetch unbounded", outcome: "match", files: [{ path: "src/client.ts", source: "await fetch('https://api.example.com/items');" }], focusPath: "src/client.ts", expectedCount: 1, public: true }
|
|
@@ -20113,22 +20110,23 @@ function matchesAnyPattern3(filename, patterns) {
|
|
|
20113
20110
|
}
|
|
20114
20111
|
return false;
|
|
20115
20112
|
}
|
|
20116
|
-
function initProvablyLacksSignal(init) {
|
|
20117
|
-
if (init.type !== AST_NODE_TYPES93.ObjectExpression)
|
|
20118
|
-
|
|
20119
|
-
}
|
|
20113
|
+
function initProvablyLacksSignal(init, resolvesToGlobal) {
|
|
20114
|
+
if (init.type !== AST_NODE_TYPES93.ObjectExpression) return false;
|
|
20115
|
+
let lacksSignal = true;
|
|
20120
20116
|
for (const prop of init.properties) {
|
|
20121
20117
|
if (prop.type === AST_NODE_TYPES93.SpreadElement) {
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
if (prop.key.type === AST_NODE_TYPES93.Identifier && prop.key.name === "signal" || prop.key.type === AST_NODE_TYPES93.Literal && prop.key.value === "signal") {
|
|
20125
|
-
return false;
|
|
20118
|
+
lacksSignal = false;
|
|
20119
|
+
continue;
|
|
20126
20120
|
}
|
|
20127
|
-
|
|
20128
|
-
|
|
20121
|
+
const isSignal = !prop.computed && prop.key.type === AST_NODE_TYPES93.Identifier && prop.key.name === "signal" || prop.key.type === AST_NODE_TYPES93.Literal && prop.key.value === "signal";
|
|
20122
|
+
if (isSignal) {
|
|
20123
|
+
const value = prop.value;
|
|
20124
|
+
lacksSignal = prop.kind === "init" && !prop.method && (value.type === AST_NODE_TYPES93.Literal && value.value === null || value.type === AST_NODE_TYPES93.Identifier && value.name === "undefined" && resolvesToGlobal(value) || value.type === AST_NODE_TYPES93.UnaryExpression && value.operator === "void" && value.argument.type === AST_NODE_TYPES93.Literal && value.argument.value === 0);
|
|
20125
|
+
} else if (prop.computed && prop.key.type !== AST_NODE_TYPES93.Literal) {
|
|
20126
|
+
lacksSignal = false;
|
|
20129
20127
|
}
|
|
20130
20128
|
}
|
|
20131
|
-
return
|
|
20129
|
+
return lacksSignal;
|
|
20132
20130
|
}
|
|
20133
20131
|
function isInlineUrl(node, resolvesToGlobal) {
|
|
20134
20132
|
return node.type === AST_NODE_TYPES93.Literal && typeof node.value === "string" || node.type === AST_NODE_TYPES93.TemplateLiteral || node.type === AST_NODE_TYPES93.NewExpression && node.callee.type === AST_NODE_TYPES93.Identifier && node.callee.name === "URL" && resolvesToGlobal(node.callee);
|
|
@@ -20185,7 +20183,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
20185
20183
|
);
|
|
20186
20184
|
if (variable?.defs.length !== 1) return false;
|
|
20187
20185
|
const definition = variable.defs[0];
|
|
20188
|
-
if (definition?.type !== "Variable" || definition.parent.kind !== "const" || definition.node.init?.type !== AST_NODE_TYPES93.ObjectExpression || !initProvablyLacksSignal(definition.node.init)) {
|
|
20186
|
+
if (definition?.type !== "Variable" || definition.parent.kind !== "const" || definition.node.init?.type !== AST_NODE_TYPES93.ObjectExpression || !initProvablyLacksSignal(definition.node.init, resolvesToGlobal)) {
|
|
20189
20187
|
return false;
|
|
20190
20188
|
}
|
|
20191
20189
|
for (const reference of variable.references) {
|
|
@@ -20217,7 +20215,7 @@ var require_fetch_timeout_default = createRule({
|
|
|
20217
20215
|
if (first !== void 0 && (node.arguments.length === 1 && !isInlineUrl(first, resolvesToGlobal) || isForwardedRequest(first))) {
|
|
20218
20216
|
return;
|
|
20219
20217
|
}
|
|
20220
|
-
if (init === void 0 || initProvablyLacksSignal(init) || init.type === AST_NODE_TYPES93.Identifier && localConstInitProvablyLacksSignal(init)) {
|
|
20218
|
+
if (init === void 0 || initProvablyLacksSignal(init, resolvesToGlobal) || init.type === AST_NODE_TYPES93.Identifier && localConstInitProvablyLacksSignal(init)) {
|
|
20221
20219
|
context.report({ node, messageId: "missingSignal" });
|
|
20222
20220
|
}
|
|
20223
20221
|
}
|
|
@@ -21960,9 +21958,9 @@ var STEPDOWN_DOCUMENTATION = {
|
|
|
21960
21958
|
remediation: "Consider moving the private helper below its sole caller after reviewing initialization and reflection dependencies.",
|
|
21961
21959
|
category: "maintainability",
|
|
21962
21960
|
limitations: [
|
|
21963
|
-
"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.",
|
|
21964
21962
|
"Class helpers must be private; their sole caller may be public, protected, or private.",
|
|
21965
|
-
"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.",
|
|
21966
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."
|
|
21967
21965
|
],
|
|
21968
21966
|
examples: [
|
|
@@ -21974,7 +21972,7 @@ function isFunction(node) {
|
|
|
21974
21972
|
return node.type === AST_NODE_TYPES100.ArrowFunctionExpression || node.type === AST_NODE_TYPES100.FunctionDeclaration || node.type === AST_NODE_TYPES100.FunctionExpression;
|
|
21975
21973
|
}
|
|
21976
21974
|
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
|
|
21977
|
-
const byName = new Map(scopeDefinitions.map((definition) => [definition.
|
|
21975
|
+
const byName = new Map(scopeDefinitions.map((definition) => [definition.key, definition]));
|
|
21978
21976
|
const cycles = cycleComponents(calls);
|
|
21979
21977
|
const callers = /* @__PURE__ */ new Map();
|
|
21980
21978
|
for (const [caller, callees] of calls) {
|
|
@@ -21986,16 +21984,16 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned,
|
|
|
21986
21984
|
}
|
|
21987
21985
|
}
|
|
21988
21986
|
for (const helper of candidates) {
|
|
21989
|
-
const helperCallers = [...callers.get(helper.
|
|
21990
|
-
if (pinned.has(helper.
|
|
21987
|
+
const helperCallers = [...callers.get(helper.key) ?? []];
|
|
21988
|
+
if (pinned.has(helper.key) || helperCallers.length !== 1) continue;
|
|
21991
21989
|
const callerName2 = helperCallers[0];
|
|
21992
|
-
if (callerName2 === void 0 || cycles.has(helper.
|
|
21990
|
+
if (callerName2 === void 0 || cycles.has(helper.key) && cycles.get(helper.key) === cycles.get(callerName2)) continue;
|
|
21993
21991
|
const caller = byName.get(callerName2);
|
|
21994
21992
|
if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
|
|
21995
21993
|
context.report({
|
|
21996
21994
|
node: helper.node,
|
|
21997
21995
|
messageId: "helperAboveOnlyCaller",
|
|
21998
|
-
data: { helper: helper.name, caller:
|
|
21996
|
+
data: { helper: helper.name, caller: caller.name }
|
|
21999
21997
|
});
|
|
22000
21998
|
}
|
|
22001
21999
|
}
|
|
@@ -22066,6 +22064,43 @@ function cycleComponents(graph) {
|
|
|
22066
22064
|
}
|
|
22067
22065
|
return components;
|
|
22068
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
|
+
}
|
|
22069
22104
|
function moduleScope(context, program) {
|
|
22070
22105
|
const declarations = moduleDefinitions(program);
|
|
22071
22106
|
const counts = /* @__PURE__ */ new Map();
|
|
@@ -22081,6 +22116,26 @@ function moduleScope(context, program) {
|
|
|
22081
22116
|
const definitions = scopeDefinitions.filter(
|
|
22082
22117
|
(definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
|
|
22083
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) {
|
|
22084
22139
|
const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
|
|
22085
22140
|
const calls = /* @__PURE__ */ new Map();
|
|
22086
22141
|
const pinned = /* @__PURE__ */ new Set();
|
|
@@ -22088,26 +22143,33 @@ function moduleScope(context, program) {
|
|
|
22088
22143
|
const variable = context.sourceCode.getDeclaredVariables(definition.bindingNode)[0];
|
|
22089
22144
|
for (const reference of variable?.references ?? []) {
|
|
22090
22145
|
if (reference.isWrite() && reference.init !== true) {
|
|
22091
|
-
pinned.add(definition.
|
|
22146
|
+
pinned.add(definition.key);
|
|
22092
22147
|
continue;
|
|
22093
22148
|
}
|
|
22094
22149
|
if (!reference.isRead()) continue;
|
|
22095
|
-
const
|
|
22096
|
-
|
|
22097
|
-
|
|
22098
|
-
const parent = identifier.parent;
|
|
22099
|
-
const callerDefinition = nearestFunction2 === void 0 ? void 0 : byFunction.get(nearestFunction2);
|
|
22100
|
-
if (callerDefinition === void 0 || parent.type !== AST_NODE_TYPES100.CallExpression || parent.callee !== identifier) {
|
|
22101
|
-
pinned.add(definition.name);
|
|
22150
|
+
const callerDefinition = byFunction.get(enclosingFunction5(context, reference.identifier));
|
|
22151
|
+
if (callerDefinition?.functionNode === void 0) {
|
|
22152
|
+
pinned.add(definition.key);
|
|
22102
22153
|
continue;
|
|
22103
22154
|
}
|
|
22104
|
-
const
|
|
22105
|
-
|
|
22106
|
-
|
|
22107
|
-
|
|
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
|
+
}
|
|
22108
22163
|
}
|
|
22109
22164
|
}
|
|
22110
|
-
|
|
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
|
+
);
|
|
22111
22173
|
}
|
|
22112
22174
|
function exportedNames(program) {
|
|
22113
22175
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -22141,16 +22203,18 @@ function moduleDefinitions(program) {
|
|
|
22141
22203
|
for (const statement of program.body) {
|
|
22142
22204
|
const node = statement.type === AST_NODE_TYPES100.ExportNamedDeclaration || statement.type === AST_NODE_TYPES100.ExportDefaultDeclaration ? statement.declaration : statement;
|
|
22143
22205
|
if (node?.type === AST_NODE_TYPES100.FunctionDeclaration && node.id !== null && node.body !== null) {
|
|
22144
|
-
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 });
|
|
22145
22207
|
continue;
|
|
22146
22208
|
}
|
|
22147
|
-
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;
|
|
22148
22210
|
for (const declarator of node.declarations) {
|
|
22149
|
-
|
|
22211
|
+
const initializer = declarator.init === null ? null : unwrapExpression7(declarator.init);
|
|
22212
|
+
if (declarator.id.type === AST_NODE_TYPES100.Identifier && initializer !== null && isFunction(initializer)) {
|
|
22150
22213
|
definitions.push({
|
|
22214
|
+
key: declarator.id.name,
|
|
22151
22215
|
name: declarator.id.name,
|
|
22152
22216
|
node: declarator,
|
|
22153
|
-
functionNode:
|
|
22217
|
+
functionNode: initializer,
|
|
22154
22218
|
bindingNode: declarator
|
|
22155
22219
|
});
|
|
22156
22220
|
}
|
|
@@ -22160,15 +22224,21 @@ function moduleDefinitions(program) {
|
|
|
22160
22224
|
}
|
|
22161
22225
|
function methodName2(node) {
|
|
22162
22226
|
if (node.key.type === AST_NODE_TYPES100.PrivateIdentifier) return `#${node.key.name}`;
|
|
22163
|
-
|
|
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;
|
|
22164
22229
|
}
|
|
22165
|
-
function
|
|
22166
|
-
|
|
22167
|
-
|
|
22168
|
-
|
|
22169
|
-
|
|
22170
|
-
if (
|
|
22171
|
-
|
|
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);
|
|
22172
22242
|
}
|
|
22173
22243
|
function referencedPropertyName(node) {
|
|
22174
22244
|
if (node.property.type === AST_NODE_TYPES100.PrivateIdentifier) return `#${node.property.name}`;
|
|
@@ -22177,7 +22247,7 @@ function referencedPropertyName(node) {
|
|
|
22177
22247
|
}
|
|
22178
22248
|
function walk(node, visitorKeys, visit, nestedFunction = false) {
|
|
22179
22249
|
visit(node, nestedFunction);
|
|
22180
|
-
const nested = nestedFunction || isFunction(node);
|
|
22250
|
+
const nested = nestedFunction || isFunction(node) || node.type === AST_NODE_TYPES100.ClassDeclaration || node.type === AST_NODE_TYPES100.ClassExpression;
|
|
22181
22251
|
forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
|
|
22182
22252
|
}
|
|
22183
22253
|
function classScope(context, node, computedReferenceNames) {
|
|
@@ -22185,123 +22255,127 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
22185
22255
|
(member) => member.type === AST_NODE_TYPES100.MethodDefinition
|
|
22186
22256
|
);
|
|
22187
22257
|
const counts = classMethodCounts(node.body, methods);
|
|
22188
|
-
const
|
|
22258
|
+
const implementations = methods.filter((method) => isFunction(method.value));
|
|
22259
|
+
const implementationCounts = classMethodCounts(node.body, implementations);
|
|
22260
|
+
const scopeDefinitions = implementations.flatMap((method) => {
|
|
22189
22261
|
const name = methodName2(method);
|
|
22190
|
-
|
|
22191
|
-
|
|
22192
|
-
const definitions = methods.flatMap((method) => {
|
|
22193
|
-
const name = methodName2(method);
|
|
22194
|
-
const isPrivate = method.accessibility === "private" || method.key.type === AST_NODE_TYPES100.PrivateIdentifier;
|
|
22195
|
-
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 }] : [];
|
|
22196
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
|
+
);
|
|
22197
22268
|
if (definitions.length === 0) return;
|
|
22198
|
-
const
|
|
22269
|
+
const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
|
|
22199
22270
|
const calls = /* @__PURE__ */ new Map();
|
|
22200
22271
|
const pinned = /* @__PURE__ */ new Set();
|
|
22201
|
-
const
|
|
22202
|
-
|
|
22203
|
-
const
|
|
22204
|
-
|
|
22205
|
-
|
|
22206
|
-
|
|
22207
|
-
|
|
22208
|
-
|
|
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
|
+
}
|
|
22209
22290
|
}
|
|
22210
22291
|
function collectMethodCalls(method) {
|
|
22211
|
-
const
|
|
22212
|
-
if (
|
|
22213
|
-
|
|
22214
|
-
|
|
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);
|
|
22215
22301
|
const parameterDecoratorNodes = /* @__PURE__ */ new Set();
|
|
22216
22302
|
for (const parameter of method.value.params) {
|
|
22217
22303
|
for (const decorator of parameter.decorators) {
|
|
22218
22304
|
walk(decorator, context.sourceCode.visitorKeys, (current) => parameterDecoratorNodes.add(current));
|
|
22219
22305
|
}
|
|
22220
22306
|
}
|
|
22221
|
-
const thisValue = (value) => {
|
|
22222
|
-
let current = value;
|
|
22223
|
-
while (current?.type === AST_NODE_TYPES100.TSAsExpression || current?.type === AST_NODE_TYPES100.TSSatisfiesExpression || current?.type === AST_NODE_TYPES100.TSNonNullExpression) current = current.expression;
|
|
22224
|
-
return current?.type === AST_NODE_TYPES100.ThisExpression;
|
|
22225
|
-
};
|
|
22226
22307
|
const collectAlias = (current, nestedFunction) => {
|
|
22227
|
-
if (nestedFunction || current.type !== AST_NODE_TYPES100.VariableDeclarator
|
|
22228
|
-
|
|
22229
|
-
|
|
22230
|
-
|
|
22231
|
-
|
|
22232
|
-
if (binding.type === AST_NODE_TYPES100.ObjectPattern) {
|
|
22233
|
-
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);
|
|
22234
22313
|
return;
|
|
22235
22314
|
}
|
|
22236
|
-
if (
|
|
22237
|
-
|
|
22238
|
-
|
|
22239
|
-
|
|
22240
|
-
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;
|
|
22241
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);
|
|
22242
22329
|
};
|
|
22243
|
-
for (const
|
|
22244
|
-
walk(parameter, context.sourceCode.visitorKeys, collectAlias);
|
|
22245
|
-
}
|
|
22246
|
-
for (const statement of method.value.body.body) {
|
|
22247
|
-
walk(statement, context.sourceCode.visitorKeys, collectAlias);
|
|
22248
|
-
}
|
|
22249
|
-
function pinDestructuredMembers(binding) {
|
|
22250
|
-
for (const property of binding.properties) {
|
|
22251
|
-
if (property.type === AST_NODE_TYPES100.RestElement) {
|
|
22252
|
-
for (const name of privateNames) pinned.add(name);
|
|
22253
|
-
} else if (property.key.type === AST_NODE_TYPES100.Identifier && privateNames.has(property.key.name)) {
|
|
22254
|
-
pinned.add(property.key.name);
|
|
22255
|
-
}
|
|
22256
|
-
}
|
|
22257
|
-
}
|
|
22330
|
+
for (const statement of method.value.body.body) walk(statement, context.sourceCode.visitorKeys, collectAlias);
|
|
22258
22331
|
const visitCall = (current, nestedFunction) => {
|
|
22259
|
-
|
|
22260
|
-
|
|
22332
|
+
const destructuring = receiverDestructuring(current);
|
|
22333
|
+
if (destructuring !== null) {
|
|
22334
|
+
const domain2 = receiverDomain(context, destructuring.value, receivers, method.static);
|
|
22335
|
+
pinDestructuredMembers(destructuring.binding, domain2);
|
|
22261
22336
|
}
|
|
22262
22337
|
if (current.type !== AST_NODE_TYPES100.MemberExpression) return;
|
|
22263
|
-
const
|
|
22264
|
-
|
|
22265
|
-
|
|
22266
|
-
|
|
22338
|
+
const domain = receiverDomain(context, current.object, receivers, method.static);
|
|
22339
|
+
const targetName = referencedPropertyName(current);
|
|
22340
|
+
if (domain === null) {
|
|
22341
|
+
pinNames(targetName);
|
|
22267
22342
|
return;
|
|
22268
22343
|
}
|
|
22269
|
-
if (
|
|
22270
|
-
|
|
22271
|
-
if (objectVariable !== null && methodAliases.has(objectVariable)) {
|
|
22272
|
-
pinned.add(target);
|
|
22344
|
+
if (targetName === null) {
|
|
22345
|
+
if (current.computed) pinNames(null, domain);
|
|
22273
22346
|
return;
|
|
22274
22347
|
}
|
|
22275
|
-
|
|
22348
|
+
const target = methodKey(targetName, domain);
|
|
22349
|
+
if (!methodKeys.has(target)) return;
|
|
22350
|
+
if (current.computed || nestedFunction || parameterDecoratorNodes.has(current)) {
|
|
22276
22351
|
pinned.add(target);
|
|
22277
22352
|
return;
|
|
22278
22353
|
}
|
|
22279
|
-
const
|
|
22280
|
-
|
|
22281
|
-
|
|
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
|
+
}
|
|
22282
22362
|
};
|
|
22283
|
-
for (const decorator of method.decorators)
|
|
22284
|
-
walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
|
|
22285
|
-
}
|
|
22363
|
+
for (const decorator of method.decorators) walk(decorator, context.sourceCode.visitorKeys, visitCall, true);
|
|
22286
22364
|
if (method.computed) walk(method.key, context.sourceCode.visitorKeys, visitCall, true);
|
|
22287
|
-
for (const parameter of method.value.params)
|
|
22288
|
-
|
|
22289
|
-
}
|
|
22290
|
-
for (const statement of method.value.body.body) {
|
|
22291
|
-
walk(statement, context.sourceCode.visitorKeys, visitCall);
|
|
22292
|
-
}
|
|
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);
|
|
22293
22367
|
}
|
|
22294
22368
|
for (const method of methods) collectMethodCalls(method);
|
|
22295
22369
|
for (const member of node.body.body) {
|
|
22296
22370
|
if (member.type === AST_NODE_TYPES100.MethodDefinition || member.type === AST_NODE_TYPES100.TSAbstractMethodDefinition) continue;
|
|
22297
22371
|
walk(member, context.sourceCode.visitorKeys, (current) => {
|
|
22298
22372
|
if (current.type !== AST_NODE_TYPES100.MemberExpression) return;
|
|
22299
|
-
|
|
22300
|
-
const possibleTarget = target ?? referencedPropertyName(current);
|
|
22301
|
-
if (possibleTarget !== null && privateNames.has(possibleTarget)) pinned.add(possibleTarget);
|
|
22373
|
+
pinNames(referencedPropertyName(current));
|
|
22302
22374
|
});
|
|
22303
22375
|
}
|
|
22304
|
-
for (const
|
|
22376
|
+
for (const definition of definitions) {
|
|
22377
|
+
if (computedReferenceNames.has(definition.name)) pinned.add(definition.key);
|
|
22378
|
+
}
|
|
22305
22379
|
const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
|
|
22306
22380
|
const runtimeBarrierPrefix = [0];
|
|
22307
22381
|
for (const member of node.body.body) {
|
|
@@ -22315,6 +22389,27 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
22315
22389
|
};
|
|
22316
22390
|
reportMisordered(context, definitions, scopeDefinitions, calls, pinned, canMove);
|
|
22317
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
|
+
}
|
|
22318
22413
|
function isClassRuntimeBarrier(member) {
|
|
22319
22414
|
switch (member.type) {
|
|
22320
22415
|
case AST_NODE_TYPES100.StaticBlock:
|
|
@@ -22365,12 +22460,18 @@ function classMethodCounts(body2, methods) {
|
|
|
22365
22460
|
const counts = /* @__PURE__ */ new Map();
|
|
22366
22461
|
for (const method of methods) {
|
|
22367
22462
|
const name = methodName2(method);
|
|
22368
|
-
if (name !== null)
|
|
22463
|
+
if (name !== null) {
|
|
22464
|
+
const key = methodKey(name, method.static);
|
|
22465
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
22466
|
+
}
|
|
22369
22467
|
}
|
|
22370
22468
|
for (const member of body2.body) {
|
|
22371
22469
|
if (member.type !== AST_NODE_TYPES100.TSAbstractMethodDefinition) continue;
|
|
22372
22470
|
const name = !member.computed && member.key.type === AST_NODE_TYPES100.Identifier ? member.key.name : null;
|
|
22373
|
-
if (name !== null)
|
|
22471
|
+
if (name !== null) {
|
|
22472
|
+
const key = methodKey(name, member.static);
|
|
22473
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
22474
|
+
}
|
|
22374
22475
|
}
|
|
22375
22476
|
return counts;
|
|
22376
22477
|
}
|
|
@@ -22703,14 +22804,14 @@ function importedAssertionKind(specifier) {
|
|
|
22703
22804
|
// src/rules/sole-export-matches-filename.ts
|
|
22704
22805
|
import { AST_NODE_TYPES as AST_NODE_TYPES102 } from "@typescript-eslint/utils";
|
|
22705
22806
|
var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
22706
|
-
defaultLevel: "
|
|
22807
|
+
defaultLevel: "error",
|
|
22707
22808
|
summary: "Make a module filename reflect its sole named public runtime export.",
|
|
22708
22809
|
rationale: "When a module owns one runtime responsibility, matching names make that responsibility directly discoverable.",
|
|
22709
|
-
remediation: "Name the module for the exported responsibility, using
|
|
22810
|
+
remediation: "Name the module for the exported responsibility, using the full kebab-case export name; otherwise colocate genuinely related exports.",
|
|
22710
22811
|
category: "maintainability",
|
|
22711
22812
|
limitations: [
|
|
22712
|
-
"Framework entrypoints, generic stems covered by no-generic-single-export-module, tests, generated files, anonymous defaults, CommonJS,
|
|
22713
|
-
"The rule compares the primary filename stem and preserves a single private underscore prefix and conventional suffixes such as .server or .worker.
|
|
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.",
|
|
22714
22815
|
"Exported destructuring patterns are excluded rather than undercounted as public exports."
|
|
22715
22816
|
],
|
|
22716
22817
|
examples: [
|
|
@@ -22719,39 +22820,16 @@ var SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION = {
|
|
|
22719
22820
|
]
|
|
22720
22821
|
};
|
|
22721
22822
|
var EXCLUDED_STEMS = /* @__PURE__ */ new Set([
|
|
22722
|
-
"common",
|
|
22723
22823
|
"global",
|
|
22724
|
-
"helpers",
|
|
22725
22824
|
"index",
|
|
22726
22825
|
"layout",
|
|
22727
22826
|
"loading",
|
|
22728
22827
|
"middleware",
|
|
22729
|
-
"misc",
|
|
22730
22828
|
"not-found",
|
|
22731
22829
|
"page",
|
|
22732
22830
|
"route",
|
|
22733
|
-
"shared",
|
|
22734
22831
|
"template",
|
|
22735
|
-
"types"
|
|
22736
|
-
"util",
|
|
22737
|
-
"utils"
|
|
22738
|
-
]);
|
|
22739
|
-
var WEAK_DOMAIN_TOKENS = /* @__PURE__ */ new Set([
|
|
22740
|
-
"adapter",
|
|
22741
|
-
"client",
|
|
22742
|
-
"config",
|
|
22743
|
-
"controller",
|
|
22744
|
-
"factory",
|
|
22745
|
-
"handler",
|
|
22746
|
-
"manager",
|
|
22747
|
-
"provider",
|
|
22748
|
-
"record",
|
|
22749
|
-
"repository",
|
|
22750
|
-
"router",
|
|
22751
|
-
"schema",
|
|
22752
|
-
"service",
|
|
22753
|
-
"store",
|
|
22754
|
-
"worker"
|
|
22832
|
+
"types"
|
|
22755
22833
|
]);
|
|
22756
22834
|
function stem3(filename) {
|
|
22757
22835
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
@@ -22760,34 +22838,6 @@ function stem3(filename) {
|
|
|
22760
22838
|
function kebabCase(name) {
|
|
22761
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();
|
|
22762
22840
|
}
|
|
22763
|
-
function reflectsExportName(fileStem, exportedStem) {
|
|
22764
|
-
if (fileStem.startsWith("_")) return false;
|
|
22765
|
-
const visibleFileStem = fileStem.toLowerCase();
|
|
22766
|
-
const fileTokens = visibleFileStem.split("-").filter(Boolean);
|
|
22767
|
-
const exportTokens = exportedStem.split("-").filter(Boolean);
|
|
22768
|
-
if (fileTokens.length === 0 || exportTokens.length === 0) return false;
|
|
22769
|
-
if (fileTokens.length === 1) {
|
|
22770
|
-
const [token] = fileTokens;
|
|
22771
|
-
return token !== void 0 && token.length >= 4 && !WEAK_DOMAIN_TOKENS.has(token) && (exportTokens[0] === token || exportTokens.at(-1) === token);
|
|
22772
|
-
}
|
|
22773
|
-
const compactFile = fileTokens.join("");
|
|
22774
|
-
if (compactFile.length < 6) return false;
|
|
22775
|
-
const boundaryPhrases = /* @__PURE__ */ new Set();
|
|
22776
|
-
for (let index = 1; index <= exportTokens.length; index += 1) {
|
|
22777
|
-
boundaryPhrases.add(exportTokens.slice(0, index).join(""));
|
|
22778
|
-
boundaryPhrases.add(exportTokens.slice(-index).join(""));
|
|
22779
|
-
}
|
|
22780
|
-
return boundaryPhrases.has(compactFile);
|
|
22781
|
-
}
|
|
22782
|
-
function declarationExport(statement) {
|
|
22783
|
-
const declaration = statement.declaration;
|
|
22784
|
-
if (declaration === null || declaration.declare === true) return [];
|
|
22785
|
-
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 }];
|
|
22786
|
-
if (declaration.type !== AST_NODE_TYPES102.VariableDeclaration) return [];
|
|
22787
|
-
return declaration.declarations.flatMap(
|
|
22788
|
-
(item) => item.id.type === AST_NODE_TYPES102.Identifier ? [{ name: item.id.name, node: item }] : []
|
|
22789
|
-
);
|
|
22790
|
-
}
|
|
22791
22841
|
var sole_export_matches_filename_default = createRule({
|
|
22792
22842
|
name: "sole-export-matches-filename",
|
|
22793
22843
|
documentation: SOLE_EXPORT_MATCHES_FILENAME_DOCUMENTATION,
|
|
@@ -22803,64 +22853,24 @@ var sole_export_matches_filename_default = createRule({
|
|
|
22803
22853
|
create(context) {
|
|
22804
22854
|
const fileStem = stem3(context.filename);
|
|
22805
22855
|
const normalizedFilename = context.filename.replaceAll("\\", "/");
|
|
22806
|
-
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;
|
|
22807
22858
|
return {
|
|
22859
|
+
"CallExpression, MemberExpression"(node) {
|
|
22860
|
+
if (isCommonJsExportReference(node, context.sourceCode)) hasCommonJsExport = true;
|
|
22861
|
+
},
|
|
22808
22862
|
"Program:exit"(program) {
|
|
22809
|
-
|
|
22810
|
-
const
|
|
22811
|
-
|
|
22812
|
-
|
|
22813
|
-
if (statement.type === AST_NODE_TYPES102.ExportDefaultDeclaration) {
|
|
22814
|
-
publicExports.add("default");
|
|
22815
|
-
const declaration = statement.declaration;
|
|
22816
|
-
if ((declaration.type === AST_NODE_TYPES102.ClassDeclaration || declaration.type === AST_NODE_TYPES102.FunctionDeclaration) && declaration.id !== null) exports.push({ name: declaration.id.name, node: declaration });
|
|
22817
|
-
else return false;
|
|
22818
|
-
}
|
|
22819
|
-
if (statement.type !== AST_NODE_TYPES102.ExportNamedDeclaration) return true;
|
|
22820
|
-
return collectNamedExport(statement);
|
|
22821
|
-
}
|
|
22822
|
-
function collectNamedExport(statement) {
|
|
22823
|
-
const declaration = statement.declaration;
|
|
22824
|
-
if (declaration !== null && "id" in declaration && declaration.id?.type === AST_NODE_TYPES102.Identifier) {
|
|
22825
|
-
publicExports.add(declaration.id.name);
|
|
22826
|
-
}
|
|
22827
|
-
if (declaration?.type === AST_NODE_TYPES102.VariableDeclaration) {
|
|
22828
|
-
if (declaration.declarations.some((item) => item.id.type !== AST_NODE_TYPES102.Identifier)) return false;
|
|
22829
|
-
for (const item of declaration.declarations) {
|
|
22830
|
-
if (item.id.type === AST_NODE_TYPES102.Identifier) publicExports.add(item.id.name);
|
|
22831
|
-
}
|
|
22832
|
-
}
|
|
22833
|
-
for (const specifier of statement.specifiers) {
|
|
22834
|
-
const exported = specifier.exported.type === AST_NODE_TYPES102.Identifier ? specifier.exported.name : specifier.exported.value;
|
|
22835
|
-
publicExports.add(String(exported));
|
|
22836
|
-
}
|
|
22837
|
-
if (statement.exportKind === "type") return true;
|
|
22838
|
-
exports.push(...declarationExport(statement));
|
|
22839
|
-
collectRuntimeSpecifiers(statement);
|
|
22840
|
-
return true;
|
|
22841
|
-
}
|
|
22842
|
-
function collectRuntimeSpecifiers(statement) {
|
|
22843
|
-
for (const specifier of statement.specifiers) {
|
|
22844
|
-
const exported = specifier.exported.type === AST_NODE_TYPES102.Identifier ? specifier.exported.name : specifier.exported.value;
|
|
22845
|
-
publicExports.add(String(exported));
|
|
22846
|
-
if (specifier.exportKind === "type") continue;
|
|
22847
|
-
if (exported === "default") exports.push({ name: specifier.local.name, node: specifier });
|
|
22848
|
-
else exports.push({ name: String(exported), node: specifier });
|
|
22849
|
-
}
|
|
22850
|
-
}
|
|
22851
|
-
for (const statement of program.body) {
|
|
22852
|
-
if (!collectPublicExport(statement)) return;
|
|
22853
|
-
}
|
|
22854
|
-
const unique = new Map(exports.map((item) => [item.name, item]));
|
|
22855
|
-
if (unique.size !== 1 || publicExports.size !== 1) return;
|
|
22856
|
-
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];
|
|
22857
22867
|
if (only === void 0) return;
|
|
22858
22868
|
if (only.name === "onRouterTransitionStart" && /(?:^|\/)instrumentation-client\.[jt]s$/u.test(normalizedFilename)) return;
|
|
22859
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;
|
|
22860
22870
|
const exportedStem = kebabCase(only.name);
|
|
22861
22871
|
if (exportedStem === "") return;
|
|
22862
22872
|
const expected = `${fileStem.startsWith("_") ? "_" : ""}${exportedStem}`;
|
|
22863
|
-
if (expected === fileStem.toLowerCase()
|
|
22873
|
+
if (expected === fileStem.toLowerCase()) return;
|
|
22864
22874
|
context.report({ node: only.node, messageId: "matchSoleExport", data: { exported: only.name, expected } });
|
|
22865
22875
|
}
|
|
22866
22876
|
};
|
|
@@ -23078,7 +23088,7 @@ var chainMemberNames2 = (node) => {
|
|
|
23078
23088
|
names.reverse();
|
|
23079
23089
|
return names;
|
|
23080
23090
|
};
|
|
23081
|
-
var
|
|
23091
|
+
var unwrapExpression8 = (node) => {
|
|
23082
23092
|
let current = node;
|
|
23083
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) {
|
|
23084
23094
|
current = current.expression;
|
|
@@ -23129,7 +23139,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
23129
23139
|
return binding !== null && schemaBindings.has(binding);
|
|
23130
23140
|
}
|
|
23131
23141
|
function isConfirmedSchema(expression) {
|
|
23132
|
-
const init =
|
|
23142
|
+
const init = unwrapExpression8(expression);
|
|
23133
23143
|
if (init.type === AST_NODE_TYPES103.Identifier) return isSchemaBinding(init);
|
|
23134
23144
|
if (init.type !== AST_NODE_TYPES103.CallExpression || init.callee.type !== AST_NODE_TYPES103.MemberExpression) {
|
|
23135
23145
|
return false;
|
|
@@ -23174,7 +23184,7 @@ var require_pascal_case_zod_schema_name_default = createRule({
|
|
|
23174
23184
|
}
|
|
23175
23185
|
});
|
|
23176
23186
|
|
|
23177
|
-
// src/rules/
|
|
23187
|
+
// src/rules/_renamed-rules.ts
|
|
23178
23188
|
var RENAMED_RULES = {
|
|
23179
23189
|
"jsdoc-restates-signature": "no-restated-jsdoc",
|
|
23180
23190
|
"zod-naming-convention": "require-pascal-case-zod-schema-name",
|
|
@@ -23183,7 +23193,7 @@ var RENAMED_RULES = {
|
|
|
23183
23193
|
"trailing-value-narration": "no-trailing-value-narration"
|
|
23184
23194
|
};
|
|
23185
23195
|
|
|
23186
|
-
// src/rules/_retired.ts
|
|
23196
|
+
// src/rules/_retired-rules.ts
|
|
23187
23197
|
var RETIRED_RULES = {
|
|
23188
23198
|
"ban-loose-type-guards-in-tests": {
|
|
23189
23199
|
removedIn: "5.0.0",
|
|
@@ -23375,7 +23385,7 @@ var RULES = {
|
|
|
23375
23385
|
};
|
|
23376
23386
|
var meta = {
|
|
23377
23387
|
name: "@sarj/eslint-plugin",
|
|
23378
|
-
version: "
|
|
23388
|
+
version: "16.0.0"
|
|
23379
23389
|
};
|
|
23380
23390
|
var APPLICATION_ONLY_RULES = [];
|
|
23381
23391
|
var LIBRARY_IMPORT_POLICY = ["error", {
|
|
@@ -23400,8 +23410,7 @@ var ADVISORY_RULES = [
|
|
|
23400
23410
|
"@sarj/prefer-whole-object-assertion",
|
|
23401
23411
|
"@sarj/require-camelcase-properties",
|
|
23402
23412
|
"@sarj/require-interface-for-exported-class",
|
|
23403
|
-
"@sarj/require-sql-access-class"
|
|
23404
|
-
"@sarj/sole-export-matches-filename"
|
|
23413
|
+
"@sarj/require-sql-access-class"
|
|
23405
23414
|
];
|
|
23406
23415
|
var RECOMMENDED_RULES = {
|
|
23407
23416
|
"@sarj/no-conditional-empty-object-spread": "error",
|
|
@@ -23513,7 +23522,7 @@ var RECOMMENDED_RULES = {
|
|
|
23513
23522
|
"@sarj/store-insert-requires-on-conflict": "error",
|
|
23514
23523
|
"@sarj/stepdown": "error",
|
|
23515
23524
|
"@sarj/source-coupled-test": "error",
|
|
23516
|
-
"@sarj/sole-export-matches-filename": "
|
|
23525
|
+
"@sarj/sole-export-matches-filename": "error",
|
|
23517
23526
|
"@sarj/test-phase-label-comment": "error",
|
|
23518
23527
|
"@sarj/require-pascal-case-zod-schema-name": "error"
|
|
23519
23528
|
};
|
|
@@ -23631,7 +23640,7 @@ var STRICT_RULES = {
|
|
|
23631
23640
|
"@sarj/store-insert-requires-on-conflict": "error",
|
|
23632
23641
|
"@sarj/stepdown": "error",
|
|
23633
23642
|
"@sarj/source-coupled-test": "error",
|
|
23634
|
-
"@sarj/sole-export-matches-filename": "
|
|
23643
|
+
"@sarj/sole-export-matches-filename": "error",
|
|
23635
23644
|
"@sarj/test-phase-label-comment": "error",
|
|
23636
23645
|
"@sarj/require-pascal-case-zod-schema-name": "error"
|
|
23637
23646
|
};
|
|
@@ -23654,7 +23663,7 @@ var PLUGIN = {
|
|
|
23654
23663
|
};
|
|
23655
23664
|
}
|
|
23656
23665
|
};
|
|
23657
|
-
var
|
|
23666
|
+
var src_default = PLUGIN;
|
|
23658
23667
|
export {
|
|
23659
23668
|
ADVISORY_RULES,
|
|
23660
23669
|
APPLICATION_ONLY_RULES,
|
|
@@ -23665,7 +23674,7 @@ export {
|
|
|
23665
23674
|
STRICT_RULES,
|
|
23666
23675
|
ADVISORY_RULES as advisoryRules,
|
|
23667
23676
|
APPLICATION_ONLY_RULES as applicationOnlyRules,
|
|
23668
|
-
|
|
23677
|
+
src_default as default,
|
|
23669
23678
|
publicDocumentation,
|
|
23670
23679
|
RECOMMENDED_RULES as recommendedRules,
|
|
23671
23680
|
RENAMED_RULES as renamedRules,
|