@sarj/eslint-plugin 16.26.0 → 16.26.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.
Files changed (3) hide show
  1. package/dist/index.cjs +168 -60
  2. package/dist/index.js +1900 -1792
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -883,7 +883,7 @@ function isSearchParamReceiver(call, services) {
883
883
  // package.json
884
884
  var package_default = {
885
885
  name: "@sarj/eslint-plugin",
886
- version: "16.26.0",
886
+ version: "16.26.2",
887
887
  packageManager: "npm@12.2.0",
888
888
  description: "Custom ESLint rules for hypermodern TypeScript / React / Next.js projects",
889
889
  type: "module",
@@ -8845,6 +8845,14 @@ var import_utils53 = require("@typescript-eslint/utils");
8845
8845
 
8846
8846
  // src/rules/_stable-value-binding.ts
8847
8847
  var import_utils52 = require("@typescript-eslint/utils");
8848
+ function typeOnlyReference2(reference) {
8849
+ if (reference.isTypeReference && !reference.isValueReference) return true;
8850
+ let current = outerExpression(reference.identifier);
8851
+ while (current.parent?.type === import_utils52.AST_NODE_TYPES.TSQualifiedName && current.parent.left === current) {
8852
+ current = current.parent;
8853
+ }
8854
+ return current.parent?.type === import_utils52.AST_NODE_TYPES.TSTypeQuery && current.parent.exprName === current;
8855
+ }
8848
8856
  function stableValueBinding(variable, acceptRead) {
8849
8857
  let initializer;
8850
8858
  return variable.references.every((reference) => {
@@ -11076,6 +11084,7 @@ var no_bare_return_from_test_catch_default = createRule({
11076
11084
  const returnBlock = node.parent;
11077
11085
  if (parameter?.type === import_utils64.AST_NODE_TYPES.Identifier && returnBlock?.type === import_utils64.AST_NODE_TYPES.BlockStatement) {
11078
11086
  const errorBinding = import_utils64.ASTUtils.findVariable(context.sourceCode.getScope(parameter), parameter.name);
11087
+ const errorReferences = errorBinding !== null && errorBinding.references.length > 32 ? new Set(errorBinding.references.map((reference) => reference.identifier)) : void 0;
11079
11088
  const assertedError = returnBlock.body.some((statement) => {
11080
11089
  if (statement.range[1] > node.range[0] || statement.type !== import_utils64.AST_NODE_TYPES.ExpressionStatement) return false;
11081
11090
  const expression = statement.expression;
@@ -11086,7 +11095,7 @@ var no_bare_return_from_test_catch_default = createRule({
11086
11095
  let operand = expression.callee.type === import_utils64.AST_NODE_TYPES.MemberExpression ? expression.callee.object : null;
11087
11096
  if (operand?.type === import_utils64.AST_NODE_TYPES.MemberExpression && staticMemberName3(operand) === "not") operand = operand.object;
11088
11097
  if (assertionName !== "assert" && (assertionName !== "expect" || operand?.type !== import_utils64.AST_NODE_TYPES.CallExpression || operand.callee !== root)) return false;
11089
- return walkOwnScope(expression, (current) => current.type === import_utils64.AST_NODE_TYPES.Identifier && errorBinding?.references.some((reference) => reference.identifier === current) === true);
11098
+ return walkOwnScope(expression, (current) => current.type === import_utils64.AST_NODE_TYPES.Identifier && (errorReferences?.has(current) ?? errorBinding?.references.some((reference) => reference.identifier === current)) === true);
11090
11099
  });
11091
11100
  if (assertedError) return;
11092
11101
  }
@@ -17317,6 +17326,15 @@ var test_loops_over_literal_cases_default = createRule({
17317
17326
  if (!isTestFile(context)) {
17318
17327
  return {};
17319
17328
  }
17329
+ const referenceIdentifiers = /* @__PURE__ */ new WeakMap();
17330
+ const isReference = (variable, identifier) => {
17331
+ let identifiers = referenceIdentifiers.get(variable);
17332
+ if (identifiers === void 0) {
17333
+ identifiers = new Set(variable.references.map((reference) => reference.identifier));
17334
+ referenceIdentifiers.set(variable, identifiers);
17335
+ }
17336
+ return identifiers.has(identifier);
17337
+ };
17320
17338
  const isFrameworkTest = (identifier) => TEST_CALLERS3.has(runtimeTestFrameworkName(context.sourceCode, identifier, "test") ?? "");
17321
17339
  const isFrameworkAssertion = (identifier) => ASSERTION_ROOTS.has(runtimeTestFrameworkName(context.sourceCode, identifier, "assertion") ?? "");
17322
17340
  return {
@@ -17340,7 +17358,7 @@ var test_loops_over_literal_cases_default = createRule({
17340
17358
  const capturesSetup = walkOwnScope2(node.body, (current) => {
17341
17359
  if (current.type !== import_utils99.AST_NODE_TYPES.Identifier) return false;
17342
17360
  const variable = import_utils99.ASTUtils.findVariable(context.sourceCode.getScope(current), current.name);
17343
- if (variable === null || !variable.references.some((reference) => reference.identifier === current)) return false;
17361
+ if (variable === null || !isReference(variable, current)) return false;
17344
17362
  return variable.defs.some((definition) => {
17345
17363
  const declaration = definition.name;
17346
17364
  return declaration.range[0] >= enclosing.range[0] && declaration.range[1] <= enclosing.range[1] && (declaration.range[0] < node.range[0] || declaration.range[1] > node.range[1]);
@@ -20789,6 +20807,7 @@ var PREFER_NODE_CRYPTO_HASH_DOCUMENTATION = {
20789
20807
  "Only direct imports, const bindings, and inline calls with statically proven provenance from crypto or node:crypto are analyzed; arbitrary assignments and dynamic module specifiers are excluded.",
20790
20808
  "Only a literal algorithm with exactly one update call is reported; streaming and incremental hashes remain valid.",
20791
20809
  "Namespace bindings must remain read-only: visible writes, aliases, exports, inherited mutation or alias methods, unknown computed members, and other escaping uses are excluded; type-only references and direct member calls remain eligible.",
20810
+ "CommonJS loads and default imports share one mutable native module object. A visible unsafe use of that object excludes its other loads; ESM named exports remain separate unless visible native export synchronization can update them.",
20792
20811
  "Runtime support and output encoding require manual review; no autofix or guaranteed speedup is promised."
20793
20812
  ],
20794
20813
  references: ["https://nodejs.org/api/crypto.html#cryptohashalgorithm-data-options"],
@@ -20803,39 +20822,57 @@ var UNSAFE_NAMESPACE_METHODS = /* @__PURE__ */ new Set(["__defineGetter__", "__d
20803
20822
  function stableNamespace(variable) {
20804
20823
  if (variable.defs.some((definition) => definition.type === "Variable" && definition.parent.parent.type === import_utils118.AST_NODE_TYPES.ExportNamedDeclaration)) return false;
20805
20824
  return stableValueBinding(variable, (reference) => {
20806
- if (reference.isTypeReference && !reference.isValueReference) return true;
20825
+ if (typeOnlyReference2(reference)) return true;
20826
+ const receiver = outerExpression(reference.identifier);
20827
+ return readonlyNamespaceUse(receiver);
20828
+ });
20829
+ }
20830
+ function readonlyNamespaceUse(receiver) {
20831
+ const member = receiver.parent;
20832
+ if (member?.type !== import_utils118.AST_NODE_TYPES.MemberExpression || member.object !== receiver) {
20833
+ return member?.type === import_utils118.AST_NODE_TYPES.UnaryExpression && member.operator === "typeof";
20834
+ }
20835
+ const use = outerExpression(member);
20836
+ const name = import_utils118.ASTUtils.getPropertyName(member);
20837
+ return name !== null && !UNSAFE_NAMESPACE_METHODS.has(name) && use.parent?.type === import_utils118.AST_NODE_TYPES.CallExpression && use.parent.callee === use;
20838
+ }
20839
+ function readonlyLoaderUse(node, resolve2, stable) {
20840
+ const receiver = outerExpression(node);
20841
+ const parent = receiver.parent;
20842
+ if (parent?.type === import_utils118.AST_NODE_TYPES.VariableDeclarator && parent.init === receiver) {
20843
+ if (parent.id.type === import_utils118.AST_NODE_TYPES.ObjectPattern) return true;
20844
+ if (parent.id.type !== import_utils118.AST_NODE_TYPES.Identifier) return false;
20845
+ const variable = resolve2(parent.id);
20846
+ return variable !== null && stable(variable);
20847
+ }
20848
+ return parent?.type === import_utils118.AST_NODE_TYPES.ExpressionStatement || readonlyNamespaceUse(receiver);
20849
+ }
20850
+ function namespaceDefaultMayEscape(variable) {
20851
+ return variable.references.some((reference) => {
20852
+ if (typeOnlyReference2(reference)) return false;
20807
20853
  const receiver = outerExpression(reference.identifier);
20808
- if (typeQueryReference(receiver)) return true;
20809
20854
  const member = receiver.parent;
20810
20855
  if (member?.type !== import_utils118.AST_NODE_TYPES.MemberExpression || member.object !== receiver) {
20811
- return member?.type === import_utils118.AST_NODE_TYPES.UnaryExpression && member.operator === "typeof";
20856
+ return member?.type !== import_utils118.AST_NODE_TYPES.UnaryExpression || member.operator !== "typeof";
20812
20857
  }
20813
- const use = outerExpression(member);
20814
20858
  const name = import_utils118.ASTUtils.getPropertyName(member);
20815
- return name !== null && !UNSAFE_NAMESPACE_METHODS.has(name) && use.parent?.type === import_utils118.AST_NODE_TYPES.CallExpression && use.parent.callee === use;
20859
+ return (name === null || name === "default") && !readonlyNamespaceUse(outerExpression(member));
20816
20860
  });
20817
20861
  }
20818
- function typeQueryReference(node) {
20819
- let current = node;
20820
- while (current.parent?.type === import_utils118.AST_NODE_TYPES.TSQualifiedName && current.parent.left === current) {
20821
- current = current.parent;
20822
- }
20823
- return current.parent?.type === import_utils118.AST_NODE_TYPES.TSTypeQuery && current.parent.exprName === current;
20824
- }
20825
- function isCryptoLoader(node, resolve2) {
20826
- const unwrappedNodeCallee = node.type === "CallExpression" || node.type === "NewExpression" ? unwrapExpression(node.callee) : null;
20862
+ function isBuiltinLoader(node, resolve2, builtin = "crypto") {
20827
20863
  if (node.type !== import_utils118.AST_NODE_TYPES.CallExpression || node.arguments.length !== 1) return false;
20828
20864
  const [argument] = node.arguments;
20829
- if (argument === void 0 || argument.type === import_utils118.AST_NODE_TYPES.SpreadElement || !isCryptoSpecifier(argument)) {
20865
+ if (argument === void 0 || argument.type === import_utils118.AST_NODE_TYPES.SpreadElement || !isBuiltinSpecifier(argument, builtin)) {
20830
20866
  return false;
20831
20867
  }
20868
+ const unwrappedNodeCallee = unwrapExpression(node.callee);
20832
20869
  if (unwrappedNodeCallee?.type === import_utils118.AST_NODE_TYPES.Identifier) {
20833
20870
  return unwrappedNodeCallee.name === "require" && isUnshadowedBuiltinIdentifier(unwrappedNodeCallee, resolve2);
20834
20871
  }
20835
20872
  return unwrappedNodeCallee?.type === import_utils118.AST_NODE_TYPES.MemberExpression && unwrappedNodeCallee.object.type === import_utils118.AST_NODE_TYPES.Identifier && unwrappedNodeCallee.object.name === "process" && isUnshadowedBuiltinIdentifier(unwrappedNodeCallee.object, resolve2) && import_utils118.ASTUtils.getPropertyName(unwrappedNodeCallee) === "getBuiltinModule";
20836
20873
  }
20837
- function isCryptoSpecifier(node) {
20838
- return node.type === import_utils118.AST_NODE_TYPES.Literal && (node.value === "crypto" || node.value === "node:crypto");
20874
+ function isBuiltinSpecifier(node, builtin) {
20875
+ return node.type === import_utils118.AST_NODE_TYPES.Literal && (node.value === builtin || node.value === `node:${builtin}`);
20839
20876
  }
20840
20877
  function isUnshadowedBuiltinIdentifier(identifier, resolve2) {
20841
20878
  const variable = resolve2(identifier);
@@ -20854,46 +20891,84 @@ var prefer_node_crypto_hash_default = createRule({
20854
20891
  meta: { type: "problem", docs: { description: PREFER_NODE_CRYPTO_HASH_DOCUMENTATION.summary }, schema: [], messages: { preferNodeCryptoHash: "Prefer the modern one-shot node:crypto hash API when streaming state is unnecessary." } },
20855
20892
  defaultOptions: [],
20856
20893
  create(context) {
20894
+ if (!context.sourceCode.text.includes("crypto") && !context.sourceCode.text.includes("\\")) return {};
20857
20895
  const directBindings = /* @__PURE__ */ new Set();
20858
20896
  const namespaceBindings = /* @__PURE__ */ new Set();
20897
+ const sharedBindings = /* @__PURE__ */ new Set();
20898
+ const syncBindings = /* @__PURE__ */ new Set();
20899
+ const moduleBindings = /* @__PURE__ */ new Set();
20900
+ const namespaceStability = /* @__PURE__ */ new WeakMap();
20901
+ const candidates = [];
20902
+ const possibleSynchronizations = [];
20903
+ let sharedNamespaceUnsafe = false;
20904
+ function namespaceStable(variable) {
20905
+ const cached = namespaceStability.get(variable);
20906
+ if (cached !== void 0) return cached;
20907
+ const stable = stableNamespace(variable);
20908
+ namespaceStability.set(variable, stable);
20909
+ return stable;
20910
+ }
20859
20911
  function resolve2(identifier) {
20860
20912
  return import_utils118.ASTUtils.findVariable(
20861
20913
  context.sourceCode.getScope(identifier),
20862
20914
  identifier.name
20863
20915
  );
20864
20916
  }
20865
- function record(identifier, destination) {
20917
+ function record(identifier, destination, shared = false) {
20866
20918
  const variable = resolve2(identifier);
20867
- if (variable !== null && (destination === namespaceBindings ? stableNamespace(variable) : stableValueBinding(variable))) destination.add(variable);
20919
+ if (variable === null) return;
20920
+ const stable = destination === namespaceBindings ? namespaceStable(variable) : stableValueBinding(variable);
20921
+ if (shared && destination === namespaceBindings && !stable) sharedNamespaceUnsafe = true;
20922
+ if (!stable) return;
20923
+ destination.add(variable);
20924
+ if (shared) sharedBindings.add(variable);
20925
+ }
20926
+ function recordModuleImports(specifiers) {
20927
+ for (const specifier of specifiers) {
20928
+ if (specifier.type === import_utils118.AST_NODE_TYPES.ImportSpecifier) {
20929
+ if (importedName5(specifier.imported) === "syncBuiltinESMExports") record(specifier.local, syncBindings);
20930
+ else if (importedName5(specifier.imported) === "default") record(specifier.local, moduleBindings);
20931
+ } else record(specifier.local, moduleBindings);
20932
+ }
20868
20933
  }
20869
20934
  return {
20870
20935
  ImportDeclaration(node) {
20936
+ if (node.source.value === "module" || node.source.value === "node:module") {
20937
+ recordModuleImports(node.specifiers);
20938
+ return;
20939
+ }
20871
20940
  if (node.source.value !== "crypto" && node.source.value !== "node:crypto") return;
20872
20941
  for (const specifier of node.specifiers) {
20873
- if (specifier.type === import_utils118.AST_NODE_TYPES.ImportNamespaceSpecifier || specifier.type === import_utils118.AST_NODE_TYPES.ImportDefaultSpecifier) {
20874
- record(specifier.local, namespaceBindings);
20942
+ const shared = specifier.type === import_utils118.AST_NODE_TYPES.ImportDefaultSpecifier || specifier.type === import_utils118.AST_NODE_TYPES.ImportSpecifier && importedName5(specifier.imported) === "default";
20943
+ if (specifier.type === import_utils118.AST_NODE_TYPES.ImportNamespaceSpecifier || shared) {
20944
+ record(specifier.local, namespaceBindings, shared);
20945
+ const variable = resolve2(specifier.local);
20946
+ if (!shared && variable !== null && namespaceDefaultMayEscape(variable)) sharedNamespaceUnsafe = true;
20875
20947
  } else if (specifier.type === import_utils118.AST_NODE_TYPES.ImportSpecifier && importedName5(specifier.imported) === "createHash") {
20876
20948
  record(specifier.local, directBindings);
20877
20949
  }
20878
20950
  }
20879
20951
  },
20880
20952
  VariableDeclarator(node) {
20881
- if (node.parent.kind !== "const" || node.init === null || !isCryptoLoader(node.init, resolve2)) {
20953
+ if (node.parent.kind !== "const" || node.init === null || !isBuiltinLoader(node.init, resolve2) && !isBuiltinLoader(node.init, resolve2, "module")) {
20882
20954
  return;
20883
20955
  }
20956
+ const crypto = isBuiltinLoader(node.init, resolve2);
20884
20957
  if (node.id.type === import_utils118.AST_NODE_TYPES.Identifier) {
20885
- record(node.id, namespaceBindings);
20958
+ record(node.id, crypto ? namespaceBindings : moduleBindings, crypto);
20886
20959
  return;
20887
20960
  }
20888
20961
  if (node.id.type !== import_utils118.AST_NODE_TYPES.ObjectPattern) return;
20889
20962
  for (const property of node.id.properties) {
20890
- if (property.type === import_utils118.AST_NODE_TYPES.Property && propertyName3(property) === "createHash" && property.value.type === import_utils118.AST_NODE_TYPES.Identifier) {
20891
- record(property.value, directBindings);
20963
+ if (property.type === import_utils118.AST_NODE_TYPES.Property && propertyName3(property) === (crypto ? "createHash" : "syncBuiltinESMExports") && property.value.type === import_utils118.AST_NODE_TYPES.Identifier) {
20964
+ record(property.value, crypto ? directBindings : syncBindings, crypto);
20892
20965
  }
20893
20966
  }
20894
20967
  },
20895
20968
  CallExpression(node) {
20969
+ if (isBuiltinLoader(node, resolve2) && !readonlyLoaderUse(node, resolve2, namespaceStable)) sharedNamespaceUnsafe = true;
20896
20970
  const unwrappedNodeCallee = unwrapExpression(node.callee);
20971
+ if (unwrappedNodeCallee.type === import_utils118.AST_NODE_TYPES.Identifier || unwrappedNodeCallee.type === import_utils118.AST_NODE_TYPES.MemberExpression && import_utils118.ASTUtils.getPropertyName(unwrappedNodeCallee) === "syncBuiltinESMExports") possibleSynchronizations.push(node);
20897
20972
  if (unwrappedNodeCallee.type !== import_utils118.AST_NODE_TYPES.MemberExpression || !isMemberCall(node, "digest")) return;
20898
20973
  const update = unwrapExpression(unwrappedNodeCallee.object);
20899
20974
  if (update.type !== import_utils118.AST_NODE_TYPES.CallExpression || update.arguments.length !== 1 || !isMemberCall(update, "update"))
@@ -20902,14 +20977,16 @@ var prefer_node_crypto_hash_default = createRule({
20902
20977
  if (updateCallee.type !== import_utils118.AST_NODE_TYPES.MemberExpression) return;
20903
20978
  const create = unwrapExpression(updateCallee.object);
20904
20979
  const algorithm = create.type === import_utils118.AST_NODE_TYPES.CallExpression && create.arguments[0] !== void 0 ? unwrapExpression(create.arguments[0]) : null;
20905
- if (create.type !== import_utils118.AST_NODE_TYPES.CallExpression || create.arguments.length !== 1 || staticString(algorithm ?? void 0) === null || !isCreateHashCall(
20906
- create,
20907
- directBindings,
20908
- namespaceBindings,
20909
- resolve2
20910
- ))
20980
+ if (create.type !== import_utils118.AST_NODE_TYPES.CallExpression || create.arguments.length !== 1 || staticString(algorithm ?? void 0) === null)
20911
20981
  return;
20912
- context.report({ node, messageId: "preferNodeCryptoHash" });
20982
+ const provider = builtinCallProvider(create, directBindings, namespaceBindings, sharedBindings, resolve2);
20983
+ if (provider !== null) candidates.push({ node, shared: provider === "shared" });
20984
+ },
20985
+ "Program:exit"() {
20986
+ const exportsMaySynchronize = sharedNamespaceUnsafe && possibleSynchronizations.some((node) => builtinCallProvider(node, syncBindings, moduleBindings, sharedBindings, resolve2, "module") !== null);
20987
+ for (const candidate2 of candidates) {
20988
+ if (!sharedNamespaceUnsafe || !candidate2.shared && !exportsMaySynchronize) context.report({ node: candidate2.node, messageId: "preferNodeCryptoHash" });
20989
+ }
20913
20990
  }
20914
20991
  };
20915
20992
  }
@@ -20921,19 +20998,21 @@ function isMemberCall(node, name) {
20921
20998
  const unwrappedNodeCallee = unwrapExpression(node.callee);
20922
20999
  return unwrappedNodeCallee.type === import_utils118.AST_NODE_TYPES.MemberExpression && import_utils118.ASTUtils.getPropertyName(unwrappedNodeCallee) === name;
20923
21000
  }
20924
- function isCreateHashCall(node, directBindings, namespaceBindings, resolve2) {
21001
+ function builtinCallProvider(node, directBindings, namespaceBindings, sharedBindings, resolve2, builtin = "crypto") {
20925
21002
  const unwrappedNodeCallee = unwrapExpression(node.callee);
20926
21003
  if (unwrappedNodeCallee.type === import_utils118.AST_NODE_TYPES.Identifier) {
20927
21004
  const variable2 = resolve2(unwrappedNodeCallee);
20928
- return variable2 !== null && directBindings.has(variable2);
21005
+ if (variable2 === null || !directBindings.has(variable2)) return null;
21006
+ return sharedBindings.has(variable2) ? "shared" : "snapshot";
20929
21007
  }
20930
- if (unwrappedNodeCallee.type !== import_utils118.AST_NODE_TYPES.MemberExpression || import_utils118.ASTUtils.getPropertyName(unwrappedNodeCallee) !== "createHash") {
20931
- return false;
21008
+ if (unwrappedNodeCallee.type !== import_utils118.AST_NODE_TYPES.MemberExpression || import_utils118.ASTUtils.getPropertyName(unwrappedNodeCallee) !== (builtin === "crypto" ? "createHash" : "syncBuiltinESMExports")) {
21009
+ return null;
20932
21010
  }
20933
- if (isCryptoLoader(unwrappedNodeCallee.object, resolve2)) return true;
20934
- if (unwrappedNodeCallee.object.type !== import_utils118.AST_NODE_TYPES.Identifier) return false;
21011
+ if (isBuiltinLoader(unwrappedNodeCallee.object, resolve2, builtin)) return "shared";
21012
+ if (unwrappedNodeCallee.object.type !== import_utils118.AST_NODE_TYPES.Identifier) return null;
20935
21013
  const variable = resolve2(unwrappedNodeCallee.object);
20936
- return variable !== null && namespaceBindings.has(variable);
21014
+ if (variable === null || !namespaceBindings.has(variable)) return null;
21015
+ return sharedBindings.has(variable) ? "shared" : "snapshot";
20937
21016
  }
20938
21017
 
20939
21018
  // src/rules/prefer-node-fs-promises.ts
@@ -26103,6 +26182,7 @@ var require_static_next_matcher_default = createRule({
26103
26182
 
26104
26183
  // src/rules/require-use-form-default-values.ts
26105
26184
  var import_utils139 = require("@typescript-eslint/utils");
26185
+ var UNSAFE_FORM_MEMBERS = /* @__PURE__ */ new Set(["__defineGetter__", "__defineSetter__", "__proto__", "valueOf"]);
26106
26186
  var REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION = {
26107
26187
  summary: "Require explicit form-level initialization or a field default for directly bound Controller fields.",
26108
26188
  rationale: "React Hook Form exposes form-level defaultValues/values and field-level defaultValue as its explicit initialization mechanisms. A directly bound controlled field should not omit both mechanisms.",
@@ -26110,7 +26190,8 @@ var REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION = {
26110
26190
  category: "correctness",
26111
26191
  limitations: [
26112
26192
  "The rule proves only that an explicit form-level initialization option or field default is present; it does not prove that a particular field path occurs inside a defaultValues/values object.",
26113
- "The rule reports only scope-resolved Controller/useController fields explicitly bound to a directly created useForm control. FormProvider context, wrapper hooks, dynamic options, spreads, computed properties, and interprocedural flows are intentionally left unreported."
26193
+ "The rule reports only scope-resolved Controller/useController fields explicitly bound to a directly created useForm control. FormProvider context, wrapper hooks, dynamic options, spreads, computed properties, and interprocedural flows are intentionally left unreported.",
26194
+ "Form bindings must have one initializer and remain local and read-only; aliases, exports, property writes, and other escaping uses are left unreported."
26114
26195
  ],
26115
26196
  examples: [
26116
26197
  {
@@ -26139,6 +26220,29 @@ function staticPropertyName2(property) {
26139
26220
  if (property.key.type === import_utils139.AST_NODE_TYPES.Literal && typeof property.key.value === "string") return property.key.value;
26140
26221
  return null;
26141
26222
  }
26223
+ function readonlyFormReference(reference) {
26224
+ if (typeOnlyReference2(reference)) return true;
26225
+ const receiver = outerExpression(reference.identifier);
26226
+ const parent = receiver.parent;
26227
+ if (parent?.type === import_utils139.AST_NODE_TYPES.VariableDeclarator && parent.init === receiver) return parent.id.type === import_utils139.AST_NODE_TYPES.ObjectPattern;
26228
+ if (parent?.type !== import_utils139.AST_NODE_TYPES.MemberExpression || parent.object !== receiver) {
26229
+ return parent?.type === import_utils139.AST_NODE_TYPES.UnaryExpression && parent.operator === "typeof";
26230
+ }
26231
+ const name = import_utils139.ASTUtils.getPropertyName(parent);
26232
+ return name !== null && !UNSAFE_FORM_MEMBERS.has(name) && !memberWrite(parent);
26233
+ }
26234
+ function memberWrite(node) {
26235
+ let current = outerExpression(node);
26236
+ let parent = current.parent;
26237
+ while (parent?.type === import_utils139.AST_NODE_TYPES.Property && parent.value === current || parent?.type === import_utils139.AST_NODE_TYPES.ObjectPattern || parent?.type === import_utils139.AST_NODE_TYPES.ArrayPattern || parent?.type === import_utils139.AST_NODE_TYPES.RestElement || parent?.type === import_utils139.AST_NODE_TYPES.AssignmentPattern && parent.left === current) {
26238
+ current = parent;
26239
+ parent = current.parent;
26240
+ }
26241
+ return parent?.type === import_utils139.AST_NODE_TYPES.AssignmentExpression && parent.left === current || parent?.type === import_utils139.AST_NODE_TYPES.UpdateExpression && parent.argument === current || parent?.type === import_utils139.AST_NODE_TYPES.UnaryExpression && parent.operator === "delete" || (parent?.type === import_utils139.AST_NODE_TYPES.ForOfStatement || parent?.type === import_utils139.AST_NODE_TYPES.ForInStatement) && parent.left === current;
26242
+ }
26243
+ function stableForm(variable) {
26244
+ return !variable.defs.some((definition) => definition.type === "Variable" && definition.parent.parent.type === import_utils139.AST_NODE_TYPES.ExportNamedDeclaration) && stableValueBinding(variable, readonlyFormReference);
26245
+ }
26142
26246
  var require_use_form_default_values_default = createRule({
26143
26247
  name: "require-use-form-default-values",
26144
26248
  documentation: REQUIRE_USE_FORM_DEFAULT_VALUES_DOCUMENTATION,
@@ -26150,19 +26254,15 @@ var require_use_form_default_values_default = createRule({
26150
26254
  },
26151
26255
  defaultOptions: [],
26152
26256
  create(context) {
26257
+ if (!context.sourceCode.ast.body.some((statement) => statement.type === import_utils139.AST_NODE_TYPES.ImportDeclaration && statement.source.value === "react-hook-form")) return {};
26153
26258
  const imported = /* @__PURE__ */ new Map();
26154
26259
  const uninitializedForms = /* @__PURE__ */ new Set();
26155
26260
  const uninitializedControls = /* @__PURE__ */ new Set();
26156
26261
  const bindingNamed = (node, name) => import_utils139.ASTUtils.findVariable(context.sourceCode.getScope(node), name);
26157
26262
  const bindingOf = (node) => bindingNamed(node, node.name);
26158
- const stable = (variable) => !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
26159
26263
  const importedKind = (node) => {
26160
- const variable = bindingOf(node);
26161
- return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
26162
- };
26163
- const importedJsxKind = (node) => {
26164
26264
  const variable = bindingNamed(node, node.name);
26165
- return variable !== null && stable(variable) ? imported.get(variable) ?? null : null;
26265
+ return variable === null ? null : imported.get(variable) ?? null;
26166
26266
  };
26167
26267
  const isDefinitelyUndefined = (node) => {
26168
26268
  const value = unwrapExpression(node);
@@ -26191,13 +26291,13 @@ var require_use_form_default_values_default = createRule({
26191
26291
  node = unwrapExpression(node);
26192
26292
  if (node.type !== import_utils139.AST_NODE_TYPES.Identifier) return false;
26193
26293
  const variable = bindingOf(node);
26194
- return variable !== null && stable(variable) && uninitializedForms.has(variable);
26294
+ return variable !== null && uninitializedForms.has(variable);
26195
26295
  };
26196
26296
  const isUninitializedControl = (node) => {
26197
26297
  node = unwrapExpression(node);
26198
26298
  if (node.type === import_utils139.AST_NODE_TYPES.Identifier) {
26199
26299
  const variable = bindingOf(node);
26200
- return variable !== null && stable(variable) && uninitializedControls.has(variable);
26300
+ return variable !== null && uninitializedControls.has(variable);
26201
26301
  }
26202
26302
  return node.type === import_utils139.AST_NODE_TYPES.MemberExpression && import_utils139.ASTUtils.getPropertyName(node) !== null && (import_utils139.ASTUtils.getPropertyName(node) ?? "") === "control" && isUninitializedForm(node.object);
26203
26303
  };
@@ -26226,7 +26326,7 @@ var require_use_form_default_values_default = createRule({
26226
26326
  for (const property of pattern.properties) {
26227
26327
  if (property.type !== import_utils139.AST_NODE_TYPES.Property || staticPropertyName2(property) !== "control" || property.value.type !== import_utils139.AST_NODE_TYPES.Identifier) continue;
26228
26328
  const variable = bindingOf(property.value);
26229
- if (variable !== null) uninitializedControls.add(variable);
26329
+ if (variable !== null && stableValueBinding(variable)) uninitializedControls.add(variable);
26230
26330
  }
26231
26331
  };
26232
26332
  return {
@@ -26237,7 +26337,7 @@ var require_use_form_default_values_default = createRule({
26237
26337
  const name = specifier.imported.type === import_utils139.AST_NODE_TYPES.Identifier ? specifier.imported.name : String(specifier.imported.value);
26238
26338
  if (name !== "Controller" && name !== "useController" && name !== "useForm") continue;
26239
26339
  const variable = bindingOf(specifier.local);
26240
- if (variable !== null) imported.set(variable, name);
26340
+ if (variable !== null && stableValueBinding(variable)) imported.set(variable, name);
26241
26341
  }
26242
26342
  },
26243
26343
  VariableDeclarator(node) {
@@ -26245,7 +26345,7 @@ var require_use_form_default_values_default = createRule({
26245
26345
  if (uninitializedUseFormCall(node.init)) {
26246
26346
  if (node.id.type === import_utils139.AST_NODE_TYPES.Identifier) {
26247
26347
  const variable = bindingOf(node.id);
26248
- if (variable !== null) uninitializedForms.add(variable);
26348
+ if (variable !== null && stableForm(variable)) uninitializedForms.add(variable);
26249
26349
  } else if (node.id.type === import_utils139.AST_NODE_TYPES.ObjectPattern) {
26250
26350
  trackDestructuredControl(node.id);
26251
26351
  }
@@ -26257,7 +26357,7 @@ var require_use_form_default_values_default = createRule({
26257
26357
  }
26258
26358
  if (node.id.type === import_utils139.AST_NODE_TYPES.Identifier && node.init.type === import_utils139.AST_NODE_TYPES.MemberExpression && import_utils139.ASTUtils.getPropertyName(node.init) !== null && (import_utils139.ASTUtils.getPropertyName(node.init) ?? "") === "control" && isUninitializedForm(node.init.object)) {
26259
26359
  const variable = bindingOf(node.id);
26260
- if (variable !== null) uninitializedControls.add(variable);
26360
+ if (variable !== null && stableValueBinding(variable)) uninitializedControls.add(variable);
26261
26361
  }
26262
26362
  },
26263
26363
  CallExpression(node) {
@@ -26266,7 +26366,7 @@ var require_use_form_default_values_default = createRule({
26266
26366
  context.report({ node, messageId: "requireUseFormDefaultValues" });
26267
26367
  },
26268
26368
  JSXOpeningElement(node) {
26269
- if (node.name.type !== import_utils139.AST_NODE_TYPES.JSXIdentifier || importedJsxKind(node.name) !== "Controller") return;
26369
+ if (node.name.type !== import_utils139.AST_NODE_TYPES.JSXIdentifier || importedKind(node.name) !== "Controller") return;
26270
26370
  if (node.attributes.some((attribute) => attribute.type === import_utils139.AST_NODE_TYPES.JSXSpreadAttribute)) return;
26271
26371
  const control = jsxAttribute(node, "control");
26272
26372
  if (control?.value?.type !== import_utils139.AST_NODE_TYPES.JSXExpressionContainer || control.value.expression.type === import_utils139.AST_NODE_TYPES.JSXEmptyExpression || !isUninitializedControl(control.value.expression)) return;
@@ -27442,6 +27542,14 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
27442
27542
  defaultOptions: [],
27443
27543
  create(context) {
27444
27544
  if (!isTestFile(context) || isGeneratedFile(context, context.sourceCode.text)) return {};
27545
+ const referenceStability = /* @__PURE__ */ new WeakMap();
27546
+ const referenceStable = (variable) => {
27547
+ const cached = referenceStability.get(variable);
27548
+ if (cached !== void 0) return cached;
27549
+ const stable = !variable.references.some((reference) => reference.isWrite() && reference.init !== true);
27550
+ referenceStability.set(variable, stable);
27551
+ return stable;
27552
+ };
27445
27553
  const scopes = [newScope()];
27446
27554
  const reportedOrigins = /* @__PURE__ */ new Set();
27447
27555
  const currentScope = () => scopes.at(-1) ?? scopes[0];
@@ -27459,7 +27567,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
27459
27567
  };
27460
27568
  const visible = (kind, node) => {
27461
27569
  const name2 = bindingOf(node);
27462
- if (name2 === null || name2.references.some((reference) => reference.isWrite() && reference.init !== true)) return false;
27570
+ if (name2 === null || !referenceStable(name2)) return false;
27463
27571
  for (let index = scopes.length - 1; index >= 0; index--) {
27464
27572
  const scope = scopes[index];
27465
27573
  if (scope.declared.has(name2)) return scope[kind].has(name2);
@@ -27468,7 +27576,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
27468
27576
  };
27469
27577
  const visibleRawOrigins = (node) => {
27470
27578
  const name2 = bindingOf(node);
27471
- if (name2 === null || name2.references.some((reference) => reference.isWrite() && reference.init !== true)) return /* @__PURE__ */ new Set();
27579
+ if (name2 === null || !referenceStable(name2)) return /* @__PURE__ */ new Set();
27472
27580
  for (let index = scopes.length - 1; index >= 0; index--) {
27473
27581
  const scope = scopes[index];
27474
27582
  if (scope.declared.has(name2)) return scope.rawOrigins.get(name2) ?? /* @__PURE__ */ new Set();
@@ -27500,7 +27608,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
27500
27608
  };
27501
27609
  const nativeImportedName = (node, modules) => {
27502
27610
  const binding = bindingOf(node);
27503
- if (binding === null || binding.references.some((reference) => reference.isWrite() && reference.init !== true)) return null;
27611
+ if (binding === null || !referenceStable(binding)) return null;
27504
27612
  for (const definition of binding.defs) {
27505
27613
  const imported = definition.node;
27506
27614
  if (imported.type === import_utils144.AST_NODE_TYPES.ImportSpecifier && imported.imported.type === import_utils144.AST_NODE_TYPES.Identifier && imported.parent.type === import_utils144.AST_NODE_TYPES.ImportDeclaration && modules.has(String(imported.parent.source.value))) {
@@ -27511,7 +27619,7 @@ function createSourceCoupledRule(name, documentation, sourceSuffixRe) {
27511
27619
  };
27512
27620
  const stableInitializer = (node) => {
27513
27621
  const binding = bindingOf(node);
27514
- if (binding === null || binding.references.some((reference) => reference.isWrite() && reference.init !== true) || binding.defs.length !== 1) return null;
27622
+ if (binding === null || !referenceStable(binding) || binding.defs.length !== 1) return null;
27515
27623
  const definition = binding.defs[0]?.node;
27516
27624
  return definition?.type === import_utils144.AST_NODE_TYPES.VariableDeclarator ? definition.init : null;
27517
27625
  };