oxlint-plugin-react-doctor 0.7.9-dev.842a55f → 0.7.9-dev.95f9937

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.d.ts +0 -46
  2. package/dist/index.js +249 -2222
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1237,13 +1237,12 @@ const stripParenExpression = (node) => {
1237
1237
  };
1238
1238
  //#endregion
1239
1239
  //#region src/plugin/utils/resolve-const-identifier-alias.ts
1240
- const resolveConstIdentifierAlias = (identifier, scopes, allowPatternBinding = false) => {
1240
+ const resolveConstIdentifierAlias = (identifier, scopes) => {
1241
1241
  if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
1242
1242
  const visitedSymbolIds = /* @__PURE__ */ new Set();
1243
1243
  let symbol = scopes.symbolFor(identifier);
1244
1244
  while (symbol?.kind === "const") {
1245
- if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return null;
1246
- if (symbol.declarationNode.id !== symbol.bindingIdentifier) return allowPatternBinding ? symbol : null;
1245
+ if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
1247
1246
  visitedSymbolIds.add(symbol.id);
1248
1247
  const initializer = stripParenExpression(symbol.initializer);
1249
1248
  if (!isNodeOfType(initializer, "Identifier")) return symbol;
@@ -6737,28 +6736,6 @@ const asyncParallel = defineRule({
6737
6736
  }
6738
6737
  });
6739
6738
  //#endregion
6740
- //#region src/plugin/utils/collect-function-return-statements.ts
6741
- const collectFunctionReturnStatements = (functionNode) => {
6742
- if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return [];
6743
- const returnStatements = [];
6744
- walkAst(functionNode.body, (node) => {
6745
- if (node !== functionNode.body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
6746
- if (isNodeOfType(node, "ReturnStatement")) returnStatements.push(node);
6747
- });
6748
- return returnStatements;
6749
- };
6750
- //#endregion
6751
- //#region src/plugin/utils/is-nullish-expression.ts
6752
- const isNullishExpression = (expression) => isNodeOfType(expression, "Literal") && expression.value === null || isNodeOfType(expression, "Identifier") && expression.name === "undefined" || isNodeOfType(expression, "UnaryExpression") && expression.operator === "void";
6753
- //#endregion
6754
- //#region src/plugin/utils/strip-this-parameter.ts
6755
- const stripThisParameter = (parameters) => {
6756
- const firstParameter = parameters[0];
6757
- if (!firstParameter) return parameters;
6758
- if (isNodeOfType(firstParameter, "Identifier") && firstParameter.name === "this") return parameters.slice(1);
6759
- return parameters;
6760
- };
6761
- //#endregion
6762
6739
  //#region src/plugin/rules/security/auth-token-in-web-storage.ts
6763
6740
  const MESSAGE$60 = "Storing an auth token in `localStorage`/`sessionStorage` exposes it to any XSS on the page: JavaScript can read web storage and exfiltrate the token. Keep tokens in an `HttpOnly`, `Secure`, `SameSite` cookie instead.";
6764
6741
  const STORAGE_NAMES = new Set(["localStorage", "sessionStorage"]);
@@ -6770,8 +6747,12 @@ const STORAGE_GLOBALS = new Set([
6770
6747
  const SENSITIVE_KEY_PATTERN = /token|jwt|secret|password|passwd|credential|api[-_]?key|bearer|private[-_]?key/i;
6771
6748
  const NON_AUTH_TOKEN_PATTERN = /csrf|xsrf|device|fcm|apns|push|design|tokeniz|syntax|css|theme|color/i;
6772
6749
  const STRONG_AUTH_KEY_PATTERN = /jwt|secret|password|passwd|credential|private[-_]?key|api[-_]?key|bearer|access[-_]?token|refresh[-_]?token|auth[-_]?token|id[-_]?token|session/i;
6750
+ const PRODUCT_API_KEY_RECORDS_PATTERN = /(?:^|[._:-])(?:created|saved|integration|mailing)[-_]?api[-_]?keys$/i;
6751
+ const PRODUCT_API_KEY_COLLECTION_PATTERN = /[._:-](?:created|generated|saved)[-_]?api[-_]?keys$/i;
6773
6752
  const isAuthCredentialKey = (key) => {
6753
+ if (PRODUCT_API_KEY_RECORDS_PATTERN.test(key)) return false;
6774
6754
  if (!SENSITIVE_KEY_PATTERN.test(key)) return false;
6755
+ if (PRODUCT_API_KEY_COLLECTION_PATTERN.test(key)) return false;
6775
6756
  if (NON_AUTH_TOKEN_PATTERN.test(key) && !STRONG_AUTH_KEY_PATTERN.test(key)) return false;
6776
6757
  return true;
6777
6758
  };
@@ -6780,63 +6761,11 @@ const isDirectWebStorageObject = (node) => {
6780
6761
  if (isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.object, "Identifier") && STORAGE_GLOBALS.has(node.object.name) && isNodeOfType(node.property, "Identifier")) return STORAGE_NAMES.has(node.property.name);
6781
6762
  return false;
6782
6763
  };
6783
- const immutableInitializer = (identifier, visitedIdentifiers = /* @__PURE__ */ new Set()) => {
6784
- if (visitedIdentifiers.has(identifier)) return null;
6785
- visitedIdentifiers.add(identifier);
6786
- const binding = findVariableInitializer(identifier, identifier.name);
6787
- if (!binding?.initializer) return null;
6788
- if (isNodeOfType(binding.initializer, "FunctionDeclaration")) return binding.initializer;
6789
- const declarator = binding.bindingIdentifier.parent;
6790
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return null;
6791
- const declaration = declarator.parent;
6792
- if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return null;
6793
- if (declaration.kind !== "const") return null;
6794
- const initializer = stripParenExpression(binding.initializer);
6795
- if (!isNodeOfType(initializer, "Identifier")) return initializer;
6796
- return immutableInitializer(initializer, visitedIdentifiers) ?? initializer;
6797
- };
6798
- const isWebStorageFactoryResult = (node, visitedNodes) => {
6799
- const expression = stripParenExpression(node);
6800
- if (isWebStorageObject(expression, new Set(visitedNodes))) return true;
6801
- if (isNodeOfType(expression, "ConditionalExpression")) {
6802
- const consequent = stripParenExpression(expression.consequent);
6803
- const alternate = stripParenExpression(expression.alternate);
6804
- return (isNullishExpression(consequent) || isWebStorageFactoryResult(consequent, visitedNodes)) && (isNullishExpression(alternate) || isWebStorageFactoryResult(alternate, visitedNodes)) && (!isNullishExpression(consequent) || !isNullishExpression(alternate));
6805
- }
6806
- if (isNodeOfType(expression, "LogicalExpression")) {
6807
- if (expression.operator === "&&") return isWebStorageFactoryResult(expression.right, visitedNodes);
6808
- const left = stripParenExpression(expression.left);
6809
- const right = stripParenExpression(expression.right);
6810
- const isLeftStorage = isWebStorageFactoryResult(left, visitedNodes);
6811
- const isRightStorage = isWebStorageFactoryResult(right, visitedNodes);
6812
- return (isLeftStorage || isNullishExpression(left)) && (isRightStorage || isNullishExpression(right)) && (isLeftStorage || isRightStorage);
6813
- }
6814
- return false;
6815
- };
6816
- const isWebStorageObject = (node, visitedNodes = /* @__PURE__ */ new Set()) => {
6817
- const expression = stripParenExpression(node);
6818
- if (visitedNodes.has(expression)) return false;
6819
- visitedNodes.add(expression);
6820
- if (isDirectWebStorageObject(expression)) return true;
6821
- if (isNodeOfType(expression, "Identifier")) {
6822
- const initializer = immutableInitializer(expression);
6823
- return initializer ? isWebStorageObject(initializer, new Set(visitedNodes)) : false;
6824
- }
6825
- if (!isNodeOfType(expression, "CallExpression")) return false;
6826
- const callee = stripParenExpression(expression.callee);
6827
- if (!isNodeOfType(callee, "Identifier")) return false;
6828
- const factory = immutableInitializer(callee);
6829
- if (!isFunctionLike$1(factory)) return false;
6830
- if (isNodeOfType(factory, "ArrowFunctionExpression") && !isNodeOfType(factory.body, "BlockStatement")) return isWebStorageFactoryResult(factory.body, visitedNodes);
6831
- let didReturnWebStorage = false;
6832
- for (const returnStatement of collectFunctionReturnStatements(factory)) {
6833
- if (!returnStatement.argument) continue;
6834
- const strippedReturn = stripParenExpression(returnStatement.argument);
6835
- if (isNullishExpression(strippedReturn)) continue;
6836
- if (!isWebStorageFactoryResult(strippedReturn, visitedNodes)) return false;
6837
- didReturnWebStorage = true;
6838
- }
6839
- return didReturnWebStorage;
6764
+ const isWebStorageObject = (node) => {
6765
+ if (isDirectWebStorageObject(node)) return true;
6766
+ if (!isNodeOfType(node, "Identifier")) return false;
6767
+ const binding = findVariableInitializer(node, node.name);
6768
+ return binding?.initializer ? isDirectWebStorageObject(binding.initializer) : false;
6840
6769
  };
6841
6770
  const resolveStaticKeyString = (node) => {
6842
6771
  if (isNodeOfType(node, "Literal") && typeof node.value === "string") return node.value;
@@ -6856,56 +6785,6 @@ const staticMemberName = (member) => {
6856
6785
  if (member.computed && isNodeOfType(member.property, "Literal") && typeof member.property.value === "string") return member.property.value;
6857
6786
  return null;
6858
6787
  };
6859
- const parameterIndex = (expression, parameterSymbolIds, scopes, canUnwrapSerialization, visitedNodes = /* @__PURE__ */ new Set()) => {
6860
- const strippedExpression = stripParenExpression(expression);
6861
- if (visitedNodes.has(strippedExpression)) return null;
6862
- visitedNodes.add(strippedExpression);
6863
- if (isNodeOfType(strippedExpression, "Identifier")) {
6864
- const directSymbolId = scopes.symbolFor(strippedExpression)?.id;
6865
- const directIndex = directSymbolId === void 0 ? -1 : parameterSymbolIds.indexOf(directSymbolId);
6866
- if (directIndex !== -1) return directIndex;
6867
- const initializer = immutableInitializer(strippedExpression);
6868
- return initializer ? parameterIndex(initializer, parameterSymbolIds, scopes, canUnwrapSerialization, visitedNodes) : null;
6869
- }
6870
- if (canUnwrapSerialization && isNodeOfType(strippedExpression, "CallExpression") && isNodeOfType(strippedExpression.callee, "MemberExpression") && !strippedExpression.callee.computed && isNodeOfType(strippedExpression.callee.object, "Identifier") && strippedExpression.callee.object.name === "JSON" && isNodeOfType(strippedExpression.callee.property, "Identifier") && strippedExpression.callee.property.name === "stringify") {
6871
- const serializedArgument = strippedExpression.arguments[0];
6872
- return serializedArgument ? parameterIndex(serializedArgument, parameterSymbolIds, scopes, true, visitedNodes) : null;
6873
- }
6874
- return null;
6875
- };
6876
- const storageHelperSinkCache = /* @__PURE__ */ new WeakMap();
6877
- const findStorageHelperSinks = (functionNode, scopes) => {
6878
- const cachedSinks = storageHelperSinkCache.get(functionNode);
6879
- if (cachedSinks) return cachedSinks;
6880
- if (!isNodeOfType(functionNode, "FunctionDeclaration") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "ArrowFunctionExpression")) {
6881
- storageHelperSinkCache.set(functionNode, []);
6882
- return [];
6883
- }
6884
- const parameterSymbolIds = stripThisParameter(functionNode.params).map((parameter) => {
6885
- const strippedParameter = stripParenExpression(parameter);
6886
- const identifier = isNodeOfType(strippedParameter, "Identifier") ? strippedParameter : isNodeOfType(strippedParameter, "AssignmentPattern") && isNodeOfType(strippedParameter.left, "Identifier") ? strippedParameter.left : null;
6887
- return identifier ? scopes.symbolFor(identifier)?.id ?? null : null;
6888
- });
6889
- const helperSinks = [];
6890
- walkAst(functionNode.body, (child) => {
6891
- if (child !== functionNode.body && isFunctionLike$1(child)) return false;
6892
- if (!isNodeOfType(child, "CallExpression")) return;
6893
- const callee = stripParenExpression(child.callee);
6894
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "setItem" || !isWebStorageObject(callee.object)) return;
6895
- const keyExpression = child.arguments[0];
6896
- const valueExpression = child.arguments[1];
6897
- if (!keyExpression || !valueExpression) return;
6898
- const keyParameterIndex = parameterIndex(keyExpression, parameterSymbolIds, scopes, false);
6899
- const valueParameterIndex = parameterIndex(valueExpression, parameterSymbolIds, scopes, true);
6900
- if (keyParameterIndex === null || valueParameterIndex === null) return;
6901
- helperSinks.push({
6902
- keyParameterIndex,
6903
- valueParameterIndex
6904
- });
6905
- });
6906
- storageHelperSinkCache.set(functionNode, helperSinks);
6907
- return helperSinks;
6908
- };
6909
6788
  const authTokenInWebStorage = defineRule({
6910
6789
  id: "auth-token-in-web-storage",
6911
6790
  title: "Auth token in web storage",
@@ -6913,23 +6792,14 @@ const authTokenInWebStorage = defineRule({
6913
6792
  recommendation: "Don't persist auth tokens (JWTs, access/refresh tokens, secrets) in `localStorage`/`sessionStorage`; they're readable by any XSS. Use an `HttpOnly` cookie set by the server.",
6914
6793
  create: skipNonProductionFiles((context) => ({
6915
6794
  CallExpression(node) {
6916
- const callee = stripParenExpression(node.callee);
6917
- const keyArguments = [];
6918
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "setItem" && isWebStorageObject(stripParenExpression(callee.object))) {
6919
- const keyArgument = node.arguments[0];
6920
- if (keyArgument) keyArguments.push(keyArgument);
6921
- } else if (isNodeOfType(callee, "Identifier")) {
6922
- const helperFunction = immutableInitializer(callee);
6923
- const helperSinks = helperFunction ? findStorageHelperSinks(helperFunction, context.scopes) : [];
6924
- for (const helperSink of helperSinks) {
6925
- const keyArgument = node.arguments[helperSink.keyParameterIndex];
6926
- if (keyArgument && node.arguments[helperSink.valueParameterIndex]) keyArguments.push(keyArgument);
6927
- }
6928
- }
6929
- if (!keyArguments.some((keyArgument) => {
6930
- const keyString = resolveStaticKeyString(keyArgument);
6931
- return keyString !== null && isAuthCredentialKey(keyString);
6932
- })) return;
6795
+ const callee = node.callee;
6796
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return;
6797
+ if (!isNodeOfType(callee.property, "Identifier") || callee.property.name !== "setItem") return;
6798
+ if (!isWebStorageObject(stripParenExpression(callee.object))) return;
6799
+ const keyArgument = node.arguments?.[0];
6800
+ if (!keyArgument) return;
6801
+ const keyString = resolveStaticKeyString(keyArgument);
6802
+ if (keyString === null || !isAuthCredentialKey(keyString)) return;
6933
6803
  context.report({
6934
6804
  node,
6935
6805
  message: MESSAGE$60
@@ -7177,6 +7047,9 @@ const isCreateElementCall = (node) => {
7177
7047
  return false;
7178
7048
  };
7179
7049
  //#endregion
7050
+ //#region src/plugin/utils/is-nullish-expression.ts
7051
+ const isNullishExpression = (expression) => isNodeOfType(expression, "Literal") && expression.value === null || isNodeOfType(expression, "Identifier") && expression.name === "undefined" || isNodeOfType(expression, "UnaryExpression") && expression.operator === "void";
7052
+ //#endregion
7180
7053
  //#region src/plugin/rules/react-builtins/button-has-type.ts
7181
7054
  const MISSING_MESSAGE$2 = "Your users can submit the form by accident because a `<button>` with no `type` defaults to submit.";
7182
7055
  const INVALID_MESSAGE = "This button has an invalid `type`, so the browser may treat it like a submit button.";
@@ -7638,7 +7511,6 @@ const areExpressionsStructurallyEqual = (a, b) => {
7638
7511
  if (a.type !== b.type) return false;
7639
7512
  if (isNodeOfType(a, "ThisExpression")) return true;
7640
7513
  if (isNodeOfType(a, "Identifier") && isNodeOfType(b, "Identifier")) return a.name === b.name;
7641
- if (isNodeOfType(a, "PrivateIdentifier") && isNodeOfType(b, "PrivateIdentifier")) return a.name === b.name;
7642
7514
  if (isNodeOfType(a, "Literal") && isNodeOfType(b, "Literal")) return a.value === b.value;
7643
7515
  if (isNodeOfType(a, "MemberExpression") && isNodeOfType(b, "MemberExpression")) {
7644
7516
  if (a.computed !== b.computed) return false;
@@ -8694,6 +8566,17 @@ const createMethodMutationAnalysis = (context) => {
8694
8566
  //#region src/plugin/utils/is-member-property.ts
8695
8567
  const isMemberProperty = (node, propertyName) => Boolean(node && isNodeOfType(node, "MemberExpression") && isNodeOfType(node.property, "Identifier") && node.property.name === propertyName);
8696
8568
  //#endregion
8569
+ //#region src/plugin/utils/collect-function-return-statements.ts
8570
+ const collectFunctionReturnStatements = (functionNode) => {
8571
+ if (!isFunctionLike$1(functionNode) || !isNodeOfType(functionNode.body, "BlockStatement")) return [];
8572
+ const returnStatements = [];
8573
+ walkAst(functionNode.body, (node) => {
8574
+ if (node !== functionNode.body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
8575
+ if (isNodeOfType(node, "ReturnStatement")) returnStatements.push(node);
8576
+ });
8577
+ return returnStatements;
8578
+ };
8579
+ //#endregion
8697
8580
  //#region src/plugin/utils/statement-always-exits.ts
8698
8581
  const statementAlwaysExits = (statement) => {
8699
8582
  if (isNodeOfType(statement, "ReturnStatement") || isNodeOfType(statement, "ThrowStatement")) return true;
@@ -8960,7 +8843,7 @@ const isReactNamespaceImport = (identifier, scopes) => {
8960
8843
  if (!symbol || !isImportedFromReact(symbol)) return false;
8961
8844
  return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
8962
8845
  };
8963
- const isReactNamespaceReceiver$1 = (receiver, scopes, options) => {
8846
+ const isReactNamespaceReceiver = (receiver, scopes, options) => {
8964
8847
  if (!isNodeOfType(receiver, "Identifier")) return false;
8965
8848
  if (isReactNamespaceImport(receiver, scopes)) return true;
8966
8849
  return Boolean(options.allowGlobalReactNamespace && receiver.name === "React" && scopes.isGlobalReference(receiver));
@@ -8973,7 +8856,7 @@ const isDestructuredReactApiBinding = (identifier, apiNames, scopes, options) =>
8973
8856
  for (const property of pattern.properties) {
8974
8857
  if (!isNodeOfType(property, "Property") || property.value !== symbol.bindingIdentifier) continue;
8975
8858
  const propertyName = getStaticPropertyKeyName(property);
8976
- return Boolean(propertyName && includesApiName(apiNames, propertyName) && isReactNamespaceReceiver$1(stripParenExpression(symbol.initializer), scopes, options));
8859
+ return Boolean(propertyName && includesApiName(apiNames, propertyName) && isReactNamespaceReceiver(stripParenExpression(symbol.initializer), scopes, options));
8977
8860
  }
8978
8861
  return false;
8979
8862
  };
@@ -8997,7 +8880,7 @@ const isReactApiCallee = (rawCallee, apiNames, scopes, options, visitedSymbolIds
8997
8880
  return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
8998
8881
  }
8999
8882
  if (!isNodeOfType(callee, "MemberExpression") || !includesApiName(apiNames, getStaticPropertyName(callee) ?? "")) return false;
9000
- return isReactNamespaceReceiver$1(stripParenExpression(callee.object), scopes, options);
8883
+ return isReactNamespaceReceiver(stripParenExpression(callee.object), scopes, options);
9001
8884
  };
9002
8885
  //#endregion
9003
8886
  //#region src/plugin/utils/is-proven-browser-api-receiver.ts
@@ -13653,7 +13536,7 @@ const getPromiseChainCallForCallback = (candidate) => {
13653
13536
  if (!callbackContainer.arguments?.some((argument) => stripParenExpression(argument) === candidate)) return null;
13654
13537
  return isPromiseChainCall(stripParenExpression(callbackContainer.callee)) ? callbackContainer : null;
13655
13538
  };
13656
- const collectInvokedFunctions = (effectCallback, includePromiseCallbacks) => {
13539
+ const collectEffectInvokedFunctions = (effectCallback) => {
13657
13540
  const invokedFunctions = new Set([effectCallback]);
13658
13541
  const localFunctionBindings = /* @__PURE__ */ new Map();
13659
13542
  const calledBindingNames = /* @__PURE__ */ new Set();
@@ -13687,14 +13570,12 @@ const collectInvokedFunctions = (effectCallback, includePromiseCallbacks) => {
13687
13570
  calledBindingNames.add(callee.name);
13688
13571
  return;
13689
13572
  }
13690
- if (includePromiseCallbacks && isPromiseChainCall(callee)) for (const callArgument of child.arguments ?? []) enqueue(callArgument);
13573
+ if (isPromiseChainCall(callee)) for (const callArgument of child.arguments ?? []) enqueue(callArgument);
13691
13574
  });
13692
13575
  for (const calledName of calledBindingNames) enqueue(localFunctionBindings.get(calledName));
13693
13576
  }
13694
13577
  return invokedFunctions;
13695
13578
  };
13696
- const collectEffectInvokedFunctions = (effectCallback) => collectInvokedFunctions(effectCallback, true);
13697
- const collectSynchronouslyEffectInvokedFunctions = (effectCallback) => collectInvokedFunctions(effectCallback, false);
13698
13579
  //#endregion
13699
13580
  //#region src/plugin/utils/is-react-hook-name.ts
13700
13581
  const isReactHookName = (name) => {
@@ -13853,19 +13734,15 @@ const resolveReactRefSymbol = (memberExpression, scopes) => {
13853
13734
  if (!isNodeOfType(initializer, "CallExpression")) return null;
13854
13735
  return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
13855
13736
  };
13856
- const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13737
+ const hasReactRefCurrentOrigin = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13857
13738
  const expression = stripParenExpression(node);
13858
- const refSymbol = resolveReactRefSymbol(expression, scopes);
13859
- if (refSymbol) return refSymbol;
13860
- if (!isNodeOfType(expression, "Identifier")) return null;
13861
- const symbol = scopes.symbolFor(expression);
13862
- if (!symbol || visitedSymbolIds.has(symbol.id)) return null;
13863
- const initializer = getDirectUnreassignedInitializer(symbol);
13864
- if (!initializer) return null;
13739
+ if (resolveReactRefSymbol(expression, scopes)) return true;
13740
+ if (!isNodeOfType(expression, "Identifier")) return false;
13741
+ const symbol = resolveConstIdentifierAlias(expression, scopes);
13742
+ if (!symbol?.initializer || visitedSymbolIds.has(symbol.id)) return false;
13865
13743
  visitedSymbolIds.add(symbol.id);
13866
- return resolveReactRefCurrentOriginSymbol(initializer, scopes, visitedSymbolIds);
13744
+ return hasReactRefCurrentOrigin(symbol.initializer, scopes, visitedSymbolIds);
13867
13745
  };
13868
- const hasReactRefCurrentOrigin = (node, scopes) => resolveReactRefCurrentOriginSymbol(node, scopes) !== null;
13869
13746
  //#endregion
13870
13747
  //#region src/plugin/utils/walk-inside-statement-blocks.ts
13871
13748
  const walkInsideStatementBlocks = (node, visitor) => {
@@ -14347,39 +14224,6 @@ const findContainingCollectionKey = (resourceNode, context) => {
14347
14224
  }
14348
14225
  return null;
14349
14226
  };
14350
- const findPushedResourceCollectionKey = (usage, context) => {
14351
- if (!isNodeOfType(usage.node, "CallExpression")) return null;
14352
- const registrationCallee = stripParenExpression(usage.node.callee);
14353
- if (!isNodeOfType(registrationCallee, "MemberExpression") || registrationCallee.computed) return null;
14354
- const resourceIdentifier = stripParenExpression(registrationCallee.object);
14355
- if (!isPrivatePlainConstIdentifier(resourceIdentifier, context)) return null;
14356
- const resourceSymbol = context.scopes.symbolFor(resourceIdentifier);
14357
- if (!resourceSymbol) return null;
14358
- const pushCalls = resourceSymbol.references.flatMap((reference) => {
14359
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
14360
- const callNode = referenceRoot.parent;
14361
- if (!isNodeOfType(callNode, "CallExpression") || !callNode.arguments?.some((argument) => argument === referenceRoot)) return [];
14362
- const pushCallee = stripParenExpression(callNode.callee);
14363
- return isNodeOfType(pushCallee, "MemberExpression") && !pushCallee.computed && isNodeOfType(pushCallee.object, "Identifier") && isNodeOfType(pushCallee.property, "Identifier") && pushCallee.property.name === "push" ? [callNode] : [];
14364
- });
14365
- if (pushCalls.length !== 1) return null;
14366
- const pushCall = pushCalls[0];
14367
- if (findEnclosingFunction$1(pushCall) !== findEnclosingFunction$1(usage.node) || !doMatchingNodesCoverEveryPathAfterUsage(usage.node, [pushCall], context)) return null;
14368
- const pushCallee = stripParenExpression(pushCall.callee);
14369
- if (!isNodeOfType(pushCallee, "MemberExpression") || !isNodeOfType(pushCallee.object, "Identifier") || !isPrivatePlainConstIdentifier(pushCallee.object, context)) return null;
14370
- const collectionSymbol = context.scopes.symbolFor(pushCallee.object);
14371
- const collectionInitializer = collectionSymbol?.initializer ? stripParenExpression(collectionSymbol.initializer) : null;
14372
- if (!collectionSymbol || !isNodeOfType(collectionInitializer, "ArrayExpression") || (collectionInitializer.elements?.length ?? 0) !== 0 || findEnclosingFunction$1(collectionSymbol.declarationNode) !== findEnclosingFunction$1(usage.node)) return null;
14373
- return collectionSymbol.references.every((reference) => {
14374
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
14375
- const forOfStatement = referenceRoot.parent;
14376
- if (isNodeOfType(forOfStatement, "ForOfStatement") && forOfStatement.right === referenceRoot && forOfStatement.await !== true) return true;
14377
- const memberNode = referenceRoot.parent;
14378
- const callNode = memberNode?.parent;
14379
- if (!isNodeOfType(memberNode, "MemberExpression") || memberNode.object !== referenceRoot || memberNode.computed || !isNodeOfType(memberNode.property, "Identifier") || !isNodeOfType(callNode, "CallExpression") || callNode.callee !== memberNode) return false;
14380
- return memberNode.property.name === "forEach" || memberNode.property.name === "push";
14381
- }) ? resolveExpressionKey(pushCallee.object, context) : null;
14382
- };
14383
14227
  const isWithinAssignmentTarget = (identifier) => {
14384
14228
  let currentNode = identifier;
14385
14229
  let parentNode = currentNode.parent;
@@ -14438,14 +14282,6 @@ const isSynchronousIteratorCallback = (functionNode) => {
14438
14282
  if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments?.[1] === functionNode;
14439
14283
  return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments?.[0] === functionNode;
14440
14284
  };
14441
- const findEnclosingForEachCall = (node) => {
14442
- const callbackNode = findEnclosingFunction$1(node);
14443
- if (!callbackNode) return null;
14444
- const callNode = callbackNode.parent;
14445
- if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments?.[0] !== callbackNode) return null;
14446
- const callee = stripParenExpression(callNode.callee);
14447
- return isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === "forEach" ? callNode : null;
14448
- };
14449
14285
  const findDirectCallForReference = (identifier) => {
14450
14286
  const expressionRoot = findTransparentExpressionRoot(identifier);
14451
14287
  const callNode = expressionRoot.parent;
@@ -14460,7 +14296,7 @@ const findSingleDirectInvocation = (functionNode, caller, context) => {
14460
14296
  const callNode = findDirectCallForReference(reference.identifier);
14461
14297
  return callNode ? [callNode] : [];
14462
14298
  });
14463
- if (invocationCalls.length !== 1 || symbol.references.length !== 1) return null;
14299
+ if (invocationCalls.length !== 1) return null;
14464
14300
  const invocationCall = invocationCalls[0];
14465
14301
  return findEnclosingFunction$1(invocationCall) === caller && isNodeReachableWithinFunction(invocationCall, context) ? invocationCall : null;
14466
14302
  };
@@ -14494,15 +14330,7 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
14494
14330
  if (cleanupChild !== cleanupFunction.body && isFunctionLike$1(cleanupChild) && !isSynchronousIteratorCallback(cleanupChild)) return false;
14495
14331
  const cleanupCall = isNodeOfType(cleanupChild, "ChainExpression") ? cleanupChild.expression : cleanupChild;
14496
14332
  if (doesReleaseCallMatchUsage(cleanupChild, usage, context)) {
14497
- const cleanupForEachCall = findEnclosingForEachCall(cleanupChild);
14498
- const cleanupCallee = isNodeOfType(cleanupCall, "CallExpression") ? stripParenExpression(cleanupCall.callee) : null;
14499
- const cleanupReceiverForOfStatement = isNodeOfType(cleanupCallee, "MemberExpression") ? findForOfStatementForIteratorExpression(cleanupCallee.object, context) : null;
14500
- const cleanupReceiverCollectionKey = cleanupReceiverForOfStatement ? resolveExpressionKey(cleanupReceiverForOfStatement.right, context) : isNodeOfType(cleanupCallee, "MemberExpression") ? resolveIteratorCollectionKey(cleanupCallee.object, context) : null;
14501
- if (cleanupReceiverCollectionKey !== null && findEnclosingFunction$1(cleanupChild) !== cleanupFunction) {
14502
- if (cleanupForEachCall && findPushedResourceCollectionKey(usage, context) === cleanupReceiverCollectionKey) matchingLoopOrHelperAnchors.push(cleanupForEachCall);
14503
- return;
14504
- }
14505
- const cleanupForOfStatement = findForOfStatementForIteratorExpression(isNodeOfType(cleanupCall, "CallExpression") ? cleanupCall.arguments?.[0] : null, context) ?? cleanupReceiverForOfStatement;
14333
+ const cleanupForOfStatement = findForOfStatementForIteratorExpression(isNodeOfType(cleanupCall, "CallExpression") ? cleanupCall.arguments?.[0] : null, context);
14506
14334
  if (!cleanupForOfStatement) {
14507
14335
  didCleanupFunctionMatch = true;
14508
14336
  return false;
@@ -14546,28 +14374,28 @@ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
14546
14374
  });
14547
14375
  return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
14548
14376
  };
14549
- const doesTestRequireLiveExpressionKey = (test, expressionKey, context) => {
14550
- if (resolveExpressionKey(test, context) === expressionKey) return true;
14551
- const unwrappedTest = stripParenExpression(test);
14552
- if (!isNodeOfType(unwrappedTest, "BinaryExpression") || unwrappedTest.operator !== "!=" && unwrappedTest.operator !== "!==") return false;
14553
- const isNullishOperand = (operand) => {
14554
- const unwrappedOperand = stripParenExpression(operand);
14555
- return isNodeOfType(unwrappedOperand, "Literal") && unwrappedOperand.value === null || isNodeOfType(unwrappedOperand, "Identifier") && unwrappedOperand.name === "undefined" && context.scopes.isGlobalReference(unwrappedOperand);
14377
+ const findDirectHandleGuardForRelease = (releaseCall, owner, usage, context) => {
14378
+ if (usage.handleKey === null) return null;
14379
+ const doesTestRequireLiveHandle = (test) => {
14380
+ if (resolveExpressionKey(test, context) === usage.handleKey) return true;
14381
+ const unwrappedTest = stripParenExpression(test);
14382
+ if (!isNodeOfType(unwrappedTest, "BinaryExpression") || unwrappedTest.operator !== "!=" && unwrappedTest.operator !== "!==") return false;
14383
+ const isNullishOperand = (operand) => {
14384
+ const unwrappedOperand = stripParenExpression(operand);
14385
+ return isNodeOfType(unwrappedOperand, "Literal") && unwrappedOperand.value === null || isNodeOfType(unwrappedOperand, "Identifier") && unwrappedOperand.name === "undefined" && context.scopes.isGlobalReference(unwrappedOperand);
14386
+ };
14387
+ return resolveExpressionKey(unwrappedTest.left, context) === usage.handleKey && isNullishOperand(unwrappedTest.right) || resolveExpressionKey(unwrappedTest.right, context) === usage.handleKey && isNullishOperand(unwrappedTest.left);
14556
14388
  };
14557
- return resolveExpressionKey(unwrappedTest.left, context) === expressionKey && isNullishOperand(unwrappedTest.right) || resolveExpressionKey(unwrappedTest.right, context) === expressionKey && isNullishOperand(unwrappedTest.left);
14558
- };
14559
- const findLiveExpressionGuardForRelease = (releaseCall, owner, expressionKey, context) => {
14560
14389
  let ancestor = releaseCall.parent;
14561
14390
  while (ancestor && ancestor !== owner) {
14562
14391
  if (isNodeOfType(ancestor, "IfStatement")) {
14563
- if (ancestor.alternate !== null || !doesTestRequireLiveExpressionKey(ancestor.test, expressionKey, context) || !doMatchingNodesCoverEveryPathAfterUsage(ancestor.consequent, [releaseCall], context)) return null;
14392
+ if (ancestor.alternate !== null || !doesTestRequireLiveHandle(ancestor.test) || !doMatchingNodesCoverEveryPathAfterUsage(ancestor.consequent, [releaseCall], context)) return null;
14564
14393
  return ancestor;
14565
14394
  }
14566
14395
  ancestor = ancestor.parent;
14567
14396
  }
14568
14397
  return null;
14569
14398
  };
14570
- const findDirectHandleGuardForRelease = (releaseCall, owner, usage, context) => usage.handleKey === null ? null : findLiveExpressionGuardForRelease(releaseCall, owner, usage.handleKey, context);
14571
14399
  const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
14572
14400
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression") || !isNodeOfType(callback.body, "BlockStatement")) return false;
14573
14401
  const functionCfg = context.cfg.cfgFor(callback);
@@ -14755,108 +14583,7 @@ const hasPotentialInterruptionAfterGuard = (callback, guardState, usageNode, con
14755
14583
  });
14756
14584
  return hasPotentialInterruption;
14757
14585
  };
14758
- const getNumericReactRefCurrentKey = (expression, context) => {
14759
- const refSymbol = resolveReactRefSymbol(stripParenExpression(expression), context.scopes);
14760
- const initializer = refSymbol?.initializer ? stripParenExpression(refSymbol.initializer) : null;
14761
- if (!isNodeOfType(initializer, "CallExpression")) return null;
14762
- const initialValue = initializer.arguments?.[0] ? stripParenExpression(initializer.arguments[0]) : null;
14763
- if (!isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return null;
14764
- return resolveExpressionKey(expression, context);
14765
- };
14766
- const getBlockingGenerationKey = (expression, context) => {
14767
- const test = stripParenExpression(expression);
14768
- if (isNodeOfType(test, "LogicalExpression") && test.operator === "||") return getBlockingGenerationKey(test.left, context) ?? getBlockingGenerationKey(test.right, context);
14769
- if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "!==" && test.operator !== "!=") return null;
14770
- const leftKey = getNumericReactRefCurrentKey(test.left, context);
14771
- const rightKey = getNumericReactRefCurrentKey(test.right, context);
14772
- const snapshotExpression = leftKey ? stripParenExpression(test.right) : stripParenExpression(test.left);
14773
- const key = leftKey ?? rightKey;
14774
- return key && isNodeOfType(snapshotExpression, "Identifier") ? key : null;
14775
- };
14776
- const findGenerationGuardKeyForDeferredUsage = (usageFunction, usageNode, context) => {
14777
- if (!isFunctionLike$1(usageFunction)) return null;
14778
- let generationKey = null;
14779
- walkAst(usageFunction.body, (child) => {
14780
- if (generationKey) return false;
14781
- if (child !== usageFunction.body && isFunctionLike$1(child)) return false;
14782
- if (!isNodeOfType(child, "IfStatement") || child.alternate) return;
14783
- const key = getBlockingGenerationKey(child.test, context);
14784
- if (!key || canNodeReachLaterNodeWithinFunction(child.consequent, usageNode, usageFunction, context) || !doMatchingNodesCoverEveryPathBeforeUsage(usageNode, [child], usageFunction, context)) return;
14785
- generationKey = key;
14786
- });
14787
- return generationKey;
14788
- };
14789
- const isGenerationAdvance = (node, generationKey, context) => {
14790
- if (isNodeOfType(node, "UpdateExpression") && resolveExpressionKey(node.argument, context) === generationKey) return true;
14791
- if (!isNodeOfType(node, "AssignmentExpression") || resolveExpressionKey(node.left, context) !== generationKey || node.operator !== "+=" && node.operator !== "-=") return false;
14792
- const amount = stripParenExpression(node.right);
14793
- return isNodeOfType(amount, "Literal") && typeof amount.value === "number" && amount.value !== 0;
14794
- };
14795
- const functionAdvancesGeneration = (owner, generationKey, context) => {
14796
- if (!isFunctionLike$1(owner)) return false;
14797
- let didAdvanceGeneration = false;
14798
- walkAst(owner.body, (child) => {
14799
- if (didAdvanceGeneration) return false;
14800
- if (child !== owner.body && isFunctionLike$1(child)) return false;
14801
- if (isGenerationAdvance(child, generationKey, context)) {
14802
- didAdvanceGeneration = true;
14803
- return false;
14804
- }
14805
- });
14806
- return didAdvanceGeneration;
14807
- };
14808
- const cleanupReturnsReleaseUsage = (cleanupReturns, usage, context) => cleanupReturns.length > 0 && cleanupReturns.every((cleanupReturn) => {
14809
- if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return false;
14810
- const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
14811
- return Boolean(cleanupFunction && isFunctionLike$1(cleanupFunction) && doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context));
14812
- });
14813
- const getOwnedFunctionReference = (reference, usageFunction, usageNode, callback, cleanupReturns, context) => {
14814
- const directCall = findDirectCallForReference(reference);
14815
- if (directCall) {
14816
- const referenceOwner = findEnclosingFunction$1(directCall);
14817
- if (referenceOwner && referenceOwner !== usageFunction && collectSynchronouslyEffectInvokedFunctions(callback).has(referenceOwner)) return { generationKey: null };
14818
- const generationKey = referenceOwner ? findGenerationGuardKeyForDeferredUsage(referenceOwner, directCall, context) : null;
14819
- return generationKey ? { generationKey } : null;
14820
- }
14821
- const referenceRoot = findTransparentExpressionRoot(reference);
14822
- const schedulerCall = referenceRoot.parent;
14823
- if (!isNodeOfType(schedulerCall, "CallExpression") || !schedulerCall.arguments.some((argument) => argument === referenceRoot) || !isNodeOfType(schedulerCall.callee, "Identifier") || schedulerCall.callee.name !== "setTimeout" || !context.scopes.isGlobalReference(schedulerCall.callee)) return null;
14824
- const schedulerUsage = {
14825
- kind: "timer",
14826
- node: schedulerCall,
14827
- resourceName: schedulerCall.callee.name,
14828
- handleKey: findAssignedResourceKey(schedulerCall, context),
14829
- receiverKey: null,
14830
- registrationVerbName: schedulerCall.callee.name,
14831
- eventKey: null,
14832
- handlerKey: null
14833
- };
14834
- const generationKey = findGenerationGuardKeyForDeferredUsage(usageFunction, usageNode, context);
14835
- return schedulerUsage.handleKey !== null && cleanupReturnsReleaseUsage(cleanupReturns, schedulerUsage, context) && generationKey ? { generationKey } : null;
14836
- };
14837
- const hasGuardedRefOwnedNestedCleanup = (callback, usage, cleanupReturns, context) => {
14838
- const usageFunction = findEnclosingFunction$1(usage.node);
14839
- const usageExpression = findTransparentExpressionRoot(usage.node);
14840
- const usageAssignment = usageExpression.parent;
14841
- if (usage.kind !== "subscribe" && usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usageAssignment, "AssignmentExpression") || usageAssignment.operator !== "=" || usageAssignment.right !== usageExpression || !resolveReactRefSymbol(stripParenExpression(usageAssignment.left), context.scopes) || !collectSynchronouslyEffectInvokedFunctions(callback).has(usageFunction) || !cleanupReturnsReleaseUsage(cleanupReturns, usage, context) || !doMatchingNodesCoverEveryPathFromFunctionEntry(callback, cleanupReturns, context)) return false;
14842
- const cleanupFunctions = cleanupReturns.flatMap((cleanupReturn) => {
14843
- if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return [];
14844
- const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
14845
- return cleanupFunction && isFunctionLike$1(cleanupFunction) ? [cleanupFunction] : [];
14846
- });
14847
- const bindingIdentifier = getFunctionBindingIdentifier$1(usageFunction);
14848
- const functionSymbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
14849
- if (!functionSymbol || functionSymbol.references.length === 0) return false;
14850
- const ownedReferences = functionSymbol.references.map((reference) => getOwnedFunctionReference(reference.identifier, usageFunction, usage.node, callback, cleanupReturns, context));
14851
- if (ownedReferences.some((reference) => reference === null)) return false;
14852
- const generationKeys = new Set(ownedReferences.flatMap((reference) => reference?.generationKey ? [reference.generationKey] : []));
14853
- if (generationKeys.size !== 1) return false;
14854
- const generationKey = generationKeys.values().next().value;
14855
- if (typeof generationKey !== "string") return false;
14856
- return [...collectSynchronouslyEffectInvokedFunctions(callback), ...cleanupFunctions].some((owner) => functionAdvancesGeneration(owner, generationKey, context));
14857
- };
14858
14586
  const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) => {
14859
- if (hasGuardedRefOwnedNestedCleanup(callback, usage, cleanupReturns, context)) return true;
14860
14587
  const usageFunction = findEnclosingFunction$1(usage.node);
14861
14588
  const promiseChainCall = usageFunction ? getPromiseChainCallForCallback(usageFunction) : null;
14862
14589
  if (usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usage.node, "CallExpression") || !isNodeOfType(usage.node.callee, "Identifier") || !context.scopes.isGlobalReference(usage.node.callee) || !promiseChainCall || !collectEffectInvokedFunctions(callback).has(usageFunction) || !doMatchingNodesCoverEveryPathAfterUsage(promiseChainCall, cleanupReturns, context)) return false;
@@ -15024,85 +14751,6 @@ const getReleaseVerbName = (node) => {
15024
14751
  }
15025
14752
  return null;
15026
14753
  };
15027
- const isRetainedAbortControllerRefRelease = (releaseReceiver, usage, context) => {
15028
- const releaseFunction = findEnclosingFunction$1(releaseReceiver);
15029
- const usageFunction = findEnclosingFunction$1(usage.node);
15030
- if (!releaseFunction || !usageFunction || !isFunctionLike$1(usageFunction) || !isReturnedEffectCleanupFunction(releaseFunction) || !resolveReactRefCurrentOriginSymbol(releaseReceiver, context.scopes)) return false;
15031
- const controllerKey = getListenerAbortControllerKey(usage, context);
15032
- const refCurrentKey = resolveExpressionKey(releaseReceiver, context);
15033
- if (controllerKey === null || refCurrentKey === null) return false;
15034
- const usageFunctionBody = usageFunction.body;
15035
- const previousAbortCalls = [];
15036
- const ownershipAssignments = [];
15037
- walkAst(usageFunctionBody, (child) => {
15038
- if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
15039
- if (isNodeOfType(child, "AssignmentExpression") && resolveExpressionKey(child.left, context) === refCurrentKey && resolveExpressionKey(child.right, context) === controllerKey) {
15040
- ownershipAssignments.push(child);
15041
- return;
15042
- }
15043
- if (!isNodeOfType(child, "CallExpression")) return;
15044
- const childCallee = isNodeOfType(child.callee, "ChainExpression") ? child.callee.expression : stripParenExpression(child.callee);
15045
- if (isNodeOfType(childCallee, "MemberExpression") && !childCallee.computed && isNodeOfType(childCallee.property, "Identifier") && childCallee.property.name === "abort" && resolveExpressionKey(childCallee.object, context) === refCurrentKey) previousAbortCalls.push(child);
15046
- });
15047
- const safeOwnershipAssignments = ownershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, previousAbortCalls, usageFunction, context));
15048
- return doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15049
- };
15050
- const isJsxRefAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && node.name.name === "ref";
15051
- const isFunctionForwardedToReactRef = (functionNode, context) => {
15052
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
15053
- if (!bindingIdentifier) return false;
15054
- const symbol = context.scopes.symbolFor(bindingIdentifier);
15055
- if (!symbol) return false;
15056
- return symbol.references.some((reference) => {
15057
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
15058
- const expressionContainer = referenceRoot.parent;
15059
- return Boolean(isNodeOfType(expressionContainer, "JSXExpressionContainer") && expressionContainer.expression === referenceRoot && isJsxRefAttribute(expressionContainer.parent));
15060
- });
15061
- };
15062
- const isFunctionReturnedFromReactHook = (functionNode, context, requireRefPropertyName) => {
15063
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
15064
- if (!bindingIdentifier) return false;
15065
- const symbol = context.scopes.symbolFor(bindingIdentifier);
15066
- if (!symbol) return false;
15067
- return symbol.references.some((reference) => {
15068
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
15069
- const property = referenceRoot.parent;
15070
- const propertyName = isNodeOfType(property, "Property") ? getStaticPropertyKeyName(property) : null;
15071
- if (!isNodeOfType(property, "Property") || property.value !== referenceRoot || !isNodeOfType(property.parent, "ObjectExpression") || requireRefPropertyName && propertyName !== "ref" && !propertyName?.endsWith("Ref")) return false;
15072
- const returnedObject = findTransparentExpressionRoot(property.parent);
15073
- const returnStatement = returnedObject.parent;
15074
- if (!isNodeOfType(returnStatement, "ReturnStatement") || returnStatement.argument !== returnedObject) return false;
15075
- const ownerFunction = findEnclosingFunction$1(returnStatement);
15076
- return Boolean(ownerFunction && isReactHookName(getFunctionBindingIdentifier$1(ownerFunction)?.name ?? ""));
15077
- });
15078
- };
15079
- const isFunctionUsedAsReactRef = (functionNode, context) => isFunctionForwardedToReactRef(functionNode, context) || isFunctionReturnedFromReactHook(functionNode, context, true);
15080
- const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15081
- if (!isNodeOfType(usage.node, "CallExpression")) return false;
15082
- const usageFunction = findEnclosingFunction$1(usage.node);
15083
- if (!usageFunction || !isFunctionLike$1(usageFunction) || usageFunction !== findEnclosingFunction$1(releaseCall) || !isFunctionUsedAsReactRef(usageFunction, context)) return false;
15084
- const registrationCallee = stripParenExpression(usage.node.callee);
15085
- const releaseCallee = stripParenExpression(releaseCall.callee);
15086
- const releaseRefSymbol = isNodeOfType(releaseCallee, "MemberExpression") ? resolveReactRefCurrentOriginSymbol(releaseCallee.object, context.scopes) : null;
15087
- if (!isNodeOfType(registrationCallee, "MemberExpression") || registrationCallee.computed || !isNodeOfType(registrationCallee.property, "Identifier") || registrationCallee.property.name !== "addEventListener" || !isNodeOfType(releaseCallee, "MemberExpression") || releaseCallee.computed || !isNodeOfType(releaseCallee.property, "Identifier") || releaseCallee.property.name !== "removeEventListener" || !releaseRefSymbol) return false;
15088
- const registrationReceiverKey = resolveExpressionKey(stripParenExpression(registrationCallee.object), context);
15089
- const nodeParameterKey = resolveExpressionKey(usageFunction.params?.[0], context);
15090
- const releaseReceiverKey = resolveExpressionKey(releaseCallee.object, context);
15091
- if (registrationReceiverKey === null || registrationReceiverKey !== nodeParameterKey || releaseReceiverKey === null || usage.eventKey === null || usage.eventKey !== resolveExpressionKey(releaseCall.arguments?.[0], context) || usage.handlerKey === null || usage.handlerKey !== resolveExpressionKey(releaseCall.arguments?.[1], context)) return false;
15092
- const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15093
- const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15094
- if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15095
- const releaseStart = getRangeStart(releaseCall);
15096
- const matchingOwnershipAssignments = [];
15097
- const usageFunctionBody = usageFunction.body;
15098
- walkAst(usageFunctionBody, (child) => {
15099
- if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
15100
- if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveReactRefSymbol(stripParenExpression(child.left), context.scopes)?.id === releaseRefSymbol.id && resolveExpressionKey(child.right, context) === registrationReceiverKey && releaseStart !== null && (getRangeStart(child) ?? -1) > releaseStart) matchingOwnershipAssignments.push(child);
15101
- });
15102
- const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
15103
- const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
15104
- return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15105
- };
15106
14754
  const doesReleaseCallMatchUsage = (node, usage, context) => {
15107
14755
  const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
15108
14756
  if (!isNodeOfType(callNode, "CallExpression")) return false;
@@ -15119,21 +14767,13 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15119
14767
  if (!releaseVerbName) return false;
15120
14768
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
15121
14769
  const releaseReceiverKey = resolveExpressionKey(callee.object, context);
15122
- const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
15123
- const pairedReleaseVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
15124
- const pushedResourceCollectionKey = findPushedResourceCollectionKey(usage, context);
15125
- const releaseReceiverForOfStatement = findForOfStatementForIteratorExpression(callee.object, context);
15126
- const releaseReceiverCollectionKey = releaseReceiverForOfStatement ? resolveExpressionKey(releaseReceiverForOfStatement.right, context) : resolveIteratorCollectionKey(callee.object, context);
15127
- if (pairedReleaseVerbNames && matchesPairedReleaseVerb(releaseVerbName, pairedReleaseVerbNames) && pushedResourceCollectionKey !== null && pushedResourceCollectionKey === releaseReceiverCollectionKey && (releaseVerbName !== "unobserve" || usage.eventKey !== null && releaseEventKey === usage.eventKey)) return true;
15128
- if (isReactRefListenerReplacementRelease(callNode, usage, context)) return true;
15129
14770
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
15130
14771
  if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
15131
14772
  if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
15132
- if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
15133
14773
  if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
15134
- if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
15135
14774
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
15136
14775
  if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
14776
+ const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
15137
14777
  const usageEventArgument = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[0] : null;
15138
14778
  const releaseEventArgument = callNode.arguments?.[0];
15139
14779
  if (isAssignmentFormForOfIteratorReference(usageEventArgument, context) || isAssignmentFormForOfIteratorReference(releaseEventArgument, context)) return false;
@@ -15163,12 +14803,9 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15163
14803
  return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
15164
14804
  }
15165
14805
  if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
15166
- const usesUnaryListenerSignature = usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1 && callNode.arguments?.length === 1;
15167
- const releaseHandler = usesUnaryListenerSignature ? callNode.arguments?.[0] : callNode.arguments?.[1];
14806
+ const releaseHandler = callNode.arguments?.[1];
15168
14807
  if (!releaseHandler) return releaseVerbName === "off";
15169
- const expectedHandlerKey = usesUnaryListenerSignature ? usage.eventKey : usage.handlerKey;
15170
- const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignature ? 0 : 1] : null;
15171
- return expectedHandlerKey !== null && resolveExpressionKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
14808
+ return usage.handlerKey !== null && resolveExpressionKey(releaseHandler, context) === usage.handlerKey;
15172
14809
  }
15173
14810
  if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
15174
14811
  return true;
@@ -15181,7 +14818,8 @@ const isReturnedEffectCleanupFunction = (functionNode) => {
15181
14818
  currentNode = parentNode;
15182
14819
  parentNode = currentNode.parent;
15183
14820
  }
15184
- const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
14821
+ if (!isNodeOfType(parentNode, "ReturnStatement") || parentNode.argument !== currentNode) return false;
14822
+ const effectCallback = findEnclosingFunction$1(parentNode);
15185
14823
  const effectCall = effectCallback?.parent;
15186
14824
  return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
15187
14825
  };
@@ -15193,165 +14831,13 @@ const isPotentiallyReachableFunction = (functionNode, context) => {
15193
14831
  if (!symbol) return false;
15194
14832
  return symbol.references.some((reference) => findEnclosingFunction$1(reference.identifier) !== functionNode);
15195
14833
  };
15196
- const findRetainedDisposerStorages = (disposerFunction, usage, context) => {
15197
- if (!isFunctionLike$1(disposerFunction) || disposerFunction.async || disposerFunction.generator) return [];
15198
- const usageFunction = findEnclosingFunction$1(usage.node);
15199
- if (!usageFunction || !isFunctionLike$1(usageFunction)) return [];
15200
- const assignments = /* @__PURE__ */ new Map();
15201
- const collectAssignment = (expression) => {
15202
- const expressionRoot = findTransparentExpressionRoot(expression);
15203
- const assignment = expressionRoot.parent;
15204
- if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.right !== expressionRoot) return;
15205
- const refSymbol = resolveReactRefSymbol(stripParenExpression(assignment.left), context.scopes);
15206
- const refCurrentKey = resolveExpressionKey(assignment.left, context);
15207
- const retainedFunction = findEnclosingFunction$1(assignment);
15208
- const assignmentStart = getRangeStart(assignment);
15209
- if (!refSymbol || !refCurrentKey || !retainedFunction || retainedFunction !== usageFunction || assignmentStart === null) return;
15210
- assignments.set(assignmentStart, {
15211
- assignmentNode: assignment,
15212
- refCurrentKey,
15213
- retainedFunction
15214
- });
15215
- };
15216
- collectAssignment(disposerFunction);
15217
- const bindingIdentifier = getFunctionBindingIdentifier$1(disposerFunction);
15218
- const symbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
15219
- for (const reference of symbol?.references ?? []) collectAssignment(reference.identifier);
15220
- walkAst(usageFunction.body, (child) => {
15221
- if (child !== usageFunction.body && isFunctionLike$1(child)) return false;
15222
- if (isNodeOfType(child, "AssignmentExpression") && resolveStableValue(child.right, context) === disposerFunction) collectAssignment(child.right);
15223
- });
15224
- return [...assignments.values()];
15225
- };
15226
- const isRetainedDisposerStorageEstablished = (storage, usage, context) => doMatchingNodesCoverEveryPathBeforeUsage(usage.node, [storage.assignmentNode], storage.retainedFunction, context) || doMatchingNodesCoverEveryPathAfterUsage(usage.node, [storage.assignmentNode], context);
15227
- const hasUnsafeRetainedDisposerOverwrite = (storage, usage, context) => {
15228
- let hasUnsafeOverwrite = false;
15229
- walkAst(storage.retainedFunction.body, (child) => {
15230
- if (hasUnsafeOverwrite) return false;
15231
- if (child !== storage.retainedFunction.body && isFunctionLike$1(child)) return false;
15232
- if (!isNodeOfType(child, "AssignmentExpression") || child === storage.assignmentNode || resolveExpressionKey(child.left, context) !== storage.refCurrentKey || !canNodeReachLaterNodeWithinFunction(usage.node, child, storage.retainedFunction, context)) return;
15233
- const storedValue = resolveStableValue(child.right, context);
15234
- if (!storedValue || !isFunctionLike$1(storedValue) || !doesCleanupFunctionReleaseUsage(storedValue, usage, context)) {
15235
- hasUnsafeOverwrite = true;
15236
- return false;
15237
- }
15238
- });
15239
- return hasUnsafeOverwrite;
15240
- };
15241
- const hasEffectCleanupInvocation = (storage, usage, context) => {
15242
- const componentFunction = findEnclosingFunction$1(storage.retainedFunction);
15243
- if (!componentFunction || !isFunctionLike$1(componentFunction)) return false;
15244
- const cleanupFunctionInvokesRef = (cleanupFunction) => {
15245
- if (!isFunctionLike$1(cleanupFunction)) return false;
15246
- let didFindCleanupCall = false;
15247
- walkAst(cleanupFunction.body, (child) => {
15248
- if (didFindCleanupCall) return false;
15249
- if (child !== cleanupFunction.body && isFunctionLike$1(child)) return false;
15250
- if (isNodeOfType(child, "CallExpression") && resolveExpressionKey(child.callee, context) === storage.refCurrentKey) {
15251
- const callRoot = findTransparentExpressionRoot(child);
15252
- const callStatement = callRoot.parent;
15253
- const isDirectBlockStatement = isNodeOfType(cleanupFunction.body, "BlockStatement") && isNodeOfType(callStatement, "ExpressionStatement") && callStatement.parent === cleanupFunction.body;
15254
- const isConciseBody = cleanupFunction.body === callRoot;
15255
- if ((isDirectBlockStatement || isConciseBody) && !hasUnprovenReturnBeforeRefOwnedRelease(cleanupFunction, child, storage.refCurrentKey, context)) {
15256
- didFindCleanupCall = true;
15257
- return false;
15258
- }
15259
- }
15260
- });
15261
- return didFindCleanupCall;
15262
- };
15263
- const effectReturnsCleanup = (effectCallback) => {
15264
- if (!isFunctionLike$1(effectCallback)) return false;
15265
- if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
15266
- const cleanupFunction = resolveRefOwnedCleanupFunction(effectCallback.body, context);
15267
- return Boolean(cleanupFunction && cleanupFunctionInvokesRef(cleanupFunction));
15268
- }
15269
- const matchingReturns = [];
15270
- walkInsideStatementBlocks(effectCallback.body, (child) => {
15271
- if (!isNodeOfType(child, "ReturnStatement") || !child.argument) return;
15272
- const cleanupFunction = resolveRefOwnedCleanupFunction(child.argument, context);
15273
- if (!cleanupFunction || !cleanupFunctionInvokesRef(cleanupFunction)) return;
15274
- matchingReturns.push(child);
15275
- });
15276
- return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15277
- };
15278
- let didFindInvocation = false;
15279
- walkAst(componentFunction.body, (child) => {
15280
- if (didFindInvocation) return false;
15281
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactApiCall(child, "useEffect", context.scopes)) return;
15282
- const effectCallback = getEffectCallback(child);
15283
- if (effectCallback && effectReturnsCleanup(effectCallback)) {
15284
- didFindInvocation = true;
15285
- return false;
15286
- }
15287
- });
15288
- return didFindInvocation;
15289
- };
15290
- const hasCallbackRefReplacementInvocation = (storage, usage, context) => {
15291
- const isReturnedCallbackRefShape = () => {
15292
- if (!isFunctionLike$1(storage.retainedFunction)) return false;
15293
- const callbackCall = findTransparentExpressionRoot(storage.retainedFunction).parent;
15294
- if (!isNodeOfType(callbackCall, "CallExpression") || !isReactApiCall(callbackCall, "useCallback", context.scopes)) return false;
15295
- const nodeParameter = storage.retainedFunction.params?.[0];
15296
- const nodeParameterKey = resolveExpressionKey(nodeParameter, context);
15297
- if (!nodeParameterKey || usage.receiverKey !== nodeParameterKey) return false;
15298
- if (!isFunctionReturnedFromReactHook(storage.retainedFunction, context, false)) return false;
15299
- const usageStart = getRangeStart(usage.node);
15300
- if (usageStart === null) return false;
15301
- let hasNullExit = false;
15302
- walkAst(storage.retainedFunction.body, (child) => {
15303
- if (hasNullExit) return false;
15304
- if (child !== storage.retainedFunction.body && isFunctionLike$1(child)) return false;
15305
- if (!isNodeOfType(child, "IfStatement") || (getRangeStart(child) ?? usageStart) >= usageStart) return;
15306
- const test = stripParenExpression(child.test);
15307
- if (!isNodeOfType(test, "UnaryExpression") || test.operator !== "!" || resolveExpressionKey(test.argument, context) !== nodeParameterKey) return;
15308
- const consequent = child.consequent;
15309
- hasNullExit = isNodeOfType(consequent, "ReturnStatement") || isNodeOfType(consequent, "BlockStatement") && consequent.body.some((statement) => isNodeOfType(statement, "ReturnStatement"));
15310
- if (hasNullExit) return false;
15311
- });
15312
- return hasNullExit;
15313
- };
15314
- if (!isFunctionForwardedToReactRef(storage.retainedFunction, context) && !isReturnedCallbackRefShape()) return false;
15315
- const cleanupCalls = [];
15316
- walkAst(storage.retainedFunction.body, (child) => {
15317
- if (child !== storage.retainedFunction.body && isFunctionLike$1(child)) return false;
15318
- if (isNodeOfType(child, "CallExpression") && resolveExpressionKey(child.callee, context) === storage.refCurrentKey) cleanupCalls.push(child);
15319
- });
15320
- return doMatchingNodesCoverEveryPathBeforeUsage(usage.node, cleanupCalls, storage.retainedFunction, context);
15321
- };
15322
- const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
15323
- const disposerFunction = findEnclosingFunction$1(releaseNode);
15324
- if (!disposerFunction) return false;
15325
- return findRetainedDisposerStorages(disposerFunction, usage, context).some((storage) => isRetainedDisposerStorageEstablished(storage, usage, context) && !hasUnsafeRetainedDisposerOverwrite(storage, usage, context) && (hasEffectCleanupInvocation(storage, usage, context) || hasCallbackRefReplacementInvocation(storage, usage, context)));
15326
- };
15327
- const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
15328
- if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseNode], context)) return false;
15329
- const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15330
- const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
15331
- if (!isNodeOfType(releaseCall, "CallExpression")) return false;
15332
- const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15333
- if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15334
- const ownerFunction = findEnclosingFunction$1(releaseFunction);
15335
- if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
15336
- const triggerRegistrations = [];
15337
- walkAst(ownerFunction.body, (child) => {
15338
- if (child !== ownerFunction.body && isFunctionLike$1(child)) return false;
15339
- if (!isNodeOfType(child, "CallExpression")) return;
15340
- const registrationDetails = getCallRegistrationDetails(child, context);
15341
- if (registrationDetails.registrationVerbName === "addEventListener" && registrationDetails.receiverKey === usage.receiverKey && resolveStableValue(child.arguments?.[1], context) === releaseFunction) triggerRegistrations.push(child);
15342
- });
15343
- if (triggerRegistrations.some((triggerRegistration) => triggerRegistration === usage.node)) return true;
15344
- return doMatchingNodesCoverEveryPathAfterUsage(usage.node, triggerRegistrations, context) || doMatchingNodesCoverEveryPathBeforeUsage(usage.node, triggerRegistrations, ownerFunction, context);
15345
- };
15346
14834
  const isReleaseReachableForUsage = (releaseNode, usage, context) => {
15347
14835
  if (!isNodeReachableWithinFunction(releaseNode, context)) return false;
15348
14836
  const releaseFunction = findEnclosingFunction$1(releaseNode);
15349
14837
  if (!releaseFunction) return true;
15350
14838
  if (releaseFunction === findEnclosingFunction$1(usage.node)) return true;
15351
- if (isRetainedDisposerRefRelease(releaseNode, usage, context)) return true;
15352
14839
  const usageFunction = findEnclosingFunction$1(usage.node);
15353
14840
  if (usageFunction && isFunctionLike$1(usageFunction) && getAssignedReactRefSymbol(usageFunction, context) && isCleanupFunctionReferencedByReturn(usageFunction, releaseFunction, context)) return isReactRefCallbackCleanupOwnedByEffect(usageFunction, releaseFunction, usage, context);
15354
- if (isSelfReleasingListenerRelease(releaseNode, releaseFunction, usage, context)) return true;
15355
14841
  return isPotentiallyReachableFunction(releaseFunction, context);
15356
14842
  };
