@sarj/eslint-plugin 18.1.3 → 18.1.5

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.
Files changed (3) hide show
  1. package/dist/index.cjs +111 -34
  2. package/dist/index.js +111 -34
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -204,8 +204,15 @@ function createNativeRule(config, url) {
204
204
  const defaults = metadataDefaults ?? legacyDefaults;
205
205
  if (defaults === void 0) throw new TypeError("Declared defaults disappeared while creating the rule.");
206
206
  const create = config.create;
207
+ const applied = /* @__PURE__ */ new WeakMap();
207
208
  return { ...base, create(context) {
208
- return create(context, import_utils2.ESLintUtils.applyDefault(defaults, context.options));
209
+ const options = context.options;
210
+ let merged = applied.get(options);
211
+ if (merged === void 0) {
212
+ merged = import_utils2.ESLintUtils.applyDefault(defaults, options);
213
+ applied.set(options, merged);
214
+ }
215
+ return create(context, merged);
209
216
  } };
210
217
  }
211
218
  function hasNoDefaultOptions(_config, metaDefaults, legacyDefaults) {
@@ -612,12 +619,17 @@ function isGeneratedFile(filename, sourceText = "", gates = []) {
612
619
  if (gates.includes("externalTree") && EXTERNAL_TREE_RE.test(normalized)) {
613
620
  return true;
614
621
  }
622
+ return generatedHeader(sourceText);
623
+ }
624
+ var lastHeaderText;
625
+ var lastHeaderGenerated = false;
626
+ function generatedHeader(sourceText) {
627
+ if (sourceText === lastHeaderText) return lastHeaderGenerated;
615
628
  const headerBodies = leadingCommentBodies(sourceText.slice(0, 2048));
616
629
  const nonAi = headerBodies.filter((body3) => !AI_ATTRIBUTION_RE.test(body3));
617
- if (nonAi.some((body3) => GENERATED_MARKER_RE.test(body3))) {
618
- return true;
619
- }
620
- return !headerBodies.some((body3) => AI_ATTRIBUTION_RE.test(body3)) && headerBodies.some((body3) => DO_NOT_EDIT_RE.test(body3));
630
+ lastHeaderGenerated = nonAi.some((body3) => GENERATED_MARKER_RE.test(body3)) || !headerBodies.some((body3) => AI_ATTRIBUTION_RE.test(body3)) && headerBodies.some((body3) => DO_NOT_EDIT_RE.test(body3));
631
+ lastHeaderText = sourceText;
632
+ return lastHeaderGenerated;
621
633
  }
622
634
  function leadingCommentBodies(sourceText) {
623
635
  const bodies = [];
@@ -658,6 +670,16 @@ function isScriptFile(filename) {
658
670
  }
659
671
  function scopedFilename(input) {
660
672
  if (typeof input === "string") return input.replaceAll("\\", "/");
673
+ if (input.filename === lastScopedFilename && input.cwd === lastScopedCwd) return lastScoped;
674
+ lastScoped = computeScopedFilename(input);
675
+ lastScopedFilename = input.filename;
676
+ lastScopedCwd = input.cwd;
677
+ return lastScoped;
678
+ }
679
+ var lastScopedFilename;
680
+ var lastScopedCwd;
681
+ var lastScoped = "";
682
+ function computeScopedFilename(input) {
661
683
  const filename = input.filename.replaceAll("\\", "/");
662
684
  const cwd = input.cwd.replaceAll("\\", "/");
663
685
  const fileWindows = windowsAbsolute(filename);
@@ -2299,8 +2321,18 @@ var FRONTEND_IMPORT = /^(?:react(?:-dom)?|next|preact|vue|svelte|@angular\/core|
2299
2321
  function isFrontendSource(context) {
2300
2322
  return /\.[jt]sx$/u.test(context.filename) || frontendModule(context.sourceCode.ast) || frontendPackage(context.filename);
2301
2323
  }
2324
+ var FRONTEND_PACKAGE_BY_DIRECTORY = /* @__PURE__ */ new Map();
2302
2325
  function frontendPackage(filename) {
2303
- let directory = (0, import_node_path2.dirname)((0, import_node_path2.resolve)(filename));
2326
+ const directory = (0, import_node_path2.dirname)((0, import_node_path2.resolve)(filename));
2327
+ let cached = FRONTEND_PACKAGE_BY_DIRECTORY.get(directory);
2328
+ if (cached === void 0) {
2329
+ cached = nearestManifestDeclaresFrontend(directory);
2330
+ FRONTEND_PACKAGE_BY_DIRECTORY.set(directory, cached);
2331
+ }
2332
+ return cached;
2333
+ }
2334
+ function nearestManifestDeclaresFrontend(start) {
2335
+ let directory = start;
2304
2336
  for (; ; ) {
2305
2337
  const manifestPath = (0, import_node_path2.join)(directory, "package.json");
2306
2338
  if ((0, import_node_fs.existsSync)(manifestPath)) {
@@ -2896,6 +2928,11 @@ var prefer_dependency_interface_default = createRule({
2896
2928
  }
2897
2929
  });
2898
2930
 
2931
+ // src/rules/_is-present.ts
2932
+ function isPresent(value) {
2933
+ return value !== null && value !== void 0;
2934
+ }
2935
+
2899
2936
  // src/rules/no-mutating-input-with-result.ts
2900
2937
  var import_utils22 = require("@typescript-eslint/utils");
2901
2938
  var NO_MUTATING_INPUT_WITH_RESULT_DOCUMENTATION = {
@@ -3000,7 +3037,7 @@ var no_mutating_input_with_result_default = createRule({
3000
3037
  if (annotation === void 0 || command(annotation) || node.body === void 0) return;
3001
3038
  const inputs = /* @__PURE__ */ new Map();
3002
3039
  for (const parameter of node.params) {
3003
- if (parameter.type !== import_utils22.AST_NODE_TYPES.Identifier || parameter.typeAnnotation === void 0) continue;
3040
+ if (parameter.type !== import_utils22.AST_NODE_TYPES.Identifier || !isPresent(parameter.typeAnnotation)) continue;
3004
3041
  const variable = import_utils22.ASTUtils.findVariable(sourceCode.getScope(parameter), parameter.name);
3005
3042
  if (variable === null || variable.references.some((reference) => reference.isWrite())) continue;
3006
3043
  const names = fields(parameter.typeAnnotation.typeAnnotation, sourceCode);
@@ -3242,6 +3279,21 @@ function isUnshadowedTestMockGlobal(source, identifier) {
3242
3279
  return binding3 === null || binding3.defs.length === 0 && !binding3.references.some((reference) => reference.isWrite());
3243
3280
  }
3244
3281
  var TEST_FRAMEWORK_MODULES = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "node:test", "vitest"]);
3282
+ function acceptedTestFrameworkName(source, identifier, kind, accepted) {
3283
+ if (!accepted.has(identifier.name) && !importedLocalNames(source.ast).has(identifier.name)) return null;
3284
+ const name = runtimeTestFrameworkName(source, identifier, kind);
3285
+ return name !== null && accepted.has(name) ? name : null;
3286
+ }
3287
+ var EXPECT_ASSERTION = /* @__PURE__ */ new Set(["expect"]);
3288
+ var IMPORTED_LOCAL_NAMES_BY_PROGRAM = /* @__PURE__ */ new WeakMap();
3289
+ function importedLocalNames(program) {
3290
+ let names = IMPORTED_LOCAL_NAMES_BY_PROGRAM.get(program);
3291
+ if (names === void 0) {
3292
+ names = new Set(program.body.flatMap((statement) => statement.type === import_utils26.AST_NODE_TYPES.ImportDeclaration ? statement.specifiers.map((specifier) => specifier.local.name) : []));
3293
+ IMPORTED_LOCAL_NAMES_BY_PROGRAM.set(program, names);
3294
+ }
3295
+ return names;
3296
+ }
3245
3297
  function runtimeTestFrameworkName(source, identifier, kind) {
3246
3298
  const binding3 = import_utils26.ASTUtils.findVariable(source.getScope(identifier), identifier.name);
3247
3299
  if (binding3 === null || binding3.defs.length === 0) {
@@ -4508,7 +4560,7 @@ var no_mutating_container_transform_default = createRule({
4508
4560
  if (node.body?.type !== import_utils37.AST_NODE_TYPES.BlockStatement || !returnsArray(node, sourceCode) || !supported(node.body.body)) return;
4509
4561
  const initial = /* @__PURE__ */ new Map();
4510
4562
  for (const parameter of node.params) {
4511
- if (parameter.type !== import_utils37.AST_NODE_TYPES.Identifier || parameter.typeAnnotation === void 0) continue;
4563
+ if (parameter.type !== import_utils37.AST_NODE_TYPES.Identifier || !isPresent(parameter.typeAnnotation)) continue;
4512
4564
  const element = arrayElement(parameter.typeAnnotation.typeAnnotation, sourceCode);
4513
4565
  const variable = binding3(parameter);
4514
4566
  if (element !== null && PRIMITIVES.has(element.type) && variable !== null) initial.set(variable, parameter.name);
@@ -6121,7 +6173,7 @@ function isSearchParamReceiver(call, services) {
6121
6173
  // package.json
6122
6174
  var package_default = {
6123
6175
  name: "@sarj/eslint-plugin",
6124
- version: "18.1.3",
6176
+ version: "18.1.5",
6125
6177
  packageManager: "npm@12.2.1",
6126
6178
  description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6127
6179
  type: "module",
@@ -11778,6 +11830,7 @@ var enforce_file_structure_default = createRule({
11778
11830
  // src/rules/duplicate-test-body.ts
11779
11831
  var import_utils76 = require("@typescript-eslint/utils");
11780
11832
  var TEST_CALLERS = /* @__PURE__ */ new Set(["it", "test"]);
11833
+ var ASSERTION_NAMES = /* @__PURE__ */ new Set(["assert", "expect"]);
11781
11834
  var TEST_MODIFIERS2 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
11782
11835
  var FUNCTION_TYPES = /* @__PURE__ */ new Set([
11783
11836
  import_utils76.AST_NODE_TYPES.FunctionExpression,
@@ -11889,7 +11942,7 @@ function constAssertionLiteral(node) {
11889
11942
  function isAssertionCall(node, sourceCode) {
11890
11943
  const unwrappedNodeCallee = unwrapExpression(node.callee);
11891
11944
  const root = rootIdentifier(unwrappedNodeCallee);
11892
- return root !== null && ["assert", "expect"].includes(runtimeTestFrameworkName(sourceCode, root, "assertion") ?? "");
11945
+ return root !== null && acceptedTestFrameworkName(sourceCode, root, "assertion", ASSERTION_NAMES) !== null;
11893
11946
  }
11894
11947
  function rootIdentifier(callee) {
11895
11948
  callee = unwrapExpression(callee);
@@ -11923,7 +11976,16 @@ function normalizedLiteral(node) {
11923
11976
  function normalizedStringLiteral(value) {
11924
11977
  return value.includes("\n") || value.length > MAX_NORMALIZED_STRING_LENGTH ? ["Literal", "string", value] : ["Literal", "string"];
11925
11978
  }
11979
+ var CANDIDATE_BY_CALL = /* @__PURE__ */ new WeakMap();
11926
11980
  function duplicateTestBodyCandidate(call, sourceCode) {
11981
+ let candidate2 = CANDIDATE_BY_CALL.get(call);
11982
+ if (candidate2 === void 0) {
11983
+ candidate2 = computeDuplicateTestBodyCandidate(call, sourceCode);
11984
+ CANDIDATE_BY_CALL.set(call, candidate2);
11985
+ }
11986
+ return candidate2;
11987
+ }
11988
+ function computeDuplicateTestBodyCandidate(call, sourceCode) {
11927
11989
  if (call.parent?.type !== import_utils76.AST_NODE_TYPES.ExpressionStatement) return null;
11928
11990
  const container = call.parent.parent;
11929
11991
  if (container?.type !== import_utils76.AST_NODE_TYPES.Program && container?.type !== import_utils76.AST_NODE_TYPES.BlockStatement) return null;
@@ -11965,8 +12027,7 @@ function testBody(call, sourceCode) {
11965
12027
  function testCallerSignature(callee, sourceCode) {
11966
12028
  callee = unwrapExpression(callee);
11967
12029
  if (callee.type === import_utils76.AST_NODE_TYPES.Identifier) {
11968
- const name = runtimeTestFrameworkName(sourceCode, callee, "test");
11969
- return name !== null && TEST_CALLERS.has(name) ? name : null;
12030
+ return acceptedTestFrameworkName(sourceCode, callee, "test", TEST_CALLERS);
11970
12031
  }
11971
12032
  if (callee.type === import_utils76.AST_NODE_TYPES.MemberExpression) {
11972
12033
  const base = testCallerSignature(callee.object, sourceCode);
@@ -16225,8 +16286,14 @@ var no_exception_text_in_5xx_response_default = createRule({
16225
16286
  const candidates = [];
16226
16287
  const changedErrors = /* @__PURE__ */ new Set();
16227
16288
  const stableBindings = /* @__PURE__ */ new WeakMap();
16228
- const referenceIdentifiers = new WeakSet(sourceCode.scopeManager?.scopes.flatMap((scope) => scope.references.filter((reference) => reference.isValueReference !== false).map((reference) => reference.identifier)) ?? []);
16289
+ let referenceIdentifiers;
16229
16290
  let uncertainSerialization = false;
16291
+ const catchNames = /* @__PURE__ */ new Set();
16292
+ const responseNames = /* @__PURE__ */ new Set(["Response", ...nextResponseLocals(sourceCode.ast)]);
16293
+ function valueReference(node) {
16294
+ referenceIdentifiers ??= new WeakSet(sourceCode.scopeManager?.scopes.flatMap((scope) => scope.references.filter((reference) => reference.isValueReference !== false).map((reference) => reference.identifier)) ?? []);
16295
+ return referenceIdentifiers.has(node);
16296
+ }
16230
16297
  function binding3(identifier) {
16231
16298
  return import_utils98.ASTUtils.findVariable(sourceCode.getScope(identifier), identifier.name);
16232
16299
  }
@@ -16245,14 +16312,14 @@ var no_exception_text_in_5xx_response_default = createRule({
16245
16312
  }
16246
16313
  function caughtValue(node) {
16247
16314
  const value = unwrapExpression(node);
16248
- if (value.type !== import_utils98.AST_NODE_TYPES.Identifier) return null;
16315
+ if (value.type !== import_utils98.AST_NODE_TYPES.Identifier || !catchNames.has(value.name)) return null;
16249
16316
  const variable = binding3(value);
16250
16317
  const definition = variable?.defs.length === 1 ? variable.defs[0] : void 0;
16251
16318
  return definition?.type === "CatchClause" && definition.node.param?.type === import_utils98.AST_NODE_TYPES.Identifier && variable !== null && stable2(variable) ? variable : null;
16252
16319
  }
16253
16320
  function responseFactory(node) {
16254
16321
  const value = unwrapExpression(node);
16255
- if (value.type !== import_utils98.AST_NODE_TYPES.Identifier) return false;
16322
+ if (value.type !== import_utils98.AST_NODE_TYPES.Identifier || !responseNames.has(value.name)) return false;
16256
16323
  const variable = binding3(value);
16257
16324
  if (value.name === "Response" && (variable === null || variable.defs.length === 0)) {
16258
16325
  return variable === null || stable2(variable);
@@ -16335,8 +16402,11 @@ var no_exception_text_in_5xx_response_default = createRule({
16335
16402
  if (error !== null) changedErrors.add(error);
16336
16403
  }
16337
16404
  return {
16405
+ CatchClause(node) {
16406
+ if (node.param?.type === import_utils98.AST_NODE_TYPES.Identifier) catchNames.add(node.param.name);
16407
+ },
16338
16408
  Identifier(node) {
16339
- if (!referenceIdentifiers.has(node)) return;
16409
+ if (!catchNames.has(node.name) && !responseNames.has(node.name) || !valueReference(node)) return;
16340
16410
  let typeName = node;
16341
16411
  while (typeName.parent?.type === import_utils98.AST_NODE_TYPES.TSQualifiedName) typeName = typeName.parent;
16342
16412
  if (typeName.parent?.type === import_utils98.AST_NODE_TYPES.TSTypeQuery) return;
@@ -16392,6 +16462,9 @@ var no_exception_text_in_5xx_response_default = createRule({
16392
16462
  };
16393
16463
  }
16394
16464
  });
16465
+ function nextResponseLocals(program) {
16466
+ return program.body.flatMap((statement) => statement.type === import_utils98.AST_NODE_TYPES.ImportDeclaration && statement.source.value === "next/server" ? statement.specifiers.flatMap((specifier) => specifier.type === import_utils98.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === import_utils98.AST_NODE_TYPES.Identifier && specifier.imported.name === "NextResponse" ? [specifier.local.name] : []) : []);
16467
+ }
16395
16468
 
16396
16469
  // src/rules/no-json-stringify-error.ts
16397
16470
  var import_utils99 = require("@typescript-eslint/utils");
@@ -18485,7 +18558,7 @@ var NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION = {
18485
18558
  var TEST_NAMES = /* @__PURE__ */ new Set(["it", "test"]);
18486
18559
  var TEST_MODIFIERS3 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
18487
18560
  var TEST_BUILDERS = /* @__PURE__ */ new Set(["each", "for", "runIf", "skipIf"]);
18488
- var ASSERTION_NAMES = /* @__PURE__ */ new Set(["assert", "assertType", "expect", "expectTypeOf"]);
18561
+ var ASSERTION_NAMES2 = /* @__PURE__ */ new Set(["assert", "assertType", "expect", "expectTypeOf"]);
18489
18562
  var FUNCTION_TYPES3 = /* @__PURE__ */ new Set([import_utils111.AST_NODE_TYPES.ArrowFunctionExpression, import_utils111.AST_NODE_TYPES.FunctionExpression, import_utils111.AST_NODE_TYPES.FunctionDeclaration]);
18490
18563
  function staticMemberName3(node) {
18491
18564
  if (!node.computed && node.property.type === import_utils111.AST_NODE_TYPES.Identifier) return node.property.name;
@@ -18503,7 +18576,7 @@ function isDirectTestCallback(node, context) {
18503
18576
  const call = directArgumentCall(node);
18504
18577
  if (call === null) return false;
18505
18578
  const root = testRoot(call.callee);
18506
- return root !== null && TEST_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "test") ?? "");
18579
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "test", TEST_NAMES) !== null;
18507
18580
  }
18508
18581
  function testRoot(callee) {
18509
18582
  callee = unwrapExpression(callee);
@@ -18529,13 +18602,13 @@ function walkOwnScope(node, predicate) {
18529
18602
  function isAssertion(node, context) {
18530
18603
  if (node.type !== import_utils111.AST_NODE_TYPES.CallExpression) return false;
18531
18604
  const root = rootIdentifier2(node.callee);
18532
- return root !== null && ASSERTION_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "assertion") ?? "");
18605
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "assertion", ASSERTION_NAMES2) !== null;
18533
18606
  }
18534
18607
  function isExplicitSkip(node, context) {
18535
18608
  const unwrappedNodeCallee = node.type === "CallExpression" || node.type === "NewExpression" ? unwrapExpression(node.callee) : null;
18536
18609
  if (node.type !== import_utils111.AST_NODE_TYPES.CallExpression || unwrappedNodeCallee?.type !== import_utils111.AST_NODE_TYPES.MemberExpression || staticMemberName3(unwrappedNodeCallee) !== "skip") return false;
18537
18610
  const root = rootIdentifier2(unwrappedNodeCallee.object);
18538
- return root !== null && TEST_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "test") ?? "");
18611
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "test", TEST_NAMES) !== null;
18539
18612
  }
18540
18613
  function isUnconditionalContextSkip(node, owner, context) {
18541
18614
  if (node.type !== import_utils111.AST_NODE_TYPES.CallExpression || node.optional) return false;
@@ -21688,6 +21761,7 @@ var no_server_file_uploads_default = createRule({
21688
21761
  defaultOptions: [],
21689
21762
  create(context) {
21690
21763
  const sourceCode = context.sourceCode;
21764
+ const mayReferenceFile = sourceCode.text.includes("File") || sourceCode.text.includes("\\u");
21691
21765
  const program = sourceCode.ast;
21692
21766
  const moduleServer = !hasDirective(program.body, "use client") && (hasDirective(program.body, "use server") || program.body.some(serverImport));
21693
21767
  const fileNames = /* @__PURE__ */ new Set(["File"]);
@@ -21722,6 +21796,7 @@ var no_server_file_uploads_default = createRule({
21722
21796
  }
21723
21797
  return {
21724
21798
  "Program:exit"() {
21799
+ if (!mayReferenceFile) return;
21725
21800
  for (const scope of sourceCode.scopeManager?.scopes ?? []) {
21726
21801
  for (const reference of scope.references) {
21727
21802
  const identifier = reference.identifier;
@@ -21741,9 +21816,9 @@ var no_server_file_uploads_default = createRule({
21741
21816
  }
21742
21817
  },
21743
21818
  VariableDeclarator(node) {
21744
- if (node.id.type !== import_utils128.AST_NODE_TYPES.ObjectPattern || node.init?.type !== import_utils128.AST_NODE_TYPES.Identifier || !nativeNamespace2(node.init) || !inServer(node)) return;
21819
+ if (node.id.type !== import_utils128.AST_NODE_TYPES.ObjectPattern || node.init?.type !== import_utils128.AST_NODE_TYPES.Identifier) return;
21745
21820
  for (const property of node.id.properties) {
21746
- if (property.type === import_utils128.AST_NODE_TYPES.Property && staticPropertyName(property, sourceCode) === "File") {
21821
+ if (property.type === import_utils128.AST_NODE_TYPES.Property && staticPropertyName(property, sourceCode) === "File" && nativeNamespace2(node.init) && inServer(node)) {
21747
21822
  context.report({ node: property, messageId: "serverFile" });
21748
21823
  }
21749
21824
  }
@@ -23774,7 +23849,7 @@ function expectOperand(callee, sourceCode) {
23774
23849
  const negated = receiver.type === import_utils138.AST_NODE_TYPES.MemberExpression && import_utils138.ASTUtils.getPropertyName(receiver) === "not";
23775
23850
  if (negated && receiver.type === import_utils138.AST_NODE_TYPES.MemberExpression) receiver = unwrapExpression(receiver.object);
23776
23851
  const expectCallee = receiver.type === import_utils138.AST_NODE_TYPES.CallExpression ? unwrapExpression(receiver.callee) : null;
23777
- if (receiver.type !== import_utils138.AST_NODE_TYPES.CallExpression || expectCallee?.type !== import_utils138.AST_NODE_TYPES.Identifier || runtimeTestFrameworkName(sourceCode, expectCallee, "assertion") !== "expect" || receiver.arguments.length !== 1 && !hasStaticMessage(receiver, expectCallee, sourceCode)) {
23852
+ if (receiver.type !== import_utils138.AST_NODE_TYPES.CallExpression || expectCallee?.type !== import_utils138.AST_NODE_TYPES.Identifier || acceptedTestFrameworkName(sourceCode, expectCallee, "assertion", EXPECT_ASSERTION) === null || receiver.arguments.length !== 1 && !hasStaticMessage(receiver, expectCallee, sourceCode)) {
23778
23853
  return null;
23779
23854
  }
23780
23855
  const operand = receiver.arguments[0];
@@ -23802,6 +23877,7 @@ function isExceptionContract(node, source) {
23802
23877
  }
23803
23878
  function customizedMatchers(source) {
23804
23879
  const customized = /* @__PURE__ */ new Set();
23880
+ if (!source.text.includes("extend") && !source.text.includes("addEqualityTesters") && !source.text.includes("\\u")) return customized;
23805
23881
  const visit = (node) => {
23806
23882
  if (node.type === import_utils138.AST_NODE_TYPES.CallExpression) collectCustomization(node, source, customized);
23807
23883
  for (const key of source.visitorKeys[node.type] ?? []) {
@@ -23818,7 +23894,7 @@ function collectCustomization(node, source, customized) {
23818
23894
  const member3 = unwrapExpression(node.callee);
23819
23895
  if (member3.type !== import_utils138.AST_NODE_TYPES.MemberExpression) return;
23820
23896
  const receiver = unwrapExpression(member3.object);
23821
- if (receiver.type !== import_utils138.AST_NODE_TYPES.Identifier || runtimeTestFrameworkName(source, receiver, "assertion") !== "expect") return;
23897
+ if (receiver.type !== import_utils138.AST_NODE_TYPES.Identifier || acceptedTestFrameworkName(source, receiver, "assertion", EXPECT_ASSERTION) === null) return;
23822
23898
  const method = import_utils138.ASTUtils.getPropertyName(member3);
23823
23899
  const argument = node.arguments[0] === void 0 ? null : unwrapExpression(node.arguments[0]);
23824
23900
  if (method === "addEqualityTesters" && !(argument?.type === import_utils138.AST_NODE_TYPES.ArrayExpression && argument.elements.length === 0)) {
@@ -25931,8 +26007,8 @@ var test_loops_over_literal_cases_default = createRule({
25931
26007
  }
25932
26008
  return identifiers.has(identifier);
25933
26009
  };
25934
- const isFrameworkTest = (identifier) => TEST_CALLERS3.has(runtimeTestFrameworkName(context.sourceCode, identifier, "test") ?? "");
25935
- const isFrameworkAssertion = (identifier) => ASSERTION_ROOTS.has(runtimeTestFrameworkName(context.sourceCode, identifier, "assertion") ?? "");
26010
+ const isFrameworkTest = (identifier) => acceptedTestFrameworkName(context.sourceCode, identifier, "test", TEST_CALLERS3) !== null;
26011
+ const isFrameworkAssertion = (identifier) => acceptedTestFrameworkName(context.sourceCode, identifier, "assertion", ASSERTION_ROOTS) !== null;
25936
26012
  return {
25937
26013
  ForOfStatement(node) {
25938
26014
  const enclosing = nearestEnclosingFunction2(node);
@@ -26359,7 +26435,7 @@ function discriminatedUnionReturns(node, sourceCode) {
26359
26435
  }
26360
26436
  function returnContract(node, sourceCode) {
26361
26437
  const annotation = node.returnType;
26362
- if (annotation === void 0) return void 0;
26438
+ if (!isPresent(annotation)) return void 0;
26363
26439
  let type = annotation.typeAnnotation;
26364
26440
  if (node.async && type.type === import_utils153.AST_NODE_TYPES.TSTypeReference && type.typeName.type === import_utils153.AST_NODE_TYPES.Identifier && type.typeName.name === "Promise" && type.typeArguments?.params.length === 1 && definitions(type, sourceCode).length === 0) {
26365
26441
  const inner = type.typeArguments.params[0];
@@ -29438,7 +29514,7 @@ function checkBoundary(node, context) {
29438
29514
  const roles = /* @__PURE__ */ new Map();
29439
29515
  for (const parameter of node.params) {
29440
29516
  const identifier = parameterIdentifier(parameter);
29441
- if (identifier?.typeAnnotation === void 0) continue;
29517
+ if (!isPresent(identifier?.typeAnnotation)) continue;
29442
29518
  const role = identifierRole(identifier.name);
29443
29519
  if (role === void 0) continue;
29444
29520
  const carrier = rawCarrier(
@@ -30586,7 +30662,7 @@ function processOwnershipValue(identifier, source, graph) {
30586
30662
  if (binding3 === null || graph.values.has(binding3)) return;
30587
30663
  graph.values.add(binding3);
30588
30664
  const annotation = exportedValueAnnotation(source, identifier);
30589
- if (annotation !== void 0) graph.pending.push({ kind: "syntax", node: annotation });
30665
+ if (isPresent(annotation)) graph.pending.push({ kind: "syntax", node: annotation });
30590
30666
  const resolved = exportedCallable(source, identifier, graph.cache);
30591
30667
  if (resolved === null) return;
30592
30668
  if (resolved.callable !== null) graph.callables.add(resolved.callable);
@@ -30672,7 +30748,7 @@ function exportedCallable(source, identifier, cache) {
30672
30748
  }
30673
30749
  const step = constantCallableStep(binding3);
30674
30750
  if (step === null) break;
30675
- if (step.annotation !== void 0) annotations.push(step.annotation);
30751
+ if (isPresent(step.annotation)) annotations.push(step.annotation);
30676
30752
  if (isFunctionNode2(step.target)) {
30677
30753
  target = { callable: step.target, signatures: [step.target] };
30678
30754
  break;
@@ -30711,11 +30787,11 @@ function valueBinding(source, identifier) {
30711
30787
  }
30712
30788
  function callableAnnotations(callable) {
30713
30789
  const annotations = [];
30714
- if (callable.returnType !== void 0) annotations.push(callable.returnType);
30715
- if (callable.typeParameters !== void 0) annotations.push(callable.typeParameters);
30790
+ if (isPresent(callable.returnType)) annotations.push(callable.returnType);
30791
+ if (isPresent(callable.typeParameters)) annotations.push(callable.typeParameters);
30716
30792
  for (const raw of callable.params) {
30717
30793
  const parameter = raw.type === import_utils173.AST_NODE_TYPES.AssignmentPattern ? raw.left : raw;
30718
- if ((parameter.type === import_utils173.AST_NODE_TYPES.Identifier || parameter.type === import_utils173.AST_NODE_TYPES.ObjectPattern || parameter.type === import_utils173.AST_NODE_TYPES.ArrayPattern || parameter.type === import_utils173.AST_NODE_TYPES.RestElement) && parameter.typeAnnotation !== void 0) annotations.push(parameter.typeAnnotation);
30794
+ if ((parameter.type === import_utils173.AST_NODE_TYPES.Identifier || parameter.type === import_utils173.AST_NODE_TYPES.ObjectPattern || parameter.type === import_utils173.AST_NODE_TYPES.ArrayPattern || parameter.type === import_utils173.AST_NODE_TYPES.RestElement) && isPresent(parameter.typeAnnotation)) annotations.push(parameter.typeAnnotation);
30719
30795
  }
30720
30796
  return annotations;
30721
30797
  }
@@ -33156,7 +33232,7 @@ var prefer_whole_object_assertion_default = createRule({
33156
33232
  return assertionExpectation(statement, call, receiver, key, matcher);
33157
33233
  }
33158
33234
  function isTestExpect(callee) {
33159
- return runtimeTestFrameworkName(sourceCode, callee, "assertion") === "expect";
33235
+ return acceptedTestFrameworkName(sourceCode, callee, "assertion", EXPECT_ASSERTION) !== null;
33160
33236
  }
33161
33237
  function isExactLengthAssertion(statement, receiver) {
33162
33238
  if (statement?.type !== import_utils181.AST_NODE_TYPES.ExpressionStatement || statement.expression.type !== import_utils181.AST_NODE_TYPES.CallExpression) return false;
@@ -37360,6 +37436,7 @@ var stepdown_default = createRule({
37360
37436
  "Program:exit": (program) => {
37361
37437
  declarationScope(context, program);
37362
37438
  for (const body3 of functionBodies) declarationScope(context, body3);
37439
+ if (classes.length === 0) return;
37363
37440
  const computedReferences = [];
37364
37441
  walk3(program, context.sourceCode.visitorKeys, (node) => {
37365
37442
  if (node.type === import_utils197.AST_NODE_TYPES.MemberExpression && node.computed)
package/dist/index.js CHANGED
@@ -156,8 +156,15 @@ function createNativeRule(config, url) {
156
156
  const defaults = metadataDefaults ?? legacyDefaults;
157
157
  if (defaults === void 0) throw new TypeError("Declared defaults disappeared while creating the rule.");
158
158
  const create = config.create;
159
+ const applied = /* @__PURE__ */ new WeakMap();
159
160
  return { ...base, create(context) {
160
- return create(context, ESLintUtils.applyDefault(defaults, context.options));
161
+ const options = context.options;
162
+ let merged = applied.get(options);
163
+ if (merged === void 0) {
164
+ merged = ESLintUtils.applyDefault(defaults, options);
165
+ applied.set(options, merged);
166
+ }
167
+ return create(context, merged);
161
168
  } };
162
169
  }
163
170
  function hasNoDefaultOptions(_config, metaDefaults, legacyDefaults) {
@@ -564,12 +571,17 @@ function isGeneratedFile(filename, sourceText = "", gates = []) {
564
571
  if (gates.includes("externalTree") && EXTERNAL_TREE_RE.test(normalized)) {
565
572
  return true;
566
573
  }
574
+ return generatedHeader(sourceText);
575
+ }
576
+ var lastHeaderText;
577
+ var lastHeaderGenerated = false;
578
+ function generatedHeader(sourceText) {
579
+ if (sourceText === lastHeaderText) return lastHeaderGenerated;
567
580
  const headerBodies = leadingCommentBodies(sourceText.slice(0, 2048));
568
581
  const nonAi = headerBodies.filter((body3) => !AI_ATTRIBUTION_RE.test(body3));
569
- if (nonAi.some((body3) => GENERATED_MARKER_RE.test(body3))) {
570
- return true;
571
- }
572
- return !headerBodies.some((body3) => AI_ATTRIBUTION_RE.test(body3)) && headerBodies.some((body3) => DO_NOT_EDIT_RE.test(body3));
582
+ lastHeaderGenerated = nonAi.some((body3) => GENERATED_MARKER_RE.test(body3)) || !headerBodies.some((body3) => AI_ATTRIBUTION_RE.test(body3)) && headerBodies.some((body3) => DO_NOT_EDIT_RE.test(body3));
583
+ lastHeaderText = sourceText;
584
+ return lastHeaderGenerated;
573
585
  }
574
586
  function leadingCommentBodies(sourceText) {
575
587
  const bodies = [];
@@ -610,6 +622,16 @@ function isScriptFile(filename) {
610
622
  }
611
623
  function scopedFilename(input) {
612
624
  if (typeof input === "string") return input.replaceAll("\\", "/");
625
+ if (input.filename === lastScopedFilename && input.cwd === lastScopedCwd) return lastScoped;
626
+ lastScoped = computeScopedFilename(input);
627
+ lastScopedFilename = input.filename;
628
+ lastScopedCwd = input.cwd;
629
+ return lastScoped;
630
+ }
631
+ var lastScopedFilename;
632
+ var lastScopedCwd;
633
+ var lastScoped = "";
634
+ function computeScopedFilename(input) {
613
635
  const filename = input.filename.replaceAll("\\", "/");
614
636
  const cwd = input.cwd.replaceAll("\\", "/");
615
637
  const fileWindows = windowsAbsolute(filename);
@@ -2257,8 +2279,18 @@ var FRONTEND_IMPORT = /^(?:react(?:-dom)?|next|preact|vue|svelte|@angular\/core|
2257
2279
  function isFrontendSource(context) {
2258
2280
  return /\.[jt]sx$/u.test(context.filename) || frontendModule(context.sourceCode.ast) || frontendPackage(context.filename);
2259
2281
  }
2282
+ var FRONTEND_PACKAGE_BY_DIRECTORY = /* @__PURE__ */ new Map();
2260
2283
  function frontendPackage(filename) {
2261
- let directory = dirname(resolve(filename));
2284
+ const directory = dirname(resolve(filename));
2285
+ let cached = FRONTEND_PACKAGE_BY_DIRECTORY.get(directory);
2286
+ if (cached === void 0) {
2287
+ cached = nearestManifestDeclaresFrontend(directory);
2288
+ FRONTEND_PACKAGE_BY_DIRECTORY.set(directory, cached);
2289
+ }
2290
+ return cached;
2291
+ }
2292
+ function nearestManifestDeclaresFrontend(start) {
2293
+ let directory = start;
2262
2294
  for (; ; ) {
2263
2295
  const manifestPath = join(directory, "package.json");
2264
2296
  if (existsSync(manifestPath)) {
@@ -2854,6 +2886,11 @@ var prefer_dependency_interface_default = createRule({
2854
2886
  }
2855
2887
  });
2856
2888
 
2889
+ // src/rules/_is-present.ts
2890
+ function isPresent(value) {
2891
+ return value !== null && value !== void 0;
2892
+ }
2893
+
2857
2894
  // src/rules/no-mutating-input-with-result.ts
2858
2895
  import { AST_NODE_TYPES as AST_NODE_TYPES20, ASTUtils as ASTUtils8 } from "@typescript-eslint/utils";
2859
2896
  var NO_MUTATING_INPUT_WITH_RESULT_DOCUMENTATION = {
@@ -2958,7 +2995,7 @@ var no_mutating_input_with_result_default = createRule({
2958
2995
  if (annotation === void 0 || command(annotation) || node.body === void 0) return;
2959
2996
  const inputs = /* @__PURE__ */ new Map();
2960
2997
  for (const parameter of node.params) {
2961
- if (parameter.type !== AST_NODE_TYPES20.Identifier || parameter.typeAnnotation === void 0) continue;
2998
+ if (parameter.type !== AST_NODE_TYPES20.Identifier || !isPresent(parameter.typeAnnotation)) continue;
2962
2999
  const variable = ASTUtils8.findVariable(sourceCode.getScope(parameter), parameter.name);
2963
3000
  if (variable === null || variable.references.some((reference) => reference.isWrite())) continue;
2964
3001
  const names = fields(parameter.typeAnnotation.typeAnnotation, sourceCode);
@@ -3200,6 +3237,21 @@ function isUnshadowedTestMockGlobal(source, identifier) {
3200
3237
  return binding3 === null || binding3.defs.length === 0 && !binding3.references.some((reference) => reference.isWrite());
3201
3238
  }
3202
3239
  var TEST_FRAMEWORK_MODULES = /* @__PURE__ */ new Set(["@jest/globals", "@playwright/test", "bun:test", "node:test", "vitest"]);
3240
+ function acceptedTestFrameworkName(source, identifier, kind, accepted) {
3241
+ if (!accepted.has(identifier.name) && !importedLocalNames(source.ast).has(identifier.name)) return null;
3242
+ const name = runtimeTestFrameworkName(source, identifier, kind);
3243
+ return name !== null && accepted.has(name) ? name : null;
3244
+ }
3245
+ var EXPECT_ASSERTION = /* @__PURE__ */ new Set(["expect"]);
3246
+ var IMPORTED_LOCAL_NAMES_BY_PROGRAM = /* @__PURE__ */ new WeakMap();
3247
+ function importedLocalNames(program) {
3248
+ let names = IMPORTED_LOCAL_NAMES_BY_PROGRAM.get(program);
3249
+ if (names === void 0) {
3250
+ names = new Set(program.body.flatMap((statement) => statement.type === AST_NODE_TYPES24.ImportDeclaration ? statement.specifiers.map((specifier) => specifier.local.name) : []));
3251
+ IMPORTED_LOCAL_NAMES_BY_PROGRAM.set(program, names);
3252
+ }
3253
+ return names;
3254
+ }
3203
3255
  function runtimeTestFrameworkName(source, identifier, kind) {
3204
3256
  const binding3 = ASTUtils12.findVariable(source.getScope(identifier), identifier.name);
3205
3257
  if (binding3 === null || binding3.defs.length === 0) {
@@ -4469,7 +4521,7 @@ var no_mutating_container_transform_default = createRule({
4469
4521
  if (node.body?.type !== AST_NODE_TYPES35.BlockStatement || !returnsArray(node, sourceCode) || !supported(node.body.body)) return;
4470
4522
  const initial = /* @__PURE__ */ new Map();
4471
4523
  for (const parameter of node.params) {
4472
- if (parameter.type !== AST_NODE_TYPES35.Identifier || parameter.typeAnnotation === void 0) continue;
4524
+ if (parameter.type !== AST_NODE_TYPES35.Identifier || !isPresent(parameter.typeAnnotation)) continue;
4473
4525
  const element = arrayElement(parameter.typeAnnotation.typeAnnotation, sourceCode);
4474
4526
  const variable = binding3(parameter);
4475
4527
  if (element !== null && PRIMITIVES.has(element.type) && variable !== null) initial.set(variable, parameter.name);
@@ -6082,7 +6134,7 @@ function isSearchParamReceiver(call, services) {
6082
6134
  // package.json
6083
6135
  var package_default = {
6084
6136
  name: "@sarj/eslint-plugin",
6085
- version: "18.1.3",
6137
+ version: "18.1.5",
6086
6138
  packageManager: "npm@12.2.1",
6087
6139
  description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6088
6140
  type: "module",
@@ -11742,6 +11794,7 @@ var enforce_file_structure_default = createRule({
11742
11794
  // src/rules/duplicate-test-body.ts
11743
11795
  import { AST_NODE_TYPES as AST_NODE_TYPES73 } from "@typescript-eslint/utils";
11744
11796
  var TEST_CALLERS = /* @__PURE__ */ new Set(["it", "test"]);
11797
+ var ASSERTION_NAMES = /* @__PURE__ */ new Set(["assert", "expect"]);
11745
11798
  var TEST_MODIFIERS2 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
11746
11799
  var FUNCTION_TYPES = /* @__PURE__ */ new Set([
11747
11800
  AST_NODE_TYPES73.FunctionExpression,
@@ -11853,7 +11906,7 @@ function constAssertionLiteral(node) {
11853
11906
  function isAssertionCall(node, sourceCode) {
11854
11907
  const unwrappedNodeCallee = unwrapExpression(node.callee);
11855
11908
  const root = rootIdentifier(unwrappedNodeCallee);
11856
- return root !== null && ["assert", "expect"].includes(runtimeTestFrameworkName(sourceCode, root, "assertion") ?? "");
11909
+ return root !== null && acceptedTestFrameworkName(sourceCode, root, "assertion", ASSERTION_NAMES) !== null;
11857
11910
  }
11858
11911
  function rootIdentifier(callee) {
11859
11912
  callee = unwrapExpression(callee);
@@ -11887,7 +11940,16 @@ function normalizedLiteral(node) {
11887
11940
  function normalizedStringLiteral(value) {
11888
11941
  return value.includes("\n") || value.length > MAX_NORMALIZED_STRING_LENGTH ? ["Literal", "string", value] : ["Literal", "string"];
11889
11942
  }
11943
+ var CANDIDATE_BY_CALL = /* @__PURE__ */ new WeakMap();
11890
11944
  function duplicateTestBodyCandidate(call, sourceCode) {
11945
+ let candidate2 = CANDIDATE_BY_CALL.get(call);
11946
+ if (candidate2 === void 0) {
11947
+ candidate2 = computeDuplicateTestBodyCandidate(call, sourceCode);
11948
+ CANDIDATE_BY_CALL.set(call, candidate2);
11949
+ }
11950
+ return candidate2;
11951
+ }
11952
+ function computeDuplicateTestBodyCandidate(call, sourceCode) {
11891
11953
  if (call.parent?.type !== AST_NODE_TYPES73.ExpressionStatement) return null;
11892
11954
  const container = call.parent.parent;
11893
11955
  if (container?.type !== AST_NODE_TYPES73.Program && container?.type !== AST_NODE_TYPES73.BlockStatement) return null;
@@ -11929,8 +11991,7 @@ function testBody(call, sourceCode) {
11929
11991
  function testCallerSignature(callee, sourceCode) {
11930
11992
  callee = unwrapExpression(callee);
11931
11993
  if (callee.type === AST_NODE_TYPES73.Identifier) {
11932
- const name = runtimeTestFrameworkName(sourceCode, callee, "test");
11933
- return name !== null && TEST_CALLERS.has(name) ? name : null;
11994
+ return acceptedTestFrameworkName(sourceCode, callee, "test", TEST_CALLERS);
11934
11995
  }
11935
11996
  if (callee.type === AST_NODE_TYPES73.MemberExpression) {
11936
11997
  const base = testCallerSignature(callee.object, sourceCode);
@@ -16192,8 +16253,14 @@ var no_exception_text_in_5xx_response_default = createRule({
16192
16253
  const candidates = [];
16193
16254
  const changedErrors = /* @__PURE__ */ new Set();
16194
16255
  const stableBindings = /* @__PURE__ */ new WeakMap();
16195
- const referenceIdentifiers = new WeakSet(sourceCode.scopeManager?.scopes.flatMap((scope) => scope.references.filter((reference) => reference.isValueReference !== false).map((reference) => reference.identifier)) ?? []);
16256
+ let referenceIdentifiers;
16196
16257
  let uncertainSerialization = false;
16258
+ const catchNames = /* @__PURE__ */ new Set();
16259
+ const responseNames = /* @__PURE__ */ new Set(["Response", ...nextResponseLocals(sourceCode.ast)]);
16260
+ function valueReference(node) {
16261
+ referenceIdentifiers ??= new WeakSet(sourceCode.scopeManager?.scopes.flatMap((scope) => scope.references.filter((reference) => reference.isValueReference !== false).map((reference) => reference.identifier)) ?? []);
16262
+ return referenceIdentifiers.has(node);
16263
+ }
16197
16264
  function binding3(identifier) {
16198
16265
  return ASTUtils55.findVariable(sourceCode.getScope(identifier), identifier.name);
16199
16266
  }
@@ -16212,14 +16279,14 @@ var no_exception_text_in_5xx_response_default = createRule({
16212
16279
  }
16213
16280
  function caughtValue(node) {
16214
16281
  const value = unwrapExpression(node);
16215
- if (value.type !== AST_NODE_TYPES91.Identifier) return null;
16282
+ if (value.type !== AST_NODE_TYPES91.Identifier || !catchNames.has(value.name)) return null;
16216
16283
  const variable = binding3(value);
16217
16284
  const definition = variable?.defs.length === 1 ? variable.defs[0] : void 0;
16218
16285
  return definition?.type === "CatchClause" && definition.node.param?.type === AST_NODE_TYPES91.Identifier && variable !== null && stable2(variable) ? variable : null;
16219
16286
  }
16220
16287
  function responseFactory(node) {
16221
16288
  const value = unwrapExpression(node);
16222
- if (value.type !== AST_NODE_TYPES91.Identifier) return false;
16289
+ if (value.type !== AST_NODE_TYPES91.Identifier || !responseNames.has(value.name)) return false;
16223
16290
  const variable = binding3(value);
16224
16291
  if (value.name === "Response" && (variable === null || variable.defs.length === 0)) {
16225
16292
  return variable === null || stable2(variable);
@@ -16302,8 +16369,11 @@ var no_exception_text_in_5xx_response_default = createRule({
16302
16369
  if (error !== null) changedErrors.add(error);
16303
16370
  }
16304
16371
  return {
16372
+ CatchClause(node) {
16373
+ if (node.param?.type === AST_NODE_TYPES91.Identifier) catchNames.add(node.param.name);
16374
+ },
16305
16375
  Identifier(node) {
16306
- if (!referenceIdentifiers.has(node)) return;
16376
+ if (!catchNames.has(node.name) && !responseNames.has(node.name) || !valueReference(node)) return;
16307
16377
  let typeName = node;
16308
16378
  while (typeName.parent?.type === AST_NODE_TYPES91.TSQualifiedName) typeName = typeName.parent;
16309
16379
  if (typeName.parent?.type === AST_NODE_TYPES91.TSTypeQuery) return;
@@ -16359,6 +16429,9 @@ var no_exception_text_in_5xx_response_default = createRule({
16359
16429
  };
16360
16430
  }
16361
16431
  });
16432
+ function nextResponseLocals(program) {
16433
+ return program.body.flatMap((statement) => statement.type === AST_NODE_TYPES91.ImportDeclaration && statement.source.value === "next/server" ? statement.specifiers.flatMap((specifier) => specifier.type === AST_NODE_TYPES91.ImportSpecifier && specifier.imported.type === AST_NODE_TYPES91.Identifier && specifier.imported.name === "NextResponse" ? [specifier.local.name] : []) : []);
16434
+ }
16362
16435
 
16363
16436
  // src/rules/no-json-stringify-error.ts
16364
16437
  import { ASTUtils as ASTUtils56 } from "@typescript-eslint/utils";
@@ -18458,7 +18531,7 @@ var NO_BARE_RETURN_FROM_TEST_CATCH_DOCUMENTATION = {
18458
18531
  var TEST_NAMES = /* @__PURE__ */ new Set(["it", "test"]);
18459
18532
  var TEST_MODIFIERS3 = /* @__PURE__ */ new Set(["concurrent", "fails", "only", "sequential", "skip"]);
18460
18533
  var TEST_BUILDERS = /* @__PURE__ */ new Set(["each", "for", "runIf", "skipIf"]);
18461
- var ASSERTION_NAMES = /* @__PURE__ */ new Set(["assert", "assertType", "expect", "expectTypeOf"]);
18534
+ var ASSERTION_NAMES2 = /* @__PURE__ */ new Set(["assert", "assertType", "expect", "expectTypeOf"]);
18462
18535
  var FUNCTION_TYPES3 = /* @__PURE__ */ new Set([AST_NODE_TYPES102.ArrowFunctionExpression, AST_NODE_TYPES102.FunctionExpression, AST_NODE_TYPES102.FunctionDeclaration]);
18463
18536
  function staticMemberName3(node) {
18464
18537
  if (!node.computed && node.property.type === AST_NODE_TYPES102.Identifier) return node.property.name;
@@ -18476,7 +18549,7 @@ function isDirectTestCallback(node, context) {
18476
18549
  const call = directArgumentCall(node);
18477
18550
  if (call === null) return false;
18478
18551
  const root = testRoot(call.callee);
18479
- return root !== null && TEST_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "test") ?? "");
18552
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "test", TEST_NAMES) !== null;
18480
18553
  }
18481
18554
  function testRoot(callee) {
18482
18555
  callee = unwrapExpression(callee);
@@ -18502,13 +18575,13 @@ function walkOwnScope(node, predicate) {
18502
18575
  function isAssertion(node, context) {
18503
18576
  if (node.type !== AST_NODE_TYPES102.CallExpression) return false;
18504
18577
  const root = rootIdentifier2(node.callee);
18505
- return root !== null && ASSERTION_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "assertion") ?? "");
18578
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "assertion", ASSERTION_NAMES2) !== null;
18506
18579
  }
18507
18580
  function isExplicitSkip(node, context) {
18508
18581
  const unwrappedNodeCallee = node.type === "CallExpression" || node.type === "NewExpression" ? unwrapExpression(node.callee) : null;
18509
18582
  if (node.type !== AST_NODE_TYPES102.CallExpression || unwrappedNodeCallee?.type !== AST_NODE_TYPES102.MemberExpression || staticMemberName3(unwrappedNodeCallee) !== "skip") return false;
18510
18583
  const root = rootIdentifier2(unwrappedNodeCallee.object);
18511
- return root !== null && TEST_NAMES.has(runtimeTestFrameworkName(context.sourceCode, root, "test") ?? "");
18584
+ return root !== null && acceptedTestFrameworkName(context.sourceCode, root, "test", TEST_NAMES) !== null;
18512
18585
  }
18513
18586
  function isUnconditionalContextSkip(node, owner, context) {
18514
18587
  if (node.type !== AST_NODE_TYPES102.CallExpression || node.optional) return false;
@@ -21664,6 +21737,7 @@ var no_server_file_uploads_default = createRule({
21664
21737
  defaultOptions: [],
21665
21738
  create(context) {
21666
21739
  const sourceCode = context.sourceCode;
21740
+ const mayReferenceFile = sourceCode.text.includes("File") || sourceCode.text.includes("\\u");
21667
21741
  const program = sourceCode.ast;
21668
21742
  const moduleServer = !hasDirective(program.body, "use client") && (hasDirective(program.body, "use server") || program.body.some(serverImport));
21669
21743
  const fileNames = /* @__PURE__ */ new Set(["File"]);
@@ -21698,6 +21772,7 @@ var no_server_file_uploads_default = createRule({
21698
21772
  }
21699
21773
  return {
21700
21774
  "Program:exit"() {
21775
+ if (!mayReferenceFile) return;
21701
21776
  for (const scope of sourceCode.scopeManager?.scopes ?? []) {
21702
21777
  for (const reference of scope.references) {
21703
21778
  const identifier = reference.identifier;
@@ -21717,9 +21792,9 @@ var no_server_file_uploads_default = createRule({
21717
21792
  }
21718
21793
  },
21719
21794
  VariableDeclarator(node) {
21720
- if (node.id.type !== AST_NODE_TYPES116.ObjectPattern || node.init?.type !== AST_NODE_TYPES116.Identifier || !nativeNamespace2(node.init) || !inServer(node)) return;
21795
+ if (node.id.type !== AST_NODE_TYPES116.ObjectPattern || node.init?.type !== AST_NODE_TYPES116.Identifier) return;
21721
21796
  for (const property of node.id.properties) {
21722
- if (property.type === AST_NODE_TYPES116.Property && staticPropertyName(property, sourceCode) === "File") {
21797
+ if (property.type === AST_NODE_TYPES116.Property && staticPropertyName(property, sourceCode) === "File" && nativeNamespace2(node.init) && inServer(node)) {
21723
21798
  context.report({ node: property, messageId: "serverFile" });
21724
21799
  }
21725
21800
  }
@@ -23750,7 +23825,7 @@ function expectOperand(callee, sourceCode) {
23750
23825
  const negated = receiver.type === AST_NODE_TYPES124.MemberExpression && ASTUtils82.getPropertyName(receiver) === "not";
23751
23826
  if (negated && receiver.type === AST_NODE_TYPES124.MemberExpression) receiver = unwrapExpression(receiver.object);
23752
23827
  const expectCallee = receiver.type === AST_NODE_TYPES124.CallExpression ? unwrapExpression(receiver.callee) : null;
23753
- if (receiver.type !== AST_NODE_TYPES124.CallExpression || expectCallee?.type !== AST_NODE_TYPES124.Identifier || runtimeTestFrameworkName(sourceCode, expectCallee, "assertion") !== "expect" || receiver.arguments.length !== 1 && !hasStaticMessage(receiver, expectCallee, sourceCode)) {
23828
+ if (receiver.type !== AST_NODE_TYPES124.CallExpression || expectCallee?.type !== AST_NODE_TYPES124.Identifier || acceptedTestFrameworkName(sourceCode, expectCallee, "assertion", EXPECT_ASSERTION) === null || receiver.arguments.length !== 1 && !hasStaticMessage(receiver, expectCallee, sourceCode)) {
23754
23829
  return null;
23755
23830
  }
23756
23831
  const operand = receiver.arguments[0];
@@ -23778,6 +23853,7 @@ function isExceptionContract(node, source) {
23778
23853
  }
23779
23854
  function customizedMatchers(source) {
23780
23855
  const customized = /* @__PURE__ */ new Set();
23856
+ if (!source.text.includes("extend") && !source.text.includes("addEqualityTesters") && !source.text.includes("\\u")) return customized;
23781
23857
  const visit = (node) => {
23782
23858
  if (node.type === AST_NODE_TYPES124.CallExpression) collectCustomization(node, source, customized);
23783
23859
  for (const key of source.visitorKeys[node.type] ?? []) {
@@ -23794,7 +23870,7 @@ function collectCustomization(node, source, customized) {
23794
23870
  const member3 = unwrapExpression(node.callee);
23795
23871
  if (member3.type !== AST_NODE_TYPES124.MemberExpression) return;
23796
23872
  const receiver = unwrapExpression(member3.object);
23797
- if (receiver.type !== AST_NODE_TYPES124.Identifier || runtimeTestFrameworkName(source, receiver, "assertion") !== "expect") return;
23873
+ if (receiver.type !== AST_NODE_TYPES124.Identifier || acceptedTestFrameworkName(source, receiver, "assertion", EXPECT_ASSERTION) === null) return;
23798
23874
  const method = ASTUtils82.getPropertyName(member3);
23799
23875
  const argument = node.arguments[0] === void 0 ? null : unwrapExpression(node.arguments[0]);
23800
23876
  if (method === "addEqualityTesters" && !(argument?.type === AST_NODE_TYPES124.ArrayExpression && argument.elements.length === 0)) {
@@ -25909,8 +25985,8 @@ var test_loops_over_literal_cases_default = createRule({
25909
25985
  }
25910
25986
  return identifiers.has(identifier);
25911
25987
  };
25912
- const isFrameworkTest = (identifier) => TEST_CALLERS3.has(runtimeTestFrameworkName(context.sourceCode, identifier, "test") ?? "");
25913
- const isFrameworkAssertion = (identifier) => ASSERTION_ROOTS.has(runtimeTestFrameworkName(context.sourceCode, identifier, "assertion") ?? "");
25988
+ const isFrameworkTest = (identifier) => acceptedTestFrameworkName(context.sourceCode, identifier, "test", TEST_CALLERS3) !== null;
25989
+ const isFrameworkAssertion = (identifier) => acceptedTestFrameworkName(context.sourceCode, identifier, "assertion", ASSERTION_ROOTS) !== null;
25914
25990
  return {
25915
25991
  ForOfStatement(node) {
25916
25992
  const enclosing = nearestEnclosingFunction2(node);
@@ -26337,7 +26413,7 @@ function discriminatedUnionReturns(node, sourceCode) {
26337
26413
  }
26338
26414
  function returnContract(node, sourceCode) {
26339
26415
  const annotation = node.returnType;
26340
- if (annotation === void 0) return void 0;
26416
+ if (!isPresent(annotation)) return void 0;
26341
26417
  let type = annotation.typeAnnotation;
26342
26418
  if (node.async && type.type === AST_NODE_TYPES138.TSTypeReference && type.typeName.type === AST_NODE_TYPES138.Identifier && type.typeName.name === "Promise" && type.typeArguments?.params.length === 1 && definitions(type, sourceCode).length === 0) {
26343
26419
  const inner = type.typeArguments.params[0];
@@ -29427,7 +29503,7 @@ function checkBoundary(node, context) {
29427
29503
  const roles = /* @__PURE__ */ new Map();
29428
29504
  for (const parameter of node.params) {
29429
29505
  const identifier = parameterIdentifier(parameter);
29430
- if (identifier?.typeAnnotation === void 0) continue;
29506
+ if (!isPresent(identifier?.typeAnnotation)) continue;
29431
29507
  const role = identifierRole(identifier.name);
29432
29508
  if (role === void 0) continue;
29433
29509
  const carrier = rawCarrier(
@@ -30581,7 +30657,7 @@ function processOwnershipValue(identifier, source, graph) {
30581
30657
  if (binding3 === null || graph.values.has(binding3)) return;
30582
30658
  graph.values.add(binding3);
30583
30659
  const annotation = exportedValueAnnotation(source, identifier);
30584
- if (annotation !== void 0) graph.pending.push({ kind: "syntax", node: annotation });
30660
+ if (isPresent(annotation)) graph.pending.push({ kind: "syntax", node: annotation });
30585
30661
  const resolved = exportedCallable(source, identifier, graph.cache);
30586
30662
  if (resolved === null) return;
30587
30663
  if (resolved.callable !== null) graph.callables.add(resolved.callable);
@@ -30667,7 +30743,7 @@ function exportedCallable(source, identifier, cache) {
30667
30743
  }
30668
30744
  const step = constantCallableStep(binding3);
30669
30745
  if (step === null) break;
30670
- if (step.annotation !== void 0) annotations.push(step.annotation);
30746
+ if (isPresent(step.annotation)) annotations.push(step.annotation);
30671
30747
  if (isFunctionNode2(step.target)) {
30672
30748
  target = { callable: step.target, signatures: [step.target] };
30673
30749
  break;
@@ -30706,11 +30782,11 @@ function valueBinding(source, identifier) {
30706
30782
  }
30707
30783
  function callableAnnotations(callable) {
30708
30784
  const annotations = [];
30709
- if (callable.returnType !== void 0) annotations.push(callable.returnType);
30710
- if (callable.typeParameters !== void 0) annotations.push(callable.typeParameters);
30785
+ if (isPresent(callable.returnType)) annotations.push(callable.returnType);
30786
+ if (isPresent(callable.typeParameters)) annotations.push(callable.typeParameters);
30711
30787
  for (const raw of callable.params) {
30712
30788
  const parameter = raw.type === AST_NODE_TYPES157.AssignmentPattern ? raw.left : raw;
30713
- if ((parameter.type === AST_NODE_TYPES157.Identifier || parameter.type === AST_NODE_TYPES157.ObjectPattern || parameter.type === AST_NODE_TYPES157.ArrayPattern || parameter.type === AST_NODE_TYPES157.RestElement) && parameter.typeAnnotation !== void 0) annotations.push(parameter.typeAnnotation);
30789
+ if ((parameter.type === AST_NODE_TYPES157.Identifier || parameter.type === AST_NODE_TYPES157.ObjectPattern || parameter.type === AST_NODE_TYPES157.ArrayPattern || parameter.type === AST_NODE_TYPES157.RestElement) && isPresent(parameter.typeAnnotation)) annotations.push(parameter.typeAnnotation);
30714
30790
  }
30715
30791
  return annotations;
30716
30792
  }
@@ -33151,7 +33227,7 @@ var prefer_whole_object_assertion_default = createRule({
33151
33227
  return assertionExpectation(statement, call, receiver, key, matcher);
33152
33228
  }
33153
33229
  function isTestExpect(callee) {
33154
- return runtimeTestFrameworkName(sourceCode, callee, "assertion") === "expect";
33230
+ return acceptedTestFrameworkName(sourceCode, callee, "assertion", EXPECT_ASSERTION) !== null;
33155
33231
  }
33156
33232
  function isExactLengthAssertion(statement, receiver) {
33157
33233
  if (statement?.type !== AST_NODE_TYPES164.ExpressionStatement || statement.expression.type !== AST_NODE_TYPES164.CallExpression) return false;
@@ -37361,6 +37437,7 @@ var stepdown_default = createRule({
37361
37437
  "Program:exit": (program) => {
37362
37438
  declarationScope(context, program);
37363
37439
  for (const body3 of functionBodies) declarationScope(context, body3);
37440
+ if (classes.length === 0) return;
37364
37441
  const computedReferences = [];
37365
37442
  walk3(program, context.sourceCode.visitorKeys, (node) => {
37366
37443
  if (node.type === AST_NODE_TYPES178.MemberExpression && node.computed)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sarj/eslint-plugin",
3
- "version": "18.1.3",
3
+ "version": "18.1.5",
4
4
  "packageManager": "npm@12.2.1",
5
5
  "description": "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
6
6
  "type": "module",