@sarj/eslint-plugin 16.23.1 → 16.23.3
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 +36 -9
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +37 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -424,7 +424,21 @@ function isScriptFile(filename) {
|
|
|
424
424
|
return SCRIPT_FILE_RE.test(scopedFilename(filename));
|
|
425
425
|
}
|
|
426
426
|
function scopedFilename(input) {
|
|
427
|
-
|
|
427
|
+
if (typeof input === "string") return input.replaceAll("\\", "/");
|
|
428
|
+
const filename = input.filename.replaceAll("\\", "/");
|
|
429
|
+
const cwd = input.cwd.replaceAll("\\", "/");
|
|
430
|
+
const fileWindows = windowsAbsolute(filename);
|
|
431
|
+
const cwdWindows = windowsAbsolute(cwd);
|
|
432
|
+
let scoped = filename;
|
|
433
|
+
if (fileWindows && cwdWindows) scoped = import_node_path.win32.relative(cwd, filename).replaceAll("\\", "/");
|
|
434
|
+
else if (!fileWindows && !cwdWindows && import_node_path.posix.isAbsolute(filename) && import_node_path.posix.isAbsolute(cwd)) {
|
|
435
|
+
scoped = import_node_path.posix.relative(cwd, filename);
|
|
436
|
+
}
|
|
437
|
+
if (windowsAbsolute(scoped) || import_node_path.posix.isAbsolute(scoped)) return scoped;
|
|
438
|
+
return `./${import_node_path.posix.normalize(scoped)}`;
|
|
439
|
+
}
|
|
440
|
+
function windowsAbsolute(filename) {
|
|
441
|
+
return /^[A-Za-z]:\//u.test(filename) || filename.startsWith("//");
|
|
428
442
|
}
|
|
429
443
|
function matchesAnyPattern(filename, patterns) {
|
|
430
444
|
const normalized = filename.replaceAll("\\", "/");
|
|
@@ -869,7 +883,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
869
883
|
// package.json
|
|
870
884
|
var package_default = {
|
|
871
885
|
name: "@sarj/eslint-plugin",
|
|
872
|
-
version: "16.23.
|
|
886
|
+
version: "16.23.3",
|
|
873
887
|
packageManager: "npm@12.2.0",
|
|
874
888
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
875
889
|
type: "module",
|
|
@@ -26573,7 +26587,7 @@ var STEPDOWN_DOCUMENTATION = {
|
|
|
26573
26587
|
category: "maintainability",
|
|
26574
26588
|
limitations: [
|
|
26575
26589
|
"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. Function bodies include nested sibling helpers.",
|
|
26576
|
-
"Class helpers must be private; their sole caller may be public, protected, or private.",
|
|
26590
|
+
"Class helpers must be private; their sole caller may be public, protected, or private by default. Set privateBandOnly alongside accessibility ordering to restrict class reports to private callers; module and nested function ordering is unchanged.",
|
|
26577
26591
|
"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.",
|
|
26578
26592
|
"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."
|
|
26579
26593
|
],
|
|
@@ -26894,7 +26908,7 @@ function walk(node, visitorKeys, visit, nestedFunction = false) {
|
|
|
26894
26908
|
const nested = nestedFunction || isFunction(node) || node.type === import_utils142.AST_NODE_TYPES.ClassDeclaration || node.type === import_utils142.AST_NODE_TYPES.ClassExpression;
|
|
26895
26909
|
forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
|
|
26896
26910
|
}
|
|
26897
|
-
function classScope(context, node, computedReferences) {
|
|
26911
|
+
function classScope(context, node, computedReferences, privateBandOnly) {
|
|
26898
26912
|
const methods = node.body.body.filter(
|
|
26899
26913
|
(member) => member.type === import_utils142.AST_NODE_TYPES.MethodDefinition
|
|
26900
26914
|
);
|
|
@@ -26907,7 +26921,7 @@ function classScope(context, node, computedReferences) {
|
|
|
26907
26921
|
return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
|
|
26908
26922
|
});
|
|
26909
26923
|
const definitions2 = scopeDefinitions.filter(
|
|
26910
|
-
({ key, node: method }) => (method
|
|
26924
|
+
({ key, node: method }) => isPrivateMethod(method) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
|
|
26911
26925
|
);
|
|
26912
26926
|
if (definitions2.length === 0) return;
|
|
26913
26927
|
const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
|
|
@@ -27036,6 +27050,7 @@ function classScope(context, node, computedReferences) {
|
|
|
27036
27050
|
runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
|
|
27037
27051
|
}
|
|
27038
27052
|
const canMove = (helper, caller) => {
|
|
27053
|
+
if (privateBandOnly && !isPrivateMethod(caller.node)) return false;
|
|
27039
27054
|
const helperIndex = memberIndexes.get(helper.node);
|
|
27040
27055
|
const callerIndex = memberIndexes.get(caller.node);
|
|
27041
27056
|
if (helperIndex === void 0 || callerIndex === void 0) return false;
|
|
@@ -27043,6 +27058,9 @@ function classScope(context, node, computedReferences) {
|
|
|
27043
27058
|
};
|
|
27044
27059
|
reportMisordered(context, definitions2, scopeDefinitions, calls, pinned, canMove);
|
|
27045
27060
|
}
|
|
27061
|
+
function isPrivateMethod(node) {
|
|
27062
|
+
return node.type === import_utils142.AST_NODE_TYPES.MethodDefinition && (node.accessibility === "private" || node.key.type === import_utils142.AST_NODE_TYPES.PrivateIdentifier);
|
|
27063
|
+
}
|
|
27046
27064
|
function classReceiverBindings(context, node) {
|
|
27047
27065
|
const classReceivers = /* @__PURE__ */ new Map();
|
|
27048
27066
|
if (node.id !== null) {
|
|
@@ -27083,13 +27101,22 @@ var stepdown_default = createRule({
|
|
|
27083
27101
|
meta: {
|
|
27084
27102
|
type: "suggestion",
|
|
27085
27103
|
docs: { description: "Place a private helper below its sole direct same-scope caller." },
|
|
27086
|
-
schema: [
|
|
27104
|
+
schema: [{
|
|
27105
|
+
type: "object",
|
|
27106
|
+
additionalProperties: false,
|
|
27107
|
+
properties: {
|
|
27108
|
+
privateBandOnly: {
|
|
27109
|
+
type: "boolean",
|
|
27110
|
+
description: "Restrict class reports to private callers when another rule owns accessibility-band ordering."
|
|
27111
|
+
}
|
|
27112
|
+
}
|
|
27113
|
+
}],
|
|
27087
27114
|
messages: {
|
|
27088
27115
|
helperAboveOnlyCaller: "Private helper `{{helper}}` is defined above its only caller `{{caller}}`; move it below the caller."
|
|
27089
27116
|
}
|
|
27090
27117
|
},
|
|
27091
|
-
defaultOptions: [],
|
|
27092
|
-
create(context) {
|
|
27118
|
+
defaultOptions: [{}],
|
|
27119
|
+
create(context, [options]) {
|
|
27093
27120
|
if (isTestFile(context) || isGeneratedFile(context, context.sourceCode.text)) return {};
|
|
27094
27121
|
const classes = [];
|
|
27095
27122
|
const functionBodies = [];
|
|
@@ -27110,7 +27137,7 @@ var stepdown_default = createRule({
|
|
|
27110
27137
|
walk(program, context.sourceCode.visitorKeys, (node) => {
|
|
27111
27138
|
if (node.type === import_utils142.AST_NODE_TYPES.MemberExpression && node.computed) computedReferences.push(node);
|
|
27112
27139
|
});
|
|
27113
|
-
for (const node of classes) classScope(context, node, computedReferences);
|
|
27140
|
+
for (const node of classes) classScope(context, node, computedReferences, options.privateBandOnly === true);
|
|
27114
27141
|
}
|
|
27115
27142
|
};
|
|
27116
27143
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -295,7 +295,9 @@ declare const RULES: {
|
|
|
295
295
|
readonly "require-static-next-matcher": DocumentedRule<readonly [], "dynamicMatcher">;
|
|
296
296
|
readonly "require-zod-form-validation": DocumentedRule<readonly [], "missingZodValidation">;
|
|
297
297
|
readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
|
|
298
|
-
readonly stepdown: DocumentedRule<[
|
|
298
|
+
readonly stepdown: DocumentedRule<[{
|
|
299
|
+
readonly privateBandOnly?: boolean;
|
|
300
|
+
}], "helperAboveOnlyCaller">;
|
|
299
301
|
readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
|
|
300
302
|
readonly "sole-export-matches-filename": DocumentedRule<[], "matchSoleExport">;
|
|
301
303
|
readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
|
|
@@ -1134,7 +1136,9 @@ declare const PLUGIN: {
|
|
|
1134
1136
|
readonly "require-static-next-matcher": DocumentedRule<readonly [], "dynamicMatcher">;
|
|
1135
1137
|
readonly "require-zod-form-validation": DocumentedRule<readonly [], "missingZodValidation">;
|
|
1136
1138
|
readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
|
|
1137
|
-
readonly stepdown: DocumentedRule<[
|
|
1139
|
+
readonly stepdown: DocumentedRule<[{
|
|
1140
|
+
readonly privateBandOnly?: boolean;
|
|
1141
|
+
}], "helperAboveOnlyCaller">;
|
|
1138
1142
|
readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
|
|
1139
1143
|
readonly "sole-export-matches-filename": DocumentedRule<[], "matchSoleExport">;
|
|
1140
1144
|
readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
|
package/dist/index.d.ts
CHANGED
|
@@ -295,7 +295,9 @@ declare const RULES: {
|
|
|
295
295
|
readonly "require-static-next-matcher": DocumentedRule<readonly [], "dynamicMatcher">;
|
|
296
296
|
readonly "require-zod-form-validation": DocumentedRule<readonly [], "missingZodValidation">;
|
|
297
297
|
readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
|
|
298
|
-
readonly stepdown: DocumentedRule<[
|
|
298
|
+
readonly stepdown: DocumentedRule<[{
|
|
299
|
+
readonly privateBandOnly?: boolean;
|
|
300
|
+
}], "helperAboveOnlyCaller">;
|
|
299
301
|
readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
|
|
300
302
|
readonly "sole-export-matches-filename": DocumentedRule<[], "matchSoleExport">;
|
|
301
303
|
readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
|
|
@@ -1134,7 +1136,9 @@ declare const PLUGIN: {
|
|
|
1134
1136
|
readonly "require-static-next-matcher": DocumentedRule<readonly [], "dynamicMatcher">;
|
|
1135
1137
|
readonly "require-zod-form-validation": DocumentedRule<readonly [], "missingZodValidation">;
|
|
1136
1138
|
readonly "store-insert-requires-on-conflict": DocumentedRule<readonly [], "storeInsertRequiresOnConflict">;
|
|
1137
|
-
readonly stepdown: DocumentedRule<[
|
|
1139
|
+
readonly stepdown: DocumentedRule<[{
|
|
1140
|
+
readonly privateBandOnly?: boolean;
|
|
1141
|
+
}], "helperAboveOnlyCaller">;
|
|
1138
1142
|
readonly "source-coupled-test": DocumentedRule<readonly [], "rawSourceOracle">;
|
|
1139
1143
|
readonly "sole-export-matches-filename": DocumentedRule<[], "matchSoleExport">;
|
|
1140
1144
|
readonly "require-pascal-case-zod-schema-name": DocumentedRule<readonly [], "requirePascalSchema">;
|
package/dist/index.js
CHANGED
|
@@ -288,7 +288,7 @@ function isTypeOnly(node) {
|
|
|
288
288
|
import { AST_NODE_TYPES as AST_NODE_TYPES4, ASTUtils as ASTUtils2 } from "@typescript-eslint/utils";
|
|
289
289
|
|
|
290
290
|
// src/rules/_paths.ts
|
|
291
|
-
import { posix } from "path";
|
|
291
|
+
import { posix, win32 } from "path";
|
|
292
292
|
var LINE_TERMINATOR_RE = /[\n\r\u2028\u2029]/u;
|
|
293
293
|
var SCRIPT_FILE_RE = /(?:^|[\\/])scripts[\\/]|\.mjs$/;
|
|
294
294
|
var STORY_FILE_RE = /\.stories\.[cm]?[jt]sx?$/i;
|
|
@@ -373,7 +373,21 @@ function isScriptFile(filename) {
|
|
|
373
373
|
return SCRIPT_FILE_RE.test(scopedFilename(filename));
|
|
374
374
|
}
|
|
375
375
|
function scopedFilename(input) {
|
|
376
|
-
|
|
376
|
+
if (typeof input === "string") return input.replaceAll("\\", "/");
|
|
377
|
+
const filename = input.filename.replaceAll("\\", "/");
|
|
378
|
+
const cwd = input.cwd.replaceAll("\\", "/");
|
|
379
|
+
const fileWindows = windowsAbsolute(filename);
|
|
380
|
+
const cwdWindows = windowsAbsolute(cwd);
|
|
381
|
+
let scoped = filename;
|
|
382
|
+
if (fileWindows && cwdWindows) scoped = win32.relative(cwd, filename).replaceAll("\\", "/");
|
|
383
|
+
else if (!fileWindows && !cwdWindows && posix.isAbsolute(filename) && posix.isAbsolute(cwd)) {
|
|
384
|
+
scoped = posix.relative(cwd, filename);
|
|
385
|
+
}
|
|
386
|
+
if (windowsAbsolute(scoped) || posix.isAbsolute(scoped)) return scoped;
|
|
387
|
+
return `./${posix.normalize(scoped)}`;
|
|
388
|
+
}
|
|
389
|
+
function windowsAbsolute(filename) {
|
|
390
|
+
return /^[A-Za-z]:\//u.test(filename) || filename.startsWith("//");
|
|
377
391
|
}
|
|
378
392
|
function matchesAnyPattern(filename, patterns) {
|
|
379
393
|
const normalized = filename.replaceAll("\\", "/");
|
|
@@ -822,7 +836,7 @@ function isSearchParamReceiver(call, services) {
|
|
|
822
836
|
// package.json
|
|
823
837
|
var package_default = {
|
|
824
838
|
name: "@sarj/eslint-plugin",
|
|
825
|
-
version: "16.23.
|
|
839
|
+
version: "16.23.3",
|
|
826
840
|
packageManager: "npm@12.2.0",
|
|
827
841
|
description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
|
|
828
842
|
type: "module",
|
|
@@ -26597,7 +26611,7 @@ var STEPDOWN_DOCUMENTATION = {
|
|
|
26597
26611
|
category: "maintainability",
|
|
26598
26612
|
limitations: [
|
|
26599
26613
|
"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. Function bodies include nested sibling helpers.",
|
|
26600
|
-
"Class helpers must be private; their sole caller may be public, protected, or private.",
|
|
26614
|
+
"Class helpers must be private; their sole caller may be public, protected, or private by default. Set privateBandOnly alongside accessibility ordering to restrict class reports to private callers; module and nested function ordering is unchanged.",
|
|
26601
26615
|
"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.",
|
|
26602
26616
|
"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."
|
|
26603
26617
|
],
|
|
@@ -26918,7 +26932,7 @@ function walk(node, visitorKeys, visit, nestedFunction = false) {
|
|
|
26918
26932
|
const nested = nestedFunction || isFunction(node) || node.type === AST_NODE_TYPES121.ClassDeclaration || node.type === AST_NODE_TYPES121.ClassExpression;
|
|
26919
26933
|
forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
|
|
26920
26934
|
}
|
|
26921
|
-
function classScope(context, node, computedReferences) {
|
|
26935
|
+
function classScope(context, node, computedReferences, privateBandOnly) {
|
|
26922
26936
|
const methods = node.body.body.filter(
|
|
26923
26937
|
(member) => member.type === AST_NODE_TYPES121.MethodDefinition
|
|
26924
26938
|
);
|
|
@@ -26931,7 +26945,7 @@ function classScope(context, node, computedReferences) {
|
|
|
26931
26945
|
return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
|
|
26932
26946
|
});
|
|
26933
26947
|
const definitions2 = scopeDefinitions.filter(
|
|
26934
|
-
({ key, node: method }) => (method
|
|
26948
|
+
({ key, node: method }) => isPrivateMethod(method) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
|
|
26935
26949
|
);
|
|
26936
26950
|
if (definitions2.length === 0) return;
|
|
26937
26951
|
const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
|
|
@@ -27060,6 +27074,7 @@ function classScope(context, node, computedReferences) {
|
|
|
27060
27074
|
runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
|
|
27061
27075
|
}
|
|
27062
27076
|
const canMove = (helper, caller) => {
|
|
27077
|
+
if (privateBandOnly && !isPrivateMethod(caller.node)) return false;
|
|
27063
27078
|
const helperIndex = memberIndexes.get(helper.node);
|
|
27064
27079
|
const callerIndex = memberIndexes.get(caller.node);
|
|
27065
27080
|
if (helperIndex === void 0 || callerIndex === void 0) return false;
|
|
@@ -27067,6 +27082,9 @@ function classScope(context, node, computedReferences) {
|
|
|
27067
27082
|
};
|
|
27068
27083
|
reportMisordered(context, definitions2, scopeDefinitions, calls, pinned, canMove);
|
|
27069
27084
|
}
|
|
27085
|
+
function isPrivateMethod(node) {
|
|
27086
|
+
return node.type === AST_NODE_TYPES121.MethodDefinition && (node.accessibility === "private" || node.key.type === AST_NODE_TYPES121.PrivateIdentifier);
|
|
27087
|
+
}
|
|
27070
27088
|
function classReceiverBindings(context, node) {
|
|
27071
27089
|
const classReceivers = /* @__PURE__ */ new Map();
|
|
27072
27090
|
if (node.id !== null) {
|
|
@@ -27107,13 +27125,22 @@ var stepdown_default = createRule({
|
|
|
27107
27125
|
meta: {
|
|
27108
27126
|
type: "suggestion",
|
|
27109
27127
|
docs: { description: "Place a private helper below its sole direct same-scope caller." },
|
|
27110
|
-
schema: [
|
|
27128
|
+
schema: [{
|
|
27129
|
+
type: "object",
|
|
27130
|
+
additionalProperties: false,
|
|
27131
|
+
properties: {
|
|
27132
|
+
privateBandOnly: {
|
|
27133
|
+
type: "boolean",
|
|
27134
|
+
description: "Restrict class reports to private callers when another rule owns accessibility-band ordering."
|
|
27135
|
+
}
|
|
27136
|
+
}
|
|
27137
|
+
}],
|
|
27111
27138
|
messages: {
|
|
27112
27139
|
helperAboveOnlyCaller: "Private helper `{{helper}}` is defined above its only caller `{{caller}}`; move it below the caller."
|
|
27113
27140
|
}
|
|
27114
27141
|
},
|
|
27115
|
-
defaultOptions: [],
|
|
27116
|
-
create(context) {
|
|
27142
|
+
defaultOptions: [{}],
|
|
27143
|
+
create(context, [options]) {
|
|
27117
27144
|
if (isTestFile(context) || isGeneratedFile(context, context.sourceCode.text)) return {};
|
|
27118
27145
|
const classes = [];
|
|
27119
27146
|
const functionBodies = [];
|
|
@@ -27134,7 +27161,7 @@ var stepdown_default = createRule({
|
|
|
27134
27161
|
walk(program, context.sourceCode.visitorKeys, (node) => {
|
|
27135
27162
|
if (node.type === AST_NODE_TYPES121.MemberExpression && node.computed) computedReferences.push(node);
|
|
27136
27163
|
});
|
|
27137
|
-
for (const node of classes) classScope(context, node, computedReferences);
|
|
27164
|
+
for (const node of classes) classScope(context, node, computedReferences, options.privateBandOnly === true);
|
|
27138
27165
|
}
|
|
27139
27166
|
};
|
|
27140
27167
|
}
|