15357
14843
  const fileContainsReleaseForUsage = (usage, context) => {
@@ -15619,11 +15105,6 @@ const doesResourceResultEscape = (resourceNode, allowReturnedResourceEscape, all
15619
15105
  parentNode = currentNode.parent;
15620
15106
  continue;
15621
15107
  }
15622
- if (isNodeOfType(parentNode, "ConditionalExpression") && (parentNode.consequent === currentNode || parentNode.alternate === currentNode) || isNodeOfType(parentNode, "LogicalExpression") && (parentNode.right === currentNode || parentNode.left === currentNode && parentNode.operator !== "&&")) {
15623
- currentNode = parentNode;
15624
- parentNode = currentNode.parent;
15625
- continue;
15626
- }
15627
15108
  if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode && isNodeOfType(parentNode.id, "Identifier") && isNodeOfType(parentNode.parent, "VariableDeclaration") && parentNode.parent.kind === "const") {
15628
15109
  const ownerFunction = findEnclosingFunction$1(resourceNode);
15629
15110
  const resourceSymbol = context.scopes.symbolFor(parentNode.id);
@@ -17283,38 +16764,7 @@ const symbolHasStableImportedAlias = (symbol, scopes) => {
17283
16764
  const resolvedSymbol = resolveConstIdentifierAlias(symbol.bindingIdentifier, scopes);
17284
16765
  return resolvedSymbol !== null && resolvedSymbol !== symbol && resolvedSymbol.kind === "import";
17285
16766
  };
17286
- const isAssignmentTarget = (node) => {
17287
- let currentNode = findTransparentExpressionRoot(node);
17288
- while (currentNode.parent) {
17289
- const parentNode = currentNode.parent;
17290
- if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
17291
- if (isNodeOfType(parentNode, "UpdateExpression")) return parentNode.argument === currentNode;
17292
- if (isNodeOfType(parentNode, "UnaryExpression")) return parentNode.operator === "delete" && parentNode.argument === currentNode;
17293
- if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
17294
- if (isNodeOfType(parentNode, "ArrayPattern") && parentNode.elements.some((element) => element === currentNode) || isNodeOfType(parentNode, "ObjectPattern") && parentNode.properties.some((property) => property === currentNode) || isNodeOfType(parentNode, "RestElement") && parentNode.argument === currentNode || isNodeOfType(parentNode, "AssignmentPattern") && parentNode.left === currentNode || isNodeOfType(parentNode, "Property") && parentNode.value === currentNode && isNodeOfType(parentNode.parent, "ObjectPattern")) {
17295
- currentNode = parentNode;
17296
- continue;
17297
- }
17298
- return false;
17299
- }
17300
- return false;
17301
- };
17302
- const symbolHasStableRefLazyInitialization = (symbol, scopes) => {
17303
- if (symbol.kind !== "const" || symbol.references.some((reference) => reference.flag !== "read")) return false;
17304
- const initializer = symbol.initializer ? unwrapExpression$3(symbol.initializer) : null;
17305
- if (!isNodeOfType(initializer, "AssignmentExpression") || initializer.operator !== "??=") return false;
17306
- const refSymbol = resolveReactRefSymbol(unwrapExpression$3(initializer.left), scopes);
17307
- if (!refSymbol) return false;
17308
- return refSymbol.references.every((reference) => {
17309
- const memberExpression = findTransparentExpressionRoot(reference.identifier).parent;
17310
- if (!isNodeOfType(memberExpression, "MemberExpression") || unwrapExpression$3(memberExpression.object) !== reference.identifier || getStaticPropertyName(memberExpression) !== "current") return false;
17311
- const referenceRoot = findTransparentExpressionRoot(memberExpression);
17312
- const parentNode = referenceRoot.parent;
17313
- if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.left === referenceRoot) return parentNode === initializer;
17314
- return !isAssignmentTarget(referenceRoot);
17315
- });
17316
- };
17317
- const symbolHasStableValue = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => symbolHasStableHookOrigin(symbol, scopes) || symbolHasStableRefLazyInitialization(symbol, scopes) || symbolHasStableImportedAlias(symbol, scopes) || symbolHasStableFunctionOrigin(symbol, scopes, visitedSymbolIds) || symbolHasStableMemoizedOrigin(symbol, scopes, visitedSymbolIds);
16767
+ const symbolHasStableValue = (symbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => symbolHasStableHookOrigin(symbol, scopes) || symbolHasStableImportedAlias(symbol, scopes) || symbolHasStableFunctionOrigin(symbol, scopes, visitedSymbolIds) || symbolHasStableMemoizedOrigin(symbol, scopes, visitedSymbolIds);
17318
16768
  //#endregion
17319
16769
  //#region src/plugin/utils/symbol-has-react-use-effect-event-origin.ts
17320
16770
  const symbolHasReactUseEffectEventOrigin = (symbol, scopes) => {
@@ -17516,7 +16966,7 @@ const collectCaptureDepKeys = (callback, scopes, declaredExactBindingKeys, allow
17516
16966
  keys.add(depKey);
17517
16967
  continue;
17518
16968
  }
17519
- const identitySourceKeys = resolvePureCalledFunctionSourceKeys(reference, symbol, scopes) ?? resolveRenderDerivedMutableSourceKeys(reference, symbol, scopes) ?? resolveReactiveIdentitySourceKeys(symbol, scopes);
16969
+ const identitySourceKeys = resolveReactiveIdentitySourceKeys(symbol, scopes);
17520
16970
  if (identitySourceKeys) {
17521
16971
  if (identitySourceKeys.size === 0) stableCapturedNames.add(depKey);
17522
16972
  for (const identitySourceKey of identitySourceKeys) keys.add(identitySourceKey);
@@ -17599,161 +17049,6 @@ const resolveReactiveIdentitySourceKeys = (symbol, scopes) => {
17599
17049
  if (symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier || symbol.references.some((reference) => reference.flag !== "read")) return null;
17600
17050
  return resolveIdentitySourceKeysFromExpression(symbol.initializer, scopes, new Set([symbol.id]));
17601
17051
  };
17602
- const isPureDerivedExpression = (expression) => {
17603
- const candidate = unwrapExpression$3(expression);
17604
- if (isNodeOfType(candidate, "Literal") || isNodeOfType(candidate, "Identifier")) return true;
17605
- if (isNodeOfType(candidate, "MemberExpression")) return isPureDerivedExpression(candidate.object) && (!candidate.computed || isPureDerivedExpression(candidate.property));
17606
- if (isNodeOfType(candidate, "BinaryExpression") || isNodeOfType(candidate, "LogicalExpression")) return isPureDerivedExpression(candidate.left) && isPureDerivedExpression(candidate.right);
17607
- if (isNodeOfType(candidate, "UnaryExpression")) return candidate.operator !== "delete" && isPureDerivedExpression(candidate.argument);
17608
- if (isNodeOfType(candidate, "ConditionalExpression")) return isPureDerivedExpression(candidate.test) && isPureDerivedExpression(candidate.consequent) && isPureDerivedExpression(candidate.alternate);
17609
- if (isNodeOfType(candidate, "TemplateLiteral")) return candidate.expressions.every((nestedExpression) => isPureDerivedExpression(nestedExpression));
17610
- return false;
17611
- };
17612
- const isPureDerivedStatement = (statement) => {
17613
- if (isNodeOfType(statement, "BlockStatement")) return statement.body.every((nestedStatement) => isPureDerivedStatement(nestedStatement));
17614
- if (isNodeOfType(statement, "ReturnStatement")) return !statement.argument || isPureDerivedExpression(statement.argument);
17615
- if (isNodeOfType(statement, "IfStatement")) return isPureDerivedExpression(statement.test) && isPureDerivedStatement(statement.consequent) && (!statement.alternate || isPureDerivedStatement(statement.alternate));
17616
- return false;
17617
- };
17618
- const isPureDerivedFunction = (functionNode) => {
17619
- if (!isNodeOfType(functionNode, "FunctionDeclaration") && !isNodeOfType(functionNode, "FunctionExpression") && !isNodeOfType(functionNode, "ArrowFunctionExpression")) return false;
17620
- if (functionNode.async || functionNode.generator) return false;
17621
- return isNodeOfType(functionNode.body, "BlockStatement") ? isPureDerivedStatement(functionNode.body) : isPureDerivedExpression(functionNode.body);
17622
- };
17623
- const resolvePureCalledFunctionSourceKeys = (reference, symbol, scopes) => {
17624
- if (symbol.references.some((symbolReference) => symbolReference.flag !== "read")) return null;
17625
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
17626
- const callExpression = referenceRoot.parent;
17627
- if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== referenceRoot) return null;
17628
- const functionNode = getFunctionValueNode(symbol);
17629
- if (!functionNode || !isPureDerivedFunction(functionNode)) return null;
17630
- const sourceKeys = /* @__PURE__ */ new Set();
17631
- for (const capturedReference of closureCaptures(functionNode, scopes)) {
17632
- const capturedSymbol = capturedReference.resolvedSymbol;
17633
- if (!capturedSymbol || capturedSymbol.id === symbol.id) continue;
17634
- if (isOutsideAllFunctions(capturedSymbol) || symbolHasStableValue(capturedSymbol, scopes)) continue;
17635
- const capturedKey = computeDepKey(capturedReference);
17636
- if (!capturedKey) return null;
17637
- if (capturedKey === capturedSymbol.name) {
17638
- const nestedSourceKeys = resolveReactiveIdentitySourceKeys(capturedSymbol, scopes);
17639
- if (nestedSourceKeys) {
17640
- for (const nestedSourceKey of nestedSourceKeys) sourceKeys.add(nestedSourceKey);
17641
- continue;
17642
- }
17643
- }
17644
- sourceKeys.add(capturedKey);
17645
- }
17646
- return sourceKeys.size > 0 ? sourceKeys : null;
17647
- };
17648
- const mergeDerivedExpressionSourceKeys = (expressions, scopes, visitedSymbolIds) => {
17649
- const sourceKeys = /* @__PURE__ */ new Set();
17650
- for (const expression of expressions) {
17651
- const expressionSourceKeys = resolveDerivedExpressionSourceKeys(expression, scopes, visitedSymbolIds);
17652
- if (!expressionSourceKeys) return null;
17653
- for (const expressionSourceKey of expressionSourceKeys) sourceKeys.add(expressionSourceKey);
17654
- }
17655
- return sourceKeys;
17656
- };
17657
- const resolveDerivedExpressionSourceKeys = (expression, scopes, visitedSymbolIds) => {
17658
- const candidate = unwrapExpression$3(expression);
17659
- if (isNodeOfType(candidate, "Literal")) return /* @__PURE__ */ new Set();
17660
- if (isNodeOfType(candidate, "Identifier")) {
17661
- if (scopes.isGlobalReference(candidate)) return /* @__PURE__ */ new Set();
17662
- const sourceSymbol = scopes.symbolFor(candidate);
17663
- if (!sourceSymbol) return null;
17664
- if (isOutsideAllFunctions(sourceSymbol) || symbolHasStableValue(sourceSymbol, scopes)) return /* @__PURE__ */ new Set();
17665
- if (sourceSymbol.kind === "const" && sourceSymbol.initializer && isNodeOfType(sourceSymbol.declarationNode, "VariableDeclarator") && sourceSymbol.declarationNode.id === sourceSymbol.bindingIdentifier && sourceSymbol.references.every((sourceReference) => sourceReference.flag === "read") && !visitedSymbolIds.has(sourceSymbol.id)) {
17666
- visitedSymbolIds.add(sourceSymbol.id);
17667
- const sourceKeys = resolveDerivedExpressionSourceKeys(sourceSymbol.initializer, scopes, visitedSymbolIds);
17668
- visitedSymbolIds.delete(sourceSymbol.id);
17669
- if (sourceKeys) return sourceKeys;
17670
- }
17671
- return new Set([sourceSymbol.name]);
17672
- }
17673
- if (isNodeOfType(candidate, "MemberExpression")) {
17674
- if (hasComputedMemberExpression(candidate)) return null;
17675
- const sourceKey = stringifyMemberChain(candidate);
17676
- const rootIdentifier = getMemberRootIdentifier(candidate);
17677
- const rootSymbol = rootIdentifier ? scopes.symbolFor(rootIdentifier) : null;
17678
- if (!sourceKey || !rootSymbol) return null;
17679
- if (isOutsideAllFunctions(rootSymbol) || symbolHasStableValue(rootSymbol, scopes)) return /* @__PURE__ */ new Set();
17680
- return new Set([sourceKey]);
17681
- }
17682
- if (isNodeOfType(candidate, "BinaryExpression") || isNodeOfType(candidate, "LogicalExpression")) return mergeDerivedExpressionSourceKeys([candidate.left, candidate.right], scopes, visitedSymbolIds);
17683
- if (isNodeOfType(candidate, "UnaryExpression") && candidate.operator !== "delete") return resolveDerivedExpressionSourceKeys(candidate.argument, scopes, visitedSymbolIds);
17684
- if (isNodeOfType(candidate, "ConditionalExpression")) return mergeDerivedExpressionSourceKeys([
17685
- candidate.test,
17686
- candidate.consequent,
17687
- candidate.alternate
17688
- ], scopes, visitedSymbolIds);
17689
- if (isNodeOfType(candidate, "TemplateLiteral")) return mergeDerivedExpressionSourceKeys(candidate.expressions, scopes, visitedSymbolIds);
17690
- if (isNodeOfType(candidate, "NewExpression")) {
17691
- const callee = unwrapExpression$3(candidate.callee);
17692
- if (!isNodeOfType(callee, "Identifier") || callee.name !== "Error" || !scopes.isGlobalReference(callee)) return null;
17693
- const argumentsToAnalyze = [];
17694
- for (const argument of candidate.arguments) {
17695
- if (!isAstNode(argument) || isNodeOfType(argument, "SpreadElement")) return null;
17696
- argumentsToAnalyze.push(argument);
17697
- }
17698
- return mergeDerivedExpressionSourceKeys(argumentsToAnalyze, scopes, visitedSymbolIds);
17699
- }
17700
- return null;
17701
- };
17702
- const resolveWriteControlSourceKeys = (assignment, boundaryFunction, scopes) => {
17703
- const sourceKeys = /* @__PURE__ */ new Set();
17704
- let currentNode = assignment;
17705
- while (currentNode.parent && currentNode.parent !== boundaryFunction) {
17706
- const parentNode = currentNode.parent;
17707
- if (isNodeOfType(parentNode, "IfStatement")) {
17708
- if (parentNode.test === currentNode) return null;
17709
- const testSourceKeys = resolveDerivedExpressionSourceKeys(parentNode.test, scopes, /* @__PURE__ */ new Set());
17710
- if (!testSourceKeys) return null;
17711
- for (const testSourceKey of testSourceKeys) sourceKeys.add(testSourceKey);
17712
- } else if (!isNodeOfType(parentNode, "ExpressionStatement") && !isNodeOfType(parentNode, "BlockStatement")) return null;
17713
- currentNode = parentNode;
17714
- }
17715
- return currentNode.parent === boundaryFunction ? sourceKeys : null;
17716
- };
17717
- const isReadOnlyInitialStateUse = (referenceNode, scopes) => {
17718
- const referenceRoot = findTransparentExpressionRoot(referenceNode);
17719
- const callExpression = referenceRoot.parent;
17720
- return isNodeOfType(callExpression, "CallExpression") && callExpression.arguments.some((argument) => argument === referenceRoot) && isReactApiCall(callExpression, "useState", scopes, {
17721
- allowGlobalReactNamespace: true,
17722
- allowUnboundBareCalls: true,
17723
- resolveNamedAliases: true
17724
- });
17725
- };
17726
- const resolveRenderDerivedMutableSourceKeys = (capturedReference, symbol, scopes) => {
17727
- if (symbol.kind !== "let" || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
17728
- const boundaryFunction = findEnclosingFunction$1(symbol.bindingIdentifier);
17729
- if (!boundaryFunction) return null;
17730
- const capturingFunction = findEnclosingFunction$1(capturedReference.identifier);
17731
- if (!capturingFunction || capturingFunction === boundaryFunction) return null;
17732
- const sourceKeys = /* @__PURE__ */ new Set();
17733
- if (symbol.initializer) {
17734
- const initializerSourceKeys = resolveDerivedExpressionSourceKeys(symbol.initializer, scopes, new Set([symbol.id]));
17735
- if (!initializerSourceKeys) return null;
17736
- for (const initializerSourceKey of initializerSourceKeys) sourceKeys.add(initializerSourceKey);
17737
- }
17738
- let writeCount = 0;
17739
- for (const symbolReference of symbol.references) {
17740
- if (symbolReference.flag === "read") {
17741
- if (findEnclosingFunction$1(symbolReference.identifier) !== capturingFunction && !isReadOnlyInitialStateUse(symbolReference.identifier, scopes)) return null;
17742
- continue;
17743
- }
17744
- if (symbolReference.flag !== "write") return null;
17745
- const referenceRoot = findTransparentExpressionRoot(symbolReference.identifier);
17746
- const assignment = referenceRoot.parent;
17747
- if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== referenceRoot || findEnclosingFunction$1(referenceRoot) !== boundaryFunction) return null;
17748
- const assignmentSourceKeys = resolveDerivedExpressionSourceKeys(assignment.right, scopes, new Set([symbol.id]));
17749
- const controlSourceKeys = resolveWriteControlSourceKeys(assignment, boundaryFunction, scopes);
17750
- if (!assignmentSourceKeys || !controlSourceKeys) return null;
17751
- for (const assignmentSourceKey of assignmentSourceKeys) sourceKeys.add(assignmentSourceKey);
17752
- for (const controlSourceKey of controlSourceKeys) sourceKeys.add(controlSourceKey);
17753
- writeCount += 1;
17754
- }
17755
- return writeCount > 0 && sourceKeys.size > 0 ? sourceKeys : null;
17756
- };
17757
17052
  const isUseCallbackResultDep = (node, scopes) => {
17758
17053
  const rootSymbol = getRootSymbol(node, scopes);
17759
17054
  const initializer = rootSymbol?.initializer ? unwrapExpression$3(rootSymbol.initializer) : null;
@@ -18497,7 +17792,7 @@ If the missing value is recreated every render, move it inside the hook or stabi
18497
17792
  if (!isUsed) continue;
18498
17793
  const reportNode = declaredKeyToReportNode.get(declaredKey) ?? depsArgument;
18499
17794
  const rootSymbol = getRootSymbol(reportNode, context.scopes);
18500
- if (!rootSymbol || !hasDirectIdentifierDeclarator(rootSymbol) || symbolHasStableValue(rootSymbol, context.scopes) || !isUnstableInitializer(rootSymbol.initializer)) continue;
17795
+ if (!rootSymbol || !hasDirectIdentifierDeclarator(rootSymbol) || !isUnstableInitializer(rootSymbol.initializer)) continue;
18501
17796
  context.report({
18502
17797
  node: reportNode,
18503
17798
  message: buildUnstableDepMessage(hookName, declaredKey)
@@ -29981,7 +29276,7 @@ const nextjsNoVercelOgImport = defineRule({
29981
29276
  //#endregion
29982
29277
  //#region src/plugin/rules/a11y/no-access-key.ts
29983
29278
  const MESSAGE$39 = "Screen reader users can lose their shortcuts because `accessKey` clashes with them, so remove it.";
29984
- const isUndefinedIdentifier$1 = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
29279
+ const isUndefinedIdentifier = (expression) => isNodeOfType(expression, "Identifier") && expression.name === "undefined";
29985
29280
  const noAccessKey = defineRule({
29986
29281
  id: "no-access-key",
29987
29282
  title: "accessKey attribute used",
@@ -30006,7 +29301,7 @@ const noAccessKey = defineRule({
30006
29301
  if (isNodeOfType(attributeValue, "JSXExpressionContainer")) {
30007
29302
  const expression = attributeValue.expression;
30008
29303
  if (!expression || expression.type === "JSXEmptyExpression") return;
30009
- if (isUndefinedIdentifier$1(expression)) return;
29304
+ if (isUndefinedIdentifier(expression)) return;
30010
29305
  context.report({
30011
29306
  node: accessKey,
30012
29307
  message: MESSAGE$39
@@ -30771,12 +30066,6 @@ const isReactNamespaceImportReference = (ref) => Boolean(ref?.resolved?.defs.som
30771
30066
  const importDeclaration = declarationNode.parent;
30772
30067
  return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && isNodeOfType(importDeclaration.source, "Literal") && importDeclaration.source.value === "react");
30773
30068
  }));
30774
- const isReactNamespaceReceiver = (analysis, node) => {
30775
- const receiver = stripParenExpression(node);
30776
- if (!isNodeOfType(receiver, "Identifier")) return false;
30777
- const namespaceReference = getRef(analysis, receiver);
30778
- return namespaceReference?.resolved ? isReactNamespaceImportReference(namespaceReference) : receiver.name === "React";
30779
- };
30780
30069
  const isGenuineReactHookDeclarator = (analysis, declarator, hookName) => {
30781
30070
  if (!isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.init, "CallExpression")) return false;
30782
30071
  const callee = stripParenExpression(declarator.init.callee);
@@ -30785,20 +30074,24 @@ const isGenuineReactHookDeclarator = (analysis, declarator, hookName) => {
30785
30074
  if (!reference?.resolved) return callee.name === hookName;
30786
30075
  return isReactNamedImportReference(reference, hookName);
30787
30076
  }
30788
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== hookName) return false;
30789
- return isReactNamespaceReceiver(analysis, callee.object);
30077
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.object, "Identifier") || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== hookName) return false;
30078
+ const namespaceReference = getRef(analysis, callee.object);
30079
+ if (!namespaceReference?.resolved) return callee.object.name === "React";
30080
+ return isReactNamespaceImportReference(namespaceReference);
30790
30081
  };
30791
30082
  const isHookCallee$1 = (analysis, node, hookName) => {
30792
30083
  if (!node) return false;
30793
30084
  if (isNodeOfType(node, "Identifier")) {
30794
30085
  if (node.name === hookName) return true;
30795
30086
  if (isReactNamedImportReference(getRef(analysis, node), hookName)) return true;
30796
- const receiverRoot = findTransparentExpressionRoot(node);
30797
- const parent = receiverRoot.parent;
30798
- if (parent && isNodeOfType(parent, "MemberExpression") && parent.object === receiverRoot && isReactNamespaceReceiver(analysis, node) && isNodeOfType(parent.property, "Identifier") && parent.property.name === hookName) return true;
30087
+ const parent = node.parent;
30088
+ if (parent && isNodeOfType(parent, "MemberExpression") && isNodeOfType(parent.object, "Identifier") && parent.object.name === "React" && isNodeOfType(parent.property, "Identifier") && parent.property.name === hookName) return true;
30799
30089
  return false;
30800
30090
  }
30801
- if (isNodeOfType(node, "MemberExpression")) return isReactNamespaceReceiver(analysis, node.object) && isNodeOfType(node.property, "Identifier") && node.property.name === hookName;
30091
+ if (isNodeOfType(node, "MemberExpression")) {
30092
+ const receiver = stripParenExpression(node.object);
30093
+ return isNodeOfType(receiver, "Identifier") && receiver.name === "React" && isNodeOfType(node.property, "Identifier") && node.property.name === hookName;
30094
+ }
30802
30095
  return false;
30803
30096
  };
30804
30097
  const isUseEffect = (node) => {
@@ -30906,27 +30199,7 @@ const isRefCurrent = (ref) => {
30906
30199
  if (!isNodeOfType(parent.property, "Identifier")) return false;
30907
30200
  return parent.property.name === "current";
30908
30201
  };
30909
- const resolveStateSetterReference = (analysis, ref) => {
30910
- const visitedReferences = /* @__PURE__ */ new Set();
30911
- let currentReference = ref;
30912
- while (currentReference && !visitedReferences.has(currentReference)) {
30913
- if (isStateSetter(analysis, currentReference)) return currentReference;
30914
- visitedReferences.add(currentReference);
30915
- const definitions = currentReference.resolved?.defs ?? [];
30916
- if (definitions.length !== 1) return null;
30917
- const definitionNode = definitions[0].node;
30918
- if (!isNodeOfType(definitionNode, "VariableDeclarator")) return null;
30919
- if (!isNodeOfType(definitionNode.id, "Identifier")) return null;
30920
- const declaration = definitionNode.parent;
30921
- if (!isNodeOfType(declaration, "VariableDeclaration") || declaration.kind !== "const") return null;
30922
- if (!definitionNode.init) return null;
30923
- const initializer = stripParenExpression(definitionNode.init);
30924
- if (!isNodeOfType(initializer, "Identifier")) return null;
30925
- currentReference = getRef(analysis, initializer);
30926
- }
30927
- return null;
30928
- };
30929
- const isStateSetterCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => resolveStateSetterReference(analysis, innerRef) !== null);
30202
+ const isStateSetterCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => isStateSetter(analysis, innerRef));
30930
30203
  const isSyncStateSetterCall = (analysis, ref, effectFn) => isStateSetterCall(analysis, ref) && isSynchronous(ref.identifier, effectFn) && !resolvesToAsyncFunction(ref);
30931
30204
  const HANDLER_NAMED_METHOD_PATTERN = /^(on|handle)[A-Z]/;
30932
30205
  const SYNCHRONOUS_CALLBACK_ARGUMENT_INDEX_BY_METHOD = new Map([
@@ -31077,11 +30350,9 @@ const isPropCallbackInvocationRef = (analysis, ref, options = {}) => {
31077
30350
  };
31078
30351
  const isRefCall = (analysis, ref) => isEventualCallTo(analysis, ref, (innerRef) => isRefCurrent(innerRef) || isRef(analysis, innerRef));
31079
30352
  const getUseStateDecl = (analysis, ref) => {
31080
- const definition = getUpstreamRefs(analysis, ref).find((upstreamReference) => isState(analysis, upstreamReference) || isStateSetter(analysis, upstreamReference))?.resolved?.defs.find((candidateDefinition) => {
31081
- const definitionNode = candidateDefinition.node;
31082
- return isNodeOfType(definitionNode, "VariableDeclarator") && isNodeOfType(definitionNode.init, "CallExpression") && isHookCallee$1(analysis, definitionNode.init.callee, "useState");
31083
- });
31084
- return definition ? definition.node : null;
30353
+ let node = getUpstreamRefs(analysis, ref).find((upRef) => isHookCallee$1(analysis, upRef.identifier, "useState"))?.identifier;
30354
+ while (node && !isNodeOfType(node, "VariableDeclarator")) node = node.parent;
30355
+ return node ?? null;
31085
30356
  };
31086
30357
  const isCleanupReturnArgument = (analysis, node) => {
31087
30358
  if (isFunctionLike$1(node)) return true;
@@ -31244,88 +30515,7 @@ const isIndependentWriterIdentifier = (componentFunction, identifier, includeDef
31244
30515
  if (HANDLER_BINDING_NAME_PATTERN.test(bindingName)) return true;
31245
30516
  return isSetterWiredToJsxHandler(componentFunction, bindingName);
31246
30517
  };
31247
- const isSynchronousFunction = (functionNode) => {
31248
- const functionMetadata = functionNode;
31249
- return functionMetadata.async !== true && functionMetadata.generator !== true;
31250
- };
31251
- const findBindingVariable = (analysis, bindingIdentifier) => {
31252
- for (const scope of analysis.scopeManager.scopes) for (const variable of scope.variables) if (variable.identifiers.includes(bindingIdentifier)) return variable;
31253
- return null;
31254
- };
31255
- const getImmutableFunctionVariable = (analysis, componentFunction, functionNode) => {
31256
- if (!isSynchronousFunction(functionNode) || !isAstDescendant(functionNode, componentFunction)) return null;
31257
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
31258
- if (!bindingIdentifier) return null;
31259
- const variable = findBindingVariable(analysis, bindingIdentifier);
31260
- if (!variable || variable.defs.length !== 1 || variable.references.some((reference) => reference.isWrite() && !reference.init)) return null;
31261
- const definition = variable.defs[0];
31262
- if (definition.type === "FunctionName") return definition.node === functionNode ? variable : null;
31263
- if (definition.type !== "Variable") return null;
31264
- const declarator = definition.node;
31265
- if (!isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.parent, "VariableDeclaration") || declarator.parent.kind !== "const") return null;
31266
- if (declarator.init === functionNode) return variable;
31267
- if (isNodeOfType(declarator.init, "CallExpression") && declarator.init.arguments?.[0] === functionNode && isGenuineReactHookDeclarator(analysis, declarator, "useCallback")) return variable;
31268
- return null;
31269
- };
31270
- const getJsxEventValueAttribute = (identifier) => {
31271
- const expression = findTransparentExpressionRoot(identifier);
31272
- const expressionContainer = expression.parent;
31273
- if (!isNodeOfType(expressionContainer, "JSXExpressionContainer") || expressionContainer.expression !== expression) return null;
31274
- const attribute = expressionContainer.parent;
31275
- if (!isNodeOfType(attribute, "JSXAttribute")) return null;
31276
- const attributeName = getJsxAttributeName(attribute.name);
31277
- return attributeName && isEventHandlerName(attributeName) ? attribute : null;
31278
- };
31279
- const getInlineJsxEventCallbackAttribute = (callExpression) => {
31280
- const callbackFunction = findEnclosingFunction$1(callExpression);
31281
- if (!callbackFunction || !isSynchronousFunction(callbackFunction)) return null;
31282
- return getJsxEventValueAttribute(callbackFunction);
31283
- };
31284
- const isReactHookDependencyReference = (identifier) => {
31285
- const expression = findTransparentExpressionRoot(identifier);
31286
- const dependencyArray = expression.parent;
31287
- if (!isNodeOfType(dependencyArray, "ArrayExpression") || !(dependencyArray.elements ?? []).includes(expression)) return false;
31288
- const hookCall = dependencyArray.parent;
31289
- if (!isNodeOfType(hookCall, "CallExpression") || hookCall.arguments?.[1] !== dependencyArray) return false;
31290
- const callee = hookCall.callee;
31291
- if (isNodeOfType(callee, "Identifier")) return /^use[A-Z0-9]/.test(callee.name);
31292
- return Boolean(isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && /^use[A-Z0-9]/.test(callee.property.name));
31293
- };
31294
- const hasReachableJsxEventCallPath = (analysis, context, componentFunction, functionVariable, visitedVariables) => {
31295
- if (visitedVariables.has(functionVariable)) return false;
31296
- const nextVisitedVariables = new Set(visitedVariables).add(functionVariable);
31297
- const callExpressions = [];
31298
- let hasDirectJsxEventReference = false;
31299
- for (const reference of functionVariable.references) {
31300
- if (reference.init) continue;
31301
- const identifier = reference.identifier;
31302
- if (reference.isWrite()) return false;
31303
- const jsxEventValueAttribute = getJsxEventValueAttribute(identifier);
31304
- if (jsxEventValueAttribute) {
31305
- if (isNodeReachableWithinFunction(jsxEventValueAttribute, context)) hasDirectJsxEventReference = true;
31306
- continue;
31307
- }
31308
- if (isReactHookDependencyReference(identifier)) continue;
31309
- const callExpression = getCallExpr(reference);
31310
- if (!callExpression) return false;
31311
- const jsxEventCallbackAttribute = getInlineJsxEventCallbackAttribute(callExpression);
31312
- if (jsxEventCallbackAttribute) {
31313
- if (isNodeReachableWithinFunction(callExpression, context) && isNodeReachableWithinFunction(jsxEventCallbackAttribute, context)) hasDirectJsxEventReference = true;
31314
- continue;
31315
- }
31316
- callExpressions.push(callExpression);
31317
- }
31318
- if (hasDirectJsxEventReference) return true;
31319
- for (const callExpression of callExpressions) {
31320
- if (!isNodeReachableWithinFunction(callExpression, context)) continue;
31321
- const callerFunction = findEnclosingFunction$1(callExpression);
31322
- if (!callerFunction || callerFunction === componentFunction) continue;
31323
- const callerVariable = getImmutableFunctionVariable(analysis, componentFunction, callerFunction);
31324
- if (callerVariable && hasReachableJsxEventCallPath(analysis, context, componentFunction, callerVariable, nextVisitedVariables)) return true;
31325
- }
31326
- return false;
31327
- };
31328
- const hasUserInputSetterWriter = (analysis, context, setterRef, effectNode, includeDeferredWriters = false) => {
30518
+ const hasUserInputSetterWriter = (setterRef, effectNode, includeDeferredWriters = false) => {
31329
30519
  if (!setterRef.resolved) return false;
31330
30520
  const componentFunction = findEnclosingFunction$1(effectNode);
31331
30521
  if (!componentFunction) return false;
@@ -31334,11 +30524,6 @@ const hasUserInputSetterWriter = (analysis, context, setterRef, effectNode, incl
31334
30524
  const identifier = reference.identifier;
31335
30525
  if (isAstDescendant(identifier, effectNode)) continue;
31336
30526
  if (isIndependentWriterIdentifier(componentFunction, identifier, includeDeferredWriters)) return true;
31337
- if (!isNodeReachableWithinFunction(identifier, context)) continue;
31338
- const writerFunction = findEnclosingFunction$1(identifier);
31339
- if (!writerFunction || writerFunction === componentFunction) continue;
31340
- const writerVariable = getImmutableFunctionVariable(analysis, componentFunction, writerFunction);
31341
- if (writerVariable && hasReachableJsxEventCallPath(analysis, context, componentFunction, writerVariable, /* @__PURE__ */ new Set())) return true;
31342
30527
  }
31343
30528
  return false;
31344
30529
  };
@@ -32228,7 +31413,7 @@ const areInMutuallyExclusiveBranches = (leftNode, rightNode) => {
32228
31413
  }
32229
31414
  return false;
32230
31415
  };
32231
- const collectEffectStateWriteFacts = (analysis, context, effectNode, currentFilename) => {
31416
+ const collectEffectStateWriteFacts = (analysis, effectNode, currentFilename) => {
32232
31417
  const frames = collectBoundedEffectExecutionFrames(analysis, effectNode, currentFilename);
32233
31418
  if (frames.length === 0) return [];
32234
31419
  const effectHasCleanup = hasCleanup(analysis, effectNode);
@@ -32258,7 +31443,7 @@ const collectEffectStateWriteFacts = (analysis, context, effectNode, currentFile
32258
31443
  for (const returnedExpression of returnedExpressions) mergeEvidence(valueEvidence, collectValueEvidence(analysis, returnedExpression, updaterFrame, remainingValueCallFrames));
32259
31444
  } else valueEvidence = collectValueEvidence(analysis, writtenValue, frame, remainingValueCallFrames);
32260
31445
  const sourceReferences = [...valueEvidence.sourceReferences].filter((sourceReference) => getUseStateDecl(analysis, sourceReference) !== stateDeclarator);
32261
- const hasIndependentWriter = hasUserInputSetterWriter(analysis, context, setterReference, effectNode, true);
31446
+ const hasIndependentWriter = hasUserInputSetterWriter(setterReference, effectNode, true);
32262
31447
  const doesMatchStateInitializer = matchesStateInitializer(analysis, callExpression, stateDeclarator);
32263
31448
  if (effectHasCleanup && (frame.isDeferred || valueEvidence.hasUnknownSource || valueEvidence.hasDeferredIntroducedValue || valueEvidence.readsExternalValue)) cleanupManagedStateDeclarators.add(stateDeclarator);
32264
31449
  const isRenderKnownCopy = sourceReferences.length > 0 && !frame.isDeferred && !valueEvidence.hasUnknownSource && !valueEvidence.hasDeferredIntroducedValue && !valueEvidence.readsExternalValue && !hasIndependentWriter;
@@ -32293,18 +31478,13 @@ const noAdjustStateOnPropChange = defineRule({
32293
31478
  tags: ["test-noise"],
32294
31479
  recommendation: "Adjust the state inline during render with a `prev`-prop comparison (`if (prop !== prevProp) { setPrevProp(prop); setX(...); }`), or refactor to remove the duplicated state. Routing the adjustment through a useEffect forces an extra render with a stale UI between the two commits. See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes",
32295
31480
  create: (context) => ({ CallExpression(node) {
32296
- if (!isReactApiCall(node, "useEffect", context.scopes, {
32297
- allowGlobalReactNamespace: true,
32298
- allowUnboundBareCalls: true,
32299
- resolveConditionalAliases: true,
32300
- resolveNamedAliases: true
32301
- })) return;
31481
+ if (!isUseEffect(node)) return;
32302
31482
  const analysis = getProgramAnalysis(node);
32303
31483
  if (!analysis) return;
32304
31484
  const dependencyReferences = getEffectDepsRefs(analysis, node);
32305
31485
  if (!dependencyReferences) return;
32306
31486
  if (!dependencyReferences.flatMap((reference) => isState(analysis, reference) ? [] : getUpstreamRefs(analysis, reference)).some((reference) => isProp(analysis, reference))) return;
32307
- for (const fact of collectEffectStateWriteFacts(analysis, context, node, context.filename)) {
31487
+ for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
32308
31488
  if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
32309
31489
  context.report({
32310
31490
  node: fact.callExpression,
@@ -35025,7 +34205,7 @@ const noChainStateUpdates = defineRule({
35025
34205
  if (!callExpr) continue;
35026
34206
  if (!isReachableFromStateTrigger(callExpr)) continue;
35027
34207
  if (!readsPostMountValueThroughLocals(callExpr, effectFn, { ignoreBareRefCurrent: true })) continue;
35028
- const declarator = getUseStateDeclarator(resolveStateSetterReference(analysis, ref) ?? ref);
34208
+ const declarator = getUseStateDeclarator(ref);
35029
34209
  if (declarator) domSyncedStateDeclarators.add(declarator);
35030
34210
  }
35031
34211
  for (const ref of effectFnRefs) {
@@ -35034,7 +34214,7 @@ const noChainStateUpdates = defineRule({
35034
34214
  if (!callExpr) continue;
35035
34215
  if (!isReachableFromStateTrigger(callExpr)) continue;
35036
34216
  if (getArgsUpstreamRefs(analysis, ref).some((argRef) => isState(analysis, argRef))) continue;
35037
- const setterDeclarator = getUseStateDeclarator(resolveStateSetterReference(analysis, ref) ?? ref);
34217
+ const setterDeclarator = getUseStateDeclarator(ref);
35038
34218
  if (setterDeclarator && domSyncedStateDeclarators.has(setterDeclarator)) continue;
35039
34219
  const isSelfTargeting = setterDeclarator !== null && stateDepDeclarators.has(setterDeclarator);
35040
34220
  const setterArguments = isNodeOfType(callExpr, "CallExpression") ? callExpr.arguments ?? [] : [];
@@ -36875,15 +36055,10 @@ const noDerivedState = defineRule({
36875
36055
  for (const fact of collectRenderStateWriteFacts(analysis, componentBody, context.filename)) reportStateWrite(fact.callExpression, fact.stateDeclarator);
36876
36056
  } }).visitors,
36877
36057
  CallExpression(node) {
36878
- if (!isReactApiCall(node, "useEffect", context.scopes, {
36879
- allowGlobalReactNamespace: true,
36880
- allowUnboundBareCalls: true,
36881
- resolveConditionalAliases: true,
36882
- resolveNamedAliases: true
36883
- })) return;
36058
+ if (!isUseEffect(node)) return;
36884
36059
  const analysis = getProgramAnalysis(node);
36885
36060
  if (!analysis) return;
36886
- for (const fact of collectEffectStateWriteFacts(analysis, context, node, context.filename)) {
36061
+ for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
36887
36062
  if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
36888
36063
  reportStateWrite(fact.callExpression, fact.stateDeclarator);
36889
36064
  }
@@ -36900,15 +36075,10 @@ const noDerivedStateEffect = defineRule({
36900
36075
  tags: ["test-noise"],
36901
36076
  recommendation: "Work out derived values while rendering: `const x = fn(dep)`. To reset a component's state when a prop changes, give it a key prop: `<Component key={prop} />`. See https://react.dev/learn/you-might-not-need-an-effect",
36902
36077
  create: (context) => ({ CallExpression(node) {
36903
- if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
36904
- allowGlobalReactNamespace: true,
36905
- allowUnboundBareCalls: true,
36906
- resolveConditionalAliases: true,
36907
- resolveNamedAliases: true
36908
- })) return;
36078
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
36909
36079
  const analysis = getProgramAnalysis(node);
36910
36080
  if (!analysis) return;
36911
- if (!collectEffectStateWriteFacts(analysis, context, node, context.filename).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
36081
+ if (!collectEffectStateWriteFacts(analysis, node, context.filename).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
36912
36082
  context.report({
36913
36083
  node,
36914
36084
  message: "You pay an extra render for state you can derive from other values."
@@ -37384,20 +36554,9 @@ const noDidMountSetState = defineRule({
37384
36554
  }
37385
36555
  });
37386
36556
  //#endregion
37387
- //#region src/plugin/utils/find-enclosing-class.ts
37388
- const findEnclosingClass = (node) => {
37389
- let ancestor = node.parent;
37390
- while (ancestor) {
37391
- if (isNodeOfType(ancestor, "ClassDeclaration") || isNodeOfType(ancestor, "ClassExpression")) return ancestor;
37392
- ancestor = ancestor.parent ?? null;
37393
- }
37394
- return null;
37395
- };
37396
- //#endregion
37397
36557
  //#region src/plugin/rules/react-builtins/no-did-update-set-state.ts
37398
36558
  const LIFECYCLE_NAMES$1 = new Set(["componentDidUpdate"]);
37399
36559
  const MESSAGE$27 = "Calling setState in componentDidUpdate can trigger another update immediately, loop forever, and freeze the component.";
37400
- const DIFFERENCE_OPERATORS = new Set(["!=", "!=="]);
37401
36560
  const EQUALITY_OPERATORS = new Set([
37402
36561
  "==",
37403
36562
  "===",
@@ -37409,8 +36568,6 @@ const FUNCTION_NODE_TYPES = new Set([
37409
36568
  "FunctionExpression",
37410
36569
  "ArrowFunctionExpression"
37411
36570
  ]);
37412
- const CLASS_NODE_TYPES = new Set(["ClassDeclaration", "ClassExpression"]);
37413
- const callbackRefFieldNamesByClass = /* @__PURE__ */ new WeakMap();
37414
36571
  const isLifecycleMethodFunction = (node) => {
37415
36572
  if (!FUNCTION_NODE_TYPES.has(node.type)) return false;
37416
36573
  const parent = node.parent;
@@ -37466,187 +36623,6 @@ const getStaticMemberName = (node) => {
37466
36623
  if (!isNodeOfType(node, "MemberExpression") || node.computed === true) return null;
37467
36624
  return isNodeOfType(node.property, "Identifier") ? node.property.name : null;
37468
36625
  };
37469
- const getMemberIdentity = (property) => {
37470
- const propertyName = getPropertyKeyName$2(property);
37471
- if (propertyName !== void 0) return isNodeOfType(property, "PrivateIdentifier") ? `#${propertyName}` : propertyName;
37472
- return isNodeOfType(property, "Literal") && typeof property.value === "string" ? property.value : null;
37473
- };
37474
- const collectPreviousSourcePaths = (pattern, domain, members, previousSourcePaths) => {
37475
- if (!pattern) return;
37476
- const unwrappedPattern = stripParenExpression(pattern);
37477
- if (isNodeOfType(unwrappedPattern, "Identifier")) {
37478
- previousSourcePaths.set(unwrappedPattern.name, {
37479
- domain,
37480
- members: [...members],
37481
- source: "previous"
37482
- });
37483
- return;
37484
- }
37485
- if (isNodeOfType(unwrappedPattern, "AssignmentPattern")) {
37486
- collectPreviousSourcePaths(unwrappedPattern.left, domain, members, previousSourcePaths);
37487
- return;
37488
- }
37489
- if (!isNodeOfType(unwrappedPattern, "ObjectPattern")) return;
37490
- for (const property of unwrappedPattern.properties) {
37491
- if (!isNodeOfType(property, "Property")) continue;
37492
- const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
37493
- if (!propertyName) continue;
37494
- collectPreviousSourcePaths(property.value, domain, [...members, propertyName], previousSourcePaths);
37495
- }
37496
- };
37497
- const getStateSourcePath = (node, previousSourcePaths) => {
37498
- let currentNode = stripParenExpression(node);
37499
- const members = [];
37500
- while (isNodeOfType(currentNode, "MemberExpression")) {
37501
- const memberName = getStaticMemberName(currentNode);
37502
- if (!memberName) return null;
37503
- members.unshift(memberName);
37504
- currentNode = stripParenExpression(currentNode.object);
37505
- }
37506
- if (isNodeOfType(currentNode, "ThisExpression")) {
37507
- const [domain, ...pathMembers] = members;
37508
- if (domain !== "props" && domain !== "state") return null;
37509
- return {
37510
- domain,
37511
- members: pathMembers,
37512
- source: "current"
37513
- };
37514
- }
37515
- if (!isNodeOfType(currentNode, "Identifier")) return null;
37516
- const previousSourcePath = previousSourcePaths.get(currentNode.name);
37517
- return previousSourcePath ? {
37518
- ...previousSourcePath,
37519
- members: [...previousSourcePath.members, ...members]
37520
- } : null;
37521
- };
37522
- const haveMatchingStateSourcePaths = (left, right) => left.domain === right.domain && left.members.length === right.members.length && left.members.every((member, index) => member === right.members[index]);
37523
- const collectConjunctiveStateSourceComparisons = (test, previousSourcePaths, comparisons) => {
37524
- const expression = stripParenExpression(test);
37525
- if (isNodeOfType(expression, "LogicalExpression") && expression.operator === "&&") {
37526
- collectConjunctiveStateSourceComparisons(expression.left, previousSourcePaths, comparisons);
37527
- collectConjunctiveStateSourceComparisons(expression.right, previousSourcePaths, comparisons);
37528
- return;
37529
- }
37530
- if (!isNodeOfType(expression, "BinaryExpression") || !EQUALITY_OPERATORS.has(expression.operator)) return;
37531
- const leftPath = getStateSourcePath(expression.left, previousSourcePaths);
37532
- const rightPath = getStateSourcePath(expression.right, previousSourcePaths);
37533
- if (Boolean(leftPath) === Boolean(rightPath)) return;
37534
- const path = leftPath ?? rightPath;
37535
- if (!path) return;
37536
- comparisons.push({
37537
- comparedValue: leftPath ? expression.right : expression.left,
37538
- isDifference: DIFFERENCE_OPERATORS.has(expression.operator),
37539
- path
37540
- });
37541
- };
37542
- const isHistoricalToCurrentTransitionGuard = (test, previousSourcePaths) => {
37543
- const expression = stripParenExpression(test);
37544
- if (isNodeOfType(expression, "LogicalExpression") && expression.operator === "||") return isHistoricalToCurrentTransitionGuard(expression.left, previousSourcePaths) && isHistoricalToCurrentTransitionGuard(expression.right, previousSourcePaths);
37545
- const comparisons = [];
37546
- collectConjunctiveStateSourceComparisons(expression, previousSourcePaths, comparisons);
37547
- return comparisons.some((comparison, index) => comparisons.slice(index + 1).some((candidate) => comparison.path.source !== candidate.path.source && comparison.isDifference !== candidate.isDifference && haveMatchingStateSourcePaths(comparison.path, candidate.path) && areExpressionsStructurallyEqual(comparison.comparedValue, candidate.comparedValue)));
37548
- };
37549
- const getThisFieldName = (node) => {
37550
- const unwrappedNode = stripParenExpression(node);
37551
- if (!isNodeOfType(unwrappedNode, "MemberExpression") || unwrappedNode.computed === true || !isNodeOfType(stripParenExpression(unwrappedNode.object), "ThisExpression")) return null;
37552
- return getMemberIdentity(unwrappedNode.property);
37553
- };
37554
- const isUndefinedIdentifier = (node) => {
37555
- const unwrappedNode = stripParenExpression(node);
37556
- return isNodeOfType(unwrappedNode, "Identifier") && unwrappedNode.name === "undefined";
37557
- };
37558
- const isDirectRefParameterValue = (node, parameterSymbolId, scopes) => {
37559
- const unwrappedNode = stripParenExpression(node);
37560
- if (isNodeOfType(unwrappedNode, "Identifier")) return scopes.symbolFor(unwrappedNode)?.id === parameterSymbolId;
37561
- if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "??") return false;
37562
- const left = stripParenExpression(unwrappedNode.left);
37563
- return isNodeOfType(left, "Identifier") && scopes.symbolFor(left)?.id === parameterSymbolId && isUndefinedIdentifier(unwrappedNode.right);
37564
- };
37565
- const getCallbackRefAssignedFields = (callback, scopes) => {
37566
- const firstParameter = (callback.params ?? [])[0];
37567
- if (!firstParameter) return /* @__PURE__ */ new Set();
37568
- const parameterIdentifier = isNodeOfType(firstParameter, "AssignmentPattern") ? firstParameter.left : firstParameter;
37569
- if (!isNodeOfType(parameterIdentifier, "Identifier")) return /* @__PURE__ */ new Set();
37570
- const parameterSymbolId = scopes.symbolFor(parameterIdentifier)?.id;
37571
- if (parameterSymbolId === void 0) return /* @__PURE__ */ new Set();
37572
- const body = callback.body;
37573
- if (!body) return /* @__PURE__ */ new Set();
37574
- const assignedFieldNames = /* @__PURE__ */ new Set();
37575
- walkAst(body, (node) => {
37576
- if (node !== body && (FUNCTION_NODE_TYPES.has(node.type) && !isImmediatelyInvokedFunction(node) || CLASS_NODE_TYPES.has(node.type))) return false;
37577
- const assignmentTarget = isNodeOfType(node, "AssignmentExpression") && node.left || isNodeOfType(node, "UpdateExpression") && node.argument || isNodeOfType(node, "UnaryExpression") && node.operator === "delete" && node.argument || null;
37578
- if (!assignmentTarget) return;
37579
- const fieldName = getThisFieldName(assignmentTarget);
37580
- if (!fieldName) return;
37581
- if (isNodeOfType(node, "AssignmentExpression") && node.operator === "=" && isDirectRefParameterValue(node.right, parameterSymbolId, scopes)) {
37582
- assignedFieldNames.add(fieldName);
37583
- return;
37584
- }
37585
- assignedFieldNames.delete(fieldName);
37586
- });
37587
- return assignedFieldNames;
37588
- };
37589
- const getClassMemberCallback = (classNode, memberName) => {
37590
- const classBody = classNode.body?.body ?? [];
37591
- for (const member of classBody) {
37592
- if (!isNodeOfType(member, "MethodDefinition") && !isNodeOfType(member, "PropertyDefinition")) continue;
37593
- if (member.static === true) continue;
37594
- const key = member.key;
37595
- if (getMemberIdentity(key) !== memberName) continue;
37596
- const value = member.value;
37597
- return value && FUNCTION_NODE_TYPES.has(value.type) ? value : null;
37598
- }
37599
- return null;
37600
- };
37601
- const collectCallbackRefFieldsFromExpression = (expression, classNode, fieldNames, scopes) => {
37602
- const unwrappedExpression = stripParenExpression(expression);
37603
- if (FUNCTION_NODE_TYPES.has(unwrappedExpression.type)) {
37604
- for (const fieldName of getCallbackRefAssignedFields(unwrappedExpression, scopes)) fieldNames.add(fieldName);
37605
- return;
37606
- }
37607
- const handlerName = getThisFieldName(unwrappedExpression);
37608
- if (handlerName) {
37609
- const callback = getClassMemberCallback(classNode, handlerName);
37610
- if (callback) for (const fieldName of getCallbackRefAssignedFields(callback, scopes)) fieldNames.add(fieldName);
37611
- return;
37612
- }
37613
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
37614
- collectCallbackRefFieldsFromExpression(unwrappedExpression.consequent, classNode, fieldNames, scopes);
37615
- collectCallbackRefFieldsFromExpression(unwrappedExpression.alternate, classNode, fieldNames, scopes);
37616
- return;
37617
- }
37618
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
37619
- if (unwrappedExpression.operator !== "&&") collectCallbackRefFieldsFromExpression(unwrappedExpression.left, classNode, fieldNames, scopes);
37620
- collectCallbackRefFieldsFromExpression(unwrappedExpression.right, classNode, fieldNames, scopes);
37621
- }
37622
- };
37623
- const getCallbackRefFieldNames = (classNode, scopes) => {
37624
- if (!classNode) return /* @__PURE__ */ new Set();
37625
- const cachedFieldNames = callbackRefFieldNamesByClass.get(classNode);
37626
- if (cachedFieldNames) return cachedFieldNames;
37627
- const fieldNames = /* @__PURE__ */ new Set();
37628
- const classBody = classNode.body;
37629
- if (classBody) walkAst(classBody, (node) => {
37630
- if (node !== classBody && CLASS_NODE_TYPES.has(node.type)) return false;
37631
- if (!isNodeOfType(node, "JSXAttribute") || !isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "ref" || !node.value || !isNodeOfType(node.value, "JSXExpressionContainer") || !node.value.expression) return;
37632
- collectCallbackRefFieldsFromExpression(node.value.expression, classNode, fieldNames, scopes);
37633
- });
37634
- callbackRefFieldNamesByClass.set(classNode, fieldNames);
37635
- return fieldNames;
37636
- };
37637
- const collectLifecycleWrittenFieldNames = (lifecycleFunction) => {
37638
- const fieldNames = /* @__PURE__ */ new Set();
37639
- const body = lifecycleFunction.body;
37640
- if (!body) return fieldNames;
37641
- walkAst(body, (node) => {
37642
- if (FUNCTION_NODE_TYPES.has(node.type) && !isImmediatelyInvokedFunction(node)) return false;
37643
- const target = isNodeOfType(node, "AssignmentExpression") && node.left || isNodeOfType(node, "UpdateExpression") && node.argument || null;
37644
- if (!target) return;
37645
- const fieldName = getThisFieldName(target);
37646
- if (fieldName) fieldNames.add(fieldName);
37647
- });
37648
- return fieldNames;
37649
- };
37650
36626
  const getThisStateFieldName = (node) => {
37651
36627
  const unwrappedNode = stripParenExpression(node);
37652
36628
  if (!isNodeOfType(unwrappedNode, "MemberExpression")) return null;
@@ -37664,17 +36640,15 @@ const collectLocalInitializers = (lifecycleFunction) => {
37664
36640
  });
37665
36641
  return initializers;
37666
36642
  };
37667
- const derivesFromPostMountValue = (node, localInitializers, callbackRefFieldNames, visitedNames = /* @__PURE__ */ new Set()) => {
36643
+ const derivesFromPostMountValue = (node, localInitializers, visitedNames = /* @__PURE__ */ new Set()) => {
37668
36644
  if (readsPostMountValue(node)) return true;
37669
- const fieldName = getThisFieldName(node);
37670
- if (fieldName && callbackRefFieldNames.has(fieldName)) return true;
37671
36645
  const referencedNames = /* @__PURE__ */ new Set();
37672
36646
  collectReferenceIdentifierNames(node, referencedNames);
37673
36647
  for (const referencedName of referencedNames) {
37674
36648
  if (visitedNames.has(referencedName)) continue;
37675
36649
  const initializer = localInitializers.get(referencedName);
37676
36650
  if (!initializer) continue;
37677
- if (derivesFromPostMountValue(initializer, localInitializers, callbackRefFieldNames, new Set([...visitedNames, referencedName]))) return true;
36651
+ if (derivesFromPostMountValue(initializer, localInitializers, new Set([...visitedNames, referencedName]))) return true;
37678
36652
  }
37679
36653
  return false;
37680
36654
  };
@@ -37688,84 +36662,50 @@ const getSetStateFieldValue = (setStateCall, fieldName) => {
37688
36662
  }
37689
36663
  return null;
37690
36664
  };
37691
- const isConvergentPostMountGuard = (test, setStateCall, localInitializers, callbackRefFieldNames, isTruthfulBranch) => {
37692
- const expression = stripParenExpression(test);
37693
- if (isNodeOfType(expression, "LogicalExpression")) {
37694
- if (expression.operator !== "&&" && expression.operator !== "||") return false;
37695
- const leftIsConvergent = isConvergentPostMountGuard(expression.left, setStateCall, localInitializers, callbackRefFieldNames, isTruthfulBranch);
37696
- const rightIsConvergent = isConvergentPostMountGuard(expression.right, setStateCall, localInitializers, callbackRefFieldNames, isTruthfulBranch);
37697
- return isTruthfulBranch && expression.operator === "||" || !isTruthfulBranch && expression.operator === "&&" ? leftIsConvergent && rightIsConvergent : leftIsConvergent || rightIsConvergent;
37698
- }
37699
- if (!isNodeOfType(expression, "BinaryExpression") || !(isTruthfulBranch ? DIFFERENCE_OPERATORS.has(expression.operator) : EQUALITY_OPERATORS.has(expression.operator) && !DIFFERENCE_OPERATORS.has(expression.operator))) return false;
37700
- const leftFieldName = getThisStateFieldName(expression.left);
37701
- const rightFieldName = getThisStateFieldName(expression.right);
37702
- const fieldName = leftFieldName ?? rightFieldName;
37703
- const comparedValue = leftFieldName ? expression.right : expression.left;
37704
- if (!fieldName) return false;
37705
- const assignedValue = getSetStateFieldValue(setStateCall, fieldName);
37706
- if (!assignedValue || !areExpressionsStructurallyEqual(comparedValue, assignedValue)) return false;
37707
- return isUndefinedIdentifier(comparedValue) || derivesFromPostMountValue(comparedValue, localInitializers, callbackRefFieldNames);
37708
- };
37709
- const containsPositiveStateFieldTest = (test, fieldName) => {
37710
- const unwrappedTest = stripParenExpression(test);
37711
- if (getThisStateFieldName(unwrappedTest) === fieldName) return true;
37712
- return isNodeOfType(unwrappedTest, "LogicalExpression") && unwrappedTest.operator === "&&" && (containsPositiveStateFieldTest(unwrappedTest.left, fieldName) || containsPositiveStateFieldTest(unwrappedTest.right, fieldName));
37713
- };
37714
- const isConvergentUndefinedClearGuard = (test, setStateCall) => {
37715
- if (!isNodeOfType(setStateCall, "CallExpression")) return false;
37716
- const argument = setStateCall.arguments?.[0];
37717
- if (!argument || !isNodeOfType(argument, "ObjectExpression")) return false;
37718
- for (const property of argument.properties ?? []) {
37719
- if (!isNodeOfType(property, "Property") || property.computed === true || !isUndefinedIdentifier(property.value)) continue;
37720
- const fieldName = isNodeOfType(property.key, "Identifier") && property.key.name || isNodeOfType(property.key, "Literal") && typeof property.key.value === "string" && property.key.value || null;
37721
- if (fieldName && containsPositiveStateFieldTest(test, fieldName)) return true;
37722
- }
37723
- return false;
37724
- };
37725
- const isDiffGuardTest = (test, paramNames, derivedNames, isTruthfulBranch) => {
37726
- const expression = stripParenExpression(test);
37727
- if (isNodeOfType(expression, "LogicalExpression")) {
37728
- if (expression.operator !== "&&" && expression.operator !== "||") return false;
37729
- const leftIsDiffGuard = isDiffGuardTest(expression.left, paramNames, derivedNames, isTruthfulBranch);
37730
- const rightIsDiffGuard = isDiffGuardTest(expression.right, paramNames, derivedNames, isTruthfulBranch);
37731
- return isTruthfulBranch && expression.operator === "||" || !isTruthfulBranch && expression.operator === "&&" ? leftIsDiffGuard && rightIsDiffGuard : leftIsDiffGuard || rightIsDiffGuard;
37732
- }
37733
- if (!isNodeOfType(expression, "BinaryExpression") || !(isTruthfulBranch ? DIFFERENCE_OPERATORS.has(expression.operator) : EQUALITY_OPERATORS.has(expression.operator) && !DIFFERENCE_OPERATORS.has(expression.operator))) return false;
37734
- return isStatefulOperand(expression.left, paramNames, derivedNames) && isStatefulOperand(expression.right, paramNames, derivedNames) && (referencesAnyName(expression.left, paramNames) || referencesAnyName(expression.right, paramNames) || referencesAnyName(expression.left, derivedNames) || referencesAnyName(expression.right, derivedNames));
36665
+ const isConvergentPostMountGuard = (test, setStateCall, localInitializers) => {
36666
+ let qualifies = false;
36667
+ walkAst(test, (node) => {
36668
+ if (qualifies) return false;
36669
+ if (!isNodeOfType(node, "BinaryExpression") || !EQUALITY_OPERATORS.has(node.operator)) return;
36670
+ const leftFieldName = getThisStateFieldName(node.left);
36671
+ const rightFieldName = getThisStateFieldName(node.right);
36672
+ const fieldName = leftFieldName ?? rightFieldName;
36673
+ const comparedValue = leftFieldName ? node.right : node.left;
36674
+ if (!fieldName || !leftFieldName && !rightFieldName) return;
36675
+ const assignedValue = getSetStateFieldValue(setStateCall, fieldName);
36676
+ if (!assignedValue || !areExpressionsStructurallyEqual(comparedValue, assignedValue)) return;
36677
+ if (!derivesFromPostMountValue(comparedValue, localInitializers)) return;
36678
+ qualifies = true;
36679
+ return false;
36680
+ });
36681
+ return qualifies;
36682
+ };
36683
+ const isDiffGuardTest = (test, paramNames, derivedNames) => {
36684
+ if (referencesAnyName(test, paramNames)) return true;
36685
+ let qualifies = false;
36686
+ walkAst(test, (node) => {
36687
+ if (qualifies) return false;
36688
+ if (!isNodeOfType(node, "BinaryExpression")) return;
36689
+ if (!EQUALITY_OPERATORS.has(node.operator)) return;
36690
+ if (isStatefulOperand(node.left, paramNames, derivedNames) && isStatefulOperand(node.right, paramNames, derivedNames) && (referencesAnyName(node.left, derivedNames) || referencesAnyName(node.right, derivedNames))) {
36691
+ qualifies = true;
36692
+ return false;
36693
+ }
36694
+ });
36695
+ return qualifies;
37735
36696
  };
37736
- const isInsideDiffGuard = (setStateCall, scopes) => {
36697
+ const isInsideDiffGuard = (setStateCall) => {
37737
36698
  const lifecycleFunction = findEnclosingLifecycleFunction(setStateCall);
37738
36699
  if (!lifecycleFunction) return false;
37739
36700
  const paramNames = /* @__PURE__ */ new Set();
37740
- const parameters = lifecycleFunction.params ?? [];
37741
- for (const param of parameters) collectPatternNames(param, paramNames);
37742
- const previousSourcePaths = /* @__PURE__ */ new Map();
37743
- const [previousPropsParameter, previousStateParameter] = parameters;
37744
- collectPreviousSourcePaths(previousPropsParameter, "props", [], previousSourcePaths);
37745
- collectPreviousSourcePaths(previousStateParameter, "state", [], previousSourcePaths);
36701
+ for (const param of lifecycleFunction.params ?? []) collectPatternNames(param, paramNames);
37746
36702
  const derivedNames = collectDiffSourceLocalNames(lifecycleFunction, paramNames);
37747
36703
  const localInitializers = collectLocalInitializers(lifecycleFunction);
37748
- const lifecycleWrittenFieldNames = collectLifecycleWrittenFieldNames(lifecycleFunction);
37749
- const callbackRefFieldNames = new Set([...getCallbackRefFieldNames(findEnclosingClass(lifecycleFunction), scopes)].filter((fieldName) => !lifecycleWrittenFieldNames.has(fieldName)));
37750
36704
  let child = setStateCall;
37751
36705
  let ancestor = setStateCall.parent;
37752
36706
  while (ancestor && ancestor !== lifecycleFunction) {
37753
- let guardTest = null;
37754
- let isTruthfulBranch = true;
37755
- if (isNodeOfType(ancestor, "IfStatement")) {
37756
- if (child === ancestor.consequent) guardTest = ancestor.test;
37757
- else if (child === ancestor.alternate) {
37758
- guardTest = ancestor.test;
37759
- isTruthfulBranch = false;
37760
- }
37761
- } else if (isNodeOfType(ancestor, "ConditionalExpression")) {
37762
- if (child === ancestor.consequent) guardTest = ancestor.test;
37763
- else if (child === ancestor.alternate) {
37764
- guardTest = ancestor.test;
37765
- isTruthfulBranch = false;
37766
- }
37767
- } else if (isNodeOfType(ancestor, "LogicalExpression") && ancestor.operator === "&&" && child === ancestor.right) guardTest = ancestor.left;
37768
- if (guardTest && (isDiffGuardTest(guardTest, paramNames, derivedNames, isTruthfulBranch) || isTruthfulBranch && isHistoricalToCurrentTransitionGuard(guardTest, previousSourcePaths) || isConvergentPostMountGuard(guardTest, setStateCall, localInitializers, callbackRefFieldNames, isTruthfulBranch) || isTruthfulBranch && isConvergentUndefinedClearGuard(guardTest, setStateCall))) return true;
36707
+ const guardTest = isNodeOfType(ancestor, "IfStatement") && child !== ancestor.test && ancestor.test || isNodeOfType(ancestor, "ConditionalExpression") && child !== ancestor.test && ancestor.test || isNodeOfType(ancestor, "LogicalExpression") && ancestor.operator === "&&" && child === ancestor.right && ancestor.left || null;
36708
+ if (guardTest && (isDiffGuardTest(guardTest, paramNames, derivedNames) || isConvergentPostMountGuard(guardTest, setStateCall, localInitializers))) return true;
37769
36709
  child = ancestor;
37770
36710
  ancestor = ancestor.parent ?? null;
37771
36711
  }
@@ -37787,7 +36727,7 @@ const noDidUpdateSetState = defineRule({
37787
36727
  if (!isNodeOfType(stripParenExpression(node.callee.object), "ThisExpression")) return;
37788
36728
  if (!isNodeOfType(node.callee.property, "Identifier") || node.callee.property.name !== "setState") return;
37789
36729
  if (!isSetStateCallInLifecycle(node, LIFECYCLE_NAMES$1, { disallowInNestedFunctions: mode === "disallow-in-func" })) return;
37790
- if (isInsideDiffGuard(node, context.scopes)) return;
36730
+ if (isInsideDiffGuard(node)) return;
37791
36731
  context.report({
37792
36732
  node: node.callee,
37793
36733
  message: MESSAGE$27
@@ -37857,7 +36797,7 @@ const noDirectMutationState = defineRule({
37857
36797
  const isSetterIdentifier = (name) => SETTER_PATTERN.test(name);
37858
36798
  //#endregion
37859
36799
  //#region src/plugin/rules/state-and-effects/utils/collect-use-state-bindings.ts
37860
- const collectUseStateBindings = (componentBody, scopes) => {
36800
+ const collectUseStateBindings = (componentBody) => {
37861
36801
  const bindings = [];
37862
36802
  if (!isNodeOfType(componentBody, "BlockStatement")) return bindings;
37863
36803
  for (const statement of componentBody.body ?? []) {
@@ -37870,12 +36810,7 @@ const collectUseStateBindings = (componentBody, scopes) => {
37870
36810
  const setterElement = elements[1];
37871
36811
  if (!isNodeOfType(valueElement, "Identifier") || !isNodeOfType(setterElement, "Identifier") || !isSetterIdentifier(setterElement.name)) continue;
37872
36812
  if (!isNodeOfType(declarator.init, "CallExpression")) continue;
37873
- if (!(scopes ? isReactApiCall(declarator.init, "useState", scopes, {
37874
- allowGlobalReactNamespace: true,
37875
- allowUnboundBareCalls: true,
37876
- resolveConditionalAliases: true,
37877
- resolveNamedAliases: true
37878
- }) : isHookCall$2(declarator.init, "useState"))) continue;
36813
+ if (!isHookCall$2(declarator.init, "useState")) continue;
37879
36814
  bindings.push({
37880
36815
  valueName: valueElement.name,
37881
36816
  setterName: setterElement.name,
@@ -38602,36 +37537,25 @@ const isCleanupReturn = (returnedValue, knownCleanupFunctionNames, knownBoundSub
38602
37537
  };
38603
37538
  //#endregion
38604
37539
  //#region src/plugin/rules/state-and-effects/no-effect-chain.ts
38605
- const findTopLevelEffectCalls = (componentBody, scopes) => {
37540
+ const findTopLevelEffectCalls = (componentBody) => {
38606
37541
  const effectCalls = [];
38607
37542
  if (!isNodeOfType(componentBody, "BlockStatement")) return effectCalls;
38608
37543
  for (const statement of componentBody.body ?? []) {
38609
37544
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
38610
37545
  const expression = unwrapDiscardedExpression(statement);
38611
37546
  if (!isNodeOfType(expression, "CallExpression")) continue;
38612
- if (!isReactApiCall(expression, EFFECT_HOOK_NAMES$1, scopes, {
38613
- allowGlobalReactNamespace: true,
38614
- allowUnboundBareCalls: true,
38615
- resolveConditionalAliases: true,
38616
- resolveNamedAliases: true
38617
- })) continue;
37547
+ if (!isHookCall$2(expression, EFFECT_HOOK_NAMES$1)) continue;
38618
37548
  effectCalls.push(expression);
38619
37549
  }
38620
37550
  return effectCalls;
38621
37551
  };
38622
- const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) => {
38623
- const dependencyStateSymbolIds = /* @__PURE__ */ new Set();
38624
- if (!isNodeOfType(effectNode, "CallExpression")) return dependencyStateSymbolIds;
37552
+ const collectDepIdentifierNames = (effectNode) => {
37553
+ const depNames = /* @__PURE__ */ new Set();
37554
+ if (!isNodeOfType(effectNode, "CallExpression")) return depNames;
38625
37555
  const depsNode = effectNode.arguments?.[1];
38626
- if (!isNodeOfType(depsNode, "ArrayExpression")) return dependencyStateSymbolIds;
38627
- for (const element of depsNode.elements ?? []) {
38628
- if (!element || isNodeOfType(element, "SpreadElement")) continue;
38629
- const rootIdentifier = getRootIdentifier$1(element);
38630
- if (!isNodeOfType(rootIdentifier, "Identifier")) continue;
38631
- const symbol = resolveConstIdentifierAlias(rootIdentifier, scopes, true);
38632
- if (symbol && stateSymbolIds.has(symbol.id)) dependencyStateSymbolIds.add(symbol.id);
38633
- }
38634
- return dependencyStateSymbolIds;
37556
+ if (!isNodeOfType(depsNode, "ArrayExpression")) return depNames;
37557
+ for (const element of depsNode.elements ?? []) if (isNodeOfType(element, "Identifier")) depNames.add(element.name);
37558
+ return depNames;
38635
37559
  };
38636
37560
  const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
38637
37561
  const analysisFunctions = new Set([effectCallback]);
@@ -38737,13 +37661,12 @@ const readStaticSetterValue = (setterCall, scopes) => {
38737
37661
  if (updater) return readStaticUpdaterReturnValue(updater, scopes);
38738
37662
  return readStaticEffectValue(argument, scopes, null, null);
38739
37663
  };
38740
- const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName, scopes) => {
37664
+ const collectStateWritesInEffect = (analysisFunctions, setterToStateName, scopes) => {
38741
37665
  const stateWrites = /* @__PURE__ */ new Map();
38742
37666
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
38743
37667
  if (!isNodeOfType(child, "CallExpression")) return;
38744
37668
  if (!isNodeOfType(child.callee, "Identifier")) return;
38745
- const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
38746
- const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
37669
+ const stateName = setterToStateName.get(child.callee.name);
38747
37670
  if (!stateName) return;
38748
37671
  const writeInfo = stateWrites.get(stateName) ?? {
38749
37672
  values: /* @__PURE__ */ new Set(),
@@ -38829,12 +37752,11 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
38829
37752
  "keys",
38830
37753
  "values"
38831
37754
  ]);
38832
- const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
37755
+ const isFunctionShapedReturn = (returnedValue, setterToStateName, isExplicitReturnStatement) => {
38833
37756
  if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
38834
37757
  if (isNodeOfType(returnedValue, "CallExpression")) {
38835
37758
  if (isNodeOfType(returnedValue.callee, "Identifier")) {
38836
- const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
38837
- if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
37759
+ if (setterToStateName.has(returnedValue.callee.name)) return false;
38838
37760
  if (isSetterIdentifier(returnedValue.callee.name)) return true;
38839
37761
  }
38840
37762
  return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
@@ -39001,11 +37923,11 @@ const isExternalSyncNode = (node) => {
39001
37923
  const receiverRootName = getRootIdentifierName(node.callee.object);
39002
37924
  return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
39003
37925
  };
39004
- const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
37926
+ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, scopes, allowCommittedDomSync) => {
39005
37927
  if (!isFunctionLike$1(effectCallback)) return false;
39006
37928
  if (!isNodeOfType(effectCallback.body, "BlockStatement")) {
39007
- if (isFunctionShapedReturn(effectCallback.body, setterToStateName, setterSymbolIdToStateName, scopes, false)) return true;
39008
- } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
37929
+ if (isFunctionShapedReturn(effectCallback.body, setterToStateName, false)) return true;
37930
+ } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, true)) return true;
39009
37931
  let didFindExternalCall = false;
39010
37932
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
39011
37933
  if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
@@ -39021,11 +37943,10 @@ const noEffectChain = defineRule({
39021
37943
  create: (context) => {
39022
37944
  const checkComponent = (componentBody) => {
39023
37945
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
39024
- const useStateBindings = collectUseStateBindings(componentBody, context.scopes);
37946
+ const useStateBindings = collectUseStateBindings(componentBody);
39025
37947
  if (useStateBindings.length === 0) return;
39026
37948
  const setterToStateName = /* @__PURE__ */ new Map();
39027
37949
  const stateSymbolIds = /* @__PURE__ */ new Map();
39028
- const setterSymbolIdToStateName = /* @__PURE__ */ new Map();
39029
37950
  for (const binding of useStateBindings) {
39030
37951
  setterToStateName.set(binding.setterName, binding.valueName);
39031
37952
  if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
@@ -39034,27 +37955,21 @@ const noEffectChain = defineRule({
39034
37955
  const stateSymbol = context.scopes.symbolFor(stateIdentifier);
39035
37956
  if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
39036
37957
  }
39037
- const setterIdentifier = binding.declarator.id.elements[1];
39038
- if (isNodeOfType(setterIdentifier, "Identifier")) {
39039
- const setterSymbol = context.scopes.symbolFor(setterIdentifier);
39040
- if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
39041
- }
39042
37958
  }
39043
37959
  const storageSetterNames = collectStorageHookSetterNames(componentBody);
39044
- const stateSymbolIdSet = new Set(stateSymbolIds.values());
39045
37960
  const effectInfos = [];
39046
- for (const effectCall of findTopLevelEffectCalls(componentBody, context.scopes)) {
37961
+ for (const effectCall of findTopLevelEffectCalls(componentBody)) {
39047
37962
  const callback = getEffectCallback(effectCall, context.scopes);
39048
37963
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
39049
37964
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
39050
- const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
37965
+ const stateWrites = collectStateWritesInEffect(analysisFunctions, setterToStateName, context.scopes);
39051
37966
  const writtenStateNames = new Set(stateWrites.keys());
39052
37967
  effectInfos.push({
39053
37968
  node: effectCall,
39054
- dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
37969
+ depNames: collectDepIdentifierNames(effectCall),
39055
37970
  stateWrites,
39056
37971
  analysisFunctions,
39057
- isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, context.scopes, writtenStateNames.size === 0) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
37972
+ isExternalSync: isExternalSyncEffect(callback, analysisFunctions, setterToStateName, context.scopes, writtenStateNames.size === 0) || callsStorageHookSetter(analysisFunctions, storageSetterNames) || writtenStateNames.size === 0 && callsOpaqueExternalSetter(analysisFunctions, setterToStateName)
39058
37973
  });
39059
37974
  }
39060
37975
  if (effectInfos.length < 2) return;
@@ -39065,11 +37980,10 @@ const noEffectChain = defineRule({
39065
37980
  for (const readerEffect of effectInfos) {
39066
37981
  if (readerEffect === writerEffect) continue;
39067
37982
  if (readerEffect.isExternalSync) continue;
39068
- if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
37983
+ if (readerEffect.depNames.size === 0) continue;
39069
37984
  let chainedStateName = null;
39070
37985
  for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
39071
- const writtenStateSymbolId = stateSymbolIds.get(writtenName);
39072
- if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
37986
+ if (!readerEffect.depNames.has(writtenName)) continue;
39073
37987
  if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
39074
37988
  chainedStateName = writtenName;
39075
37989
  break;
@@ -40885,17 +39799,6 @@ const DOM_MEASUREMENT_NAMES = new Set([
40885
39799
  "scrollHeight"
40886
39800
  ]);
40887
39801
  const MEASUREMENT_HELPER_CALLEE_PATTERN = /^(?:get|measure|read)\w*(?:Width|Height|Rect|Rects|Size|Bounds|Position)$/;
40888
- const IMPERATIVE_DOM_MUTATION_NAMES = new Set([
40889
- "blur",
40890
- "focus",
40891
- "restoreSelection",
40892
- "scroll",
40893
- "scrollBy",
40894
- "scrollIntoView",
40895
- "scrollTo",
40896
- "setRangeText",
40897
- "setSelectionRange"
40898
- ]);
40899
39802
  const subtreeReadsDomMeasurement = (root) => {
40900
39803
  if (!root) return false;
40901
39804
  let found = false;
@@ -40914,66 +39817,29 @@ const subtreeReadsDomMeasurement = (root) => {
40914
39817
  });
40915
39818
  return found;
40916
39819
  };
40917
- const collectFunctionNamesMatchingBody = (program, matchesBody) => {
39820
+ const collectMeasuringFunctionNames = (program) => {
40918
39821
  const names = /* @__PURE__ */ new Set();
40919
39822
  walkAst(program, (child) => {
40920
39823
  if (isNodeOfType(child, "FunctionDeclaration")) {
40921
- if (child.id && isNodeOfType(child.id, "Identifier") && matchesBody(child.body)) names.add(child.id.name);
39824
+ if (child.id && isNodeOfType(child.id, "Identifier") && subtreeReadsDomMeasurement(child.body)) names.add(child.id.name);
40922
39825
  return;
40923
39826
  }
40924
39827
  if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier")) return;
40925
39828
  let functionValue = child.init;
40926
39829
  if (functionValue && isNodeOfType(functionValue, "CallExpression") && isNodeOfType(functionValue.callee, "Identifier") && /^use[A-Z]/.test(functionValue.callee.name)) functionValue = functionValue.arguments?.[0];
40927
- if (functionValue && isFunctionLike$1(functionValue) && matchesBody(functionValue.body)) names.add(child.id.name);
39830
+ if (functionValue && isFunctionLike$1(functionValue) && subtreeReadsDomMeasurement(functionValue.body)) names.add(child.id.name);
40928
39831
  });
40929
39832
  return names;
40930
39833
  };
40931
- const collectMeasuringFunctionNames = (program) => collectFunctionNamesMatchingBody(program, subtreeReadsDomMeasurement);
40932
- const subtreeMutatesDomImperatively = (root) => {
40933
- if (!root || isFunctionLike$1(root)) return false;
40934
- let found = false;
40935
- walkAst(root, (child) => {
40936
- if (found) return false;
40937
- if (child !== root && isFunctionLike$1(child)) return false;
40938
- if (!isNodeOfType(child, "CallExpression")) return;
40939
- const callee = stripParenExpression(child.callee);
40940
- const propertyName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null;
40941
- if (propertyName !== null && IMPERATIVE_DOM_MUTATION_NAMES.has(propertyName)) {
40942
- found = true;
40943
- return false;
40944
- }
40945
- });
40946
- return found;
40947
- };
40948
- const collectImperativeDomFunctionNames = (program) => collectFunctionNamesMatchingBody(program, subtreeMutatesDomImperatively);
40949
- const callsAnyName = (root, names, shouldSkipNestedFunctions = false) => {
40950
- if (!root || names.size === 0 || shouldSkipNestedFunctions && isFunctionLike$1(root)) return false;
39834
+ const callsAnyName = (root, names) => {
39835
+ if (!root || names.size === 0) return false;
40951
39836
  let found = false;
40952
39837
  walkAst(root, (child) => {
40953
39838
  if (found) return false;
40954
- if (shouldSkipNestedFunctions && child !== root && isFunctionLike$1(child)) return false;
40955
39839
  if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && names.has(child.callee.name)) found = true;
40956
39840
  });
40957
39841
  return found;
40958
39842
  };
40959
- const isFollowedByImperativeDomMutation = (call, imperativeDomFunctionNames) => {
40960
- let statement = call;
40961
- let parent = statement.parent;
40962
- while (parent) {
40963
- const statements = isNodeOfType(parent, "BlockStatement") || isNodeOfType(parent, "Program") || isNodeOfType(parent, "StaticBlock") ? parent.body : isNodeOfType(parent, "SwitchCase") ? parent.consequent : null;
40964
- if (statements) {
40965
- const statementIndex = statements.findIndex((siblingStatement) => siblingStatement === statement);
40966
- if (statementIndex >= 0) {
40967
- const nextStatement = statements[statementIndex + 1];
40968
- return subtreeMutatesDomImperatively(nextStatement) || callsAnyName(nextStatement, imperativeDomFunctionNames, true);
40969
- }
40970
- }
40971
- if (isFunctionLike$1(parent) || parent.type.endsWith("Statement") && !isNodeOfType(parent, "ExpressionStatement")) return false;
40972
- statement = parent;
40973
- parent = parent.parent;
40974
- }
40975
- return false;
40976
- };
40977
39843
  const isInsideStartViewTransition = (node) => {
40978
39844
  let cursor = node.parent;
40979
39845
  while (cursor) {
@@ -41014,12 +39880,11 @@ const importsImperativeDomLibrary = (program) => {
41014
39880
  };
41015
39881
  const hasExemptFlushSyncCall = (program, localName) => {
41016
39882
  const measuringFunctionNames = collectMeasuringFunctionNames(program);
41017
- const imperativeDomFunctionNames = collectImperativeDomFunctionNames(program);
41018
39883
  let exempt = false;
41019
39884
  walkAst(program, (child) => {
41020
39885
  if (exempt) return false;
41021
39886
  if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "Identifier") || child.callee.name !== localName) return;
41022
- if (isInsideStartViewTransition(child) || enclosingFunctionChainReadsMeasurement(child, measuringFunctionNames) || isFollowedByImperativeDomMutation(child, imperativeDomFunctionNames)) {
39887
+ if (isInsideStartViewTransition(child) || enclosingFunctionChainReadsMeasurement(child, measuringFunctionNames)) {
41023
39888
  exempt = true;
41024
39889
  return false;
41025
39890
  }
@@ -41584,223 +40449,41 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
41584
40449
  if (leftResult === false && rightResult === false) return false;
41585
40450
  return null;
41586
40451
  };
41587
- const readHydrationConditionResult = (expression, context, runtime, state) => {
40452
+ const readHydrationConditionResult = (expression, context, runtime) => {
41588
40453
  const unwrappedExpression = stripParenExpression(expression);
41589
40454
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
41590
40455
  if (predicateMatch) return predicateMatch[`${runtime}Result`];
41591
40456
  const staticResult = readInitialStateBoolean(unwrappedExpression, context.scopes);
41592
40457
  if (staticResult !== null) return staticResult;
41593
- const expressionSymbol = isNodeOfType(unwrappedExpression, "Identifier") ? context.scopes.symbolFor(unwrappedExpression) : null;
41594
- const parameterValue = expressionSymbol ? state.parameterValuesBySymbolId.get(expressionSymbol.id) : null;
41595
- if (expressionSymbol && parameterValue && !state.visitedSymbolIds.has(expressionSymbol.id)) {
41596
- state.visitedSymbolIds.add(expressionSymbol.id);
41597
- const result = readHydrationConditionResult(parameterValue, context, runtime, state);
41598
- state.visitedSymbolIds.delete(expressionSymbol.id);
41599
- return result;
41600
- }
41601
- if (expressionSymbol && expressionSymbol.kind === "const" && expressionSymbol.initializer && expressionSymbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(expressionSymbol.id)) {
41602
- state.visitedSymbolIds.add(expressionSymbol.id);
41603
- const result = readHydrationConditionResult(expressionSymbol.initializer, context, runtime, state);
41604
- state.visitedSymbolIds.delete(expressionSymbol.id);
41605
- return result;
41606
- }
41607
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
41608
- const callArguments = unwrappedExpression.arguments ?? [];
41609
- if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
41610
- allowGlobalReactNamespace: true,
41611
- resolveNamedAliases: true
41612
- })) {
41613
- const callbackArgument = callArguments[0];
41614
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
41615
- const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
41616
- return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? readHydrationFunctionResult(callbackFunction, context, runtime, state) : null;
41617
- }
41618
- const callee = stripParenExpression(unwrappedExpression.callee);
41619
- if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) return readHydrationConditionResult(callArguments[0], context, runtime, state);
41620
- const helperFunction = resolveExactLocalFunction(callee, context.scopes);
41621
- if (!isFunctionLike$1(helperFunction) || helperFunction.async || isNodeOfType(helperFunction, "FunctionDeclaration") && helperFunction.generator || isNodeOfType(helperFunction, "FunctionExpression") && helperFunction.generator || helperFunction.params.some((parameter) => !isNodeOfType(parameter, "Identifier")) || callArguments.some((argument) => isNodeOfType(argument, "SpreadElement"))) return null;
41622
- const parameterValuesBySymbolId = new Map(state.parameterValuesBySymbolId);
41623
- for (let parameterIndex = 0; parameterIndex < helperFunction.params.length; parameterIndex++) {
41624
- const parameter = helperFunction.params[parameterIndex];
41625
- const argument = callArguments[parameterIndex];
41626
- if (!argument || !isNodeOfType(parameter, "Identifier")) continue;
41627
- const parameterSymbol = context.scopes.symbolFor(parameter);
41628
- if (parameterSymbol) parameterValuesBySymbolId.set(parameterSymbol.id, argument);
41629
- }
41630
- return readHydrationFunctionResult(helperFunction, context, runtime, {
41631
- ...state,
41632
- parameterValuesBySymbolId
41633
- });
41634
- }
41635
40458
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
41636
- const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
40459
+ const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime);
41637
40460
  return argumentResult === null ? null : !argumentResult;
41638
40461
  }
41639
40462
  if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
41640
- return readLogicalConditionResult(unwrappedExpression.operator, readHydrationConditionResult(unwrappedExpression.left, context, runtime, state), readHydrationConditionResult(unwrappedExpression.right, context, runtime, state));
41641
- };
41642
- const readHydrationStatementResult = (statement, context, runtime, state) => {
41643
- if (isNodeOfType(statement, "ReturnStatement")) return {
41644
- didReturn: true,
41645
- value: statement.argument ? readHydrationConditionResult(statement.argument, context, runtime, state) : null
41646
- };
41647
- if (isNodeOfType(statement, "BlockStatement")) {
41648
- for (const childStatement of statement.body) {
41649
- const result = readHydrationStatementResult(childStatement, context, runtime, state);
41650
- if (result.didReturn) return result;
41651
- if (statementAlwaysExits(childStatement)) break;
41652
- }
41653
- return {
41654
- didReturn: false,
41655
- value: null
41656
- };
41657
- }
41658
- if (!isNodeOfType(statement, "IfStatement")) return {
41659
- didReturn: false,
41660
- value: null
41661
- };
41662
- const conditionResult = readHydrationConditionResult(statement.test, context, runtime, state);
41663
- if (conditionResult !== null) {
41664
- const selectedBranch = conditionResult ? statement.consequent : statement.alternate;
41665
- return selectedBranch ? readHydrationStatementResult(selectedBranch, context, runtime, state) : {
41666
- didReturn: false,
41667
- value: null
41668
- };
41669
- }
41670
- const consequentResult = readHydrationStatementResult(statement.consequent, context, runtime, state);
41671
- const alternateResult = statement.alternate ? readHydrationStatementResult(statement.alternate, context, runtime, state) : {
41672
- didReturn: false,
41673
- value: null
41674
- };
41675
- return consequentResult.didReturn && alternateResult.didReturn && consequentResult.value !== null && consequentResult.value === alternateResult.value ? consequentResult : {
41676
- didReturn: consequentResult.didReturn || alternateResult.didReturn,
41677
- value: null
41678
- };
40463
+ return readLogicalConditionResult(unwrappedExpression.operator, readHydrationConditionResult(unwrappedExpression.left, context, runtime), readHydrationConditionResult(unwrappedExpression.right, context, runtime));
41679
40464
  };
41680
- const readHydrationFunctionResult = (functionNode, context, runtime, state) => {
41681
- if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
41682
- state.visitedFunctionNodes.add(functionNode);
41683
- const result = isNodeOfType(functionNode.body, "BlockStatement") ? readHydrationStatementResult(functionNode.body, context, runtime, state).value : readHydrationConditionResult(functionNode.body, context, runtime, state);
41684
- state.visitedFunctionNodes.delete(functionNode);
41685
- return result;
41686
- };
41687
- const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, scopes) => {
41688
- const left = stripParenExpression(leftExpression);
41689
- const right = stripParenExpression(rightExpression);
41690
- if (isNodeOfType(left, "Identifier") && isNodeOfType(right, "Identifier")) {
41691
- const leftSymbol = scopes.symbolFor(left);
41692
- const rightSymbol = scopes.symbolFor(right);
41693
- return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
41694
- }
41695
- if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
41696
- if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
41697
- const rightArguments = right.arguments ?? [];
41698
- return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
41699
- const rightArgument = rightArguments[index];
41700
- return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
41701
- });
41702
- }
41703
- return true;
41704
- };
41705
- const areHelperReturnValuesEquivalent = (leftValue, rightValue, context) => {
41706
- if (areExpressionsStructurallyEqual(leftValue, rightValue)) return doEquivalentExpressionBindingsMatch(leftValue, rightValue, context.scopes);
41707
- const leftBoolean = readInitialStateBoolean(leftValue, context.scopes);
41708
- const rightBoolean = readInitialStateBoolean(rightValue, context.scopes);
41709
- return leftBoolean !== null && rightBoolean !== null && leftBoolean === rightBoolean;
41710
- };
41711
- const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
41712
- const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
41713
- return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
41714
- };
41715
- const matchHydrationConditionInternal = (expression, context, state) => {
40465
+ const matchHydrationCondition = (expression, context) => {
41716
40466
  const unwrappedExpression = stripParenExpression(expression);
41717
40467
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
41718
40468
  if (predicateMatch) return {
41719
40469
  predicateMatch,
41720
40470
  predicateNode: unwrappedExpression
41721
40471
  };
41722
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
41723
- const symbol = context.scopes.symbolFor(unwrappedExpression);
41724
- const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
41725
- if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
41726
- state.visitedSymbolIds.add(symbol.id);
41727
- const match = matchHydrationConditionInternal(parameterValue, context, state);
41728
- state.visitedSymbolIds.delete(symbol.id);
41729
- return match;
41730
- }
41731
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
41732
- state.visitedSymbolIds.add(symbol.id);
41733
- const match = matchHydrationConditionInternal(symbol.initializer, context, state);
41734
- state.visitedSymbolIds.delete(symbol.id);
41735
- return match;
41736
- }
41737
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
41738
- const callArguments = unwrappedExpression.arguments ?? [];
41739
- if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
41740
- allowGlobalReactNamespace: true,
41741
- resolveNamedAliases: true
41742
- })) {
41743
- const callbackArgument = callArguments[0];
41744
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
41745
- const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
41746
- return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionResult(callbackFunction, context, state) : null;
41747
- }
41748
- const callee = stripParenExpression(unwrappedExpression.callee);
41749
- if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) return matchHydrationConditionInternal(callArguments[0], context, state);
41750
- const helperFunction = resolveExactLocalFunction(callee, context.scopes);
41751
- if (!isFunctionLike$1(helperFunction) || helperFunction.async || isNodeOfType(helperFunction, "FunctionDeclaration") && helperFunction.generator || isNodeOfType(helperFunction, "FunctionExpression") && helperFunction.generator || helperFunction.params.some((parameter) => !isNodeOfType(parameter, "Identifier")) || callArguments.some((argument) => isNodeOfType(argument, "SpreadElement"))) return null;
41752
- const parameterValuesBySymbolId = new Map(state.parameterValuesBySymbolId);
41753
- for (let parameterIndex = 0; parameterIndex < helperFunction.params.length; parameterIndex++) {
41754
- const parameter = helperFunction.params[parameterIndex];
41755
- const argument = callArguments[parameterIndex];
41756
- if (!argument || !isNodeOfType(parameter, "Identifier")) continue;
41757
- const parameterSymbol = context.scopes.symbolFor(parameter);
41758
- if (parameterSymbol) parameterValuesBySymbolId.set(parameterSymbol.id, argument);
41759
- }
41760
- return matchHydrationFunctionResult(helperFunction, context, {
41761
- ...state,
41762
- parameterValuesBySymbolId
41763
- });
41764
- }
41765
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
40472
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationCondition(unwrappedExpression.argument, context);
41766
40473
  if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
41767
- const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
41768
- const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
40474
+ const leftMatch = matchHydrationCondition(unwrappedExpression.left, context);
40475
+ const rightMatch = matchHydrationCondition(unwrappedExpression.right, context);
40476
+ if (leftMatch && rightMatch) {
40477
+ const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client");
40478
+ const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server");
40479
+ return clientResult !== null && serverResult !== null && clientResult !== serverResult ? leftMatch : null;
40480
+ }
41769
40481
  const nestedMatch = leftMatch ?? rightMatch;
41770
40482
  if (!nestedMatch) return null;
41771
- const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
41772
- const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
41773
- return clientResult !== null && serverResult !== null && clientResult === serverResult ? null : nestedMatch;
40483
+ const otherResult = readInitialStateBoolean(leftMatch ? unwrappedExpression.right : unwrappedExpression.left, context.scopes);
40484
+ if (unwrappedExpression.operator === "&&" && otherResult === false || unwrappedExpression.operator === "||" && otherResult === true) return null;
40485
+ return nestedMatch;
41774
40486
  };
41775
- const matchHydrationReturningStatement = (statement, context, state) => {
41776
- if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? matchHydrationConditionInternal(statement.argument, context, state) : null;
41777
- if (isNodeOfType(statement, "IfStatement")) {
41778
- const conditionMatch = matchHydrationConditionInternal(statement.test, context, state);
41779
- const consequentValues = getReturnedValues(statement.consequent);
41780
- const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
41781
- if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
41782
- return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
41783
- }
41784
- if (!isNodeOfType(statement, "BlockStatement")) return null;
41785
- for (const childStatement of statement.body) {
41786
- const match = matchHydrationReturningStatement(childStatement, context, state);
41787
- if (match) return match;
41788
- if (statementAlwaysExits(childStatement)) break;
41789
- }
41790
- return null;
41791
- };
41792
- const matchHydrationFunctionResult = (functionNode, context, state) => {
41793
- if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
41794
- state.visitedFunctionNodes.add(functionNode);
41795
- const match = isNodeOfType(functionNode.body, "BlockStatement") ? matchHydrationReturningStatement(functionNode.body, context, state) : matchHydrationConditionInternal(functionNode.body, context, state);
41796
- state.visitedFunctionNodes.delete(functionNode);
41797
- return match;
41798
- };
41799
- const matchHydrationCondition = (expression, context) => matchHydrationConditionInternal(expression, context, {
41800
- parameterValuesBySymbolId: /* @__PURE__ */ new Map(),
41801
- visitedFunctionNodes: /* @__PURE__ */ new Set(),
41802
- visitedSymbolIds: /* @__PURE__ */ new Set()
41803
- });
41804
40487
  const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
41805
40488
  const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
41806
40489
  if (!leftNode || !rightNode) return leftNode === rightNode;
@@ -41943,17 +40626,17 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
41943
40626
  const { predicateMatch, predicateNode } = conditionMatch;
41944
40627
  if (reportedNodes.has(predicateNode)) return;
41945
40628
  if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
41946
- const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
40629
+ const componentOrHookNode = findRenderPhaseComponentOrHook(predicateNode, context.scopes);
41947
40630
  if (!componentOrHookNode) return;
41948
40631
  if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
41949
- if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
40632
+ if (requiresRenderedContext && !isInRenderedOutput(predicateNode, componentOrHookNode, context.scopes)) return;
41950
40633
  if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
41951
- const attribute = findEnclosingJsxAttribute(conditionNode);
40634
+ const attribute = findEnclosingJsxAttribute(predicateNode);
41952
40635
  if (!attribute || isEventHandlerAttribute(attribute)) return;
41953
40636
  }
41954
- if (fileIsEmailTemplate || isGatedByFalsyInitialState(conditionNode, context.scopes)) return;
41955
- if (isAfterClientOnlyEarlyReturn(conditionNode, componentOrHookNode, context.scopes)) return;
41956
- const openingElement = findEnclosingJsxOpeningElement(conditionNode);
40637
+ if (fileIsEmailTemplate || isGatedByFalsyInitialState(predicateNode, context.scopes)) return;
40638
+ if (isAfterClientOnlyEarlyReturn(predicateNode, componentOrHookNode, context.scopes)) return;
40639
+ const openingElement = findEnclosingJsxOpeningElement(predicateNode);
41957
40640
  if (hasSuppressHydrationWarningAttribute(openingElement) && !isStructuralRenderedValue(leftBranch) && !isStructuralRenderedValue(rightBranch)) return;
41958
40641
  if (branchRootsSuppressSameElement(leftBranch, rightBranch)) return;
41959
40642
  if (isGeneratedImageRenderContext(context, openingElement ?? leftBranch)) return;
@@ -42335,7 +41018,7 @@ const noInitializeState = defineRule({
42335
41018
  if (!dependencies || !isNodeOfType(dependencies, "ArrayExpression") || (dependencies.elements ?? []).length !== 0) return;
42336
41019
  const analysis = getProgramAnalysis(node);
42337
41020
  if (!analysis) return;
42338
- for (const fact of collectEffectStateWriteFacts(analysis, context, node, context.filename)) {
41021
+ for (const fact of collectEffectStateWriteFacts(analysis, node, context.filename)) {
42339
41022
  if (!fact.isRenderKnownCopy || fact.matchesStateInitializer || fact.resetsSourceState) continue;
42340
41023
  const stateName = getStateName(fact.stateDeclarator);
42341
41024
  context.report({
@@ -42693,8 +41376,7 @@ const noJsxElementType = defineRule({
42693
41376
  create: (context) => {
42694
41377
  let isJsxImported = false;
42695
41378
  const flaggedAnnotations = [];
42696
- const collectComponentReturnType = (functionNode, returnType) => {
42697
- if (!(isNodeOfType(functionNode, "TSDeclareFunction") ? Boolean(functionNode.id && isReactComponentName(functionNode.id.name)) : isComponentFunction$1(functionNode))) return;
41379
+ const checkReturnType = (returnType) => {
42698
41380
  const typeAnnotation = extractReturnTypeAnnotation(returnType);
42699
41381
  if (!typeAnnotation) return;
42700
41382
  if (isJsxElementTypeReference(typeAnnotation)) flaggedAnnotations.push(typeAnnotation);
@@ -42704,16 +41386,19 @@ const noJsxElementType = defineRule({
42704
41386
  if (isJsxImportBinding(node)) isJsxImported = true;
42705
41387
  },
42706
41388
  FunctionDeclaration(node) {
42707
- collectComponentReturnType(node, node.returnType);
41389
+ checkReturnType(node.returnType);
42708
41390
  },
42709
41391
  ArrowFunctionExpression(node) {
42710
- collectComponentReturnType(node, node.returnType);
41392
+ checkReturnType(node.returnType);
42711
41393
  },
42712
41394
  FunctionExpression(node) {
42713
- collectComponentReturnType(node, node.returnType);
41395
+ checkReturnType(node.returnType);
42714
41396
  },
42715
41397
  TSDeclareFunction(node) {
42716
- collectComponentReturnType(node, node.returnType);
41398
+ checkReturnType(node.returnType);
41399
+ },
41400
+ TSMethodSignature(node) {
41401
+ checkReturnType(node.returnType);
42717
41402
  },
42718
41403
  "Program:exit"() {
42719
41404
  if (isJsxImported) return;
@@ -45740,114 +44425,6 @@ const DATA_SINK_METHOD_NAMES = new Set([
45740
44425
  "deserialize"
45741
44426
  ]);
45742
44427
  //#endregion
45743
- //#region src/plugin/utils/get-transparent-react-callback-wrapper-argument.ts
45744
- const getTransparentReactCallbackWrapperArgument = (initializer, resultSymbol, scopes) => {
45745
- const callExpression = stripParenExpression(initializer);
45746
- if (!isNodeOfType(callExpression, "CallExpression")) return null;
45747
- const callbackArgument = callExpression.arguments[0];
45748
- if (!callbackArgument) return null;
45749
- if (resultSymbol && symbolHasReactUseEffectEventOrigin(resultSymbol, scopes)) return callbackArgument;
45750
- return isReactApiCall(callExpression, "useCallback", scopes, {
45751
- allowGlobalReactNamespace: true,
45752
- allowUnboundBareCalls: true
45753
- }) ? callbackArgument : null;
45754
- };
45755
- //#endregion
45756
- //#region src/plugin/rules/state-and-effects/utils/resolve-parent-callback-provenance.ts
45757
- const getDeclarationKind$1 = (declarator) => {
45758
- const declaration = declarator.parent;
45759
- return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
45760
- };
45761
- const hasMutableBindingWrite$2 = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
45762
- const mergeRequiredBranches = (leftNames, rightNames) => {
45763
- if (!leftNames || !rightNames) return null;
45764
- return new Set([...leftNames, ...rightNames]);
45765
- };
45766
- const getPropReferenceName = (analysis, identifier) => {
45767
- if (!isNodeOfType(identifier, "Identifier")) return null;
45768
- const reference = getRef(analysis, identifier);
45769
- if (!reference || !isProp(analysis, reference) || isWholePropsObjectReference(analysis, reference)) return null;
45770
- const bindingIdentifier = (reference.resolved?.defs.find((definition) => definition.type === "Parameter"))?.name;
45771
- return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? identifier.name;
45772
- };
45773
- const getSingleConstDeclarator = (reference) => {
45774
- if (!reference.resolved || hasMutableBindingWrite$2(reference)) return null;
45775
- const declarators = reference.resolved.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
45776
- if (declarators.length !== 1) return null;
45777
- const declarator = declarators[0];
45778
- if (!declarator || getDeclarationKind$1(declarator) !== "const") return null;
45779
- return declarator;
45780
- };
45781
- const resolveParentCallbackPropNames = (analysis, expression, scopes, visitedReferences, allowFunctionForwarder = false) => {
45782
- const unwrappedExpression = stripParenExpression(expression);
45783
- if (isFunctionLike$1(unwrappedExpression)) {
45784
- if (!allowFunctionForwarder || Boolean(unwrappedExpression.async)) return null;
45785
- const callbackNames = /* @__PURE__ */ new Set();
45786
- walkInsideStatementBlocks(unwrappedExpression.body, (child) => {
45787
- if (!isNodeOfType(child, "CallExpression")) return;
45788
- const resolvedNames = resolveParentCallbackPropNames(analysis, child.callee, scopes, new Set(visitedReferences), false);
45789
- if (!resolvedNames) return;
45790
- for (const resolvedName of resolvedNames) callbackNames.add(resolvedName);
45791
- });
45792
- return callbackNames.size > 0 ? callbackNames : null;
45793
- }
45794
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return mergeRequiredBranches(resolveParentCallbackPropNames(analysis, unwrappedExpression.consequent, scopes, new Set(visitedReferences), false), resolveParentCallbackPropNames(analysis, unwrappedExpression.alternate, scopes, new Set(visitedReferences), false));
45795
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) return mergeRequiredBranches(resolveParentCallbackPropNames(analysis, unwrappedExpression.left, scopes, new Set(visitedReferences), false), resolveParentCallbackPropNames(analysis, unwrappedExpression.right, scopes, new Set(visitedReferences)));
45796
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
45797
- const propName = getPropReferenceName(analysis, unwrappedExpression);
45798
- if (propName) return new Set([propName]);
45799
- const reference = getRef(analysis, unwrappedExpression);
45800
- if (!reference?.resolved || visitedReferences.has(reference.resolved)) return null;
45801
- const declarator = getSingleConstDeclarator(reference);
45802
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !declarator.init) return null;
45803
- visitedReferences.add(reference.resolved);
45804
- const wrappedArgument = getTransparentReactCallbackWrapperArgument(declarator.init, scopes.symbolFor(unwrappedExpression), scopes);
45805
- const allowsFunctionForwarder = Boolean(wrappedArgument && !isReactApiCall(declarator.init, "useCallback", scopes, {
45806
- allowGlobalReactNamespace: true,
45807
- allowUnboundBareCalls: true
45808
- }));
45809
- return resolveParentCallbackPropNames(analysis, wrappedArgument ?? declarator.init, scopes, visitedReferences, allowsFunctionForwarder);
45810
- }
45811
- if (!isNodeOfType(unwrappedExpression, "MemberExpression")) return null;
45812
- const propertyName = getStaticMemberPropertyName(unwrappedExpression);
45813
- if (!propertyName) return null;
45814
- const receiver = stripParenExpression(unwrappedExpression.object);
45815
- if (!isNodeOfType(receiver, "Identifier")) return null;
45816
- const receiverReference = getRef(analysis, receiver);
45817
- if (!receiverReference?.resolved || visitedReferences.has(receiverReference.resolved)) return null;
45818
- if (isWholePropsObjectReference(analysis, receiverReference)) return new Set([propertyName]);
45819
- const declarator = getSingleConstDeclarator(receiverReference);
45820
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || !declarator.init) return null;
45821
- visitedReferences.add(receiverReference.resolved);
45822
- const initializer = stripParenExpression(declarator.init);
45823
- if (propertyName === "current" && isNodeOfType(initializer, "CallExpression")) {
45824
- if (!isReactApiCall(initializer, "useRef", scopes, {
45825
- allowGlobalReactNamespace: true,
45826
- allowUnboundBareCalls: true
45827
- })) return null;
45828
- const callbackArgument = initializer.arguments[0];
45829
- if (!callbackArgument) return null;
45830
- let callbackNames = resolveParentCallbackPropNames(analysis, callbackArgument, scopes, new Set(visitedReferences), false);
45831
- if (!callbackNames) return null;
45832
- for (const candidateReference of receiverReference.resolved.references) {
45833
- const candidateIdentifier = candidateReference.identifier;
45834
- const candidateMember = candidateIdentifier.parent;
45835
- if (!candidateMember || !isNodeOfType(candidateMember, "MemberExpression") || candidateMember.object !== candidateIdentifier || getStaticMemberPropertyName(candidateMember) !== "current") continue;
45836
- const assignment = candidateMember.parent;
45837
- if (!assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== candidateMember) continue;
45838
- if (assignment.operator !== "=") return null;
45839
- callbackNames = mergeRequiredBranches(callbackNames, resolveParentCallbackPropNames(analysis, assignment.right, scopes, new Set(visitedReferences), false));
45840
- if (!callbackNames) return null;
45841
- }
45842
- return callbackNames;
45843
- }
45844
- if (!isNodeOfType(initializer, "ObjectExpression")) return null;
45845
- const property = initializer.properties.find((candidateProperty) => isNodeOfType(candidateProperty, "Property") && getStaticPropertyKeyName(candidateProperty, { allowComputedString: true }) === propertyName);
45846
- if (!property || !isNodeOfType(property, "Property")) return null;
45847
- return resolveParentCallbackPropNames(analysis, property.value, scopes, visitedReferences, false);
45848
- };
45849
- const getParentCallbackPropNames = ({ analysis, expression, scopes }) => resolveParentCallbackPropNames(analysis, expression, scopes, /* @__PURE__ */ new Set(), false);
45850
- //#endregion
45851
44428
  //#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
45852
44429
  const isUseStateIdentifier = (identifier) => {
45853
44430
  if (!isNodeOfType(identifier, "Identifier")) return false;
@@ -45876,18 +44453,14 @@ const FUNCTION_WRAPPER_HOOK_NAMES$1 = new Set([
45876
44453
  "useStableCallback",
45877
44454
  "useCallbackRef"
45878
44455
  ]);
45879
- const getWrapperHookWrappedFunction = (initializer, resultSymbol, scopes) => {
44456
+ const getWrapperHookWrappedFunction = (initializer) => {
45880
44457
  if (!isNodeOfType(initializer, "CallExpression")) return null;
45881
- const transparentReactArgument = getTransparentReactCallbackWrapperArgument(initializer, resultSymbol, scopes);
45882
- if (transparentReactArgument) return transparentReactArgument;
45883
44458
  const callee = initializer.callee;
45884
44459
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier") ? callee.property.name : null;
45885
44460
  if (!calleeName || !FUNCTION_WRAPPER_HOOK_NAMES$1.has(calleeName)) return null;
45886
44461
  const wrapped = initializer.arguments?.[0];
45887
- if (!wrapped) return null;
45888
- if (calleeName === "useEffectEvent") return null;
45889
- if (isFunctionLike$1(wrapped)) return wrapped;
45890
- return null;
44462
+ if (!wrapped || !isFunctionLike$1(wrapped)) return null;
44463
+ return wrapped;
45891
44464
  };
45892
44465
  const HANDLER_NAMED_PROP_PATTERN = /^(on|handle)[A-Z]/;
45893
44466
  const wrappedFunctionNotifiesParent = (analysis, wrappedFunction) => getDownstreamRefs(analysis, wrappedFunction).some((innerRef) => {
@@ -45897,29 +44470,16 @@ const wrappedFunctionNotifiesParent = (analysis, wrappedFunction) => getDownstre
45897
44470
  const innerParent = innerIdentifier.parent;
45898
44471
  return Boolean(innerParent && isNodeOfType(innerParent, "CallExpression") && innerParent.callee === innerIdentifier);
45899
44472
  });
45900
- const isDirectParentCallbackRef = (analysis, ref, scopes) => {
44473
+ const isDirectParentCallbackRef = (analysis, ref) => {
45901
44474
  if (isProp(analysis, ref)) return true;
45902
- if (hasMutableBindingWrite$1(ref)) {
45903
- if (!(ref.resolved?.references.filter((candidateReference) => candidateReference.isWrite() && !candidateReference.init) ?? []).every((candidateReference) => {
45904
- const candidateIdentifier = candidateReference.identifier;
45905
- const assignment = candidateIdentifier.parent;
45906
- if (!assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== candidateIdentifier) return false;
45907
- const assignedReferences = getDownstreamRefs(analysis, assignment.right);
45908
- return assignedReferences.length > 0 && assignedReferences.every((assignedReference) => isProp(analysis, assignedReference));
45909
- })) return false;
45910
- }
45911
44475
  return Boolean(ref.resolved?.defs.some((def) => {
45912
44476
  const node = def.node;
45913
44477
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
45914
44478
  const initializer = unwrapChainExpression(node.init);
45915
- const wrappedFunction = getWrapperHookWrappedFunction(initializer, isNodeOfType(node.id, "Identifier") ? scopes.symbolFor(node.id) ?? null : null, scopes);
44479
+ const wrappedFunction = getWrapperHookWrappedFunction(initializer);
45916
44480
  if (wrappedFunction) {
45917
44481
  if (wrappedFunction.async) return false;
45918
- if (isFunctionLike$1(wrappedFunction)) return wrappedFunctionNotifiesParent(analysis, wrappedFunction);
45919
- const directName = getParentCallbackPropName(analysis, wrappedFunction);
45920
- const downstreamReferences = getDownstreamRefs(analysis, wrappedFunction);
45921
- if (directName !== null) return true;
45922
- return downstreamReferences.some((wrappedReference) => !hasMutableBindingWrite$1(wrappedReference) && getUpstreamRefs(analysis, wrappedReference).some((upstreamReference) => isProp(analysis, upstreamReference)));
44482
+ return wrappedFunctionNotifiesParent(analysis, wrappedFunction);
45923
44483
  }
45924
44484
  if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return false;
45925
44485
  return getDownstreamRefs(analysis, initializer).some((initializerRef) => getUpstreamRefs(analysis, initializerRef).some((upstreamRef) => isProp(analysis, upstreamRef)));
@@ -45929,7 +44489,7 @@ const getDeclarationKind = (declarator) => {
45929
44489
  const declaration = declarator.parent;
45930
44490
  return declaration && isNodeOfType(declaration, "VariableDeclaration") ? declaration.kind : null;
45931
44491
  };
45932
- const hasMutableBindingWrite$1 = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
44492
+ const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
45933
44493
  const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @__PURE__ */ new Set()) => {
45934
44494
  const unwrappedExpression = stripParenExpression(expression);
45935
44495
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
@@ -45941,7 +44501,7 @@ const getParentCallbackPropName = (analysis, expression, visitedVariables = /* @
45941
44501
  const bindingIdentifier = callbackVariable.defs.find((definition) => definition.type === "Parameter")?.name;
45942
44502
  return (bindingIdentifier && getDestructuredBindingPropertyName(bindingIdentifier)) ?? unwrappedExpression.name;
45943
44503
  }
45944
- if (hasMutableBindingWrite$1(callbackReference)) return null;
44504
+ if (hasMutableBindingWrite(callbackReference)) return null;
45945
44505
  const definitions = callbackVariable.defs.map((definition) => definition.node).filter((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
45946
44506
  if (definitions.length !== 1) return null;
45947
44507
  const declarator = definitions[0];
@@ -46007,7 +44567,7 @@ const getRefAliasDeclarator = (identifier) => {
46007
44567
  const getRefBindingProvenance = (analysis, receiver, isReactUseRefCall) => {
46008
44568
  if (!isNodeOfType(receiver, "Identifier")) return null;
46009
44569
  const receiverReference = getRef(analysis, receiver);
46010
- if (!receiverReference?.resolved || hasMutableBindingWrite$1(receiverReference)) return null;
44570
+ if (!receiverReference?.resolved || hasMutableBindingWrite(receiverReference)) return null;
46011
44571
  const variables = /* @__PURE__ */ new Set();
46012
44572
  let currentVariable = receiverReference.resolved;
46013
44573
  let refCall = null;
@@ -46023,7 +44583,7 @@ const getRefBindingProvenance = (analysis, receiver, isReactUseRefCall) => {
46023
44583
  }
46024
44584
  if (getDeclarationKind(declarator) !== "const" || !isNodeOfType(stripParenExpression(declarator.init), "Identifier")) return null;
46025
44585
  const upstreamReference = getRef(analysis, stripParenExpression(declarator.init));
46026
- if (!upstreamReference?.resolved || hasMutableBindingWrite$1(upstreamReference)) return null;
44586
+ if (!upstreamReference?.resolved || hasMutableBindingWrite(upstreamReference)) return null;
46027
44587
  currentVariable = upstreamReference.resolved;
46028
44588
  }
46029
44589
  if (!refCall) return null;
@@ -46098,7 +44658,7 @@ const isParentPropsContextMerge = (analysis, expression) => {
46098
44658
  while (isNodeOfType(currentExpression, "Identifier")) {
46099
44659
  const currentReference = getRef(analysis, currentExpression);
46100
44660
  const currentVariable = currentReference?.resolved;
46101
- if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite$1(currentReference)) return false;
44661
+ if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite(currentReference)) return false;
46102
44662
  visitedVariables.add(currentVariable);
46103
44663
  const definitions = currentVariable.defs.filter((definition) => isNodeOfType(definition.node, "VariableDeclarator"));
46104
44664
  if (definitions.length !== 1) return false;
@@ -46112,11 +44672,11 @@ const isParentPropsContextMerge = (analysis, expression) => {
46112
44672
  const propsExpression = stripParenExpression(propsSpread.argument);
46113
44673
  if (!isNodeOfType(propsExpression, "Identifier")) return false;
46114
44674
  const propsReference = getRef(analysis, propsExpression);
46115
- if (!propsReference?.resolved || !isWholePropsObjectReference(analysis, propsReference) || hasMutableBindingWrite$1(propsReference) || propsReference.resolved.references.some((candidateReference) => candidateReference !== propsReference)) return false;
44675
+ if (!propsReference?.resolved || !isWholePropsObjectReference(analysis, propsReference) || hasMutableBindingWrite(propsReference) || propsReference.resolved.references.some((candidateReference) => candidateReference !== propsReference)) return false;
46116
44676
  const contextExpression = stripParenExpression(contextSpread.argument);
46117
44677
  if (!isNodeOfType(contextExpression, "Identifier")) return false;
46118
44678
  const contextReference = getRef(analysis, contextExpression);
46119
- if (!contextReference?.resolved || hasMutableBindingWrite$1(contextReference) || contextReference.resolved.references.some((candidateReference) => !candidateReference.init && candidateReference !== contextReference)) return false;
44679
+ if (!contextReference?.resolved || hasMutableBindingWrite(contextReference) || contextReference.resolved.references.some((candidateReference) => !candidateReference.init && candidateReference !== contextReference)) return false;
46120
44680
  const contextInitializer = contextReference.resolved?.defs.map((definition) => definition.node).find((definitionNode) => isNodeOfType(definitionNode, "VariableDeclarator"));
46121
44681
  if (!contextInitializer || !isNodeOfType(contextInitializer, "VariableDeclarator") || getDeclarationKind(contextInitializer) !== "const" || !contextInitializer.init || !isNodeOfType(contextInitializer.init, "CallExpression")) return false;
46122
44682
  const contextHook = stripParenExpression(contextInitializer.init.callee);
@@ -46130,7 +44690,7 @@ const getImmutableParentCallbackPropName = (analysis, expression) => {
46130
44690
  while (isNodeOfType(currentExpression, "Identifier")) {
46131
44691
  const currentReference = getRef(analysis, currentExpression);
46132
44692
  const currentVariable = currentReference?.resolved;
46133
- if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite$1(currentReference)) return null;
44693
+ if (!currentReference || !currentVariable || visitedVariables.has(currentVariable) || hasMutableBindingWrite(currentReference)) return null;
46134
44694
  visitedVariables.add(currentVariable);
46135
44695
  const definition = currentVariable.defs.length === 1 ? currentVariable.defs[0] : null;
46136
44696
  const bindingIdentifier = definition?.name;
@@ -46199,7 +44759,7 @@ const getCommandCallbackPropName = (analysis, expression, isReactUseRefCall) =>
46199
44759
  while (isNodeOfType(currentExpression, "Identifier")) {
46200
44760
  const callbackReference = getRef(analysis, currentExpression);
46201
44761
  const callbackVariable = callbackReference?.resolved;
46202
- if (!callbackReference || !callbackVariable || visitedVariables.has(callbackVariable) || hasMutableBindingWrite$1(callbackReference)) return null;
44762
+ if (!callbackReference || !callbackVariable || visitedVariables.has(callbackVariable) || hasMutableBindingWrite(callbackReference)) return null;
46203
44763
  visitedVariables.add(callbackVariable);
46204
44764
  const definition = callbackVariable.defs.length === 1 ? callbackVariable.defs[0] : null;
46205
44765
  const declarator = definition?.node;
@@ -46219,11 +44779,10 @@ const getCommandCallbackPropName = (analysis, expression, isReactUseRefCall) =>
46219
44779
  if (!propertyName || !COMMAND_PROP_NAME_PATTERN.test(propertyName)) return null;
46220
44780
  return refCurrentObjectPreservesCallbackProperty(analysis, currentExpression.object, propertyName, isReactUseRefCall) ? propertyName : null;
46221
44781
  };
46222
- const isWrapperHookCallbackRef = (analysis, ref, scopes) => Boolean(ref.resolved?.defs.some((def) => {
44782
+ const isWrapperHookCallbackRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
46223
44783
  const node = def.node;
46224
44784
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
46225
- const resultSymbol = isNodeOfType(node.id, "Identifier") ? scopes.symbolFor(node.id) ?? null : null;
46226
- return getWrapperHookWrappedFunction(unwrapChainExpression(node.init), resultSymbol, scopes) !== null;
44785
+ return getWrapperHookWrappedFunction(unwrapChainExpression(node.init)) !== null;
46227
44786
  }));
46228
44787
  const isHandlerBagArgument = (analysis, argument) => {
46229
44788
  if (!isNodeOfType(argument, "ObjectExpression")) return false;
@@ -46242,26 +44801,14 @@ const isHandlerBagArgument = (analysis, argument) => {
46242
44801
  };
46243
44802
  const getFunctionalUpdaterDataRefs = (analysis, updater) => getDownstreamRefs(analysis, updater).filter((updaterRef) => !updaterRef.resolved?.defs.some((def) => def.type === "Parameter" && def.node === updater));
46244
44803
  const HOOK_NAME_PATTERN$1 = /^use[A-Z0-9]/;
46245
- const EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1 = new Set([
44804
+ const EXTERNAL_SUBSCRIPTION_HOOK_NAMES = new Set([
46246
44805
  "useIntersectionObserver",
46247
44806
  "useMatchMedia",
46248
- "useMediaJobProgress",
46249
44807
  "useMediaQuery",
46250
- "useMediaQueryState",
46251
44808
  "useResizeObserver",
46252
44809
  "useVisibility",
46253
44810
  "useWindowSize"
46254
44811
  ]);
46255
- const isCallbackPropReference = (analysis, ref) => {
46256
- if (!isProp(analysis, ref)) return false;
46257
- const identifier = ref.identifier;
46258
- if (!isNodeOfType(identifier, "Identifier")) return false;
46259
- if (!isWholePropsObjectReference(analysis, ref)) return HANDLER_NAMED_PROP_PATTERN.test(identifier.name);
46260
- const member = identifier.parent;
46261
- if (!member || !isNodeOfType(member, "MemberExpression") || member.object !== identifier) return false;
46262
- const propertyName = getStaticMemberPropertyName(member);
46263
- return Boolean(propertyName && HANDLER_NAMED_PROP_PATTERN.test(propertyName));
46264
- };
46265
44812
  const isParentWiredHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
46266
44813
  const node = def.node;
46267
44814
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
@@ -46269,7 +44816,7 @@ const isParentWiredHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs
46269
44816
  if (!isNodeOfType(init, "CallExpression")) return false;
46270
44817
  const callee = init.callee;
46271
44818
  if (!isNodeOfType(callee, "Identifier") || !HOOK_NAME_PATTERN$1.test(callee.name)) return false;
46272
- return (init.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isCallbackPropReference(analysis, downstreamRef)));
44819
+ return (init.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isProp(analysis, downstreamRef)));
46273
44820
  }));
46274
44821
  const isParentWiredHookResultArgument = (analysis, argument) => {
46275
44822
  if (!isNodeOfType(argument, "Identifier")) return false;
@@ -46282,40 +44829,19 @@ const isParentWiredHookCalleeRef = (analysis, ref) => {
46282
44829
  if (!isNodeOfType(identifier, "Identifier") || !HOOK_NAME_PATTERN$1.test(identifier.name)) return false;
46283
44830
  const parent = identifier.parent;
46284
44831
  if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== identifier) return false;
46285
- return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isCallbackPropReference(analysis, downstreamRef)));
44832
+ return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isProp(analysis, downstreamRef)));
46286
44833
  };
46287
- const getLocalHookExternalStateProof = (analysis, ref) => {
46288
- let hookFunction = resolveToFunction(ref);
46289
- if (!hookFunction) for (const definition of ref.resolved?.defs ?? []) {
46290
- const definitionNode = definition.node;
46291
- if (!isNodeOfType(definitionNode, "VariableDeclarator") || !definitionNode.init) continue;
46292
- const initializer = stripParenExpression(definitionNode.init);
46293
- if (!isNodeOfType(initializer, "CallExpression")) continue;
46294
- const callee = stripParenExpression(initializer.callee);
46295
- if (!isNodeOfType(callee, "Identifier")) continue;
46296
- const calleeReference = getRef(analysis, callee);
46297
- if (!calleeReference) continue;
46298
- hookFunction = resolveToFunction(calleeReference);
46299
- if (hookFunction) break;
46300
- }
46301
- if (!hookFunction) return null;
46302
- const returnedReferences = collectFunctionReturnStatements(hookFunction).flatMap((returnStatement) => returnStatement.argument ? getDownstreamRefs(analysis, returnStatement.argument) : []);
46303
- if (returnedReferences.length === 0) return null;
46304
- return returnedReferences.every((returnedReference) => isState(analysis, returnedReference) && isExternallyDrivenState(analysis, returnedReference));
46305
- };
46306
- const isExternalSubscriptionHookRef = (analysis, ref) => {
44834
+ const isExternalSubscriptionHookRef = (ref) => {
46307
44835
  const identifier = ref.identifier;
46308
44836
  if (!isNodeOfType(identifier, "Identifier")) return false;
46309
- const localHookProof = getLocalHookExternalStateProof(analysis, ref);
46310
- if (localHookProof !== null) return localHookProof;
46311
- if (EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(identifier.name) && isCalleePosition(identifier)) return true;
44837
+ if (EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(identifier.name) && isCalleePosition(identifier)) return true;
46312
44838
  return Boolean(ref.resolved?.defs.some((def) => {
46313
44839
  const node = def.node;
46314
44840
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
46315
44841
  const initializer = stripParenExpression(node.init);
46316
44842
  if (!isNodeOfType(initializer, "CallExpression")) return false;
46317
44843
  const callee = stripParenExpression(initializer.callee);
46318
- return isNodeOfType(callee, "Identifier") && EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(callee.name);
44844
+ return isNodeOfType(callee, "Identifier") && EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(callee.name);
46319
44845
  }));
46320
44846
  };
46321
44847
  const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
@@ -46351,22 +44877,16 @@ const noPassDataToParent = defineRule({
46351
44877
  const callExpr = getCallExpr(ref);
46352
44878
  if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
46353
44879
  const callbackRefProvenance = getCallbackRefProvenance(analysis, node, callExpr, isReactUseRefCall, isReactUseEffectCall);
44880
+ if (isRefCall(analysis, ref) && !callbackRefProvenance) continue;
46354
44881
  if (!isSynchronous(ref.identifier, effectFn)) continue;
46355
44882
  const calleeNode = unwrapChainExpression(callExpr.callee);
46356
44883
  const identifier = ref.identifier;
46357
- const resolvedCallbackPropNames = isNodeOfType(calleeNode, "MemberExpression") && getStaticMemberPropertyName(calleeNode) === "current" ? null : getParentCallbackPropNames({
46358
- analysis,
46359
- expression: calleeNode,
46360
- scopes: context.scopes
46361
- });
46362
- const callbackPropNames = callbackRefProvenance?.callbackPropNames ?? resolvedCallbackPropNames;
46363
- if (isRefCall(analysis, ref) && !callbackPropNames) continue;
46364
- if (callbackPropNames) {
46365
- if ([...callbackPropNames].some((callbackPropName) => COMMAND_PROP_NAME_PATTERN.test(callbackPropName))) continue;
44884
+ if (callbackRefProvenance) {
44885
+ if ([...callbackRefProvenance.callbackPropNames].some((callbackPropName) => COMMAND_PROP_NAME_PATTERN.test(callbackPropName))) continue;
46366
44886
  } else if (calleeNode === identifier) {
46367
44887
  const callbackPropName = getCommandCallbackPropName(analysis, identifier, isReactUseRefCall);
46368
44888
  if (callbackPropName && COMMAND_PROP_NAME_PATTERN.test(callbackPropName)) continue;
46369
- if (!isDirectParentCallbackRef(analysis, ref, context.scopes)) continue;
44889
+ if (!isDirectParentCallbackRef(analysis, ref)) continue;
46370
44890
  if (isNodeOfType(identifier, "Identifier") && COMMAND_PROP_NAME_PATTERN.test(identifier.name)) continue;
46371
44891
  } else if (isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === identifier) {
46372
44892
  if (!isWholePropsObjectReference(analysis, ref)) continue;
@@ -46374,10 +44894,10 @@ const noPassDataToParent = defineRule({
46374
44894
  } else continue;
46375
44895
  const methodName = getCallMethodName(calleeNode);
46376
44896
  const isPropCallbackNamedLikeStringRead = Boolean(methodName && STRING_READ_METHOD_NAMES.has(methodName) && isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === ref.identifier && isWholePropsObjectReference(analysis, ref));
46377
- if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead && !callbackPropNames) continue;
44897
+ if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead) continue;
46378
44898
  if (methodName && COMMAND_PROP_NAME_PATTERN.test(methodName)) continue;
46379
- if (!callbackPropNames && isNamespacedApiCallee(calleeNode)) continue;
46380
- const isSetterNamedCallee = callbackPropNames ? [...callbackPropNames].every((callbackPropName) => SETTER_NAMED_PROP_PATTERN.test(callbackPropName)) : Boolean((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) && SETTER_NAMED_PROP_PATTERN.test((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) ?? ""));
44899
+ if (!callbackRefProvenance && isNamespacedApiCallee(calleeNode)) continue;
44900
+ const isSetterNamedCallee = callbackRefProvenance ? [...callbackRefProvenance.callbackPropNames].every((callbackPropName) => SETTER_NAMED_PROP_PATTERN.test(callbackPropName)) : Boolean((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) && SETTER_NAMED_PROP_PATTERN.test((isNodeOfType(identifier, "Identifier") ? identifier.name : methodName) ?? ""));
46381
44901
  const isLeafRef = (argRef) => getUpstreamRefs(analysis, argRef).length === 1;
46382
44902
  const argsUpstreamRefs = (callExpr.arguments ?? []).flatMap((argument) => {
46383
44903
  if (isFunctionLike$1(argument)) {
@@ -46391,11 +44911,11 @@ const noPassDataToParent = defineRule({
46391
44911
  if (argumentRef && resolveToFunction(argumentRef)) return [];
46392
44912
  }
46393
44913
  return getDownstreamRefs(analysis, argument);
46394
- }).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) || isExternalSubscriptionHookRef(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
46395
- if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref, context.scopes)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
44914
+ }).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
44915
+ if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
46396
44916
  if (!argsUpstreamRefs.some((argRef) => {
46397
44917
  if (isUseStateIdentifier(argRef.identifier)) return false;
46398
- if (isExternalSubscriptionHookRef(analysis, argRef)) return false;
44918
+ if (isExternalSubscriptionHookRef(argRef)) return false;
46399
44919
  if (isProp(analysis, argRef)) return false;
46400
44920
  if (isUseRefIdentifier(argRef.identifier)) return false;
46401
44921
  if (isRefCurrent(argRef)) return false;
@@ -46430,47 +44950,9 @@ const isCallResultConsumedAsArgument = (callExpression) => {
46430
44950
  return false;
46431
44951
  };
46432
44952
  //#endregion
46433
- //#region src/plugin/rules/state-and-effects/utils/is-custom-hook-state-result-reference.ts
46434
- const NON_STATE_CUSTOM_HOOK_NAMES = new Set([
46435
- "useCallbackRef",
46436
- "useEffectEvent",
46437
- "useEvent",
46438
- "useEventCallback",
46439
- "useLatest",
46440
- "useMemoizedFn",
46441
- "useStableCallback"
46442
- ]);
46443
- const EXTERNAL_SUBSCRIPTION_HOOK_NAMES = new Set([
46444
- "useIntersectionObserver",
46445
- "useMatchMedia",
46446
- "useMediaJobProgress",
46447
- "useMediaQuery",
46448
- "useResizeObserver",
46449
- "useVisibility",
46450
- "useWindowSize"
46451
- ]);
46452
- const getHookCalleeName = (initializer) => {
46453
- const unwrappedInitializer = stripParenExpression(initializer);
46454
- if (!isNodeOfType(unwrappedInitializer, "CallExpression")) return null;
46455
- const callee = stripParenExpression(unwrappedInitializer.callee);
46456
- if (isNodeOfType(callee, "Identifier")) return callee.name;
46457
- if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
46458
- return null;
46459
- };
46460
- const isCustomHookStateResultReference = (analysis, reference) => Boolean(reference.resolved?.defs.some((definition) => {
46461
- const declarator = definition.node;
46462
- if (!isNodeOfType(declarator, "VariableDeclarator") || !declarator.init) return false;
46463
- const calleeName = getHookCalleeName(declarator.init);
46464
- if (!calleeName || !HOOK_NAME_PATTERN$3.test(calleeName) || BUILTIN_HOOK_NAMES.has(calleeName) || NON_STATE_CUSTOM_HOOK_NAMES.has(calleeName) || EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(calleeName)) return false;
46465
- const initializer = stripParenExpression(declarator.init);
46466
- if (!isNodeOfType(initializer, "CallExpression")) return false;
46467
- return initializer.arguments.some((argument) => getDownstreamRefs(analysis, argument).some((argumentReference) => isProp(analysis, argumentReference)));
46468
- }));
46469
- //#endregion
46470
44953
  //#region src/plugin/rules/state-and-effects/no-pass-live-state-to-parent.ts
46471
44954
  const SETTER_NAMED_CALLBACK_PATTERN = /^set[A-Z]/;
46472
44955
  const DATA_FETCHING_CALLBACK_PATTERN = /^(fetch|refetch|load|query|request)([A-Z_]|$)/;
46473
- const hasMutableBindingWrite = (reference) => Boolean(reference.resolved?.references.some((candidateReference) => candidateReference.isWrite() && !candidateReference.init));
46474
44956
  const getCallCalleeName = (callExpr) => {
46475
44957
  if (!isNodeOfType(callExpr, "CallExpression")) return null;
46476
44958
  const callee = callExpr.callee;
@@ -46515,10 +44997,6 @@ const collectUpstreamStateRefs = (analysis, ref, stateRefs, visited) => {
46515
44997
  stateRefs.push(ref);
46516
44998
  return;
46517
44999
  }
46518
- if (isCustomHookStateResultReference(analysis, ref)) {
46519
- stateRefs.push(ref);
46520
- return;
46521
- }
46522
45000
  for (const def of ref.resolved?.defs ?? []) {
46523
45001
  if (def.type === "ImportBinding" || def.type === "Parameter") continue;
46524
45002
  const defNode = def.node;
@@ -46548,32 +45026,6 @@ const collectPropCallbackBoundStateRefs = (analysis, ref, isPropCallbackRef) =>
46548
45026
  }
46549
45027
  return stateRefs;
46550
45028
  };
46551
- const collectDirectCallStateRefs = (analysis, callExpression) => {
46552
- const stateReferences = [];
46553
- for (const argument of callExpression.arguments) {
46554
- if (isFunctionLike$1(argument)) continue;
46555
- for (const argumentReference of getDownstreamRefs(analysis, argument)) {
46556
- if (resolveToFunction(argumentReference)) continue;
46557
- collectUpstreamStateRefs(analysis, argumentReference, stateReferences, /* @__PURE__ */ new Set());
46558
- }
46559
- }
46560
- return stateReferences;
46561
- };
46562
- const getTransparentWrapperPropReference = (analysis, reference, context) => {
46563
- for (const definition of reference.resolved?.defs ?? []) {
46564
- const declarator = definition.node;
46565
- if (!isNodeOfType(declarator, "VariableDeclarator") || !isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
46566
- const resultSymbol = context.scopes.symbolFor(declarator.id);
46567
- const callbackArgument = getTransparentReactCallbackWrapperArgument(declarator.init, resultSymbol, context.scopes);
46568
- if (!callbackArgument) continue;
46569
- const callbackReferences = getDownstreamRefs(analysis, callbackArgument);
46570
- const callbackReference = callbackReferences.find((candidateReference) => isPropCallbackInvocationRef(analysis, candidateReference));
46571
- if (callbackReference) return callbackReference;
46572
- const propReference = callbackReferences.find((candidateReference) => isProp(analysis, candidateReference) && !candidateReference.resolved?.references.some((candidateUsage) => candidateUsage.isWrite() && !candidateUsage.init));
46573
- if (propReference) return propReference;
46574
- }
46575
- return null;
46576
- };
46577
45029
  const isSetterNamedCallbackReceivingData = (callbackRef) => {
46578
45030
  const callExpr = getCallExpr(callbackRef);
46579
45031
  if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) return false;
@@ -46609,16 +45061,6 @@ const resolvesToLocalHookReturnBinding = (ref) => Boolean(ref?.resolved?.defs?.s
46609
45061
  const calleeName = getInitializerCalleeName(node.init);
46610
45062
  return calleeName !== null && isReactHookName(calleeName) && !FUNCTION_WRAPPER_HOOK_NAMES.has(calleeName);
46611
45063
  }));
46612
- const getDirectLocalEffectHelper = (callExpression, effectFunction, context) => {
46613
- const helperFunction = resolveExactLocalFunction(callExpression.callee, context.scopes);
46614
- if (!helperFunction) return null;
46615
- let ancestor = callExpression.parent;
46616
- while (ancestor && ancestor !== effectFunction) {
46617
- if (isFunctionLike$1(ancestor)) return null;
46618
- ancestor = ancestor.parent;
46619
- }
46620
- return ancestor === effectFunction ? helperFunction : null;
46621
- };
46622
45064
  const noPassLiveStateToParent = defineRule({
46623
45065
  id: "no-pass-live-state-to-parent",
46624
45066
  title: "Live state pushed to parent via effect",
@@ -46633,32 +45075,20 @@ const noPassLiveStateToParent = defineRule({
46633
45075
  if (!effectFnRefs) return;
46634
45076
  const effectFn = getEffectFn(analysis, node);
46635
45077
  if (!effectFn) return;
46636
- const effectFunctionBody = isNodeOfType(effectFn, "ArrowFunctionExpression") || isNodeOfType(effectFn, "FunctionExpression") || isNodeOfType(effectFn, "FunctionDeclaration") ? effectFn.body : null;
46637
45078
  for (const ref of effectFnRefs) {
45079
+ const propCallbackRefs = getEventualCallRefsTo(analysis, ref, (innerRef) => isParentNotificationCallbackRef(analysis, innerRef));
45080
+ if (propCallbackRefs.length === 0) continue;
45081
+ if (resolvesToLocalHookReturnBinding(ref)) continue;
45082
+ if (!isSynchronous(ref.identifier, effectFn)) continue;
46638
45083
  const callExpr = getCallExpr(ref);
46639
- if (!callExpr || !isNodeOfType(callExpr, "CallExpression")) continue;
46640
- const directLocalEffectHelper = getDirectLocalEffectHelper(callExpr, effectFn, context);
46641
- const callGraphReferences = directLocalEffectHelper ? [ref, ...getDownstreamRefs(analysis, directLocalEffectHelper)] : [ref];
46642
- const resolvedCallbackPropNames = getParentCallbackPropNames({
46643
- analysis,
46644
- expression: callExpr.callee,
46645
- scopes: context.scopes
46646
- });
46647
- const callExpressionRoot = findTransparentExpressionRoot(callExpr);
46648
- const notificationCallbackPropNames = Boolean(resolvedCallbackPropNames && callExpr.arguments.length > 0 && (!isCallResultCapturedToLocal(callExpr) || isNodeOfType(callExpressionRoot.parent, "ReturnStatement") && callExpressionRoot.parent.parent === effectFunctionBody) && [...resolvedCallbackPropNames].every((callbackPropName) => !DATA_FETCHING_CALLBACK_PATTERN.test(callbackPropName))) ? resolvedCallbackPropNames : null;
46649
- if (!notificationCallbackPropNames && hasMutableBindingWrite(ref)) continue;
46650
- const propCallbackRefs = callGraphReferences.flatMap((callGraphReference) => getEventualCallRefsTo(analysis, callGraphReference, (innerRef) => isParentNotificationCallbackRef(analysis, innerRef)));
46651
- const transparentPropReference = propCallbackRefs.length === 0 ? getTransparentWrapperPropReference(analysis, ref, context) : null;
46652
- if (propCallbackRefs.length === 0 && !transparentPropReference && !notificationCallbackPropNames) continue;
46653
- if (!notificationCallbackPropNames && resolvesToLocalHookReturnBinding(ref)) continue;
46654
- if (!isSynchronous(ref.identifier, effectFn) && !directLocalEffectHelper) continue;
45084
+ if (!callExpr) continue;
46655
45085
  if (isCallResultConsumedAsArgument(callExpr)) continue;
46656
45086
  const calleeNode = callExpr.callee;
46657
45087
  const methodName = calleeNode ? getCallMethodName(calleeNode) : null;
46658
45088
  const isPropCallbackNamedLikeStringRead = Boolean(methodName && STRING_READ_METHOD_NAMES.has(methodName) && calleeNode && isNodeOfType(calleeNode, "MemberExpression") && stripParenExpression(calleeNode.object) === ref.identifier && isWholePropsObjectReference(analysis, ref));
46659
- if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead && !notificationCallbackPropNames) continue;
46660
- if (!notificationCallbackPropNames && calleeNode && isNamespacedApiCallee(calleeNode)) continue;
46661
- const stateArgRefs = transparentPropReference || notificationCallbackPropNames ? collectDirectCallStateRefs(analysis, callExpr) : callGraphReferences.flatMap((callGraphReference) => collectPropCallbackBoundStateRefs(analysis, callGraphReference, (innerRef) => isParentNotificationCallbackRef(analysis, innerRef)));
45089
+ if (methodName && DATA_SINK_METHOD_NAMES.has(methodName) && !isPropCallbackNamedLikeStringRead) continue;
45090
+ if (calleeNode && isNamespacedApiCallee(calleeNode)) continue;
45091
+ const stateArgRefs = collectPropCallbackBoundStateRefs(analysis, ref, (innerRef) => isParentNotificationCallbackRef(analysis, innerRef));
46662
45092
  const handsSetterNamedCallbackData = propCallbackRefs.some(isSetterNamedCallbackReceivingData);
46663
45093
  if (stateArgRefs.length === 0 && !handsSetterNamedCallbackData) continue;
46664
45094
  context.report({
@@ -47051,7 +45481,6 @@ const isStateLikeDependency = (analysis, element, isPropName) => {
47051
45481
  if (!analysis) return true;
47052
45482
  const reference = getRef(analysis, element);
47053
45483
  if (!reference) return true;
47054
- if (isCustomHookStateResultReference(analysis, reference)) return true;
47055
45484
  const upstreamReferences = getUpstreamRefs(analysis, reference);
47056
45485
  if (upstreamReferences.some((upstreamReference) => isState(analysis, upstreamReference))) return true;
47057
45486
  return !upstreamReferences.some((upstreamReference) => isProp(analysis, upstreamReference));
@@ -47068,22 +45497,6 @@ const getRefHeldPropCallbackName = (callExpression, isPropName) => {
47068
45497
  if (!callbackArgument || !isNodeOfType(callbackArgument, "Identifier")) return null;
47069
45498
  return isPropName(callbackArgument.name) ? callbackArgument.name : null;
47070
45499
  };
47071
- const getTransparentWrappedPropCallbackName = (callExpression, context, isPropName) => {
47072
- const callee = stripParenExpression(callExpression.callee);
47073
- if (!isNodeOfType(callee, "Identifier")) return null;
47074
- const binding = findVariableInitializer(callExpression, callee.name);
47075
- if (!binding?.initializer) return null;
47076
- const resultSymbol = context.scopes.symbolFor(callee);
47077
- const callbackArgument = getTransparentReactCallbackWrapperArgument(binding.initializer, resultSymbol, context.scopes);
47078
- if (!callbackArgument) return null;
47079
- const callbackSource = stripParenExpression(callbackArgument);
47080
- if (isNodeOfType(callbackSource, "Identifier")) return isPropName(callbackSource.name, callbackSource) ? callbackSource.name : null;
47081
- if (!isNodeOfType(callbackSource, "MemberExpression")) return null;
47082
- const receiver = stripParenExpression(callbackSource.object);
47083
- const propertyName = getStaticPropertyName(callbackSource);
47084
- if (!isNodeOfType(receiver, "Identifier") || !propertyName) return null;
47085
- return isPropName(receiver.name, receiver) ? propertyName : null;
47086
- };
47087
45500
  const noPropCallbackInEffect = defineRule({
47088
45501
  id: "no-prop-callback-in-effect",
47089
45502
  title: "Parent kept in sync with a callback effect",
@@ -47117,16 +45530,9 @@ const noPropCallbackInEffect = defineRule({
47117
45530
  walkInsideStatementBlocks(callback.body, (child) => {
47118
45531
  if (!isNodeOfType(child, "CallExpression")) return;
47119
45532
  const directCallee = stripParenExpression(child.callee);
47120
- const resolvedCallbackPropNames = analysis && propStackTracker.getCurrentPropNames().size > 0 ? getParentCallbackPropNames({
47121
- analysis,
47122
- expression: directCallee,
47123
- scopes: context.scopes
47124
- }) : null;
47125
- const calleeName = resolvedCallbackPropNames && [...resolvedCallbackPropNames][0] || isNodeOfType(directCallee, "Identifier") && propStackTracker.isPropName(directCallee.name) && directCallee.name || getRefHeldPropCallbackName(child, propStackTracker.isPropName) || getTransparentWrappedPropCallbackName(child, context, propStackTracker.isPropName);
45533
+ const calleeName = isNodeOfType(directCallee, "Identifier") && propStackTracker.isPropName(directCallee.name) && directCallee.name || getRefHeldPropCallbackName(child, propStackTracker.isPropName);
47126
45534
  if (!calleeName) return;
47127
- const callExpressionRoot = findTransparentExpressionRoot(child);
47128
- const isDirectEffectReturn = isNodeOfType(callExpressionRoot.parent, "ReturnStatement") && callExpressionRoot.parent.parent === callback.body;
47129
- if (!isResultDiscardedCall(child) && !isDirectEffectReturn) return;
45535
+ if (!isResultDiscardedCall(child)) return;
47130
45536
  if (reportedNodes.has(child)) return;
47131
45537
  reportedNodes.add(child);
47132
45538
  context.report({
@@ -47959,69 +46365,6 @@ const noRedundantShouldComponentUpdate = defineRule({
47959
46365
  }
47960
46366
  });
47961
46367
  //#endregion
47962
- //#region src/plugin/rules/correctness/no-ref-callback-cleanup-before-react-19.ts
47963
- const resolveFunctionExpressions = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
47964
- const expression = stripParenExpression(rawExpression);
47965
- if (isFunctionLike$1(expression)) return expression.async || expression.generator ? [] : [expression];
47966
- if (isNodeOfType(expression, "ConditionalExpression")) {
47967
- if (isNodeOfType(expression.test, "Literal")) return resolveFunctionExpressions(expression.test.value ? expression.consequent : expression.alternate, scopes, visitedSymbolIds);
47968
- return [...resolveFunctionExpressions(expression.consequent, scopes, visitedSymbolIds), ...resolveFunctionExpressions(expression.alternate, scopes, visitedSymbolIds)];
47969
- }
47970
- if (isNodeOfType(expression, "LogicalExpression")) {
47971
- if (isNodeOfType(expression.left, "Literal")) {
47972
- const isLeftTruthy = Boolean(expression.left.value);
47973
- if (expression.operator === "&&" && !isLeftTruthy) return [];
47974
- if (expression.operator === "||" && isLeftTruthy) return [];
47975
- if (expression.operator === "??" && expression.left.value !== null) return [];
47976
- }
47977
- if (expression.operator === "&&") return resolveFunctionExpressions(expression.right, scopes, visitedSymbolIds);
47978
- return [...resolveFunctionExpressions(expression.left, scopes, visitedSymbolIds), ...resolveFunctionExpressions(expression.right, scopes, visitedSymbolIds)];
47979
- }
47980
- if (isNodeOfType(expression, "SequenceExpression")) {
47981
- const finalExpression = expression.expressions.at(-1);
47982
- return finalExpression ? resolveFunctionExpressions(finalExpression, scopes, visitedSymbolIds) : [];
47983
- }
47984
- if (isNodeOfType(expression, "CallExpression")) {
47985
- if (!isReactApiCall(expression, "useCallback", scopes)) return [];
47986
- const callback = expression.arguments[0];
47987
- return callback && !isNodeOfType(callback, "SpreadElement") ? resolveFunctionExpressions(callback, scopes, visitedSymbolIds) : [];
47988
- }
47989
- if (!isNodeOfType(expression, "Identifier")) return [];
47990
- const symbol = scopes.symbolFor(expression);
47991
- if (!symbol || visitedSymbolIds.has(symbol.id)) return [];
47992
- if (symbol.kind === "function" && isNodeOfType(symbol.declarationNode, "FunctionDeclaration") && symbol.references.every((reference) => reference.flag === "read")) return resolveFunctionExpressions(symbol.declarationNode, scopes, new Set([...visitedSymbolIds, symbol.id]));
47993
- const initializer = getDirectConstInitializer(symbol);
47994
- if (!initializer) return [];
47995
- return resolveFunctionExpressions(initializer, scopes, new Set([...visitedSymbolIds, symbol.id]));
47996
- };
47997
- const functionReturnsCleanupFunction = (functionExpression, scopes) => {
47998
- if (!isFunctionLike$1(functionExpression)) return false;
47999
- if (!isNodeOfType(functionExpression.body, "BlockStatement")) return resolveFunctionExpressions(functionExpression.body, scopes).length > 0;
48000
- return collectFunctionReturnStatements(functionExpression).some((returnStatement) => Boolean(returnStatement.argument && resolveFunctionExpressions(returnStatement.argument, scopes).length > 0));
48001
- };
48002
- const callbackReturnsCleanupFunction = (callback, scopes) => {
48003
- return resolveFunctionExpressions(callback, scopes).some((functionExpression) => functionReturnsCleanupFunction(functionExpression, scopes));
48004
- };
48005
- const noRefCallbackCleanupBeforeReact19 = defineRule({
48006
- id: "no-ref-callback-cleanup-before-react-19",
48007
- title: "Ref cleanup requires React 19",
48008
- requires: ["react:18"],
48009
- disabledWhen: ["react:19"],
48010
- severity: "warn",
48011
- recommendation: "React 18 ignores functions returned from ref callbacks. Handle cleanup when React calls the ref with `null`, or require React 19 before returning a cleanup function.",
48012
- create: (context) => ({ JSXAttribute(node) {
48013
- if (getJsxAttributeName(node.name) !== "ref") return;
48014
- if (!isNodeOfType(node.value, "JSXExpressionContainer")) return;
48015
- const callback = node.value.expression;
48016
- if (!callback || isNodeOfType(callback, "JSXEmptyExpression")) return;
48017
- if (!callbackReturnsCleanupFunction(callback, context.scopes)) return;
48018
- context.report({
48019
- node,
48020
- message: "This ref callback returns a cleanup function, but React 18 ignores ref cleanup returns, so the cleanup never runs. Handle detachment when React calls the ref with `null`, or require React 19."
48021
- });
48022
- } })
48023
- });
48024
- //#endregion
48025
46368
  //#region src/plugin/rules/state-and-effects/no-ref-current-in-render.ts
48026
46369
  const REPEATED_ANCESTOR_TYPES = new Set([
48027
46370
  "DoWhileStatement",
@@ -54782,6 +53125,12 @@ const isInsideEs6Component$1 = (methodDefinition) => {
54782
53125
  if (!owningClass) return false;
54783
53126
  return isPreactOrReactComponentClass(owningClass);
54784
53127
  };
53128
+ const stripThisParameter = (params) => {
53129
+ const first = params[0];
53130
+ if (!first) return params;
53131
+ if (isNodeOfType(first, "Identifier") && first.name === "this") return params.slice(1);
53132
+ return params;
53133
+ };
54785
53134
  const preactNoRenderArguments = defineRule({
54786
53135
  id: "preact-no-render-arguments",
54787
53136
  title: "render() reads props from arguments",
@@ -57983,39 +56332,8 @@ const isUseStateSetterInScope = (node, setterName) => isHookBindingInScope(node,
57983
56332
  destructureIndex: 1
57984
56333
  });
57985
56334
  //#endregion
57986
- //#region src/plugin/utils/unwrap-return-expression.ts
57987
- const unwrapReturnExpression = (node) => isNodeOfType(node, "ReturnStatement") && node.argument ? node.argument : node;
57988
- //#endregion
57989
56335
  //#region src/plugin/rules/performance/rendering-hydration-no-flicker.ts
57990
56336
  const USE_EFFECT_ONLY = new Set(["useEffect"]);
57991
- const USE_CALLBACK_ONLY = new Set(["useCallback"]);
57992
- const USE_STATE_ONLY = new Set(["useState"]);
57993
- const REACT_API_CALL_OPTIONS = {
57994
- allowGlobalReactNamespace: true,
57995
- allowUnboundBareCalls: true,
57996
- resolveNamedAliases: true
57997
- };
57998
- const expressionReadsDerivedSymbol = (context, expression, stateDerivedSymbolIds) => {
57999
- let readsDerivedSymbol = false;
58000
- walkAst(expression, (node) => {
58001
- if (readsDerivedSymbol) return false;
58002
- if (node !== expression && isFunctionLike$1(node)) return false;
58003
- if (isNodeOfType(node, "Identifier") && stateDerivedSymbolIds.has(context.scopes.symbolFor(node)?.id ?? -1)) readsDerivedSymbol = true;
58004
- });
58005
- return readsDerivedSymbol;
58006
- };
58007
- const getStaticObjectPropertyName = (property) => {
58008
- if (!isNodeOfType(property, "Property") || property.computed || property.method || property.kind !== "init") return null;
58009
- if (isNodeOfType(property.key, "Identifier")) return property.key.name;
58010
- if (isNodeOfType(property.key, "Literal") && (typeof property.key.value === "string" || typeof property.key.value === "number")) return String(property.key.value);
58011
- return null;
58012
- };
58013
- const isNonVisibleJsxSpreadProperty = (propertyName) => propertyName === "id" || propertyName.startsWith("aria-") || /^on[A-Z]/.test(propertyName);
58014
- const isTransparentAssignmentTarget = (identifier) => {
58015
- const expressionRoot = findTransparentExpressionRoot(identifier);
58016
- const parent = expressionRoot.parent;
58017
- return Boolean(isNodeOfType(parent, "AssignmentExpression") && parent.left === expressionRoot || isNodeOfType(parent, "UpdateExpression") && parent.argument === expressionRoot || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expressionRoot);
58018
- };
58019
56337
  const argumentsReadRefCurrent = (callArguments) => callArguments.some((argument) => {
58020
56338
  let readsCurrent = false;
58021
56339
  walkAst(argument, (child) => {
@@ -58067,166 +56385,6 @@ const isStateUsedOnlyInIdOrAriaAttributes = (setterCall, setterName) => {
58067
56385
  });
58068
56386
  return referenceCount > 0 && !nonAriaReferenceFound;
58069
56387
  };
58070
- const isGlobalWindowMember = (context, node, propertyName) => {
58071
- const member = stripParenExpression(node);
58072
- if (!isNodeOfType(member, "MemberExpression") || member.computed) return false;
58073
- const receiver = stripParenExpression(member.object);
58074
- return isNodeOfType(receiver, "Identifier") && receiver.name === "window" && context.scopes.isGlobalReference(receiver) && isNodeOfType(member.property, "Identifier") && member.property.name === propertyName;
58075
- };
58076
- const getDirectWindowWidthSetter = (context, statement) => {
58077
- const call = unwrapDiscardedExpression(statement);
58078
- if (!isNodeOfType(call, "CallExpression") || call.arguments?.length !== 1) return null;
58079
- if (!isNodeOfType(call.callee, "Identifier") || !isSetterCall(call)) return null;
58080
- const argument = call.arguments[0];
58081
- return isGlobalWindowMember(context, argument, "innerWidth") ? call : null;
58082
- };
58083
- const getResizeListenerHandler = (context, statement, methodName) => {
58084
- const call = unwrapDiscardedExpression(statement);
58085
- if (!isNodeOfType(call, "CallExpression") || call.arguments?.length !== 2) return null;
58086
- if (!isGlobalWindowMember(context, call.callee, methodName)) return null;
58087
- const eventName = call.arguments[0];
58088
- const handler = call.arguments[1];
58089
- if (!isNodeOfType(eventName, "Literal") || eventName.value !== "resize") return null;
58090
- return isNodeOfType(handler, "Identifier") ? handler : null;
58091
- };
58092
- const getCleanupResizeHandler = (context, statement) => {
58093
- if (!isNodeOfType(statement, "ReturnStatement") || !isFunctionLike$1(statement.argument)) return null;
58094
- const cleanupStatements = getCallbackStatements(statement.argument);
58095
- if (cleanupStatements.length !== 1) return null;
58096
- return getResizeListenerHandler(context, unwrapReturnExpression(cleanupStatements[0]), "removeEventListener");
58097
- };
58098
- const findExactViewportState = (context, componentFunction, setterCall) => {
58099
- if (!isFunctionLike$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return null;
58100
- const componentBody = componentFunction.body;
58101
- if (!isNodeOfType(setterCall.callee, "Identifier")) return null;
58102
- const setterSymbol = context.scopes.symbolFor(setterCall.callee);
58103
- if (!setterSymbol || setterSymbol.kind !== "const" || !isNodeOfType(setterSymbol.declarationNode, "VariableDeclarator")) return null;
58104
- const declarator = setterSymbol.declarationNode;
58105
- if (!isNodeOfType(declarator.id, "ArrayPattern")) return null;
58106
- const stateIdentifier = declarator.id.elements?.[0];
58107
- const setterIdentifier = declarator.id.elements?.[1];
58108
- if (!isNodeOfType(stateIdentifier, "Identifier") || !isNodeOfType(setterIdentifier, "Identifier") || setterIdentifier !== setterSymbol.bindingIdentifier || !isNodeOfType(declarator.init, "CallExpression") || !isReactApiCall(declarator.init, USE_STATE_ONLY, context.scopes, REACT_API_CALL_OPTIONS)) return null;
58109
- const initializer = declarator.init.arguments?.[0];
58110
- if (!isNodeOfType(initializer, "Literal") || initializer.value !== 0) return null;
58111
- const stateSymbol = context.scopes.symbolFor(stateIdentifier);
58112
- if (!stateSymbol) return null;
58113
- const stateDerivedSymbolIds = new Set([stateSymbol.id]);
58114
- let didAddDerivedSymbol = true;
58115
- while (didAddDerivedSymbol) {
58116
- didAddDerivedSymbol = false;
58117
- for (const statement of componentBody.body ?? []) {
58118
- if (!isNodeOfType(statement, "VariableDeclaration")) continue;
58119
- for (const candidateDeclarator of statement.declarations ?? []) {
58120
- if (!isNodeOfType(candidateDeclarator.id, "Identifier") || !candidateDeclarator.init) continue;
58121
- const candidateInitializer = stripParenExpression(candidateDeclarator.init);
58122
- if (isFunctionLike$1(candidateInitializer) || isNodeOfType(candidateInitializer, "CallExpression") && isReactApiCall(candidateInitializer, USE_CALLBACK_ONLY, context.scopes, REACT_API_CALL_OPTIONS)) continue;
58123
- if (!expressionReadsDerivedSymbol(context, candidateInitializer, stateDerivedSymbolIds)) continue;
58124
- const candidateSymbol = context.scopes.symbolFor(candidateDeclarator.id);
58125
- if (candidateSymbol?.kind === "const" && candidateSymbol.references.every((reference) => reference.flag === "read" && !isTransparentAssignmentTarget(reference.identifier)) && !stateDerivedSymbolIds.has(candidateSymbol.id)) {
58126
- stateDerivedSymbolIds.add(candidateSymbol.id);
58127
- didAddDerivedSymbol = true;
58128
- }
58129
- }
58130
- }
58131
- }
58132
- const staticSpreadVisibilityBySymbolId = /* @__PURE__ */ new Map();
58133
- const hasOnlyStaticObjectReferences = (identifier, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
58134
- const symbol = context.scopes.symbolFor(identifier);
58135
- if (!symbol) return false;
58136
- if (visitedSymbolIds.has(symbol.id)) return true;
58137
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
58138
- nextVisitedSymbolIds.add(symbol.id);
58139
- let hasUnknownReference = false;
58140
- walkAst(componentBody, (node) => {
58141
- if (hasUnknownReference || !isNodeOfType(node, "Identifier") || context.scopes.symbolFor(node)?.id !== symbol.id || node === symbol.bindingIdentifier) return;
58142
- const referenceRoot = findTransparentExpressionRoot(node);
58143
- const parent = referenceRoot.parent;
58144
- if (isNodeOfType(parent, "JSXSpreadAttribute") && parent.argument === referenceRoot) return;
58145
- if (isNodeOfType(parent, "VariableDeclarator") && parent.init === referenceRoot && isNodeOfType(parent.id, "Identifier") && isNodeOfType(parent.parent, "VariableDeclaration") && parent.parent.kind === "const" && hasOnlyStaticObjectReferences(parent.id, nextVisitedSymbolIds)) return;
58146
- hasUnknownReference = true;
58147
- return false;
58148
- });
58149
- return !hasUnknownReference;
58150
- };
58151
- const classifyStaticSpreadObject = (identifier, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
58152
- const symbol = context.scopes.symbolFor(identifier);
58153
- if (!symbol || visitedSymbolIds.has(symbol.id)) return "unknown";
58154
- const cachedVisibility = staticSpreadVisibilityBySymbolId.get(symbol.id);
58155
- if (cachedVisibility) return cachedVisibility;
58156
- if (symbol.kind !== "const" || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "Identifier") || symbol.declarationNode.id !== symbol.bindingIdentifier || !symbol.declarationNode.init) return "unknown";
58157
- if (!hasOnlyStaticObjectReferences(identifier)) return "unknown";
58158
- const initializer = stripParenExpression(symbol.declarationNode.init);
58159
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
58160
- nextVisitedSymbolIds.add(symbol.id);
58161
- if (isNodeOfType(initializer, "Identifier")) {
58162
- const visibility = classifyStaticSpreadObject(initializer, nextVisitedSymbolIds);
58163
- staticSpreadVisibilityBySymbolId.set(symbol.id, visibility);
58164
- return visibility;
58165
- }
58166
- if (!isNodeOfType(initializer, "ObjectExpression")) return "unknown";
58167
- let visibility = "non-visible";
58168
- for (const property of initializer.properties ?? []) {
58169
- const propertyName = getStaticObjectPropertyName(property);
58170
- if (!isNodeOfType(property, "Property") || !propertyName) {
58171
- visibility = "unknown";
58172
- break;
58173
- }
58174
- if (expressionReadsDerivedSymbol(context, property.value, stateDerivedSymbolIds) && !isNonVisibleJsxSpreadProperty(propertyName)) visibility = "visible";
58175
- }
58176
- staticSpreadVisibilityBySymbolId.set(symbol.id, visibility);
58177
- return visibility;
58178
- };
58179
- let hasNonAriaReference = false;
58180
- walkAst(componentBody, (node) => {
58181
- if (hasNonAriaReference) return false;
58182
- if (!isNodeOfType(node, "Identifier") || !stateDerivedSymbolIds.has(context.scopes.symbolFor(node)?.id ?? -1)) return;
58183
- if (findEnclosingFunction$1(node) !== componentFunction) return;
58184
- const parent = node.parent;
58185
- if (parent && (isNodeOfType(parent, "MemberExpression") && parent.property === node && !parent.computed || isNodeOfType(parent, "Property") && parent.key === node && !parent.computed)) return;
58186
- let cursor = parent;
58187
- while (cursor && cursor !== componentBody) {
58188
- if (isFunctionLike$1(cursor)) return;
58189
- if (isNodeOfType(cursor, "JSXSpreadAttribute")) {
58190
- if (isNodeOfType(node, "Identifier") && classifyStaticSpreadObject(node) === "visible") hasNonAriaReference = true;
58191
- return;
58192
- }
58193
- if (isNodeOfType(cursor, "JSXAttribute")) {
58194
- if (isEventHandlerAttribute(cursor)) return;
58195
- if (!isInsideIdOrAriaAttribute(node)) hasNonAriaReference = true;
58196
- return;
58197
- }
58198
- if (isNodeOfType(cursor, "ReturnStatement")) {
58199
- hasNonAriaReference = true;
58200
- return;
58201
- }
58202
- cursor = cursor.parent;
58203
- }
58204
- });
58205
- return hasNonAriaReference ? stateIdentifier.name : null;
58206
- };
58207
- const isExactViewportSubscriptionEffect = (context, effectCall, callback) => {
58208
- if (!isReactApiCall(effectCall, USE_EFFECT_ONLY, context.scopes, REACT_API_CALL_OPTIONS)) return false;
58209
- if (!isFunctionLike$1(callback) || callback.async || !isNodeOfType(callback.body, "BlockStatement")) return false;
58210
- const statements = getCallbackStatements(callback);
58211
- if (statements.length !== 4) return false;
58212
- const handlerDeclaration = statements[0];
58213
- if (!isNodeOfType(handlerDeclaration, "VariableDeclaration") || handlerDeclaration.kind !== "const" || handlerDeclaration.declarations?.length !== 1) return false;
58214
- const handlerDeclarator = handlerDeclaration.declarations[0];
58215
- if (!isNodeOfType(handlerDeclarator.id, "Identifier") || !isFunctionLike$1(handlerDeclarator.init)) return false;
58216
- const handlerStatements = getCallbackStatements(handlerDeclarator.init);
58217
- if (handlerStatements.length !== 1) return false;
58218
- const handlerSetter = getDirectWindowWidthSetter(context, unwrapReturnExpression(handlerStatements[0]));
58219
- const subscribedHandler = getResizeListenerHandler(context, statements[1], "addEventListener");
58220
- const immediateSetter = getDirectWindowWidthSetter(context, statements[2]);
58221
- const cleanupHandler = getCleanupResizeHandler(context, statements[3]);
58222
- if (!handlerSetter || !subscribedHandler || !immediateSetter || !cleanupHandler) return false;
58223
- const handlerSymbol = context.scopes.symbolFor(handlerDeclarator.id);
58224
- if (!handlerSymbol || context.scopes.symbolFor(subscribedHandler) !== handlerSymbol || context.scopes.symbolFor(cleanupHandler) !== handlerSymbol) return false;
58225
- if (!isNodeOfType(handlerSetter.callee, "Identifier") || !isNodeOfType(immediateSetter.callee, "Identifier") || context.scopes.symbolFor(handlerSetter.callee) !== context.scopes.symbolFor(immediateSetter.callee)) return false;
58226
- const componentFunction = findEnclosingFunction$1(effectCall);
58227
- if (!isFunctionLike$1(componentFunction) || !isNodeOfType(componentFunction.body, "BlockStatement")) return false;
58228
- return findExactViewportState(context, componentFunction, immediateSetter) !== null;
58229
- };
58230
56388
  const renderingHydrationNoFlicker = defineRule({
58231
56389
  id: "rendering-hydration-no-flicker",
58232
56390
  title: "useEffect setState flashes on mount",
@@ -58239,14 +56397,7 @@ const renderingHydrationNoFlicker = defineRule({
58239
56397
  if (!isNodeOfType(depsNode, "ArrayExpression") || depsNode.elements?.length !== 0) return;
58240
56398
  const callback = getEffectCallback(node);
58241
56399
  if (!callback || !isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
58242
- if (isExactViewportSubscriptionEffect(context, node, callback)) {
58243
- context.report({
58244
- node,
58245
- message: "This flashes for your users because useEffect(setState, []) runs after the first paint, so use useSyncExternalStore, or add suppressHydrationWarning"
58246
- });
58247
- return;
58248
- }
58249
- const bodyStatements = getCallbackStatements(callback);
56400
+ const bodyStatements = (isNodeOfType(callback.body, "BlockStatement") ? callback.body.body ?? [] : [callback.body]).filter((statement) => !isNoOpStatement(statement));
58250
56401
  if (bodyStatements.length !== 1) return;
58251
56402
  const soleStatement = bodyStatements[0];
58252
56403
  if (!isNodeOfType(soleStatement, "ExpressionStatement")) return;
@@ -58409,125 +56560,6 @@ const ASYNC_DATA_CALLEE_NAMES = new Set([
58409
56560
  const RESOURCE_LOAD_EVENT_ATTRIBUTE_PATTERN = /^on(?:Load|Error|Abort|Progress|CanPlay|Stalled|Suspend|Waiting|Ended)/;
58410
56561
  const JSX_EVENT_HANDLER_ATTRIBUTE_PATTERN = /^on[A-Z]/;
58411
56562
  const REDUX_DISPATCH_HOOK_PATTERN = /^use\w*Dispatch$/;
58412
- const FILE_READER_READ_METHOD_NAMES = new Set([
58413
- "readAsArrayBuffer",
58414
- "readAsBinaryString",
58415
- "readAsDataURL",
58416
- "readAsText"
58417
- ]);
58418
- const isGlobalFileReaderConstruction = (expression, context) => {
58419
- if (!expression) return false;
58420
- const unwrappedExpression = stripParenExpression(expression);
58421
- if (!isNodeOfType(unwrappedExpression, "NewExpression") || !isNodeOfType(unwrappedExpression.callee, "Identifier")) return false;
58422
- return unwrappedExpression.callee.name === "FileReader" && context.scopes.isGlobalReference(unwrappedExpression.callee);
58423
- };
58424
- const getFileReaderOriginStartBefore = (readerSymbol, readCall, context) => {
58425
- const readFunction = findEnclosingFunction$1(readCall);
58426
- let latestValue = null;
58427
- let latestStart = null;
58428
- if (readerSymbol.initializer && findEnclosingFunction$1(readerSymbol.declarationNode) === readFunction && readerSymbol.declarationNode.range[0] < readCall.range[0]) {
58429
- latestValue = readerSymbol.initializer;
58430
- latestStart = readerSymbol.declarationNode.range[0];
58431
- }
58432
- for (const reference of readerSymbol.references) {
58433
- if (reference.flag === "read" || reference.identifier.range[0] >= readCall.range[0] || latestStart !== null && reference.identifier.range[0] <= latestStart || findEnclosingFunction$1(reference.identifier) !== readFunction) continue;
58434
- const assignment = reference.identifier.parent;
58435
- if (!assignment || !isNodeOfType(assignment, "AssignmentExpression") || assignment.operator !== "=" || assignment.left !== reference.identifier) continue;
58436
- latestValue = assignment.right;
58437
- latestStart = reference.identifier.range[0];
58438
- }
58439
- return isGlobalFileReaderConstruction(latestValue, context) ? latestStart : null;
58440
- };
58441
- const resolveLoadingCompletionFunction = (expression, context) => {
58442
- const directFunction = resolveExactLocalFunction(expression, context.scopes);
58443
- if (directFunction) return directFunction;
58444
- const unwrappedExpression = stripParenExpression(expression);
58445
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
58446
- const symbol = context.scopes.symbolFor(unwrappedExpression);
58447
- const initializer = symbol ? getDirectUnreassignedInitializer(symbol) : null;
58448
- if (!initializer || !isNodeOfType(initializer, "CallExpression") || !isReactApiCall(initializer, "useCallback", context.scopes)) return null;
58449
- const callback = initializer.arguments?.[0];
58450
- return callback && isFunctionLike$1(callback) ? callback : null;
58451
- };
58452
- const isSetterBooleanCall = (node, setterSymbol, value, context) => {
58453
- if (!isNodeOfType(node, "CallExpression")) return false;
58454
- const callee = stripParenExpression(node.callee);
58455
- const argument = node.arguments?.[0];
58456
- const unwrappedArgument = argument ? stripParenExpression(argument) : null;
58457
- return Boolean(isNodeOfType(callee, "Identifier") && context.scopes.symbolFor(callee) === setterSymbol && unwrappedArgument && isNodeOfType(unwrappedArgument, "Literal") && unwrappedArgument.value === value);
58458
- };
58459
- const functionClearsLoadingState = (functionNode, setterSymbol, context, visitedFunctions) => {
58460
- if (visitedFunctions.has(functionNode) || !isFunctionLike$1(functionNode)) return false;
58461
- visitedFunctions.add(functionNode);
58462
- let didClearLoadingState = false;
58463
- walkAst(functionNode.body, (child) => {
58464
- if (didClearLoadingState) return false;
58465
- if (child !== functionNode.body && isFunctionLike$1(child)) return false;
58466
- if (!isNodeOfType(child, "CallExpression")) return;
58467
- if (isSetterBooleanCall(child, setterSymbol, false, context)) {
58468
- didClearLoadingState = true;
58469
- return false;
58470
- }
58471
- const helperFunction = resolveLoadingCompletionFunction(child.callee, context);
58472
- if (helperFunction && functionClearsLoadingState(helperFunction, setterSymbol, context, visitedFunctions)) {
58473
- didClearLoadingState = true;
58474
- return false;
58475
- }
58476
- });
58477
- return didClearLoadingState;
58478
- };
58479
- const getLatestFileReaderCallbackBefore = (readCall, readerSymbol, propertyName, originStart, context) => {
58480
- const readFunction = findEnclosingFunction$1(readCall);
58481
- if (!readFunction || !isFunctionLike$1(readFunction)) return null;
58482
- let callback = null;
58483
- let callbackStart = originStart;
58484
- walkAst(readFunction.body, (child) => {
58485
- if (child !== readFunction.body && isFunctionLike$1(child)) return false;
58486
- if (!isNodeOfType(child, "AssignmentExpression") || child.operator !== "=" || child.range[0] >= readCall.range[0] || child.range[0] <= callbackStart || !isNodeOfType(child.left, "MemberExpression") || getStaticPropertyName(child.left) !== propertyName) return;
58487
- const receiver = stripParenExpression(child.left.object);
58488
- if (!isNodeOfType(receiver, "Identifier") || context.scopes.symbolFor(receiver) !== readerSymbol) return;
58489
- callback = child.right;
58490
- callbackStart = child.range[0];
58491
- });
58492
- return callback;
58493
- };
58494
- const setterStartsLoadingBefore = (readCall, setterSymbol, context) => {
58495
- const readFunction = findEnclosingFunction$1(readCall);
58496
- if (!readFunction || !isFunctionLike$1(readFunction)) return false;
58497
- let didStartLoading = false;
58498
- walkAst(readFunction.body, (child) => {
58499
- if (didStartLoading) return false;
58500
- if (child !== readFunction.body && isFunctionLike$1(child)) return false;
58501
- if (!isNodeOfType(child, "CallExpression") || child.range[0] >= readCall.range[0]) return;
58502
- if (isSetterBooleanCall(child, setterSymbol, true, context)) {
58503
- didStartLoading = true;
58504
- return false;
58505
- }
58506
- });
58507
- return didStartLoading;
58508
- };
58509
- const setterTracksFileReader = (functionBody, setterSymbol, context) => {
58510
- let didFindFileReaderLifecycle = false;
58511
- walkAst(functionBody, (child) => {
58512
- if (didFindFileReaderLifecycle) return false;
58513
- if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || !FILE_READER_READ_METHOD_NAMES.has(getStaticPropertyName(child.callee) ?? "")) return;
58514
- const receiver = stripParenExpression(child.callee.object);
58515
- if (!isNodeOfType(receiver, "Identifier")) return;
58516
- const readerSymbol = context.scopes.symbolFor(receiver);
58517
- if (!readerSymbol) return;
58518
- const originStart = getFileReaderOriginStartBefore(readerSymbol, child, context);
58519
- if (originStart === null || !setterStartsLoadingBefore(child, setterSymbol, context)) return;
58520
- const loadCallback = getLatestFileReaderCallbackBefore(child, readerSymbol, "onload", originStart, context);
58521
- const errorCallback = getLatestFileReaderCallbackBefore(child, readerSymbol, "onerror", originStart, context);
58522
- const loadFunction = loadCallback ? resolveLoadingCompletionFunction(loadCallback, context) : null;
58523
- const errorFunction = errorCallback ? resolveLoadingCompletionFunction(errorCallback, context) : null;
58524
- if (loadFunction && errorFunction && functionClearsLoadingState(loadFunction, setterSymbol, context, /* @__PURE__ */ new Set()) && functionClearsLoadingState(errorFunction, setterSymbol, context, /* @__PURE__ */ new Set())) {
58525
- didFindFileReaderLifecycle = true;
58526
- return false;
58527
- }
58528
- });
58529
- return didFindFileReaderLifecycle;
58530
- };
58531
56563
  const hasAsyncLoadingWork = (fnBody, setterName) => {
58532
56564
  let found = false;
58533
56565
  walkAst(fnBody, (child) => {
@@ -58701,8 +56733,6 @@ const renderingUsetransitionLoading = defineRule({
58701
56733
  const fnBody = enclosingFunctionBody(node);
58702
56734
  if (fnBody && hasAsyncLoadingWork(fnBody, setterName)) return;
58703
56735
  if (fnBody && setterName) {
58704
- const setterSymbol = isNodeOfType(secondBinding, "Identifier") ? context.scopes.symbolFor(secondBinding) : null;
58705
- if (setterSymbol && setterTracksFileReader(fnBody, setterSymbol, context)) return;
58706
56736
  if (setterEscapes(fnBody, setterName, node)) return;
58707
56737
  if (setterCalledAlongsideAsyncSignal(fnBody, setterName)) return;
58708
56738
  if (setterCalledInEventListenerHandler(fnBody, setterName)) return;
@@ -68199,6 +66229,14 @@ const isStateKey = (key) => {
68199
66229
  if (isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value === "state";
68200
66230
  return false;
68201
66231
  };
66232
+ const findEnclosingClass = (node) => {
66233
+ let ancestor = node.parent;
66234
+ while (ancestor) {
66235
+ if (isNodeOfType(ancestor, "ClassDeclaration") || isNodeOfType(ancestor, "ClassExpression")) return ancestor;
66236
+ ancestor = ancestor.parent ?? null;
66237
+ }
66238
+ return null;
66239
+ };
68202
66240
  const isInConstructor = (node) => {
68203
66241
  let ancestor = node.parent;
68204
66242
  while (ancestor) {
@@ -72910,17 +70948,6 @@ const reactDoctorRules = [
72910
70948
  requires: [...new Set(["react", ...noRedundantShouldComponentUpdate.requires ?? []])]
72911
70949
  }
72912
70950
  },
72913
- {
72914
- key: "react-doctor/no-ref-callback-cleanup-before-react-19",
72915
- id: "no-ref-callback-cleanup-before-react-19",
72916
- source: "react-doctor",
72917
- originallyExternal: false,
72918
- rule: {
72919
- ...noRefCallbackCleanupBeforeReact19,
72920
- framework: "global",
72921
- category: "Bugs"
72922
- }
72923
- },
72924
70951
  {
72925
70952
  key: "react-doctor/no-ref-current-in-render",
72926
70953
  id: "no-ref-current-in-render",