oxlint-plugin-react-doctor 0.6.2-dev.173cc0a → 0.6.2-dev.59e8178

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 (2) hide show
  1. package/dist/index.js +109 -351
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -588,7 +588,7 @@ const SETTER_PATTERN = /^set[A-Z]/;
588
588
  const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
589
589
  const UPPERCASE_PATTERN = /^[A-Z]/;
590
590
  const REACT_HANDLER_PROP_PATTERN = /^on[A-Z]/;
591
- const HOOK_NAME_PATTERN$1 = /^use[A-Z]/;
591
+ const HOOK_NAME_PATTERN = /^use[A-Z]/;
592
592
  const HANDLER_FUNCTION_NAME_PATTERN = /^(?:on|handle)[A-Z]/;
593
593
  const EFFECT_HOOK_NAMES$1 = new Set(["useEffect", "useLayoutEffect"]);
594
594
  const HOOKS_WITH_DEPS = new Set([
@@ -4928,8 +4928,7 @@ const isValidTypeValue = (rawValue, settings) => {
4928
4928
  if (rawValue === "reset") return settings.reset;
4929
4929
  return false;
4930
4930
  };
4931
- const isProvenValidExpression = (rawExpression, settings, resolvedBindings = /* @__PURE__ */ new Set()) => {
4932
- const expression = stripParenExpression(rawExpression);
4931
+ const isProvenValidExpression = (expression, settings, resolvedBindings = /* @__PURE__ */ new Set()) => {
4933
4932
  if (isNodeOfType(expression, "Literal") && typeof expression.value === "string") return isValidTypeValue(expression.value, settings);
4934
4933
  if (isNodeOfType(expression, "TemplateLiteral")) {
4935
4934
  const staticValue = getStaticTemplateLiteralValue(expression);
@@ -5096,20 +5095,11 @@ const resolveSettings$46 = (settings) => {
5096
5095
  ignoreExclusiveCheckedAttribute: ruleSettings.ignoreExclusiveCheckedAttribute ?? false
5097
5096
  };
5098
5097
  };
5099
- const isTruthyDisabledJsxValue = (attribute) => {
5100
- if (!attribute.value) return true;
5101
- if (isNodeOfType(attribute.value, "JSXExpressionContainer")) {
5102
- const expression = attribute.value.expression;
5103
- return isNodeOfType(expression, "Literal") && expression.value === true;
5104
- }
5105
- return false;
5106
- };
5107
5098
  const collectFromJsxAttributes = (attributes) => {
5108
5099
  let checkedNode = null;
5109
5100
  let defaultCheckedNode = null;
5110
5101
  let hasOnChangeOrReadOnly = false;
5111
5102
  let hasSpread = false;
5112
- let hasTruthyDisabled = false;
5113
5103
  for (const attribute of attributes) {
5114
5104
  if (isNodeOfType(attribute, "JSXSpreadAttribute")) {
5115
5105
  hasSpread = true;
@@ -5120,14 +5110,12 @@ const collectFromJsxAttributes = (attributes) => {
5120
5110
  if (name === "checked") checkedNode = attribute;
5121
5111
  else if (name === "defaultChecked" && !defaultCheckedNode) defaultCheckedNode = attribute;
5122
5112
  else if (name === "onChange" || name === "readOnly") hasOnChangeOrReadOnly = true;
5123
- else if (name === "disabled" && isTruthyDisabledJsxValue(attribute)) hasTruthyDisabled = true;
5124
5113
  }
5125
5114
  return {
5126
5115
  checkedNode,
5127
5116
  defaultCheckedNode,
5128
5117
  hasOnChangeOrReadOnly,
5129
- hasSpread,
5130
- hasTruthyDisabled
5118
+ hasSpread
5131
5119
  };
5132
5120
  };
5133
5121
  const collectFromObjectProperties = (objectExpression) => {
@@ -5135,7 +5123,6 @@ const collectFromObjectProperties = (objectExpression) => {
5135
5123
  let defaultCheckedNode = null;
5136
5124
  let hasOnChangeOrReadOnly = false;
5137
5125
  let hasSpread = false;
5138
- let hasTruthyDisabled = false;
5139
5126
  for (const property of objectExpression.properties) {
5140
5127
  if (isNodeOfType(property, "SpreadElement")) {
5141
5128
  hasSpread = true;
@@ -5150,17 +5137,12 @@ const collectFromObjectProperties = (objectExpression) => {
5150
5137
  if (propertyName === "checked") checkedNode = property;
5151
5138
  else if (propertyName === "defaultChecked" && !defaultCheckedNode) defaultCheckedNode = property;
5152
5139
  else if (propertyName === "onChange" || propertyName === "readOnly") hasOnChangeOrReadOnly = true;
5153
- else if (propertyName === "disabled") {
5154
- const propertyValue = property.value;
5155
- if (isNodeOfType(propertyValue, "Literal") && propertyValue.value === true) hasTruthyDisabled = true;
5156
- }
5157
5140
  }
5158
5141
  return {
5159
5142
  checkedNode,
5160
5143
  defaultCheckedNode,
5161
5144
  hasOnChangeOrReadOnly,
5162
- hasSpread,
5163
- hasTruthyDisabled
5145
+ hasSpread
5164
5146
  };
5165
5147
  };
5166
5148
  const checkedRequiresOnchangeOrReadonly = defineRule({
@@ -5176,7 +5158,7 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5176
5158
  node: presence.checkedNode,
5177
5159
  message: EXCLUSIVE_MESSAGE
5178
5160
  });
5179
- if (presence.checkedNode && !presence.hasOnChangeOrReadOnly && !presence.hasSpread && !presence.hasTruthyDisabled && !settings.ignoreMissingProperties) context.report({
5161
+ if (presence.checkedNode && !presence.hasOnChangeOrReadOnly && !presence.hasSpread && !settings.ignoreMissingProperties) context.report({
5180
5162
  node: presence.checkedNode,
5181
5163
  message: MISSING_MESSAGE$1
5182
5164
  });
@@ -6450,7 +6432,7 @@ const displayName = defineRule({
6450
6432
  },
6451
6433
  ArrowFunctionExpression(node) {
6452
6434
  if (!containsJsx$1(node)) return;
6453
- if (isNodeOfType(node.parent, "ArrowFunctionExpression") || isNodeOfType(node.parent, "ReturnStatement")) {
6435
+ if (isNodeOfType(node.parent, "ArrowFunctionExpression")) {
6454
6436
  reportAt(node);
6455
6437
  return;
6456
6438
  }
@@ -7367,7 +7349,7 @@ const TRANSPARENT_WRAPPER_TYPES = new Set([
7367
7349
  "ParenthesizedExpression",
7368
7350
  "ChainExpression"
7369
7351
  ]);
7370
- const unwrapExpression$3 = (node) => {
7352
+ const unwrapExpression$2 = (node) => {
7371
7353
  let current = node;
7372
7354
  while (TRANSPARENT_WRAPPER_TYPES.has(current.type)) {
7373
7355
  const inner = current.expression;
@@ -7493,7 +7475,7 @@ const symbolHasStableHookOrigin = (symbol) => {
7493
7475
  if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return false;
7494
7476
  const initializerRaw = declarator.init;
7495
7477
  if (!initializerRaw) return false;
7496
- const initializer = unwrapExpression$3(initializerRaw);
7478
+ const initializer = unwrapExpression$2(initializerRaw);
7497
7479
  if (symbol.kind === "const") {
7498
7480
  if (isNodeOfType(initializer, "Literal") && (initializer.value === null || typeof initializer.value === "number" || typeof initializer.value === "string" || typeof initializer.value === "boolean")) return true;
7499
7481
  if (isNodeOfType(initializer, "TemplateLiteral") && getStaticTemplateLiteralValue(initializer) !== null) return true;
@@ -7513,13 +7495,13 @@ const symbolHasStableHookOrigin = (symbol) => {
7513
7495
  return false;
7514
7496
  };
7515
7497
  const symbolHasUseEffectEventOrigin = (symbol) => {
7516
- const initializer = symbol.initializer ? unwrapExpression$3(symbol.initializer) : null;
7498
+ const initializer = symbol.initializer ? unwrapExpression$2(symbol.initializer) : null;
7517
7499
  if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
7518
7500
  return getHookName(initializer.callee) === "useEffectEvent";
7519
7501
  };
7520
7502
  const getFunctionValueNode = (symbol) => {
7521
7503
  if (symbol.kind === "function" && isNodeOfType(symbol.declarationNode, "FunctionDeclaration")) return symbol.declarationNode;
7522
- const initializer = symbol.initializer ? unwrapExpression$3(symbol.initializer) : null;
7504
+ const initializer = symbol.initializer ? unwrapExpression$2(symbol.initializer) : null;
7523
7505
  if (initializer && (isNodeOfType(initializer, "FunctionExpression") || isNodeOfType(initializer, "ArrowFunctionExpression"))) return initializer;
7524
7506
  return null;
7525
7507
  };
@@ -7615,14 +7597,6 @@ const flattenReferenceRootName = (reference) => {
7615
7597
  if (isNodeOfType(referencedIdentifier, "JSXIdentifier")) return referencedIdentifier.name;
7616
7598
  return "";
7617
7599
  };
7618
- const REF_CURRENT_SEGMENT = ".current";
7619
- const truncateAtRefCurrent = (chain) => {
7620
- const refCurrentIndex = chain.indexOf(REF_CURRENT_SEGMENT);
7621
- if (refCurrentIndex === -1) return chain;
7622
- const segmentEndIndex = refCurrentIndex + 8;
7623
- if (segmentEndIndex === chain.length || chain[segmentEndIndex] === ".") return chain.slice(0, refCurrentIndex);
7624
- return chain;
7625
- };
7626
7600
  const computeDepKey = (reference) => {
7627
7601
  const referencedIdentifier = reference.identifier;
7628
7602
  let parent = referencedIdentifier.parent ?? null;
@@ -7653,22 +7627,21 @@ const computeDepKey = (reference) => {
7653
7627
  const declarator = outermost.parent;
7654
7628
  if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declarator.init === outermost) {
7655
7629
  const destructuredPath = getDestructuredPropertyPath(declarator.id);
7656
- if (destructuredPath) return truncateAtRefCurrent(`${fullName}.${destructuredPath}`);
7630
+ if (destructuredPath) return `${fullName}.${destructuredPath}`;
7657
7631
  }
7658
- const truncatedName = truncateAtRefCurrent(fullName);
7659
- if (truncatedName !== fullName) return truncatedName;
7660
7632
  if (reference.flag !== "read") {
7661
7633
  const lastDotIndex = fullName.lastIndexOf(".");
7662
7634
  if (lastDotIndex !== -1) return fullName.slice(0, lastDotIndex);
7663
7635
  }
7636
+ if (fullName.endsWith(".current")) return fullName.slice(0, -8);
7664
7637
  return fullName;
7665
7638
  };
7666
7639
  const computeDeclaredDepKey = (entry) => {
7667
- const stripped = unwrapExpression$3(entry);
7640
+ const stripped = unwrapExpression$2(entry);
7668
7641
  if (isNodeOfType(stripped, "Identifier")) return stripped.name;
7669
7642
  if (isNodeOfType(stripped, "MemberExpression")) return stringifyMemberChain(stripped);
7670
7643
  if (isNodeOfType(stripped, "CallExpression") && (stripped.arguments?.length ?? 0) === 0) {
7671
- const callee = unwrapExpression$3(stripped.callee);
7644
+ const callee = unwrapExpression$2(stripped.callee);
7672
7645
  if (isNodeOfType(callee, "Identifier")) return callee.name;
7673
7646
  if (isNodeOfType(callee, "MemberExpression") && !hasComputedMemberExpression(callee)) return stringifyMemberChain(callee);
7674
7647
  }
@@ -7676,16 +7649,16 @@ const computeDeclaredDepKey = (entry) => {
7676
7649
  };
7677
7650
  const depsArrayContainsIdentifier = (depsArgument, identifierName) => {
7678
7651
  if (!depsArgument) return false;
7679
- const strippedDepsArgument = unwrapExpression$3(depsArgument);
7652
+ const strippedDepsArgument = unwrapExpression$2(depsArgument);
7680
7653
  if (!isNodeOfType(strippedDepsArgument, "ArrayExpression")) return false;
7681
7654
  return strippedDepsArgument.elements.some((element) => {
7682
7655
  if (!element) return false;
7683
- const strippedElement = unwrapExpression$3(element);
7656
+ const strippedElement = unwrapExpression$2(element);
7684
7657
  return isNodeOfType(strippedElement, "Identifier") && strippedElement.name === identifierName;
7685
7658
  });
7686
7659
  };
7687
7660
  const stringifyMemberChain = (node) => {
7688
- const stripped = unwrapExpression$3(node);
7661
+ const stripped = unwrapExpression$2(node);
7689
7662
  if (isNodeOfType(stripped, "Identifier")) return stripped.name;
7690
7663
  if (isNodeOfType(stripped, "ThisExpression")) return "this";
7691
7664
  if (isNodeOfType(stripped, "MemberExpression")) {
@@ -7727,13 +7700,13 @@ const hasBroaderDeclaredDependency = (declaredKey, declaredKeys) => {
7727
7700
  return false;
7728
7701
  };
7729
7702
  const getMemberRootIdentifier = (node) => {
7730
- const stripped = unwrapExpression$3(node);
7703
+ const stripped = unwrapExpression$2(node);
7731
7704
  if (isNodeOfType(stripped, "Identifier")) return stripped;
7732
7705
  if (isNodeOfType(stripped, "MemberExpression")) return getMemberRootIdentifier(stripped.object);
7733
7706
  return null;
7734
7707
  };
7735
7708
  const hasComputedMemberExpression = (node) => {
7736
- const stripped = unwrapExpression$3(node);
7709
+ const stripped = unwrapExpression$2(node);
7737
7710
  if (!isNodeOfType(stripped, "MemberExpression")) return false;
7738
7711
  if (stripped.computed) return true;
7739
7712
  return hasComputedMemberExpression(stripped.object);
@@ -7749,7 +7722,7 @@ const getRootSymbol = (node, scopes) => {
7749
7722
  return rootIdentifier ? scopes.symbolFor(rootIdentifier) : null;
7750
7723
  };
7751
7724
  const getDeclaredDepSymbolSource = (node) => {
7752
- const stripped = unwrapExpression$3(node);
7725
+ const stripped = unwrapExpression$2(node);
7753
7726
  if (isNodeOfType(stripped, "CallExpression") && (stripped.arguments?.length ?? 0) === 0) return stripped.callee;
7754
7727
  return node;
7755
7728
  };
@@ -7759,7 +7732,7 @@ const isRegExpLiteral = (node) => {
7759
7732
  };
7760
7733
  const isUnstableInitializer = (node) => {
7761
7734
  if (!node) return false;
7762
- const stripped = unwrapExpression$3(node);
7735
+ const stripped = unwrapExpression$2(node);
7763
7736
  if (isRegExpLiteral(stripped)) return true;
7764
7737
  if (isNodeOfType(stripped, "ConditionalExpression")) return isUnstableInitializer(stripped.consequent) || isUnstableInitializer(stripped.alternate);
7765
7738
  if (isNodeOfType(stripped, "LogicalExpression")) return isUnstableInitializer(stripped.left) || isUnstableInitializer(stripped.right);
@@ -7851,9 +7824,9 @@ const findRefCurrentInCleanup = (callback, scopes) => {
7851
7824
  };
7852
7825
  findReturn(callback);
7853
7826
  if (!cleanupFunction) return null;
7854
- let found = null;
7827
+ let refCurrentName = null;
7855
7828
  const visitCleanup = (node) => {
7856
- if (found) return;
7829
+ if (refCurrentName) return;
7857
7830
  if (isNodeOfType(node, "MemberExpression")) {
7858
7831
  const candidateName = getRefCurrentNameFromMemberExpression(node);
7859
7832
  if (candidateName) {
@@ -7861,10 +7834,7 @@ const findRefCurrentInCleanup = (callback, scopes) => {
7861
7834
  const symbol = rootIdentifier ? scopes.symbolFor(rootIdentifier) : null;
7862
7835
  const callbackScope = scopes.ownScopeFor(callback) ?? scopes.scopeFor(callback);
7863
7836
  if (!symbol || !isDescendantScope(symbol.scope, callbackScope)) {
7864
- found = {
7865
- refCurrentName: candidateName,
7866
- refSymbol: symbol
7867
- };
7837
+ refCurrentName = candidateName;
7868
7838
  return;
7869
7839
  }
7870
7840
  }
@@ -7879,19 +7849,7 @@ const findRefCurrentInCleanup = (callback, scopes) => {
7879
7849
  }
7880
7850
  };
7881
7851
  visitCleanup(cleanupFunction);
7882
- return found;
7883
- };
7884
- const hasRefCurrentAssignmentInComponent = (refSymbol) => {
7885
- if (!refSymbol) return false;
7886
- for (const reference of refSymbol.references) {
7887
- const memberParent = reference.identifier.parent;
7888
- if (!memberParent || !isNodeOfType(memberParent, "MemberExpression")) continue;
7889
- if (memberParent.object !== reference.identifier) continue;
7890
- if (getRefCurrentNameFromMemberExpression(memberParent) === null) continue;
7891
- const assignmentParent = memberParent.parent;
7892
- if (assignmentParent && isNodeOfType(assignmentParent, "AssignmentExpression") && assignmentParent.left === memberParent) return true;
7893
- }
7894
- return false;
7852
+ return refCurrentName;
7895
7853
  };
7896
7854
  const hasRefCurrentAssignment = (callback, refCurrentName) => {
7897
7855
  let didAssignRefCurrent = false;
@@ -7930,21 +7888,9 @@ const hasMemberCallForRoot = (node, rootName) => {
7930
7888
  const visit = (current) => {
7931
7889
  if (didFindMemberCall) return;
7932
7890
  if (isNodeOfType(current, "CallExpression")) {
7933
- const callee = unwrapExpression$3(current.callee);
7934
- if (isNodeOfType(callee, "MemberExpression")) {
7935
- let chainObject = unwrapExpression$3(callee.object);
7936
- let doesChainPassThroughCurrent = false;
7937
- while (chainObject && isNodeOfType(chainObject, "MemberExpression")) {
7938
- if (isNodeOfType(chainObject.property, "Identifier") && chainObject.property.name === "current") {
7939
- doesChainPassThroughCurrent = true;
7940
- break;
7941
- }
7942
- chainObject = unwrapExpression$3(chainObject.object);
7943
- }
7944
- if (!doesChainPassThroughCurrent && chainObject && isNodeOfType(chainObject, "Identifier") && chainObject.name === rootName) {
7945
- didFindMemberCall = true;
7946
- return;
7947
- }
7891
+ if (getMemberRootIdentifier(unwrapExpression$2(current.callee))?.name === rootName) {
7892
+ didFindMemberCall = true;
7893
+ return;
7948
7894
  }
7949
7895
  }
7950
7896
  const record = current;
@@ -8005,7 +7951,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
8005
7951
  return;
8006
7952
  }
8007
7953
  const depsArgumentRaw = node.arguments[depsArgumentIndex];
8008
- const callbackExpression = unwrapExpression$3(callbackArgument);
7954
+ const callbackExpression = unwrapExpression$2(callbackArgument);
8009
7955
  let callbackToAnalyze = null;
8010
7956
  const forcedCaptureKeys = /* @__PURE__ */ new Set();
8011
7957
  if (isNodeOfType(callbackExpression, "ArrowFunctionExpression") || isNodeOfType(callbackExpression, "FunctionExpression")) callbackToAnalyze = callbackExpression;
@@ -8013,7 +7959,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
8013
7959
  const callbackSymbol = context.scopes.symbolFor(callbackExpression);
8014
7960
  const functionValueNode = callbackSymbol ? getFunctionValueNode(callbackSymbol) : null;
8015
7961
  if (functionValueNode) callbackToAnalyze = functionValueNode;
8016
- else if (callbackSymbol?.initializer && isNodeOfType(unwrapExpression$3(callbackSymbol.initializer), "CallExpression")) forcedCaptureKeys.add(callbackExpression.name);
7962
+ else if (callbackSymbol?.initializer && isNodeOfType(unwrapExpression$2(callbackSymbol.initializer), "CallExpression")) forcedCaptureKeys.add(callbackExpression.name);
8017
7963
  else if (depsArgumentRaw) {
8018
7964
  if (depsArrayContainsIdentifier(depsArgumentRaw, callbackExpression.name)) return;
8019
7965
  context.report({
@@ -8046,11 +7992,11 @@ If the missing value is recreated every render, move it inside the hook or stabi
8046
7992
  message: buildAssignmentMessage(assignment.name)
8047
7993
  });
8048
7994
  if (outerAssignments.length > 0) return;
8049
- const refCurrentInCleanup = findRefCurrentInCleanup(callbackToAnalyze, context.scopes);
7995
+ const refCurrentName = findRefCurrentInCleanup(callbackToAnalyze, context.scopes);
8050
7996
  const shouldCheckRefCleanup = EFFECT_HOOKS_ALLOWING_EXTRA_REACTIVE_DEPS.has(hookName) || Boolean(additionalHooksRegex && additionalHooksRegex.test(hookName));
8051
- if (refCurrentInCleanup && shouldCheckRefCleanup && !hasRefCurrentAssignment(callbackToAnalyze, refCurrentInCleanup.refCurrentName) && !hasRefCurrentAssignmentInComponent(refCurrentInCleanup.refSymbol)) context.report({
7997
+ if (refCurrentName && shouldCheckRefCleanup && !hasRefCurrentAssignment(callbackToAnalyze, refCurrentName)) context.report({
8052
7998
  node: callbackToAnalyze,
8053
- message: buildRefCleanupMessage(refCurrentInCleanup.refCurrentName)
7999
+ message: buildRefCleanupMessage(refCurrentName)
8054
8000
  });
8055
8001
  }
8056
8002
  if (!depsArgumentRaw) {
@@ -8070,16 +8016,8 @@ If the missing value is recreated every render, move it inside the hook or stabi
8070
8016
  });
8071
8017
  return;
8072
8018
  }
8073
- const depsArgument = unwrapExpression$3(depsArgumentRaw);
8074
- if (isNodeOfType(depsArgument, "Identifier") && depsArgument.name === "undefined") {
8075
- if (isAutoDependenciesHook(hookName)) return;
8076
- if (HOOKS_REQUIRING_DEPS_ARRAY.has(hookName)) context.report({
8077
- node: depsArgument,
8078
- message: buildMissingDepArrayMessage(hookName)
8079
- });
8080
- return;
8081
- }
8082
- if (isNodeOfType(depsArgument, "Literal") && depsArgument.value === null) {
8019
+ const depsArgument = unwrapExpression$2(depsArgumentRaw);
8020
+ if (isNodeOfType(depsArgument, "Literal") && depsArgument.value === null || isNodeOfType(depsArgument, "Identifier") && depsArgument.name === "undefined") {
8083
8021
  if (isAutoDependenciesHook(hookName)) return;
8084
8022
  if (HOOKS_REQUIRING_DEPS_ARRAY.has(hookName)) {
8085
8023
  context.report({
@@ -8088,6 +8026,19 @@ If the missing value is recreated every render, move it inside the hook or stabi
8088
8026
  });
8089
8027
  return;
8090
8028
  }
8029
+ const nonArrayCaptureKeys = callbackToAnalyze !== null ? new Set(collectCaptureDepKeys(callbackToAnalyze, context.scopes).keys) : /* @__PURE__ */ new Set();
8030
+ for (const forcedCaptureKey of forcedCaptureKeys) nonArrayCaptureKeys.add(forcedCaptureKey);
8031
+ if (nonArrayCaptureKeys.size > 0) {
8032
+ context.report({
8033
+ node: depsArgument,
8034
+ message: buildNonArrayDepsMessage(hookName)
8035
+ });
8036
+ context.report({
8037
+ node: depsArgument,
8038
+ message: buildMissingDepMessage(hookName, [...nonArrayCaptureKeys].join(", "))
8039
+ });
8040
+ }
8041
+ return;
8091
8042
  }
8092
8043
  if (!isNodeOfType(depsArgument, "ArrayExpression")) {
8093
8044
  context.report({
@@ -8106,11 +8057,11 @@ If the missing value is recreated every render, move it inside the hook or stabi
8106
8057
  for (const forcedCaptureKey of forcedCaptureKeys) captureKeys.add(forcedCaptureKey);
8107
8058
  const hasLiteralDepElement = depsArgument.elements.some((element) => {
8108
8059
  if (!element) return false;
8109
- return isLiteralOrEmptyTemplate(unwrapExpression$3(element));
8060
+ return isLiteralOrEmptyTemplate(unwrapExpression$2(element));
8110
8061
  });
8111
8062
  const hasNonStringLiteralDep = depsArgument.elements.some((element) => {
8112
8063
  if (!element) return false;
8113
- return isNonStringLiteral(unwrapExpression$3(element));
8064
+ return isNonStringLiteral(unwrapExpression$2(element));
8114
8065
  });
8115
8066
  if (hasNonStringLiteralDep) context.report({
8116
8067
  node: depsArgument,
@@ -8131,7 +8082,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
8131
8082
  });
8132
8083
  continue;
8133
8084
  }
8134
- const stripped = unwrapExpression$3(elementNode);
8085
+ const stripped = unwrapExpression$2(elementNode);
8135
8086
  if (isLiteralOrEmptyTemplate(stripped)) continue;
8136
8087
  if (isNodeOfType(stripped, "Identifier")) {
8137
8088
  const depSymbol = context.scopes.symbolFor(stripped);
@@ -8273,7 +8224,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
8273
8224
  if (missingCaptureKeys.length > 0 && isOuterFunctionScopeDep(reportNode, callbackToAnalyze ?? callbackArgument, context.scopes)) continue;
8274
8225
  const rootSymbol = getRootSymbol(reportNode, context.scopes);
8275
8226
  if (rootSymbol && missingCaptureKeys.length > 0 && isRecursiveInitializerCapture(rootSymbol, callbackToAnalyze ?? callbackArgument)) continue;
8276
- if (isNodeOfType(unwrapExpression$3(reportNode), "CallExpression")) {
8227
+ if (isNodeOfType(unwrapExpression$2(reportNode), "CallExpression")) {
8277
8228
  context.report({
8278
8229
  node: reportNode,
8279
8230
  message: buildComplexDepMessage(hookName)
@@ -9297,9 +9248,9 @@ const resolveSettings$37 = (settings) => {
9297
9248
  };
9298
9249
  };
9299
9250
  const isWordBoundary = (text, start, end) => {
9300
- const isWordCharacter = (charCode) => charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122 || charCode === 45 || charCode === 95;
9301
- const startsBoundary = start === 0 || !isWordCharacter(text.charCodeAt(start - 1));
9302
- const endsBoundary = end === text.length || !isWordCharacter(text.charCodeAt(end));
9251
+ const isAlphanumeric = (charCode) => charCode >= 48 && charCode <= 57 || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122;
9252
+ const startsBoundary = start === 0 || !isAlphanumeric(text.charCodeAt(start - 1));
9253
+ const endsBoundary = end === text.length || !isAlphanumeric(text.charCodeAt(end));
9303
9254
  return startsBoundary && endsBoundary;
9304
9255
  };
9305
9256
  const containsRedundantWord = (altText, words) => {
@@ -9696,7 +9647,7 @@ const isAtomFromJotai = (callExpression) => {
9696
9647
  if (!isImportedFromModule(callExpression, localName, "jotai")) return false;
9697
9648
  return getImportedNameFromModule(callExpression, localName, "jotai") === "atom";
9698
9649
  };
9699
- const isFunctionExpressionLike$2 = (node) => Boolean(node && (isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression")));
9650
+ const isFunctionExpressionLike$1 = (node) => Boolean(node && (isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression")));
9700
9651
  const getFirstParameterName = (fn) => {
9701
9652
  const parameters = fn.params ?? [];
9702
9653
  if (parameters.length !== 1) return null;
@@ -9829,7 +9780,7 @@ const jotaiDerivedAtomReturnsFreshObject = defineRule({
9829
9780
  const args = node.arguments ?? [];
9830
9781
  if (args.length === 0) return;
9831
9782
  const reader = args[0];
9832
- if (!isFunctionExpressionLike$2(reader)) return;
9783
+ if (!isFunctionExpressionLike$1(reader)) return;
9833
9784
  const getParameterName = getFirstParameterName(reader);
9834
9785
  if (!getParameterName) return;
9835
9786
  const freshReturn = getFreshReturnForFunction(reader);
@@ -9927,14 +9878,14 @@ const getHandlerNamedBindingName = (functionNode) => {
9927
9878
  const containingFunctionIsComponentOrHook = (functionNode) => {
9928
9879
  if (isNodeOfType(functionNode, "FunctionDeclaration") && functionNode.id) {
9929
9880
  const declaredName = functionNode.id.name;
9930
- return COMPONENT_NAME_PATTERN.test(declaredName) || HOOK_NAME_PATTERN$1.test(declaredName);
9881
+ return COMPONENT_NAME_PATTERN.test(declaredName) || HOOK_NAME_PATTERN.test(declaredName);
9931
9882
  }
9932
9883
  let cursor = functionNode.parent ?? null;
9933
9884
  while (cursor && isNodeOfType(cursor, "CallExpression")) cursor = cursor.parent ?? null;
9934
9885
  if (!cursor) return false;
9935
9886
  if (!isNodeOfType(cursor, "VariableDeclarator")) return false;
9936
9887
  if (!isNodeOfType(cursor.id, "Identifier")) return false;
9937
- return COMPONENT_NAME_PATTERN.test(cursor.id.name) || HOOK_NAME_PATTERN$1.test(cursor.id.name);
9888
+ return COMPONENT_NAME_PATTERN.test(cursor.id.name) || HOOK_NAME_PATTERN.test(cursor.id.name);
9938
9889
  };
9939
9890
  const isBindingInvokedOnRenderPath = (root, bindingName) => {
9940
9891
  let didFindRenderPathInvocation = false;
@@ -15648,7 +15599,6 @@ const nextjsMissingMetadata = defineRule({
15648
15599
  const filename = normalizeFilename(context.filename ?? "");
15649
15600
  if (!PAGE_FILE_PATTERN.test(filename)) return;
15650
15601
  if (INTERNAL_PAGE_PATH_PATTERN.test(filename)) return;
15651
- if ((programNode.body ?? []).some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === "use client")) return;
15652
15602
  if (programNode.body?.some((statement) => {
15653
15603
  if (!isNodeOfType(statement, "ExportNamedDeclaration")) return false;
15654
15604
  const declaration = statement.declaration;
@@ -17378,38 +17328,14 @@ const resolvesToRefFactoryCall = (identifier) => {
17378
17328
  });
17379
17329
  return found;
17380
17330
  };
17381
- const unwrapExpression$2 = (node) => {
17382
- if (!node) return null;
17383
- if (isNodeOfType(node, "ChainExpression")) return unwrapExpression$2(node.expression);
17384
- if (isNodeOfType(node, "TSNonNullExpression")) return unwrapExpression$2(node.expression);
17385
- return node;
17386
- };
17387
- const resolvesToRefCurrentAlias = (identifier, visitedAliasNames) => {
17388
- if (visitedAliasNames.has(identifier.name)) return false;
17389
- const root = findProgramRoot(identifier);
17390
- if (!root) return false;
17391
- const nextVisited = new Set([...visitedAliasNames, identifier.name]);
17392
- let found = false;
17393
- walkAst(root, (child) => {
17394
- if (found) return false;
17395
- if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.id.name === identifier.name) {
17396
- const init = unwrapExpression$2(child.init);
17397
- if (init && isNodeOfType(init, "MemberExpression") && isNodeOfType(init.property, "Identifier") && init.property.name === "current" && isRefLikeReceiver(init.object, nextVisited)) {
17398
- found = true;
17399
- return false;
17400
- }
17401
- }
17402
- });
17403
- return found;
17404
- };
17405
- const isRefLikeReceiver = (receiver, visitedAliasNames = /* @__PURE__ */ new Set()) => {
17331
+ const isRefLikeReceiver = (receiver) => {
17406
17332
  if (!receiver) return false;
17407
- if (isNodeOfType(receiver, "ChainExpression")) return isRefLikeReceiver(receiver.expression, visitedAliasNames);
17408
- if (isNodeOfType(receiver, "TSNonNullExpression")) return isRefLikeReceiver(receiver.expression, visitedAliasNames);
17409
- if (isNodeOfType(receiver, "Identifier")) return hasRefLikeName(receiver.name) || resolvesToRefFactoryCall(receiver) || resolvesToRefCurrentAlias(receiver, visitedAliasNames);
17333
+ if (isNodeOfType(receiver, "ChainExpression")) return isRefLikeReceiver(receiver.expression);
17334
+ if (isNodeOfType(receiver, "TSNonNullExpression")) return isRefLikeReceiver(receiver.expression);
17335
+ if (isNodeOfType(receiver, "Identifier")) return hasRefLikeName(receiver.name) || resolvesToRefFactoryCall(receiver);
17410
17336
  if (isNodeOfType(receiver, "MemberExpression") && isNodeOfType(receiver.property, "Identifier")) {
17411
17337
  if (hasRefLikeName(receiver.property.name)) return true;
17412
- if (receiver.property.name === "current") return isRefLikeReceiver(receiver.object, visitedAliasNames);
17338
+ if (receiver.property.name === "current") return isRefLikeReceiver(receiver.object);
17413
17339
  }
17414
17340
  return false;
17415
17341
  };
@@ -17544,19 +17470,6 @@ const getScopeForNode = (node, manager) => {
17544
17470
  //#endregion
17545
17471
  //#region src/plugin/rules/state-and-effects/utils/effect/ast.ts
17546
17472
  const getChildKeys = (node) => VISITOR_KEYS[node.type] ?? Object.keys(node).filter((key) => key !== "parent");
17547
- const HOOK_NAME_PATTERN = /^use[A-Z0-9]/;
17548
- const isInsideCallbackArgumentOf = (identifier, initializer) => {
17549
- if (!isNodeOfType(initializer, "CallExpression") && !isNodeOfType(initializer, "NewExpression")) return false;
17550
- if (isNodeOfType(initializer, "CallExpression") && isNodeOfType(initializer.callee, "Identifier") && HOOK_NAME_PATTERN.test(initializer.callee.name)) return false;
17551
- const callbackArguments = (initializer.arguments ?? []).filter((argument) => isFunctionLike$1(argument));
17552
- if (callbackArguments.length === 0) return false;
17553
- let node = identifier;
17554
- while (node && node !== initializer) {
17555
- if (callbackArguments.includes(node)) return true;
17556
- node = node.parent;
17557
- }
17558
- return false;
17559
- };
17560
17473
  const ascend = (analysis, ref, visit, visited = /* @__PURE__ */ new Set()) => {
17561
17474
  if (visited.has(ref)) return;
17562
17475
  const result = visit(ref);
@@ -17569,10 +17482,7 @@ const ascend = (analysis, ref, visit, visited = /* @__PURE__ */ new Set()) => {
17569
17482
  const defNode = def.node;
17570
17483
  const next = defNode.init ?? defNode.body;
17571
17484
  if (!next) continue;
17572
- for (const innerRef of getDownstreamRefs(analysis, next)) {
17573
- if (isInsideCallbackArgumentOf(innerRef.identifier, next)) continue;
17574
- ascend(analysis, innerRef, visit, visited);
17575
- }
17485
+ for (const innerRef of getDownstreamRefs(analysis, next)) ascend(analysis, innerRef, visit, visited);
17576
17486
  }
17577
17487
  };
17578
17488
  const descend = (node, visit, visited = /* @__PURE__ */ new Set()) => {
@@ -18021,8 +17931,7 @@ const noAriaHiddenOnFocusable = defineRule({
18021
17931
  if (isNodeOfType(value, "Literal") && value.value !== "true") return;
18022
17932
  if (isNodeOfType(value, "JSXExpressionContainer")) {
18023
17933
  const expression = value.expression;
18024
- if (!isNodeOfType(expression, "Literal")) return;
18025
- if (expression.value !== true && expression.value !== "true") return;
17934
+ if (isNodeOfType(expression, "Literal") && !expression.value) return;
18026
17935
  }
18027
17936
  }
18028
17937
  const tag = getElementType(node, context.settings);
@@ -18277,30 +18186,10 @@ const isArrayFromCall = (node) => {
18277
18186
  *
18278
18187
  * Used both for `<receiver>.map(...)` and for `Array.from(<length>, fn)`.
18279
18188
  */
18280
- const isBindingReassigned = (referenceNode, bindingName) => {
18281
- const programRoot = findProgramRoot(referenceNode);
18282
- if (!programRoot) return false;
18283
- let didFindReassignment = false;
18284
- walkAst(programRoot, (child) => {
18285
- if (didFindReassignment) return false;
18286
- if (isNodeOfType(child, "AssignmentExpression") && isNodeOfType(child.left, "Identifier") && child.left.name === bindingName) {
18287
- didFindReassignment = true;
18288
- return false;
18289
- }
18290
- });
18291
- return didFindReassignment;
18292
- };
18293
- const isStaticPlaceholderReceiver = (receiver, depth = 0) => {
18189
+ const isStaticPlaceholderReceiver = (receiver) => {
18294
18190
  if (isArrayFromCall(receiver)) return true;
18295
18191
  if (isArrayConstructorCallWithNumericLength(receiver)) return true;
18296
18192
  if (isAllLiteralArrayExpression(receiver)) return true;
18297
- if (isNodeOfType(receiver, "Identifier")) {
18298
- if (depth >= TYPE_RESOLUTION_DEPTH_LIMIT) return false;
18299
- const binding = findVariableInitializer(receiver, receiver.name);
18300
- if (!binding?.initializer) return false;
18301
- if (isBindingReassigned(receiver, receiver.name)) return false;
18302
- return isStaticPlaceholderReceiver(binding.initializer, depth + 1);
18303
- }
18304
18193
  if (isNodeOfType(receiver, "CallExpression")) {
18305
18194
  const callee = receiver.callee;
18306
18195
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") && callee.property.name === "fill" && isArrayConstructorCallWithNumericLength(callee.object)) return true;
@@ -18326,7 +18215,6 @@ const isArrayFromLengthObjectCall = (node) => {
18326
18215
  if (!(isNodeOfType(key, "Identifier") && key.name === "length" || isNodeOfType(key, "Literal") && key.value === "length")) continue;
18327
18216
  if (isNumericLiteralOrUndefined(prop.value)) return true;
18328
18217
  if (isNodeOfType(prop.value, "Identifier")) return true;
18329
- if (isNodeOfType(prop.value, "MemberExpression") && isNodeOfType(prop.value.property, "Identifier") && prop.value.property.name === "length") return true;
18330
18218
  }
18331
18219
  return false;
18332
18220
  };
@@ -18435,11 +18323,10 @@ const isInsideStaticPlaceholderMap = (node) => isInsideIteratorMapMatching(node,
18435
18323
  /**
18436
18324
  * Walk up from a JSXAttribute node looking for the enclosing iterator
18437
18325
  * callback (`.map(cb)`, `.flatMap(cb)`, `.forEach(cb)`, `Array.from(_, cb)`)
18438
- * and return the names bound by the first parameter `item` in
18439
- * `arr.map((item, index) => …)`, or every destructured field in
18440
- * `arr.map(({ id, label }, index) => …)`.
18326
+ * and return the first parameter's name. The first param is the per-item
18327
+ * value, e.g. `item` in `arr.map((item, index) => …)`.
18441
18328
  */
18442
- const findIteratorItemNames = (node) => {
18329
+ const findIteratorItemName$1 = (node) => {
18443
18330
  let current = node;
18444
18331
  while (current.parent) {
18445
18332
  const parent = current.parent;
@@ -18449,20 +18336,18 @@ const findIteratorItemNames = (node) => {
18449
18336
  const isArrayFromCallback = isArrayFromCall(parent) && parent.arguments.length >= 2 && parent.arguments[1] === current;
18450
18337
  if (isIteratorMethodCall || isArrayFromCallback) {
18451
18338
  const first = (current.params ?? [])[0];
18452
- if (!first) return null;
18453
- const names = /* @__PURE__ */ new Set();
18454
- collectPatternNames(first, names);
18455
- return names.size > 0 ? names : null;
18339
+ if (first && isNodeOfType(first, "Identifier")) return first.name;
18340
+ return null;
18456
18341
  }
18457
18342
  }
18458
18343
  current = parent;
18459
18344
  }
18460
18345
  return null;
18461
18346
  };
18462
- const templateLiteralHasIteratorIdentity = (template, itemNames) => {
18347
+ const templateLiteralHasIteratorIdentity = (template, itemName) => {
18463
18348
  for (const expression of template.expressions ?? []) {
18464
- const rootName = getRootIdentifierName(expression, { followCallChains: true });
18465
- if (rootName !== null && itemNames.has(rootName)) return true;
18349
+ if (isNodeOfType(expression, "Identifier") && expression.name === itemName) return true;
18350
+ if (isNodeOfType(expression, "MemberExpression") && isNodeOfType(expression.object, "Identifier") && expression.object.name === itemName) return true;
18466
18351
  }
18467
18352
  return false;
18468
18353
  };
@@ -18475,32 +18360,9 @@ const templateLiteralHasIteratorIdentity = (template, itemNames) => {
18475
18360
  const isCompositeKeyWithIteratorIdentity = (keyExpression, attributeNode) => {
18476
18361
  if (!isNodeOfType(keyExpression, "TemplateLiteral")) return false;
18477
18362
  if ((keyExpression.expressions ?? []).length < 2) return false;
18478
- const itemNames = findIteratorItemNames(attributeNode);
18479
- if (!itemNames) return false;
18480
- return templateLiteralHasIteratorIdentity(keyExpression, itemNames);
18481
- };
18482
- const forLoopTestReadsDataLength = (test) => {
18483
- let didFindLengthRead = false;
18484
- walkAst(test, (child) => {
18485
- if (didFindLengthRead) return false;
18486
- if (isNodeOfType(child, "MemberExpression") && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
18487
- didFindLengthRead = true;
18488
- return false;
18489
- }
18490
- });
18491
- return didFindLengthRead;
18492
- };
18493
- const isNumericForLoopCounter = (attributeNode, indexName) => {
18494
- const binding = findVariableInitializer(attributeNode, indexName);
18495
- if (!binding) return false;
18496
- const declarator = binding.bindingIdentifier.parent;
18497
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return false;
18498
- const declaration = declarator.parent;
18499
- if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return false;
18500
- const forStatement = declaration.parent;
18501
- if (!forStatement || !isNodeOfType(forStatement, "ForStatement") || forStatement.init !== declaration || !declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
18502
- if (forStatement.test && forLoopTestReadsDataLength(forStatement.test)) return false;
18503
- return true;
18363
+ const itemName = findIteratorItemName$1(attributeNode);
18364
+ if (!itemName) return false;
18365
+ return templateLiteralHasIteratorIdentity(keyExpression, itemName);
18504
18366
  };
18505
18367
  const noArrayIndexAsKey = defineRule({
18506
18368
  id: "no-array-index-as-key",
@@ -18512,7 +18374,6 @@ const noArrayIndexAsKey = defineRule({
18512
18374
  if (!node.value || !isNodeOfType(node.value, "JSXExpressionContainer")) return;
18513
18375
  const indexName = extractIndexName(node.value.expression);
18514
18376
  if (!indexName) return;
18515
- if (isNumericForLoopCounter(node, indexName)) return;
18516
18377
  if (isInsideStaticPlaceholderMap(node)) return;
18517
18378
  if (isInsideStringDerivedMap(node)) return;
18518
18379
  if (isCompositeKeyWithIteratorIdentity(node.value.expression, node)) return;
@@ -20878,7 +20739,7 @@ const isStateMemberExpression = (node) => {
20878
20739
  };
20879
20740
  //#endregion
20880
20741
  //#region src/plugin/rules/react-builtins/no-direct-mutation-state.ts
20881
- const MESSAGE$24 = "Mutating `this.state` by hand never triggers a redraw on its own & a later setState can overwrite it, so use `this.setState` instead.";
20742
+ const MESSAGE$24 = "Your users see stale data because mutating `this.state` by hand never redraws & gets overwritten.";
20882
20743
  const shouldIgnoreMutation = (node) => {
20883
20744
  let isConstructor = false;
20884
20745
  let isInsideCallExpression = false;
@@ -20972,18 +20833,6 @@ const initializerMarksPlainState = (initializerArgument) => {
20972
20833
  }
20973
20834
  return producesPlainStateValue(unwrapped);
20974
20835
  };
20975
- const collectCallbackRefSetterNames = (componentBody) => {
20976
- const callbackRefSetterNames = /* @__PURE__ */ new Set();
20977
- walkAst(componentBody, (node) => {
20978
- if (!isNodeOfType(node, "JSXAttribute")) return;
20979
- const attributeName = node.name;
20980
- if (isNodeOfType(attributeName, "JSXIdentifier") && attributeName.name === "ref" && node.value && isNodeOfType(node.value, "JSXExpressionContainer")) {
20981
- const expression = stripParenExpression(node.value.expression);
20982
- if (isNodeOfType(expression, "Identifier")) callbackRefSetterNames.add(expression.name);
20983
- }
20984
- });
20985
- return callbackRefSetterNames;
20986
- };
20987
20836
  const collectFunctionLocalBindings = (functionNode) => {
20988
20837
  const localBindings = /* @__PURE__ */ new Set();
20989
20838
  if (!isNodeOfType(functionNode, "FunctionDeclaration") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "ArrowFunctionExpression")) return localBindings;
@@ -21026,10 +20875,8 @@ const noDirectStateMutation = defineRule({
21026
20875
  const bindings = collectUseStateBindings(componentBody);
21027
20876
  if (bindings.length === 0) return;
21028
20877
  const stateValueToSetter = new Map(bindings.map((binding) => [binding.valueName, binding.setterName]));
21029
- const callbackRefSetterNames = collectCallbackRefSetterNames(componentBody);
21030
20878
  const plainObjectStateValueNames = /* @__PURE__ */ new Set();
21031
20879
  for (const binding of bindings) {
21032
- if (callbackRefSetterNames.has(binding.setterName)) continue;
21033
20880
  if (!isNodeOfType(binding.declarator.init, "CallExpression")) continue;
21034
20881
  if (initializerMarksPlainState(binding.declarator.init.arguments?.[0])) plainObjectStateValueNames.add(binding.valueName);
21035
20882
  }
@@ -21042,7 +20889,7 @@ const noDirectStateMutation = defineRule({
21042
20889
  if (currentlyShadowed.has(rootName)) return;
21043
20890
  context.report({
21044
20891
  node: child,
21045
- message: `React can't tell you changed "${rootName}" in place, so this update can be skipped or lost.`
20892
+ message: `Your screen won't update because you change "${rootName}" in place.`
21046
20893
  });
21047
20894
  return;
21048
20895
  }
@@ -21058,7 +20905,7 @@ const noDirectStateMutation = defineRule({
21058
20905
  if (currentlyShadowed.has(rootName)) return;
21059
20906
  context.report({
21060
20907
  node: child,
21061
- message: `React can't tell .${methodName}() changed "${rootName}" in place, so this update can be skipped or lost.`
20908
+ message: `Your screen won't update because .${methodName}() changes "${rootName}" in place.`
21062
20909
  });
21063
20910
  }
21064
20911
  });
@@ -22927,16 +22774,11 @@ const NON_DETERMINISTIC_ID_GENERATOR_NAMES = new Set([
22927
22774
  "ulid",
22928
22775
  "createId"
22929
22776
  ]);
22930
- const isZeroArgDateConstruction = (node) => isNodeOfType(node, "NewExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Date" && (node.arguments?.length ?? 0) === 0;
22931
22777
  const containsNonDeterministicSource = (root) => {
22932
22778
  let found = false;
22933
22779
  walkAst(root, (child) => {
22934
22780
  if (found) return false;
22935
22781
  if (isFunctionLike$1(child)) return false;
22936
- if (isZeroArgDateConstruction(child)) {
22937
- found = true;
22938
- return false;
22939
- }
22940
22782
  if (!isNodeOfType(child, "CallExpression")) return;
22941
22783
  const callee = child.callee;
22942
22784
  if (isNodeOfType(callee, "Identifier") && NON_DETERMINISTIC_ID_GENERATOR_NAMES.has(callee.name)) {
@@ -22997,53 +22839,6 @@ const argumentReadsPostMountMeasurement = (argument, effectFn, visitedLocalNames
22997
22839
  });
22998
22840
  return found;
22999
22841
  };
23000
- const isResourceLikeInitializer = (initializer) => {
23001
- if (isNodeOfType(initializer, "AwaitExpression")) return isResourceLikeInitializer(initializer.argument);
23002
- return isNodeOfType(initializer, "NewExpression") || isNodeOfType(initializer, "CallExpression");
23003
- };
23004
- const collectArgumentSourceLocalNames = (argument, effectFn, sourceLocalNames = /* @__PURE__ */ new Set()) => {
23005
- walkAst(argument, (child) => {
23006
- if (!isNodeOfType(child, "Identifier")) return;
23007
- if (sourceLocalNames.has(child.name)) return;
23008
- const localInitializer = findEffectLocalInitializer(effectFn, child.name);
23009
- if (!localInitializer || !isResourceLikeInitializer(localInitializer)) return;
23010
- sourceLocalNames.add(child.name);
23011
- collectArgumentSourceLocalNames(localInitializer, effectFn, sourceLocalNames);
23012
- });
23013
- return sourceLocalNames;
23014
- };
23015
- const isFunctionExpressionLike$1 = (node) => isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression");
23016
- const findCleanupFunction = (effectFn) => {
23017
- if (!isNodeOfType(effectFn, "ArrowFunctionExpression") && !isNodeOfType(effectFn, "FunctionExpression")) return null;
23018
- const body = effectFn.body;
23019
- if (!isNodeOfType(body, "BlockStatement")) return null;
23020
- let cleanupFunction = null;
23021
- walkAst(body, (child) => {
23022
- if (cleanupFunction) return false;
23023
- if (isNodeOfType(child, "ReturnStatement")) {
23024
- if (child.argument && isFunctionExpressionLike$1(child.argument)) cleanupFunction = child.argument;
23025
- return false;
23026
- }
23027
- if (child !== body && isFunctionExpressionLike$1(child)) return false;
23028
- if (isNodeOfType(child, "FunctionDeclaration")) return false;
23029
- });
23030
- return cleanupFunction;
23031
- };
23032
- const cleanupDisposesArgumentSource = (argument, effectFn) => {
23033
- const cleanupFunction = findCleanupFunction(effectFn);
23034
- if (!cleanupFunction) return false;
23035
- const sourceLocalNames = collectArgumentSourceLocalNames(argument, effectFn);
23036
- if (sourceLocalNames.size === 0) return false;
23037
- let referencesSource = false;
23038
- walkAst(cleanupFunction, (child) => {
23039
- if (referencesSource) return false;
23040
- if (isNodeOfType(child, "Identifier") && sourceLocalNames.has(child.name)) {
23041
- referencesSource = true;
23042
- return false;
23043
- }
23044
- });
23045
- return referencesSource;
23046
- };
23047
22842
  const noInitializeState = defineRule({
23048
22843
  id: "no-initialize-state",
23049
22844
  title: "State initialized from a mount effect",
@@ -23066,7 +22861,6 @@ const noInitializeState = defineRule({
23066
22861
  if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
23067
22862
  if (callExpr.arguments?.some((argument) => Boolean(argument) && containsNonDeterministicSource(argument))) continue;
23068
22863
  if (callExpr.arguments?.some((argument) => Boolean(argument) && argumentReadsPostMountMeasurement(argument, effectFn))) continue;
23069
- if (callExpr.arguments?.some((argument) => Boolean(argument) && cleanupDisposesArgumentSource(argument, effectFn))) continue;
23070
22864
  const useStateDecl = getUseStateDecl(analysis, ref);
23071
22865
  if (!useStateDecl || !isNodeOfType(useStateDecl, "VariableDeclarator")) continue;
23072
22866
  if (!isNodeOfType(useStateDecl.id, "ArrayPattern")) continue;
@@ -24984,18 +24778,6 @@ const KEYBOARD_HANDLER_PROP_NAMES = [
24984
24778
  "onKeyPress"
24985
24779
  ];
24986
24780
  const isKeyboardOperable = (node) => KEYBOARD_HANDLER_PROP_NAMES.some((propName) => Boolean(hasJsxPropIgnoreCase(node.attributes, propName)));
24987
- const parseNumericBranch = (expression) => {
24988
- if (isNodeOfType(expression, "Literal") && typeof expression.value === "number") return expression.value;
24989
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "-" && isNodeOfType(expression.argument, "Literal") && typeof expression.argument.value === "number") return -expression.argument.value;
24990
- return null;
24991
- };
24992
- const isRovingTabindexValue = (value) => {
24993
- if (!isNodeOfType(value, "JSXExpressionContainer")) return false;
24994
- const expression = value.expression;
24995
- if (!isNodeOfType(expression, "ConditionalExpression")) return false;
24996
- if (isNodeOfType(expression.test, "Literal")) return false;
24997
- return [parseNumericBranch(expression.consequent), parseNumericBranch(expression.alternate)].some((branchValue) => branchValue !== null && branchValue < 0);
24998
- };
24999
24781
  const resolveSettings$14 = (settings) => {
25000
24782
  const reactDoctor = settings?.["react-doctor"];
25001
24783
  const ruleSettings = typeof reactDoctor === "object" && reactDoctor !== null ? reactDoctor.noNoninteractiveTabindex ?? {} : {};
@@ -25019,7 +24801,6 @@ const noNoninteractiveTabindex = defineRule({
25019
24801
  if (!tabIndex) return;
25020
24802
  const tabIndexValue = tabIndex.value;
25021
24803
  if (!tabIndexValue) return;
25022
- if (isRovingTabindexValue(tabIndexValue)) return;
25023
24804
  const numeric = parseJsxValue(tabIndexValue);
25024
24805
  if (numeric === null) {
25025
24806
  if (isNodeOfType(tabIndexValue, "JSXExpressionContainer") && !settings.allowExpressionValues && !isKeyboardOperable(node)) context.report({
@@ -26456,7 +26237,7 @@ const noRenderInRender = defineRule({
26456
26237
  title: "Component rendered by inline function call",
26457
26238
  severity: "warn",
26458
26239
  tags: ["test-noise"],
26459
- recommendation: "Make it a named component rendered as JSX so React can track it and preserve its state.",
26240
+ recommendation: "Make it a named component so React preserves its identity and does not remount its state.",
26460
26241
  create: (context) => ({ JSXExpressionContainer(node) {
26461
26242
  const expression = node.expression;
26462
26243
  if (!isNodeOfType(expression, "CallExpression")) return;
@@ -26471,7 +26252,7 @@ const noRenderInRender = defineRule({
26471
26252
  }
26472
26253
  context.report({
26473
26254
  node: expression,
26474
- message: `"${calleeName}()" hides a component behind an inline call, so pull it into its own component and render it as JSX so React can track it.`
26255
+ message: `Your users lose state because "${calleeName}()" builds UI from an inline call that React remounts, so pull it into its own component instead.`
26475
26256
  });
26476
26257
  } })
26477
26258
  });
@@ -26574,8 +26355,8 @@ const countUseStates = (analysis, componentNode) => {
26574
26355
  for (const ref of getDownstreamRefs(analysis, componentNode)) if (isState(analysis, ref)) stateVariables.add(ref.resolved);
26575
26356
  return stateVariables.size;
26576
26357
  };
26577
- const findPropUsedToResetAllState = (analysis, effectFnRefs, depsRefs, useEffectNode, effectFn) => {
26578
- const stateSetterRefs = effectFnRefs.filter((ref) => isSyncStateSetterCall(analysis, ref, effectFn));
26358
+ const findPropUsedToResetAllState = (analysis, effectFnRefs, depsRefs, useEffectNode) => {
26359
+ const stateSetterRefs = effectFnRefs.filter((ref) => isStateSetterCall(analysis, ref));
26579
26360
  if (stateSetterRefs.length === 0) return null;
26580
26361
  if (!stateSetterRefs.every((ref) => isSetStateToInitialValue(analysis, ref))) return null;
26581
26362
  const containing = findContainingNode(analysis, useEffectNode);
@@ -26598,9 +26379,7 @@ const noResetAllStateOnPropChange = defineRule({
26598
26379
  if (!effectFnRefs || !depsRefs) return;
26599
26380
  const containing = findContainingNode(analysis, node);
26600
26381
  if (containing && isCustomHook(containing)) return;
26601
- const effectFn = getEffectFn(analysis, node);
26602
- if (!effectFn) return;
26603
- if (!findPropUsedToResetAllState(analysis, effectFnRefs, depsRefs, node, effectFn)) return;
26382
+ if (!findPropUsedToResetAllState(analysis, effectFnRefs, depsRefs, node)) return;
26604
26383
  context.report({
26605
26384
  node,
26606
26385
  message: `Your users briefly see stale state when a prop changes because this useEffect clears all state.`
@@ -27468,7 +27247,6 @@ const noStaticElementInteractions = defineRule({
27468
27247
  if (!hasNonBlockerHandler) return;
27469
27248
  const elementType = getElementType(node, context.settings);
27470
27249
  if (!HTML_TAGS.has(elementType)) return;
27471
- if (elementType === "svg") return;
27472
27250
  if (isHiddenFromScreenReader(node, context.settings)) return;
27473
27251
  if (isPresentationRole(node)) return;
27474
27252
  if (isInteractiveElement(elementType, node)) return;
@@ -27482,8 +27260,7 @@ const noStaticElementInteractions = defineRule({
27482
27260
  });
27483
27261
  return;
27484
27262
  }
27485
- let attributeValue = roleAttribute.value;
27486
- if (isNodeOfType(attributeValue, "JSXExpressionContainer") && isNodeOfType(attributeValue.expression, "Literal") && typeof attributeValue.expression.value === "string") attributeValue = attributeValue.expression;
27263
+ const attributeValue = roleAttribute.value;
27487
27264
  if (isNodeOfType(attributeValue, "Literal") && typeof attributeValue.value === "string") {
27488
27265
  const firstRole = attributeValue.value.toLowerCase().trim().split(/\s+/)[0];
27489
27266
  if (firstRole && (isInteractiveRole(firstRole) || isNonInteractiveRole(firstRole))) return;
@@ -30282,7 +30059,7 @@ const containsVnodeFactoryCall = (root) => {
30282
30059
  };
30283
30060
  const isComponentLikeFunction = (functionNode) => {
30284
30061
  const bindingName = getFunctionBindingName(functionNode);
30285
- if (bindingName && (isReactComponentName(bindingName) || HOOK_NAME_PATTERN$1.test(bindingName))) return true;
30062
+ if (bindingName && (isReactComponentName(bindingName) || HOOK_NAME_PATTERN.test(bindingName))) return true;
30286
30063
  const body = "body" in functionNode ? functionNode.body : null;
30287
30064
  if (!body || !isAstNode(body)) return false;
30288
30065
  return containsJsxElement(body) || containsVnodeFactoryCall(body);
@@ -31739,7 +31516,7 @@ const queryMutationMissingInvalidation = defineRule({
31739
31516
  });
31740
31517
  if (!hasCacheUpdate) context.report({
31741
31518
  node,
31742
- message: "useMutation with no cache update here can leave your users looking at stale data after it runs."
31519
+ message: "useMutation with no cache update leaves your users looking at stale data after it runs."
31743
31520
  });
31744
31521
  } })
31745
31522
  });
@@ -32388,7 +32165,6 @@ const renderingHydrationMismatchTime = defineRule({
32388
32165
  return { JSXExpressionContainer(node) {
32389
32166
  if (isTestlikeFile) return;
32390
32167
  if (!node.expression) return;
32391
- if (isGeneratedImageRenderContext(context, findOpeningElementOfChild(node) ?? node)) return;
32392
32168
  const matched = NONDETERMINISTIC_RENDER_PATTERNS.find((pattern) => pattern.matches(node.expression));
32393
32169
  if (matched) {
32394
32170
  if (hasSuppressHydrationWarningAttribute(findOpeningElementOfChild(node))) return;
@@ -34204,7 +33980,7 @@ const rnNoDimensionsGet = defineRule({
34204
33980
  if (binding !== null && !isBindingReactNativeDimensions(node, binding)) return;
34205
33981
  if (isMemberProperty(node.callee, "get")) context.report({
34206
33982
  node,
34207
- message: "Dimensions.get() reads the size once and never updates, so layouts built from it go stale on rotation or resize."
33983
+ message: "Your users see a stale layout on rotation or resize because Dimensions.get() does not update."
34208
33984
  });
34209
33985
  if (isMemberProperty(node.callee, "addEventListener")) context.report({
34210
33986
  node,
@@ -35026,10 +34802,7 @@ const isExpoUiComponentElement = (openingElement, contextNode, componentName) =>
35026
34802
  };
35027
34803
  //#endregion
35028
34804
  //#region src/plugin/rules/react-native/rn-no-raw-text.ts
35029
- const truncateText = (text) => {
35030
- const collapsedText = text.replace(/\s+/g, " ");
35031
- return collapsedText.length > 30 ? `${collapsedText.slice(0, 30)}...` : collapsedText;
35032
- };
34805
+ const truncateText = (text) => text.length > 30 ? `${text.slice(0, 30)}...` : text;
35033
34806
  const isRawTextContent = (child) => {
35034
34807
  if (isNodeOfType(child, "JSXText")) return Boolean(child.value?.trim());
35035
34808
  if (!isNodeOfType(child, "JSXExpressionContainer") || !child.expression) return false;
@@ -39298,7 +39071,7 @@ const isInsideClassComponent = (node) => {
39298
39071
  const hasShortCircuitAncestor = (descendant, ancestor) => {
39299
39072
  let current = descendant.parent;
39300
39073
  while (current && current !== ancestor) {
39301
- if (isNodeOfType(current, "ConditionalExpression") && (isWithinRange(descendant, current.consequent) || isWithinRange(descendant, current.alternate))) return true;
39074
+ if (isNodeOfType(current, "ConditionalExpression")) return true;
39302
39075
  if (isNodeOfType(current, "LogicalExpression") && (current.operator === "&&" || current.operator === "||" || current.operator === "??") && isWithinRange(descendant, current.right)) return true;
39303
39076
  current = current.parent ?? null;
39304
39077
  }
@@ -39424,7 +39197,6 @@ const rulesOfHooks = defineRule({
39424
39197
  if (parentInfo.isComponentOrHook) isInsideComponentOrHook = true;
39425
39198
  }
39426
39199
  if (!isInsideComponentOrHook) {
39427
- if (!enclosing.hasResolvedName) return;
39428
39200
  if (isLocalNonHookFunctionCallee(node, context.scopes, settings)) return;
39429
39201
  context.report({
39430
39202
  node: node.callee,
@@ -39999,14 +39771,6 @@ const objectExpressionHasCachingConfig = (objectExpression) => (objectExpression
39999
39771
  const objectExpressionHasSpread = (objectExpression) => (objectExpression.properties ?? []).some((property) => isNodeOfType(property, "SpreadElement"));
40000
39772
  const APP_ROUTER_FILE_PATTERN = new RegExp(`/app/(?:[^/]+/)*(?:route|page|layout|template|loading|error|default)\\.${NEXTJS_SOURCE_FILE_EXTENSION_GROUP}$`);
40001
39773
  const NON_PROJECT_PATH_PATTERN = /\/(?:node_modules|dist|build|\.next)\//;
40002
- const REMIX_IMPORT_SOURCE_PATTERN = /^(?:@remix-run\/|@react-router\/|react-router(?:-dom)?$)/;
40003
- const programImportsRemixRouter = (programNode) => (programNode.body ?? []).some((statement) => isNodeOfType(statement, "ImportDeclaration") && !isTypeOnlyImport(statement) && typeof statement.source?.value === "string" && REMIX_IMPORT_SOURCE_PATTERN.test(statement.source.value));
40004
- const isImportMetaUrlAssetArgument = (urlArg) => {
40005
- if (!isNodeOfType(urlArg, "NewExpression")) return false;
40006
- if (!isNodeOfType(urlArg.callee, "Identifier") || urlArg.callee.name !== "URL") return false;
40007
- const baseArg = urlArg.arguments?.[1];
40008
- return isNodeOfType(baseArg, "MemberExpression") && isNodeOfType(baseArg.object, "MetaProperty") && isNodeOfType(baseArg.property, "Identifier") && baseArg.property.name === "url";
40009
- };
40010
39774
  const serverFetchWithoutRevalidate = defineRule({
40011
39775
  id: "server-fetch-without-revalidate",
40012
39776
  title: "Fetch without revalidate",
@@ -40026,7 +39790,7 @@ const serverFetchWithoutRevalidate = defineRule({
40026
39790
  isServerSideFile = false;
40027
39791
  return;
40028
39792
  }
40029
- isServerSideFile = !(node.body ?? []).some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === "use client") && !programImportsRemixRouter(node);
39793
+ isServerSideFile = !(node.body ?? []).some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === "use client");
40030
39794
  },
40031
39795
  CallExpression(node) {
40032
39796
  if (!isServerSideFile) return;
@@ -40039,7 +39803,6 @@ const serverFetchWithoutRevalidate = defineRule({
40039
39803
  if (objectExpressionHasSpread(optionsArg)) return;
40040
39804
  }
40041
39805
  const urlArg = node.arguments?.[0];
40042
- if (isImportMetaUrlAssetArgument(urlArg)) return;
40043
39806
  const urlText = isNodeOfType(urlArg, "Literal") && typeof urlArg.value === "string" ? `"${urlArg.value}"` : "url";
40044
39807
  context.report({
40045
39808
  node,
@@ -40268,7 +40031,7 @@ const serverNoMutableModuleState = defineRule({
40268
40031
  if (node.kind === "let" || node.kind === "var") {
40269
40032
  context.report({
40270
40033
  node: declarator,
40271
- message: `Module-scoped ${node.kind} "${variableName}" is shared by every request, so any write to it leaks state between your users.`
40034
+ message: `Module-scoped ${node.kind} "${variableName}" leaks state between your users, since every request shares it.`
40272
40035
  });
40273
40036
  continue;
40274
40037
  }
@@ -40329,29 +40092,25 @@ const GATE_LEADING_VERBS = new Set([
40329
40092
  "authorize",
40330
40093
  "authenticate"
40331
40094
  ]);
40332
- const declarationAwaitsExistingPromise = (declaration) => {
40333
- if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
40334
- for (const declarator of declaration.declarations ?? []) {
40335
- const init = declarator.init;
40336
- if (!isNodeOfType(init, "AwaitExpression")) continue;
40337
- const argument = init.argument;
40338
- if (isNodeOfType(argument, "Identifier") || isNodeOfType(argument, "MemberExpression")) return true;
40095
+ const isStartedPromiseBinding = (name, statements, beforeIndex) => {
40096
+ for (let index = 0; index < beforeIndex; index++) {
40097
+ const statement = statements[index];
40098
+ if (!isNodeOfType(statement, "VariableDeclaration")) continue;
40099
+ for (const declarator of statement.declarations ?? []) {
40100
+ if (!isNodeOfType(declarator.id, "Identifier")) continue;
40101
+ if (declarator.id.name !== name) continue;
40102
+ if (declarator.init && !isNodeOfType(declarator.init, "AwaitExpression")) return true;
40103
+ }
40339
40104
  }
40340
40105
  return false;
40341
40106
  };
40342
- const REQUEST_SCOPED_IMPORT_SOURCES = ["next/headers", "next-intl/server"];
40343
- const isRequestScopedCallee = (callee) => {
40344
- if (!isNodeOfType(callee, "Identifier")) return false;
40345
- if (REQUEST_SCOPED_IMPORT_SOURCES.some((moduleSource) => getImportedNameFromModule(callee, callee.name, moduleSource) !== null)) return true;
40346
- return getImportedNameFromModule(callee, callee.name, "next/server") === "connection";
40347
- };
40348
- const declarationAwaitsRequestScopedCall = (declaration) => {
40107
+ const declarationAwaitsStartedPromise = (declaration, statements, declarationIndex) => {
40349
40108
  if (!isNodeOfType(declaration, "VariableDeclaration")) return false;
40350
40109
  for (const declarator of declaration.declarations ?? []) {
40351
40110
  const init = declarator.init;
40352
40111
  if (!isNodeOfType(init, "AwaitExpression")) continue;
40353
40112
  const argument = init.argument;
40354
- if (isNodeOfType(argument, "CallExpression") && isRequestScopedCallee(argument.callee)) return true;
40113
+ if (isNodeOfType(argument, "Identifier") && isStartedPromiseBinding(argument.name, statements, declarationIndex)) return true;
40355
40114
  }
40356
40115
  return false;
40357
40116
  };
@@ -40386,8 +40145,7 @@ const serverSequentialIndependentAwait = defineRule({
40386
40145
  if (!isNodeOfType(nextStatement, "VariableDeclaration")) continue;
40387
40146
  if (!declarationStartsWithAwait(nextStatement)) continue;
40388
40147
  if (declarationReadsAnyName(nextStatement, declaredNames)) continue;
40389
- if (declarationAwaitsExistingPromise(nextStatement)) continue;
40390
- if (declarationAwaitsRequestScopedCall(currentStatement) || declarationAwaitsRequestScopedCall(nextStatement)) continue;
40148
+ if (declarationAwaitsStartedPromise(nextStatement, statements, statementIndex + 1)) continue;
40391
40149
  if (declarationAwaitsGate(currentStatement)) continue;
40392
40150
  context.report({
40393
40151
  node: nextStatement,
@@ -41340,7 +41098,7 @@ const tanstackStartNoNavigateInRender = defineRule({
41340
41098
  if (!isNodeOfType(callParent, "CallExpression")) return false;
41341
41099
  if (callParent.callee === functionNode) return false;
41342
41100
  if (isNodeOfType(callParent.callee, "MemberExpression") && !callParent.callee.computed && isNodeOfType(callParent.callee.property, "Identifier") && PROMISE_CONTINUATION_METHODS.has(callParent.callee.property.name)) return true;
41343
- return isNodeOfType(callParent.callee, "Identifier") && HOOK_NAME_PATTERN$1.test(callParent.callee.name) && !RENDER_SYNCHRONOUS_HOOK_NAMES.has(callParent.callee.name) && callParent.arguments?.[0] === functionNode;
41101
+ return isNodeOfType(callParent.callee, "Identifier") && HOOK_NAME_PATTERN.test(callParent.callee.name) && !RENDER_SYNCHRONOUS_HOOK_NAMES.has(callParent.callee.name) && callParent.arguments?.[0] === functionNode;
41344
41102
  };
41345
41103
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && REACT_HANDLER_PROP_PATTERN.test(node.name.name);
41346
41104
  const isEventHandlerNamedProperty = (node) => isNodeOfType(node, "Property") && (isNodeOfType(node.key, "Identifier") && typeof node.key.name === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.name) || isNodeOfType(node.key, "Literal") && typeof node.key.value === "string" && REACT_HANDLER_PROP_PATTERN.test(node.key.value));
@@ -41368,11 +41126,11 @@ const tanstackStartNoNavigateInRender = defineRule({
41368
41126
  if (isNodeOfType(parent, "ReturnStatement")) {
41369
41127
  const outerFunction = findEnclosingFunction(parent);
41370
41128
  const hookName = outerFunction ? getFunctionBindingName(outerFunction) : null;
41371
- return Boolean(hookName && HOOK_NAME_PATTERN$1.test(hookName));
41129
+ return Boolean(hookName && HOOK_NAME_PATTERN.test(hookName));
41372
41130
  }
41373
41131
  if (isNodeOfType(parent, "ArrowFunctionExpression") && parent.body === functionNode) {
41374
41132
  const hookName = getFunctionBindingName(parent);
41375
- return Boolean(hookName && HOOK_NAME_PATTERN$1.test(hookName));
41133
+ return Boolean(hookName && HOOK_NAME_PATTERN.test(hookName));
41376
41134
  }
41377
41135
  return false;
41378
41136
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.6.2-dev.173cc0a",
3
+ "version": "0.6.2-dev.59e8178",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",