@sarj/eslint-plugin 15.5.0 → 15.6.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 +87 -96
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +87 -96
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4231,7 +4231,7 @@ var import_utils21 = require("@typescript-eslint/utils");
|
|
|
4231
4231
|
var noGenericSingleExportModuleDocumentation = {
|
|
4232
4232
|
summary: "Disallow generic module stems when one runtime export already names the responsibility.",
|
|
4233
4233
|
rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
|
|
4234
|
-
remediation: "
|
|
4234
|
+
remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
|
|
4235
4235
|
category: "maintainability",
|
|
4236
4236
|
limitations: ["Only configured generic stems with exactly one public runtime export are reported."],
|
|
4237
4237
|
examples: [
|
|
@@ -4240,36 +4240,21 @@ var noGenericSingleExportModuleDocumentation = {
|
|
|
4240
4240
|
]
|
|
4241
4241
|
};
|
|
4242
4242
|
var GENERIC_STEMS = /* @__PURE__ */ new Set([
|
|
4243
|
-
"base",
|
|
4244
4243
|
"common",
|
|
4245
|
-
"constant",
|
|
4246
|
-
"constants",
|
|
4247
|
-
"core",
|
|
4248
|
-
"enum",
|
|
4249
|
-
"enums",
|
|
4250
4244
|
"helper",
|
|
4251
4245
|
"helpers",
|
|
4252
4246
|
"misc",
|
|
4253
|
-
"model",
|
|
4254
|
-
"models",
|
|
4255
4247
|
"shared",
|
|
4256
4248
|
"stuff",
|
|
4257
|
-
"type",
|
|
4258
|
-
"types",
|
|
4259
4249
|
"util",
|
|
4260
4250
|
"utils"
|
|
4261
4251
|
]);
|
|
4262
|
-
var CONVENTIONAL_STEMS = /* @__PURE__ */ new Set(["base", "models"]);
|
|
4263
4252
|
var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
|
|
4264
4253
|
function fileParts(filename) {
|
|
4265
4254
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
4266
4255
|
const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
|
|
4267
|
-
const
|
|
4268
|
-
return {
|
|
4269
|
-
extension,
|
|
4270
|
-
stem: segments[0]?.toLowerCase() ?? "",
|
|
4271
|
-
suffixes: segments.slice(1)
|
|
4272
|
-
};
|
|
4256
|
+
const [stem3 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
|
|
4257
|
+
return { stem: stem3, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
|
|
4273
4258
|
}
|
|
4274
4259
|
function declaredNames(declaration) {
|
|
4275
4260
|
if (declaration.declare === true) return [];
|
|
@@ -4285,50 +4270,6 @@ function declaredNames(declaration) {
|
|
|
4285
4270
|
(item) => item.id.type === import_utils21.AST_NODE_TYPES.Identifier ? [item.id.name] : []
|
|
4286
4271
|
);
|
|
4287
4272
|
}
|
|
4288
|
-
function typeOnlyBindings(program) {
|
|
4289
|
-
const names = /* @__PURE__ */ new Set();
|
|
4290
|
-
const runtimeNames = /* @__PURE__ */ new Set();
|
|
4291
|
-
for (const statement of program.body) {
|
|
4292
|
-
const declaration = statement.type === import_utils21.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
|
|
4293
|
-
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils21.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
4294
|
-
names.add(declaration.id.name);
|
|
4295
|
-
continue;
|
|
4296
|
-
}
|
|
4297
|
-
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) {
|
|
4298
|
-
names.add(declaration.id.name);
|
|
4299
|
-
continue;
|
|
4300
|
-
}
|
|
4301
|
-
if (statement.type === import_utils21.AST_NODE_TYPES.ImportDeclaration) {
|
|
4302
|
-
for (const specifier of statement.specifiers) {
|
|
4303
|
-
if (statement.importKind === "type" || specifier.type === import_utils21.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type") {
|
|
4304
|
-
names.add(specifier.local.name);
|
|
4305
|
-
} else {
|
|
4306
|
-
runtimeNames.add(specifier.local.name);
|
|
4307
|
-
}
|
|
4308
|
-
}
|
|
4309
|
-
continue;
|
|
4310
|
-
}
|
|
4311
|
-
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSDeclareFunction && declaration.id !== null) {
|
|
4312
|
-
names.add(declaration.id.name);
|
|
4313
|
-
continue;
|
|
4314
|
-
}
|
|
4315
|
-
if (declaration !== null && declaration.declare === true) {
|
|
4316
|
-
if ((declaration.type === import_utils21.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(declaration.id.name);
|
|
4317
|
-
if (declaration.type === import_utils21.AST_NODE_TYPES.VariableDeclaration) {
|
|
4318
|
-
for (const item of declaration.declarations) {
|
|
4319
|
-
if (item.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(item.id.name);
|
|
4320
|
-
}
|
|
4321
|
-
}
|
|
4322
|
-
continue;
|
|
4323
|
-
}
|
|
4324
|
-
if (declaration !== null && "type" in declaration) {
|
|
4325
|
-
for (const name of declaredNames(declaration)) {
|
|
4326
|
-
runtimeNames.add(name);
|
|
4327
|
-
}
|
|
4328
|
-
}
|
|
4329
|
-
}
|
|
4330
|
-
return new Set([...names].filter((name) => !runtimeNames.has(name)));
|
|
4331
|
-
}
|
|
4332
4273
|
function runtimeExports(program) {
|
|
4333
4274
|
const exports2 = [];
|
|
4334
4275
|
const typeBindings = typeOnlyBindings(program);
|
|
@@ -4367,25 +4308,49 @@ function runtimeExports(program) {
|
|
|
4367
4308
|
const unique = new Map(exports2.map((entry) => [entry.key, entry]));
|
|
4368
4309
|
return { exports: [...unique.values()], ambiguous };
|
|
4369
4310
|
}
|
|
4370
|
-
function
|
|
4311
|
+
function typeOnlyBindings(program) {
|
|
4371
4312
|
const names = /* @__PURE__ */ new Set();
|
|
4372
|
-
const
|
|
4313
|
+
const runtimeNames = /* @__PURE__ */ new Set();
|
|
4373
4314
|
for (const statement of program.body) {
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4315
|
+
const declaration = statement.type === import_utils21.AST_NODE_TYPES.ExportNamedDeclaration ? statement.declaration : statement;
|
|
4316
|
+
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSInterfaceDeclaration || declaration?.type === import_utils21.AST_NODE_TYPES.TSTypeAliasDeclaration) {
|
|
4317
|
+
names.add(declaration.id.name);
|
|
4318
|
+
continue;
|
|
4319
|
+
}
|
|
4320
|
+
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration && declaration.const) {
|
|
4321
|
+
names.add(declaration.id.name);
|
|
4322
|
+
continue;
|
|
4323
|
+
}
|
|
4324
|
+
if (statement.type === import_utils21.AST_NODE_TYPES.ImportDeclaration) {
|
|
4325
|
+
for (const specifier of statement.specifiers) {
|
|
4326
|
+
if (statement.importKind === "type" || specifier.type === import_utils21.AST_NODE_TYPES.ImportSpecifier && specifier.importKind === "type") {
|
|
4327
|
+
names.add(specifier.local.name);
|
|
4328
|
+
} else {
|
|
4329
|
+
runtimeNames.add(specifier.local.name);
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
continue;
|
|
4333
|
+
}
|
|
4334
|
+
if (declaration?.type === import_utils21.AST_NODE_TYPES.TSDeclareFunction && declaration.id !== null) {
|
|
4335
|
+
names.add(declaration.id.name);
|
|
4336
|
+
continue;
|
|
4337
|
+
}
|
|
4338
|
+
if (declaration !== null && declaration.declare === true) {
|
|
4339
|
+
if ((declaration.type === import_utils21.AST_NODE_TYPES.ClassDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.FunctionDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSEnumDeclaration || declaration.type === import_utils21.AST_NODE_TYPES.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(declaration.id.name);
|
|
4340
|
+
if (declaration.type === import_utils21.AST_NODE_TYPES.VariableDeclaration) {
|
|
4341
|
+
for (const item of declaration.declarations) {
|
|
4342
|
+
if (item.id.type === import_utils21.AST_NODE_TYPES.Identifier) names.add(item.id.name);
|
|
4343
|
+
}
|
|
4344
|
+
}
|
|
4345
|
+
continue;
|
|
4346
|
+
}
|
|
4347
|
+
if (declaration !== null && "type" in declaration) {
|
|
4348
|
+
for (const name of declaredNames(declaration)) {
|
|
4349
|
+
runtimeNames.add(name);
|
|
4381
4350
|
}
|
|
4382
|
-
names.add(specifier.exported.type === import_utils21.AST_NODE_TYPES.Identifier ? specifier.exported.name : specifier.exported.value);
|
|
4383
4351
|
}
|
|
4384
4352
|
}
|
|
4385
|
-
return names.
|
|
4386
|
-
}
|
|
4387
|
-
function kebabCase(name) {
|
|
4388
|
-
return name.replaceAll(/oauth/giu, "Oauth").replaceAll(/graphql/giu, "Graphql").replaceAll(/grpc/giu, "Grpc").replaceAll(/([a-z\d])([A-Z])/gu, "$1-$2").replaceAll(/([A-Z]+)([A-Z][a-z])/gu, "$1-$2").replaceAll(/[_\s]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "").toLowerCase();
|
|
4353
|
+
return new Set([...names].filter((name) => !runtimeNames.has(name)));
|
|
4389
4354
|
}
|
|
4390
4355
|
function isGlobalIdentifier(context, node) {
|
|
4391
4356
|
const variable = import_utils21.ASTUtils.findVariable(context.sourceCode.getScope(node), node.name);
|
|
@@ -4407,14 +4372,14 @@ var no_generic_single_export_module_default = createRule({
|
|
|
4407
4372
|
docs: { description: "Disallow generic module stems when one runtime export already names the responsibility." },
|
|
4408
4373
|
schema: [],
|
|
4409
4374
|
messages: {
|
|
4410
|
-
genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`;
|
|
4375
|
+
genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`; choose a responsibility-bearing module name or colocate the export with its domain."
|
|
4411
4376
|
}
|
|
4412
4377
|
},
|
|
4413
4378
|
defaultOptions: [],
|
|
4414
4379
|
create(context) {
|
|
4415
4380
|
const file = fileParts(context.filename);
|
|
4416
4381
|
const { stem: stem3 } = file;
|
|
4417
|
-
if (!GENERIC_STEMS.has(stem3) ||
|
|
4382
|
+
if (!GENERIC_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
|
|
4418
4383
|
let hasCommonJsExport = false;
|
|
4419
4384
|
return {
|
|
4420
4385
|
CallExpression(node) {
|
|
@@ -4427,25 +4392,19 @@ var no_generic_single_export_module_default = createRule({
|
|
|
4427
4392
|
},
|
|
4428
4393
|
"Program:exit"(program) {
|
|
4429
4394
|
if (hasCommonJsExport) return;
|
|
4430
|
-
if ((stem3 === "type" || stem3 === "types") && publicTypeExportCount(program) >= 2) return;
|
|
4431
4395
|
const exports2 = runtimeExports(program);
|
|
4432
4396
|
if (exports2.ambiguous || exports2.exports.length !== 1) return;
|
|
4433
4397
|
const onlyExport = exports2.exports[0];
|
|
4434
4398
|
if (onlyExport === void 0) return;
|
|
4435
4399
|
const { name: exported, node } = onlyExport;
|
|
4436
4400
|
if (isConventionalFrameworkUtility(context.filename, exported)) return;
|
|
4437
|
-
const
|
|
4438
|
-
const
|
|
4439
|
-
|
|
4440
|
-
if (expectedStem === currentResponsibility || GENERIC_STEMS.has(expectedStem) || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(expectedStem)) return;
|
|
4441
|
-
const suffixTail = suffixStem === "" ? "" : `-${suffixStem}`;
|
|
4442
|
-
const renameStem = suffixTail !== "" && expectedStem.endsWith(suffixTail) ? expectedStem.slice(0, -suffixTail.length) : expectedStem;
|
|
4443
|
-
if (renameStem === "" || GENERIC_STEMS.has(renameStem)) return;
|
|
4444
|
-
const suffix = file.suffixes.map((part) => `.${kebabCase(part)}`).join("");
|
|
4401
|
+
const exportedRole = exported.replaceAll(/[^a-z0-9]/giu, "").toLowerCase();
|
|
4402
|
+
const pathRole = [stem3, ...file.suffixes].join("").replaceAll(/[^a-z0-9]/giu, "");
|
|
4403
|
+
if (exportedRole === pathRole && file.suffixes.length > 0) return;
|
|
4445
4404
|
context.report({
|
|
4446
4405
|
node,
|
|
4447
4406
|
messageId: "genericSingleExport",
|
|
4448
|
-
data: { stem: stem3, exported
|
|
4407
|
+
data: { stem: stem3, exported }
|
|
4449
4408
|
});
|
|
4450
4409
|
}
|
|
4451
4410
|
};
|
|
@@ -13908,8 +13867,13 @@ var import_utils68 = require("@typescript-eslint/utils");
|
|
|
13908
13867
|
var stepdownDocumentation = {
|
|
13909
13868
|
summary: "Place a private helper below its sole direct same-scope caller.",
|
|
13910
13869
|
rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
|
|
13911
|
-
remediation: "Move the private helper
|
|
13870
|
+
remediation: "Move the private helper below its sole caller.",
|
|
13912
13871
|
category: "maintainability",
|
|
13872
|
+
limitations: [
|
|
13873
|
+
"Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
|
|
13874
|
+
"Class methods are reported only when both helper and caller are private so accessibility ordering remains authoritative.",
|
|
13875
|
+
"Runtime class-field, static-block, computed-member, and decorator barriers are never crossed."
|
|
13876
|
+
],
|
|
13913
13877
|
examples: [
|
|
13914
13878
|
{ id: "caller-before-helper", title: "Place the caller first", outcome: "no-match", files: [{ path: "src/run.ts", source: "function run() { return load(); }\nfunction load() { return 1; }" }], focusPath: "src/run.ts", expectedCount: 0, public: true },
|
|
13915
13879
|
{ id: "helper-before-caller", title: "Do not lead with a sole-caller helper", outcome: "match", files: [{ path: "src/run.ts", source: "function load() { return 1; }\nfunction run() { return load(); }" }], focusPath: "src/run.ts", expectedCount: 1, public: true }
|
|
@@ -13918,7 +13882,7 @@ var stepdownDocumentation = {
|
|
|
13918
13882
|
function isFunction(node) {
|
|
13919
13883
|
return node.type === import_utils68.AST_NODE_TYPES.ArrowFunctionExpression || node.type === import_utils68.AST_NODE_TYPES.FunctionDeclaration || node.type === import_utils68.AST_NODE_TYPES.FunctionExpression;
|
|
13920
13884
|
}
|
|
13921
|
-
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned) {
|
|
13885
|
+
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
|
|
13922
13886
|
const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
|
|
13923
13887
|
const cycles = cycleComponents(calls);
|
|
13924
13888
|
const callers = /* @__PURE__ */ new Map();
|
|
@@ -13936,7 +13900,7 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned)
|
|
|
13936
13900
|
const callerName2 = helperCallers[0];
|
|
13937
13901
|
if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
|
|
13938
13902
|
const caller = byName.get(callerName2);
|
|
13939
|
-
if (caller === void 0 || helper.node.range[0] >= caller.node.range[0]) continue;
|
|
13903
|
+
if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
|
|
13940
13904
|
context.report({
|
|
13941
13905
|
node: helper.node,
|
|
13942
13906
|
messageId: "helperAboveOnlyCaller",
|
|
@@ -14016,8 +13980,10 @@ function moduleScope(context, program) {
|
|
|
14016
13980
|
})
|
|
14017
13981
|
);
|
|
14018
13982
|
const exported = exportedNames(program);
|
|
14019
|
-
const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1
|
|
14020
|
-
const definitions = scopeDefinitions.filter(
|
|
13983
|
+
const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1);
|
|
13984
|
+
const definitions = scopeDefinitions.filter(
|
|
13985
|
+
(definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
|
|
13986
|
+
);
|
|
14021
13987
|
const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
|
|
14022
13988
|
const calls = /* @__PURE__ */ new Map();
|
|
14023
13989
|
const pinned = /* @__PURE__ */ new Set();
|
|
@@ -14261,11 +14227,36 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
14261
14227
|
return [definition.name, accessibility2];
|
|
14262
14228
|
})
|
|
14263
14229
|
);
|
|
14230
|
+
const methodByName = new Map(scopeDefinitions.map((definition) => [definition.name, definition.node]));
|
|
14264
14231
|
for (const [caller, callees] of calls) {
|
|
14265
|
-
|
|
14232
|
+
const callerMethod = methodByName.get(caller);
|
|
14233
|
+
if (accessibility.get(caller) === "private" && callerMethod?.type === import_utils68.AST_NODE_TYPES.MethodDefinition && callerMethod.decorators.length === 0) continue;
|
|
14266
14234
|
for (const callee of callees) pinned.add(callee);
|
|
14267
14235
|
}
|
|
14268
|
-
|
|
14236
|
+
const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
|
|
14237
|
+
const runtimeBarrierPrefix = [0];
|
|
14238
|
+
for (const member of node.body.body) {
|
|
14239
|
+
runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
|
|
14240
|
+
}
|
|
14241
|
+
reportMisordered(context, definitions, scopeDefinitions, calls, pinned, (helper, caller) => {
|
|
14242
|
+
const helperIndex = memberIndexes.get(helper.node);
|
|
14243
|
+
const callerIndex = memberIndexes.get(caller.node);
|
|
14244
|
+
if (helperIndex === void 0 || callerIndex === void 0) return false;
|
|
14245
|
+
return runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
|
|
14246
|
+
});
|
|
14247
|
+
}
|
|
14248
|
+
function isClassRuntimeBarrier(member) {
|
|
14249
|
+
switch (member.type) {
|
|
14250
|
+
case import_utils68.AST_NODE_TYPES.StaticBlock:
|
|
14251
|
+
return true;
|
|
14252
|
+
case import_utils68.AST_NODE_TYPES.PropertyDefinition:
|
|
14253
|
+
case import_utils68.AST_NODE_TYPES.AccessorProperty:
|
|
14254
|
+
return member.static || member.computed || member.decorators.length > 0 || member.value !== null;
|
|
14255
|
+
case import_utils68.AST_NODE_TYPES.MethodDefinition:
|
|
14256
|
+
return member.computed || member.decorators.length > 0;
|
|
14257
|
+
default:
|
|
14258
|
+
return false;
|
|
14259
|
+
}
|
|
14269
14260
|
}
|
|
14270
14261
|
var stepdown_default = createRule({
|
|
14271
14262
|
name: "stepdown",
|
|
@@ -14590,7 +14581,7 @@ var rules = {
|
|
|
14590
14581
|
};
|
|
14591
14582
|
var meta = {
|
|
14592
14583
|
name: "@sarj/eslint-plugin",
|
|
14593
|
-
version: "15.
|
|
14584
|
+
version: "15.6.0"
|
|
14594
14585
|
};
|
|
14595
14586
|
var applicationOnlyRules = [
|
|
14596
14587
|
"no-restricted-library-load",
|
package/dist/index.d.cts
CHANGED
|
@@ -415,7 +415,7 @@ type FlatPreset = {
|
|
|
415
415
|
declare const plugin: {
|
|
416
416
|
readonly meta: {
|
|
417
417
|
readonly name: "@sarj/eslint-plugin";
|
|
418
|
-
readonly version: "15.
|
|
418
|
+
readonly version: "15.6.0";
|
|
419
419
|
};
|
|
420
420
|
readonly rules: {
|
|
421
421
|
readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
|
package/dist/index.d.ts
CHANGED
|
@@ -415,7 +415,7 @@ type FlatPreset = {
|
|
|
415
415
|
declare const plugin: {
|
|
416
416
|
readonly meta: {
|
|
417
417
|
readonly name: "@sarj/eslint-plugin";
|
|
418
|
-
readonly version: "15.
|
|
418
|
+
readonly version: "15.6.0";
|
|
419
419
|
};
|
|
420
420
|
readonly rules: {
|
|
421
421
|
readonly "duplicate-test-body": DocumentedRule<readonly [], "duplicateTestBody">;
|
package/dist/index.js
CHANGED
|
@@ -4193,7 +4193,7 @@ import { AST_NODE_TYPES as AST_NODE_TYPES16, ASTUtils as ASTUtils4 } from "@type
|
|
|
4193
4193
|
var noGenericSingleExportModuleDocumentation = {
|
|
4194
4194
|
summary: "Disallow generic module stems when one runtime export already names the responsibility.",
|
|
4195
4195
|
rationale: "A generic filename hides the sole exported responsibility and makes navigation less descriptive.",
|
|
4196
|
-
remediation: "
|
|
4196
|
+
remediation: "Choose a responsibility-bearing module name or colocate the export with its domain.",
|
|
4197
4197
|
category: "maintainability",
|
|
4198
4198
|
limitations: ["Only configured generic stems with exactly one public runtime export are reported."],
|
|
4199
4199
|
examples: [
|
|
@@ -4202,36 +4202,21 @@ var noGenericSingleExportModuleDocumentation = {
|
|
|
4202
4202
|
]
|
|
4203
4203
|
};
|
|
4204
4204
|
var GENERIC_STEMS = /* @__PURE__ */ new Set([
|
|
4205
|
-
"base",
|
|
4206
4205
|
"common",
|
|
4207
|
-
"constant",
|
|
4208
|
-
"constants",
|
|
4209
|
-
"core",
|
|
4210
|
-
"enum",
|
|
4211
|
-
"enums",
|
|
4212
4206
|
"helper",
|
|
4213
4207
|
"helpers",
|
|
4214
4208
|
"misc",
|
|
4215
|
-
"model",
|
|
4216
|
-
"models",
|
|
4217
4209
|
"shared",
|
|
4218
4210
|
"stuff",
|
|
4219
|
-
"type",
|
|
4220
|
-
"types",
|
|
4221
4211
|
"util",
|
|
4222
4212
|
"utils"
|
|
4223
4213
|
]);
|
|
4224
|
-
var CONVENTIONAL_STEMS = /* @__PURE__ */ new Set(["base", "models"]);
|
|
4225
4214
|
var CJS_OBJECT_EXPORT_METHODS = /* @__PURE__ */ new Set(["assign", "defineProperties", "defineProperty"]);
|
|
4226
4215
|
function fileParts(filename) {
|
|
4227
4216
|
const base = filename.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
4228
4217
|
const extension = base.match(/(\.[cm]?[jt]sx?)$/u)?.[1] ?? ".ts";
|
|
4229
|
-
const
|
|
4230
|
-
return {
|
|
4231
|
-
extension,
|
|
4232
|
-
stem: segments[0]?.toLowerCase() ?? "",
|
|
4233
|
-
suffixes: segments.slice(1)
|
|
4234
|
-
};
|
|
4218
|
+
const [stem3 = "", ...suffixes] = base.slice(0, -extension.length).split(".");
|
|
4219
|
+
return { stem: stem3, suffixes: suffixes.map((suffix) => suffix.toLowerCase()) };
|
|
4235
4220
|
}
|
|
4236
4221
|
function declaredNames(declaration) {
|
|
4237
4222
|
if (declaration.declare === true) return [];
|
|
@@ -4247,50 +4232,6 @@ function declaredNames(declaration) {
|
|
|
4247
4232
|
(item) => item.id.type === AST_NODE_TYPES16.Identifier ? [item.id.name] : []
|
|
4248
4233
|
);
|
|
4249
4234
|
}
|
|
4250
|
-
function typeOnlyBindings(program) {
|
|
4251
|
-
const names = /* @__PURE__ */ new Set();
|
|
4252
|
-
const runtimeNames = /* @__PURE__ */ new Set();
|
|
4253
|
-
for (const statement of program.body) {
|
|
4254
|
-
const declaration = statement.type === AST_NODE_TYPES16.ExportNamedDeclaration ? statement.declaration : statement;
|
|
4255
|
-
if (declaration?.type === AST_NODE_TYPES16.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES16.TSTypeAliasDeclaration) {
|
|
4256
|
-
names.add(declaration.id.name);
|
|
4257
|
-
continue;
|
|
4258
|
-
}
|
|
4259
|
-
if (declaration?.type === AST_NODE_TYPES16.TSEnumDeclaration && declaration.const) {
|
|
4260
|
-
names.add(declaration.id.name);
|
|
4261
|
-
continue;
|
|
4262
|
-
}
|
|
4263
|
-
if (statement.type === AST_NODE_TYPES16.ImportDeclaration) {
|
|
4264
|
-
for (const specifier of statement.specifiers) {
|
|
4265
|
-
if (statement.importKind === "type" || specifier.type === AST_NODE_TYPES16.ImportSpecifier && specifier.importKind === "type") {
|
|
4266
|
-
names.add(specifier.local.name);
|
|
4267
|
-
} else {
|
|
4268
|
-
runtimeNames.add(specifier.local.name);
|
|
4269
|
-
}
|
|
4270
|
-
}
|
|
4271
|
-
continue;
|
|
4272
|
-
}
|
|
4273
|
-
if (declaration?.type === AST_NODE_TYPES16.TSDeclareFunction && declaration.id !== null) {
|
|
4274
|
-
names.add(declaration.id.name);
|
|
4275
|
-
continue;
|
|
4276
|
-
}
|
|
4277
|
-
if (declaration !== null && declaration.declare === true) {
|
|
4278
|
-
if ((declaration.type === AST_NODE_TYPES16.ClassDeclaration || declaration.type === AST_NODE_TYPES16.FunctionDeclaration || declaration.type === AST_NODE_TYPES16.TSEnumDeclaration || declaration.type === AST_NODE_TYPES16.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === AST_NODE_TYPES16.Identifier) names.add(declaration.id.name);
|
|
4279
|
-
if (declaration.type === AST_NODE_TYPES16.VariableDeclaration) {
|
|
4280
|
-
for (const item of declaration.declarations) {
|
|
4281
|
-
if (item.id.type === AST_NODE_TYPES16.Identifier) names.add(item.id.name);
|
|
4282
|
-
}
|
|
4283
|
-
}
|
|
4284
|
-
continue;
|
|
4285
|
-
}
|
|
4286
|
-
if (declaration !== null && "type" in declaration) {
|
|
4287
|
-
for (const name of declaredNames(declaration)) {
|
|
4288
|
-
runtimeNames.add(name);
|
|
4289
|
-
}
|
|
4290
|
-
}
|
|
4291
|
-
}
|
|
4292
|
-
return new Set([...names].filter((name) => !runtimeNames.has(name)));
|
|
4293
|
-
}
|
|
4294
4235
|
function runtimeExports(program) {
|
|
4295
4236
|
const exports = [];
|
|
4296
4237
|
const typeBindings = typeOnlyBindings(program);
|
|
@@ -4329,25 +4270,49 @@ function runtimeExports(program) {
|
|
|
4329
4270
|
const unique = new Map(exports.map((entry) => [entry.key, entry]));
|
|
4330
4271
|
return { exports: [...unique.values()], ambiguous };
|
|
4331
4272
|
}
|
|
4332
|
-
function
|
|
4273
|
+
function typeOnlyBindings(program) {
|
|
4333
4274
|
const names = /* @__PURE__ */ new Set();
|
|
4334
|
-
const
|
|
4275
|
+
const runtimeNames = /* @__PURE__ */ new Set();
|
|
4335
4276
|
for (const statement of program.body) {
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4277
|
+
const declaration = statement.type === AST_NODE_TYPES16.ExportNamedDeclaration ? statement.declaration : statement;
|
|
4278
|
+
if (declaration?.type === AST_NODE_TYPES16.TSInterfaceDeclaration || declaration?.type === AST_NODE_TYPES16.TSTypeAliasDeclaration) {
|
|
4279
|
+
names.add(declaration.id.name);
|
|
4280
|
+
continue;
|
|
4281
|
+
}
|
|
4282
|
+
if (declaration?.type === AST_NODE_TYPES16.TSEnumDeclaration && declaration.const) {
|
|
4283
|
+
names.add(declaration.id.name);
|
|
4284
|
+
continue;
|
|
4285
|
+
}
|
|
4286
|
+
if (statement.type === AST_NODE_TYPES16.ImportDeclaration) {
|
|
4287
|
+
for (const specifier of statement.specifiers) {
|
|
4288
|
+
if (statement.importKind === "type" || specifier.type === AST_NODE_TYPES16.ImportSpecifier && specifier.importKind === "type") {
|
|
4289
|
+
names.add(specifier.local.name);
|
|
4290
|
+
} else {
|
|
4291
|
+
runtimeNames.add(specifier.local.name);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
continue;
|
|
4295
|
+
}
|
|
4296
|
+
if (declaration?.type === AST_NODE_TYPES16.TSDeclareFunction && declaration.id !== null) {
|
|
4297
|
+
names.add(declaration.id.name);
|
|
4298
|
+
continue;
|
|
4299
|
+
}
|
|
4300
|
+
if (declaration !== null && declaration.declare === true) {
|
|
4301
|
+
if ((declaration.type === AST_NODE_TYPES16.ClassDeclaration || declaration.type === AST_NODE_TYPES16.FunctionDeclaration || declaration.type === AST_NODE_TYPES16.TSEnumDeclaration || declaration.type === AST_NODE_TYPES16.TSModuleDeclaration) && declaration.id !== null && declaration.id.type === AST_NODE_TYPES16.Identifier) names.add(declaration.id.name);
|
|
4302
|
+
if (declaration.type === AST_NODE_TYPES16.VariableDeclaration) {
|
|
4303
|
+
for (const item of declaration.declarations) {
|
|
4304
|
+
if (item.id.type === AST_NODE_TYPES16.Identifier) names.add(item.id.name);
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
continue;
|
|
4308
|
+
}
|
|
4309
|
+
if (declaration !== null && "type" in declaration) {
|
|
4310
|
+
for (const name of declaredNames(declaration)) {
|
|
4311
|
+
runtimeNames.add(name);
|
|
4343
4312
|
}
|
|
4344
|
-
names.add(specifier.exported.type === AST_NODE_TYPES16.Identifier ? specifier.exported.name : specifier.exported.value);
|
|
4345
4313
|
}
|
|
4346
4314
|
}
|
|
4347
|
-
return names.
|
|
4348
|
-
}
|
|
4349
|
-
function kebabCase(name) {
|
|
4350
|
-
return name.replaceAll(/oauth/giu, "Oauth").replaceAll(/graphql/giu, "Graphql").replaceAll(/grpc/giu, "Grpc").replaceAll(/([a-z\d])([A-Z])/gu, "$1-$2").replaceAll(/([A-Z]+)([A-Z][a-z])/gu, "$1-$2").replaceAll(/[_\s]+/gu, "-").replaceAll(/-+/gu, "-").replaceAll(/^-|-$/gu, "").toLowerCase();
|
|
4315
|
+
return new Set([...names].filter((name) => !runtimeNames.has(name)));
|
|
4351
4316
|
}
|
|
4352
4317
|
function isGlobalIdentifier(context, node) {
|
|
4353
4318
|
const variable = ASTUtils4.findVariable(context.sourceCode.getScope(node), node.name);
|
|
@@ -4369,14 +4334,14 @@ var no_generic_single_export_module_default = createRule({
|
|
|
4369
4334
|
docs: { description: "Disallow generic module stems when one runtime export already names the responsibility." },
|
|
4370
4335
|
schema: [],
|
|
4371
4336
|
messages: {
|
|
4372
|
-
genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`;
|
|
4337
|
+
genericSingleExport: "Module stem `{{stem}}` is generic and its only runtime export is `{{exported}}`; choose a responsibility-bearing module name or colocate the export with its domain."
|
|
4373
4338
|
}
|
|
4374
4339
|
},
|
|
4375
4340
|
defaultOptions: [],
|
|
4376
4341
|
create(context) {
|
|
4377
4342
|
const file = fileParts(context.filename);
|
|
4378
4343
|
const { stem: stem3 } = file;
|
|
4379
|
-
if (!GENERIC_STEMS.has(stem3) ||
|
|
4344
|
+
if (!GENERIC_STEMS.has(stem3) || isTestFile(context.filename) || isGeneratedFile(context.filename, context.sourceCode.text)) return {};
|
|
4380
4345
|
let hasCommonJsExport = false;
|
|
4381
4346
|
return {
|
|
4382
4347
|
CallExpression(node) {
|
|
@@ -4389,25 +4354,19 @@ var no_generic_single_export_module_default = createRule({
|
|
|
4389
4354
|
},
|
|
4390
4355
|
"Program:exit"(program) {
|
|
4391
4356
|
if (hasCommonJsExport) return;
|
|
4392
|
-
if ((stem3 === "type" || stem3 === "types") && publicTypeExportCount(program) >= 2) return;
|
|
4393
4357
|
const exports = runtimeExports(program);
|
|
4394
4358
|
if (exports.ambiguous || exports.exports.length !== 1) return;
|
|
4395
4359
|
const onlyExport = exports.exports[0];
|
|
4396
4360
|
if (onlyExport === void 0) return;
|
|
4397
4361
|
const { name: exported, node } = onlyExport;
|
|
4398
4362
|
if (isConventionalFrameworkUtility(context.filename, exported)) return;
|
|
4399
|
-
const
|
|
4400
|
-
const
|
|
4401
|
-
|
|
4402
|
-
if (expectedStem === currentResponsibility || GENERIC_STEMS.has(expectedStem) || !/^[a-z0-9]+(?:-[a-z0-9]+)*$/u.test(expectedStem)) return;
|
|
4403
|
-
const suffixTail = suffixStem === "" ? "" : `-${suffixStem}`;
|
|
4404
|
-
const renameStem = suffixTail !== "" && expectedStem.endsWith(suffixTail) ? expectedStem.slice(0, -suffixTail.length) : expectedStem;
|
|
4405
|
-
if (renameStem === "" || GENERIC_STEMS.has(renameStem)) return;
|
|
4406
|
-
const suffix = file.suffixes.map((part) => `.${kebabCase(part)}`).join("");
|
|
4363
|
+
const exportedRole = exported.replaceAll(/[^a-z0-9]/giu, "").toLowerCase();
|
|
4364
|
+
const pathRole = [stem3, ...file.suffixes].join("").replaceAll(/[^a-z0-9]/giu, "");
|
|
4365
|
+
if (exportedRole === pathRole && file.suffixes.length > 0) return;
|
|
4407
4366
|
context.report({
|
|
4408
4367
|
node,
|
|
4409
4368
|
messageId: "genericSingleExport",
|
|
4410
|
-
data: { stem: stem3, exported
|
|
4369
|
+
data: { stem: stem3, exported }
|
|
4411
4370
|
});
|
|
4412
4371
|
}
|
|
4413
4372
|
};
|
|
@@ -13883,8 +13842,13 @@ import { AST_NODE_TYPES as AST_NODE_TYPES54, ASTUtils as ASTUtils15 } from "@typ
|
|
|
13883
13842
|
var stepdownDocumentation = {
|
|
13884
13843
|
summary: "Place a private helper below its sole direct same-scope caller.",
|
|
13885
13844
|
rationale: "Caller-first ordering lets a reader follow the main flow before descending into implementation details.",
|
|
13886
|
-
remediation: "Move the private helper
|
|
13845
|
+
remediation: "Move the private helper below its sole caller.",
|
|
13887
13846
|
category: "maintainability",
|
|
13847
|
+
limitations: [
|
|
13848
|
+
"Generated and test files, cycles, dynamic references, overload targets, and helpers with multiple callers are excluded.",
|
|
13849
|
+
"Class methods are reported only when both helper and caller are private so accessibility ordering remains authoritative.",
|
|
13850
|
+
"Runtime class-field, static-block, computed-member, and decorator barriers are never crossed."
|
|
13851
|
+
],
|
|
13888
13852
|
examples: [
|
|
13889
13853
|
{ id: "caller-before-helper", title: "Place the caller first", outcome: "no-match", files: [{ path: "src/run.ts", source: "function run() { return load(); }\nfunction load() { return 1; }" }], focusPath: "src/run.ts", expectedCount: 0, public: true },
|
|
13890
13854
|
{ id: "helper-before-caller", title: "Do not lead with a sole-caller helper", outcome: "match", files: [{ path: "src/run.ts", source: "function load() { return 1; }\nfunction run() { return load(); }" }], focusPath: "src/run.ts", expectedCount: 1, public: true }
|
|
@@ -13893,7 +13857,7 @@ var stepdownDocumentation = {
|
|
|
13893
13857
|
function isFunction(node) {
|
|
13894
13858
|
return node.type === AST_NODE_TYPES54.ArrowFunctionExpression || node.type === AST_NODE_TYPES54.FunctionDeclaration || node.type === AST_NODE_TYPES54.FunctionExpression;
|
|
13895
13859
|
}
|
|
13896
|
-
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned) {
|
|
13860
|
+
function reportMisordered(context, candidates, scopeDefinitions, calls, pinned, canMove = () => true) {
|
|
13897
13861
|
const byName = new Map(scopeDefinitions.map((definition) => [definition.name, definition]));
|
|
13898
13862
|
const cycles = cycleComponents(calls);
|
|
13899
13863
|
const callers = /* @__PURE__ */ new Map();
|
|
@@ -13911,7 +13875,7 @@ function reportMisordered(context, candidates, scopeDefinitions, calls, pinned)
|
|
|
13911
13875
|
const callerName2 = helperCallers[0];
|
|
13912
13876
|
if (callerName2 === void 0 || cycles.has(helper.name) && cycles.get(helper.name) === cycles.get(callerName2)) continue;
|
|
13913
13877
|
const caller = byName.get(callerName2);
|
|
13914
|
-
if (caller === void 0 || helper.node.range[0] >= caller.node.range[0]) continue;
|
|
13878
|
+
if (caller === void 0 || helper.node.range[0] >= caller.node.range[0] || !canMove(helper, caller)) continue;
|
|
13915
13879
|
context.report({
|
|
13916
13880
|
node: helper.node,
|
|
13917
13881
|
messageId: "helperAboveOnlyCaller",
|
|
@@ -13991,8 +13955,10 @@ function moduleScope(context, program) {
|
|
|
13991
13955
|
})
|
|
13992
13956
|
);
|
|
13993
13957
|
const exported = exportedNames(program);
|
|
13994
|
-
const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1
|
|
13995
|
-
const definitions = scopeDefinitions.filter(
|
|
13958
|
+
const scopeDefinitions = declarations.filter((node) => counts.get(node.name) === 1);
|
|
13959
|
+
const definitions = scopeDefinitions.filter(
|
|
13960
|
+
(definition) => !exported.has(definition.name) && !overloadNames.has(definition.name)
|
|
13961
|
+
);
|
|
13996
13962
|
const byFunction = new Map(scopeDefinitions.map((definition) => [definition.functionNode, definition]));
|
|
13997
13963
|
const calls = /* @__PURE__ */ new Map();
|
|
13998
13964
|
const pinned = /* @__PURE__ */ new Set();
|
|
@@ -14236,11 +14202,36 @@ function classScope(context, node, computedReferenceNames) {
|
|
|
14236
14202
|
return [definition.name, accessibility2];
|
|
14237
14203
|
})
|
|
14238
14204
|
);
|
|
14205
|
+
const methodByName = new Map(scopeDefinitions.map((definition) => [definition.name, definition.node]));
|
|
14239
14206
|
for (const [caller, callees] of calls) {
|
|
14240
|
-
|
|
14207
|
+
const callerMethod = methodByName.get(caller);
|
|
14208
|
+
if (accessibility.get(caller) === "private" && callerMethod?.type === AST_NODE_TYPES54.MethodDefinition && callerMethod.decorators.length === 0) continue;
|
|
14241
14209
|
for (const callee of callees) pinned.add(callee);
|
|
14242
14210
|
}
|
|
14243
|
-
|
|
14211
|
+
const memberIndexes = new Map(node.body.body.map((member, index) => [member, index]));
|
|
14212
|
+
const runtimeBarrierPrefix = [0];
|
|
14213
|
+
for (const member of node.body.body) {
|
|
14214
|
+
runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
|
|
14215
|
+
}
|
|
14216
|
+
reportMisordered(context, definitions, scopeDefinitions, calls, pinned, (helper, caller) => {
|
|
14217
|
+
const helperIndex = memberIndexes.get(helper.node);
|
|
14218
|
+
const callerIndex = memberIndexes.get(caller.node);
|
|
14219
|
+
if (helperIndex === void 0 || callerIndex === void 0) return false;
|
|
14220
|
+
return runtimeBarrierPrefix[callerIndex + 1] === runtimeBarrierPrefix[helperIndex + 1];
|
|
14221
|
+
});
|
|
14222
|
+
}
|
|
14223
|
+
function isClassRuntimeBarrier(member) {
|
|
14224
|
+
switch (member.type) {
|
|
14225
|
+
case AST_NODE_TYPES54.StaticBlock:
|
|
14226
|
+
return true;
|
|
14227
|
+
case AST_NODE_TYPES54.PropertyDefinition:
|
|
14228
|
+
case AST_NODE_TYPES54.AccessorProperty:
|
|
14229
|
+
return member.static || member.computed || member.decorators.length > 0 || member.value !== null;
|
|
14230
|
+
case AST_NODE_TYPES54.MethodDefinition:
|
|
14231
|
+
return member.computed || member.decorators.length > 0;
|
|
14232
|
+
default:
|
|
14233
|
+
return false;
|
|
14234
|
+
}
|
|
14244
14235
|
}
|
|
14245
14236
|
var stepdown_default = createRule({
|
|
14246
14237
|
name: "stepdown",
|
|
@@ -14568,7 +14559,7 @@ var rules = {
|
|
|
14568
14559
|
};
|
|
14569
14560
|
var meta = {
|
|
14570
14561
|
name: "@sarj/eslint-plugin",
|
|
14571
|
-
version: "15.
|
|
14562
|
+
version: "15.6.0"
|
|
14572
14563
|
};
|
|
14573
14564
|
var applicationOnlyRules = [
|
|
14574
14565
|
"no-restricted-library-load",
|