@sarj/eslint-plugin 16.23.1 → 16.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -869,7 +869,7 @@ function isSearchParamReceiver(call, services) {
869
869
  // package.json
870
870
  var package_default = {
871
871
  name: "@sarj/eslint-plugin",
872
- version: "16.23.1",
872
+ version: "16.23.2",
873
873
  packageManager: "npm@12.2.0",
874
874
  description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
875
875
  type: "module",
@@ -26573,7 +26573,7 @@ var STEPDOWN_DOCUMENTATION = {
26573
26573
  category: "maintainability",
26574
26574
  limitations: [
26575
26575
  "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.",
26576
+ "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
26577
  "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
26578
  "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
26579
  ],
@@ -26894,7 +26894,7 @@ function walk(node, visitorKeys, visit, nestedFunction = false) {
26894
26894
  const nested = nestedFunction || isFunction(node) || node.type === import_utils142.AST_NODE_TYPES.ClassDeclaration || node.type === import_utils142.AST_NODE_TYPES.ClassExpression;
26895
26895
  forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
26896
26896
  }
26897
- function classScope(context, node, computedReferences) {
26897
+ function classScope(context, node, computedReferences, privateBandOnly) {
26898
26898
  const methods = node.body.body.filter(
26899
26899
  (member) => member.type === import_utils142.AST_NODE_TYPES.MethodDefinition
26900
26900
  );
@@ -26907,7 +26907,7 @@ function classScope(context, node, computedReferences) {
26907
26907
  return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
26908
26908
  });
26909
26909
  const definitions2 = scopeDefinitions.filter(
26910
- ({ key, node: method }) => (method.accessibility === "private" || method.key.type === import_utils142.AST_NODE_TYPES.PrivateIdentifier) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
26910
+ ({ key, node: method }) => isPrivateMethod(method) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
26911
26911
  );
26912
26912
  if (definitions2.length === 0) return;
26913
26913
  const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
@@ -27036,6 +27036,7 @@ function classScope(context, node, computedReferences) {
27036
27036
  runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
27037
27037
  }
27038
27038
  const canMove = (helper, caller) => {
27039
+ if (privateBandOnly && !isPrivateMethod(caller.node)) return false;
27039
27040
  const helperIndex = memberIndexes.get(helper.node);
27040
27041
  const callerIndex = memberIndexes.get(caller.node);
27041
27042
  if (helperIndex === void 0 || callerIndex === void 0) return false;
@@ -27043,6 +27044,9 @@ function classScope(context, node, computedReferences) {
27043
27044
  };
27044
27045
  reportMisordered(context, definitions2, scopeDefinitions, calls, pinned, canMove);
27045
27046
  }
27047
+ function isPrivateMethod(node) {
27048
+ return node.type === import_utils142.AST_NODE_TYPES.MethodDefinition && (node.accessibility === "private" || node.key.type === import_utils142.AST_NODE_TYPES.PrivateIdentifier);
27049
+ }
27046
27050
  function classReceiverBindings(context, node) {
27047
27051
  const classReceivers = /* @__PURE__ */ new Map();
27048
27052
  if (node.id !== null) {
@@ -27083,13 +27087,22 @@ var stepdown_default = createRule({
27083
27087
  meta: {
27084
27088
  type: "suggestion",
27085
27089
  docs: { description: "Place a private helper below its sole direct same-scope caller." },
27086
- schema: [],
27090
+ schema: [{
27091
+ type: "object",
27092
+ additionalProperties: false,
27093
+ properties: {
27094
+ privateBandOnly: {
27095
+ type: "boolean",
27096
+ description: "Restrict class reports to private callers when another rule owns accessibility-band ordering."
27097
+ }
27098
+ }
27099
+ }],
27087
27100
  messages: {
27088
27101
  helperAboveOnlyCaller: "Private helper `{{helper}}` is defined above its only caller `{{caller}}`; move it below the caller."
27089
27102
  }
27090
27103
  },
27091
- defaultOptions: [],
27092
- create(context) {
27104
+ defaultOptions: [{}],
27105
+ create(context, [options]) {
27093
27106
  if (isTestFile(context) || isGeneratedFile(context, context.sourceCode.text)) return {};
27094
27107
  const classes = [];
27095
27108
  const functionBodies = [];
@@ -27110,7 +27123,7 @@ var stepdown_default = createRule({
27110
27123
  walk(program, context.sourceCode.visitorKeys, (node) => {
27111
27124
  if (node.type === import_utils142.AST_NODE_TYPES.MemberExpression && node.computed) computedReferences.push(node);
27112
27125
  });
27113
- for (const node of classes) classScope(context, node, computedReferences);
27126
+ for (const node of classes) classScope(context, node, computedReferences, options.privateBandOnly === true);
27114
27127
  }
27115
27128
  };
27116
27129
  }
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<[], "helperAboveOnlyCaller">;
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<[], "helperAboveOnlyCaller">;
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<[], "helperAboveOnlyCaller">;
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<[], "helperAboveOnlyCaller">;
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
@@ -822,7 +822,7 @@ function isSearchParamReceiver(call, services) {
822
822
  // package.json
823
823
  var package_default = {
824
824
  name: "@sarj/eslint-plugin",
825
- version: "16.23.1",
825
+ version: "16.23.2",
826
826
  packageManager: "npm@12.2.0",
827
827
  description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
828
828
  type: "module",
@@ -26597,7 +26597,7 @@ var STEPDOWN_DOCUMENTATION = {
26597
26597
  category: "maintainability",
26598
26598
  limitations: [
26599
26599
  "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.",
26600
+ "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
26601
  "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
26602
  "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
26603
  ],
@@ -26918,7 +26918,7 @@ function walk(node, visitorKeys, visit, nestedFunction = false) {
26918
26918
  const nested = nestedFunction || isFunction(node) || node.type === AST_NODE_TYPES121.ClassDeclaration || node.type === AST_NODE_TYPES121.ClassExpression;
26919
26919
  forEachAstChild(node, visitorKeys, (child) => walk(child, visitorKeys, visit, nested));
26920
26920
  }
26921
- function classScope(context, node, computedReferences) {
26921
+ function classScope(context, node, computedReferences, privateBandOnly) {
26922
26922
  const methods = node.body.body.filter(
26923
26923
  (member) => member.type === AST_NODE_TYPES121.MethodDefinition
26924
26924
  );
@@ -26931,7 +26931,7 @@ function classScope(context, node, computedReferences) {
26931
26931
  return name !== null && key !== null && implementationCounts.get(key) === 1 ? [{ key, name, node: method }] : [];
26932
26932
  });
26933
26933
  const definitions2 = scopeDefinitions.filter(
26934
- ({ key, node: method }) => (method.accessibility === "private" || method.key.type === AST_NODE_TYPES121.PrivateIdentifier) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
26934
+ ({ key, node: method }) => isPrivateMethod(method) && counts.get(key) === 1 && method.decorators.length === 0 && !method.computed && method.kind === "method"
26935
26935
  );
26936
26936
  if (definitions2.length === 0) return;
26937
26937
  const methodKeys = new Set(scopeDefinitions.map((definition) => definition.key));
@@ -27060,6 +27060,7 @@ function classScope(context, node, computedReferences) {
27060
27060
  runtimeBarrierPrefix.push((runtimeBarrierPrefix.at(-1) ?? 0) + (isClassRuntimeBarrier(member) ? 1 : 0));
27061
27061
  }
27062
27062
  const canMove = (helper, caller) => {
27063
+ if (privateBandOnly && !isPrivateMethod(caller.node)) return false;
27063
27064
  const helperIndex = memberIndexes.get(helper.node);
27064
27065
  const callerIndex = memberIndexes.get(caller.node);
27065
27066
  if (helperIndex === void 0 || callerIndex === void 0) return false;
@@ -27067,6 +27068,9 @@ function classScope(context, node, computedReferences) {
27067
27068
  };
27068
27069
  reportMisordered(context, definitions2, scopeDefinitions, calls, pinned, canMove);
27069
27070
  }
27071
+ function isPrivateMethod(node) {
27072
+ return node.type === AST_NODE_TYPES121.MethodDefinition && (node.accessibility === "private" || node.key.type === AST_NODE_TYPES121.PrivateIdentifier);
27073
+ }
27070
27074
  function classReceiverBindings(context, node) {
27071
27075
  const classReceivers = /* @__PURE__ */ new Map();
27072
27076
  if (node.id !== null) {
@@ -27107,13 +27111,22 @@ var stepdown_default = createRule({
27107
27111
  meta: {
27108
27112
  type: "suggestion",
27109
27113
  docs: { description: "Place a private helper below its sole direct same-scope caller." },
27110
- schema: [],
27114
+ schema: [{
27115
+ type: "object",
27116
+ additionalProperties: false,
27117
+ properties: {
27118
+ privateBandOnly: {
27119
+ type: "boolean",
27120
+ description: "Restrict class reports to private callers when another rule owns accessibility-band ordering."
27121
+ }
27122
+ }
27123
+ }],
27111
27124
  messages: {
27112
27125
  helperAboveOnlyCaller: "Private helper `{{helper}}` is defined above its only caller `{{caller}}`; move it below the caller."
27113
27126
  }
27114
27127
  },
27115
- defaultOptions: [],
27116
- create(context) {
27128
+ defaultOptions: [{}],
27129
+ create(context, [options]) {
27117
27130
  if (isTestFile(context) || isGeneratedFile(context, context.sourceCode.text)) return {};
27118
27131
  const classes = [];
27119
27132
  const functionBodies = [];
@@ -27134,7 +27147,7 @@ var stepdown_default = createRule({
27134
27147
  walk(program, context.sourceCode.visitorKeys, (node) => {
27135
27148
  if (node.type === AST_NODE_TYPES121.MemberExpression && node.computed) computedReferences.push(node);
27136
27149
  });
27137
- for (const node of classes) classScope(context, node, computedReferences);
27150
+ for (const node of classes) classScope(context, node, computedReferences, options.privateBandOnly === true);
27138
27151
  }
27139
27152
  };
27140
27153
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "16.23.1",
3
+ "version": "16.23.2",
4
4
  "packageManager": "npm@12.2.0",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",