oxlint-plugin-react-doctor 0.7.9-dev.7e6cdf9 → 0.7.9-dev.842a55f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +346 -377
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -836,131 +836,22 @@ const getImportedName = (importSpecifier) => {
836
836
  if (isNodeOfType(imported, "Literal") && typeof imported.value === "string") return imported.value;
837
837
  };
838
838
  //#endregion
839
- //#region src/plugin/utils/get-static-property-key-name.ts
840
- const getStaticPropertyKeyName = (node, options = {}) => {
841
- if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
842
- const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
843
- if (node.computed) {
844
- if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
845
- if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
846
- return null;
847
- }
848
- if (isNodeOfType(key, "Identifier")) return key.name;
849
- if (isNodeOfType(key, "Literal")) {
850
- if (typeof key.value === "string") return key.value;
851
- if (options.stringifyNonStringLiterals) return String(key.value);
852
- }
853
- return null;
854
- };
855
- //#endregion
856
- //#region src/plugin/utils/get-static-property-name.ts
857
- const getStaticPropertyName = (memberExpression) => {
858
- const property = memberExpression.property;
859
- if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
860
- if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
861
- if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
839
+ //#region src/plugin/utils/get-callee-name.ts
840
+ const getCalleeName$2 = (node) => {
841
+ if (!isNodeOfType(node, "CallExpression") && !isNodeOfType(node, "NewExpression")) return null;
842
+ if (isNodeOfType(node.callee, "Identifier")) return node.callee.name;
843
+ if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.property, "Identifier")) return node.callee.property.name;
862
844
  return null;
863
845
  };
864
846
  //#endregion
865
- //#region src/plugin/utils/strip-paren-expression.ts
866
- const TRANSPARENT_EXPRESSION_WRAPPER_TYPES = new Set([
867
- "ParenthesizedExpression",
868
- "TSAsExpression",
869
- "TSSatisfiesExpression",
870
- "TSTypeAssertion",
871
- "TSNonNullExpression",
872
- "TSInstantiationExpression",
873
- "ChainExpression"
874
- ]);
875
- const stripParenExpression = (node) => {
876
- let current = node;
877
- while (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(current.type) && "expression" in current && current.expression) current = current.expression;
878
- return current;
879
- };
880
- //#endregion
881
- //#region src/plugin/utils/resolve-const-identifier-alias.ts
882
- const resolveConstIdentifierAlias = (identifier, scopes, allowPatternBinding = false) => {
883
- if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
884
- const visitedSymbolIds = /* @__PURE__ */ new Set();
885
- let symbol = scopes.symbolFor(identifier);
886
- while (symbol?.kind === "const") {
887
- if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return null;
888
- if (symbol.declarationNode.id !== symbol.bindingIdentifier) return allowPatternBinding ? symbol : null;
889
- visitedSymbolIds.add(symbol.id);
890
- const initializer = stripParenExpression(symbol.initializer);
891
- if (!isNodeOfType(initializer, "Identifier")) return symbol;
892
- symbol = scopes.symbolFor(initializer);
893
- }
894
- return symbol;
895
- };
896
- //#endregion
897
- //#region src/plugin/utils/is-react-api-call.ts
898
- const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
899
- const isImportedFromReact = (symbol) => {
900
- if (symbol.kind !== "import") return false;
901
- const importDeclaration = symbol.declarationNode.parent;
902
- return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(importDeclaration.source.value));
903
- };
904
- const isNamedReactApiImport = (identifier, apiNames, scopes, resolveAliases) => {
905
- if (!isNodeOfType(identifier, "Identifier")) return false;
906
- const symbol = resolveAliases ? resolveConstIdentifierAlias(identifier, scopes) : scopes.symbolFor(identifier);
907
- if (!symbol || !isImportedFromReact(symbol)) return false;
908
- const importedName = getImportedName(symbol.declarationNode);
909
- return Boolean(importedName && includesApiName(apiNames, importedName));
910
- };
911
- const isReactNamespaceImport = (identifier, scopes) => {
912
- const symbol = resolveConstIdentifierAlias(identifier, scopes);
913
- if (!symbol || !isImportedFromReact(symbol)) return false;
914
- return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
915
- };
916
- const isReactNamespaceReceiver$1 = (receiver, scopes, options) => {
917
- if (!isNodeOfType(receiver, "Identifier")) return false;
918
- if (isReactNamespaceImport(receiver, scopes)) return true;
919
- return Boolean(options.allowGlobalReactNamespace && receiver.name === "React" && scopes.isGlobalReference(receiver));
920
- };
921
- const isDestructuredReactApiBinding = (identifier, apiNames, scopes, options) => {
922
- const symbol = scopes.symbolFor(identifier);
923
- if (!symbol || symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
924
- const pattern = symbol.declarationNode.id;
925
- if (!isNodeOfType(pattern, "ObjectPattern")) return false;
926
- for (const property of pattern.properties) {
927
- if (!isNodeOfType(property, "Property") || property.value !== symbol.bindingIdentifier) continue;
928
- const propertyName = getStaticPropertyKeyName(property);
929
- return Boolean(propertyName && includesApiName(apiNames, propertyName) && isReactNamespaceReceiver$1(stripParenExpression(symbol.initializer), scopes, options));
930
- }
931
- return false;
932
- };
933
- const isReactApiCall = (node, apiNames, scopes, options = {}) => {
847
+ //#region src/plugin/utils/is-hook-call.ts
848
+ const isHookCall$2 = (node, hookName) => {
934
849
  if (!isNodeOfType(node, "CallExpression")) return false;
935
- return isReactApiCallee(node.callee, apiNames, scopes, options, /* @__PURE__ */ new Set());
936
- };
937
- const isReactApiCallee = (rawCallee, apiNames, scopes, options, visitedSymbolIds) => {
938
- const callee = stripParenExpression(rawCallee);
939
- if (options.resolveConditionalAliases && isNodeOfType(callee, "ConditionalExpression")) return isReactApiCallee(callee.consequent, apiNames, scopes, options, new Set(visitedSymbolIds)) && isReactApiCallee(callee.alternate, apiNames, scopes, options, new Set(visitedSymbolIds));
940
- if (isNodeOfType(callee, "Identifier")) {
941
- if (isNamedReactApiImport(callee, apiNames, scopes, Boolean(options.resolveNamedAliases))) return true;
942
- if (options.resolveNamedAliases && isDestructuredReactApiBinding(callee, apiNames, scopes, options)) return true;
943
- if (options.resolveConditionalAliases) {
944
- const symbol = scopes.symbolFor(callee);
945
- if (symbol?.kind === "const" && symbol.initializer && !visitedSymbolIds.has(symbol.id)) {
946
- visitedSymbolIds.add(symbol.id);
947
- return isReactApiCallee(symbol.initializer, apiNames, scopes, options, visitedSymbolIds);
948
- }
949
- }
950
- return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
951
- }
952
- if (!isNodeOfType(callee, "MemberExpression") || !includesApiName(apiNames, getStaticPropertyName(callee) ?? "")) return false;
953
- return isReactNamespaceReceiver$1(stripParenExpression(callee.object), scopes, options);
850
+ const calleeName = getCalleeName$2(node);
851
+ if (!calleeName) return false;
852
+ return typeof hookName === "string" ? calleeName === hookName : hookName.has(calleeName);
954
853
  };
955
854
  //#endregion
956
- //#region src/plugin/utils/is-react-hook-call.ts
957
- const isReactHookCall = (node, hookNames, scopes) => isReactApiCall(node, hookNames, scopes, {
958
- allowGlobalReactNamespace: true,
959
- allowUnboundBareCalls: true,
960
- resolveConditionalAliases: true,
961
- resolveNamedAliases: true
962
- });
963
- //#endregion
964
855
  //#region src/plugin/utils/is-ast-node.ts
965
856
  const isAstNode = (value) => value !== null && typeof value === "object" && "type" in value && typeof value.type === "string";
966
857
  //#endregion
@@ -1030,12 +921,12 @@ const collectChildComponentNames = (element, into) => {
1030
921
  into.add(name);
1031
922
  });
1032
923
  };
1033
- const countEffectHookCalls = (body, scopes) => {
924
+ const countEffectHookCalls = (body) => {
1034
925
  if (!body) return 0;
1035
926
  let count = 0;
1036
927
  walkAst(body, (child) => {
1037
928
  if (!isNodeOfType(child, "CallExpression")) return;
1038
- if (isReactHookCall(child, EFFECT_HOOK_NAMES$1, scopes)) count++;
929
+ if (isHookCall$2(child, EFFECT_HOOK_NAMES$1)) count++;
1039
930
  });
1040
931
  return count;
1041
932
  };
@@ -1061,11 +952,11 @@ const getComponentEffectIndex = (programRoot) => {
1061
952
  componentEffectIndexCache.set(programRoot, index);
1062
953
  return index;
1063
954
  };
1064
- const getSameFileComponentEffectCount = (programRoot, componentName, scopes) => {
955
+ const getSameFileComponentEffectCount = (programRoot, componentName) => {
1065
956
  const index = getComponentEffectIndex(programRoot);
1066
957
  const cachedCount = index.effectCountByName.get(componentName);
1067
958
  if (cachedCount !== void 0) return cachedCount;
1068
- const count = countEffectHookCalls(index.bodyByName.get(componentName) ?? null, scopes);
959
+ const count = countEffectHookCalls(index.bodyByName.get(componentName) ?? null);
1069
960
  index.effectCountByName.set(componentName, count);
1070
961
  return count;
1071
962
  };
@@ -1125,7 +1016,7 @@ const activityWrapsEffectHeavySubtree = defineRule({
1125
1016
  let totalEffects = 0;
1126
1017
  const effectfulChildren = [];
1127
1018
  for (const componentName of childComponentNames) {
1128
- const effectCount = getSameFileComponentEffectCount(programRoot, componentName, context.scopes);
1019
+ const effectCount = getSameFileComponentEffectCount(programRoot, componentName);
1129
1020
  if (effectCount === 0) continue;
1130
1021
  totalEffects += effectCount;
1131
1022
  effectfulChildren.push(`<${componentName}>`);
@@ -1316,14 +1207,6 @@ const findVariableInitializer = (referenceNode, bindingName) => {
1316
1207
  return best;
1317
1208
  };
1318
1209
  //#endregion
1319
- //#region src/plugin/utils/get-callee-name.ts
1320
- const getCalleeName$1 = (node) => {
1321
- if (!isNodeOfType(node, "CallExpression") && !isNodeOfType(node, "NewExpression")) return null;
1322
- if (isNodeOfType(node.callee, "Identifier")) return node.callee.name;
1323
- if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.property, "Identifier")) return node.callee.property.name;
1324
- return null;
1325
- };
1326
- //#endregion
1327
1210
  //#region src/plugin/utils/is-function-like.ts
1328
1211
  /**
1329
1212
  * Type-guard for the three "function-like" ESTree node shapes:
@@ -1337,6 +1220,38 @@ const getCalleeName$1 = (node) => {
1337
1220
  */
1338
1221
  const isFunctionLike$1 = (node) => Boolean(node && (isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "FunctionDeclaration")));
1339
1222
  //#endregion
1223
+ //#region src/plugin/utils/strip-paren-expression.ts
1224
+ const TRANSPARENT_EXPRESSION_WRAPPER_TYPES = new Set([
1225
+ "ParenthesizedExpression",
1226
+ "TSAsExpression",
1227
+ "TSSatisfiesExpression",
1228
+ "TSTypeAssertion",
1229
+ "TSNonNullExpression",
1230
+ "TSInstantiationExpression",
1231
+ "ChainExpression"
1232
+ ]);
1233
+ const stripParenExpression = (node) => {
1234
+ let current = node;
1235
+ while (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(current.type) && "expression" in current && current.expression) current = current.expression;
1236
+ return current;
1237
+ };
1238
+ //#endregion
1239
+ //#region src/plugin/utils/resolve-const-identifier-alias.ts
1240
+ const resolveConstIdentifierAlias = (identifier, scopes, allowPatternBinding = false) => {
1241
+ if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
1242
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
1243
+ let symbol = scopes.symbolFor(identifier);
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;
1247
+ visitedSymbolIds.add(symbol.id);
1248
+ const initializer = stripParenExpression(symbol.initializer);
1249
+ if (!isNodeOfType(initializer, "Identifier")) return symbol;
1250
+ symbol = scopes.symbolFor(initializer);
1251
+ }
1252
+ return symbol;
1253
+ };
1254
+ //#endregion
1340
1255
  //#region src/plugin/utils/resolve-exact-local-function.ts
1341
1256
  const resolveExactLocalFunction = (expression, scopes) => {
1342
1257
  const unwrappedExpression = stripParenExpression(expression);
@@ -1382,17 +1297,9 @@ const getRootIdentifier$1 = (node, options) => {
1382
1297
  //#region src/plugin/utils/get-root-identifier-name.ts
1383
1298
  const getRootIdentifierName = (node, options) => getRootIdentifier$1(node, options)?.name ?? null;
1384
1299
  //#endregion
1385
- //#region src/plugin/utils/is-hook-call.ts
1386
- const isHookCall$2 = (node, hookName) => {
1387
- if (!isNodeOfType(node, "CallExpression")) return false;
1388
- const calleeName = getCalleeName$1(node);
1389
- if (!calleeName) return false;
1390
- return typeof hookName === "string" ? calleeName === hookName : hookName.has(calleeName);
1391
- };
1392
- //#endregion
1393
1300
  //#region src/plugin/rules/state-and-effects/advanced-event-handler-refs.ts
1394
- const REACT_STABLE_HANDLER_HOOK_NAMES = new Set(["useCallback", "useEffectEvent"]);
1395
- const CUSTOM_STABLE_HANDLER_HOOK_NAMES = new Set([
1301
+ const STABLE_HANDLER_HOOK_NAMES = new Set([
1302
+ "useCallback",
1396
1303
  "useEffectEvent",
1397
1304
  "useEvent",
1398
1305
  "useEventCallback",
@@ -1401,21 +1308,21 @@ const CUSTOM_STABLE_HANDLER_HOOK_NAMES = new Set([
1401
1308
  ]);
1402
1309
  const THROTTLED_HANDLER_HOOK_PATTERN = /^use\w*(?:Throttle|Debounce)/i;
1403
1310
  const isThrottledHandlerHookCall = (callNode) => {
1404
- const calleeName = getCalleeName$1(callNode);
1311
+ const calleeName = getCalleeName$2(callNode);
1405
1312
  return calleeName !== null && THROTTLED_HANDLER_HOOK_PATTERN.test(calleeName);
1406
1313
  };
1407
- const isEmptyDepsUseMemoCall = (callNode, scopes) => {
1408
- if (!isReactHookCall(callNode, "useMemo", scopes)) return false;
1314
+ const isEmptyDepsUseMemoCall = (callNode) => {
1315
+ if (!isHookCall$2(callNode, "useMemo")) return false;
1409
1316
  const memoDepsNode = callNode.arguments?.[1];
1410
1317
  return isNodeOfType(memoDepsNode, "ArrayExpression") && (memoDepsNode.elements?.length ?? 0) === 0;
1411
1318
  };
1412
- const isStableHandlerInitializer = (initializer, scopes) => {
1413
- if (isNodeOfType(initializer, "CallExpression")) return isReactHookCall(initializer, REACT_STABLE_HANDLER_HOOK_NAMES, scopes) || isHookCall$2(initializer, CUSTOM_STABLE_HANDLER_HOOK_NAMES) || isEmptyDepsUseMemoCall(initializer, scopes) || isThrottledHandlerHookCall(initializer);
1319
+ const isStableHandlerInitializer = (initializer) => {
1320
+ if (isNodeOfType(initializer, "CallExpression")) return isHookCall$2(initializer, STABLE_HANDLER_HOOK_NAMES) || isEmptyDepsUseMemoCall(initializer) || isThrottledHandlerHookCall(initializer);
1414
1321
  return isNodeOfType(initializer, "MemberExpression") && isNodeOfType(initializer.property, "Identifier") && initializer.property.name === "current";
1415
1322
  };
1416
- const isStableRefReceiverDep = (referenceNode, receiverDepName, scopes) => {
1323
+ const isStableRefReceiverDep = (referenceNode, receiverDepName) => {
1417
1324
  const receiverBinding = findVariableInitializer(referenceNode, receiverDepName);
1418
- return Boolean(receiverBinding?.initializer && isReactHookCall(receiverBinding.initializer, "useRef", scopes));
1325
+ return Boolean(receiverBinding?.initializer && isHookCall$2(receiverBinding.initializer, "useRef"));
1419
1326
  };
1420
1327
  const advancedEventHandlerRefs = defineRule({
1421
1328
  id: "advanced-event-handler-refs",
@@ -1425,7 +1332,7 @@ const advancedEventHandlerRefs = defineRule({
1425
1332
  category: "Performance",
1426
1333
  recommendation: "Store the handler in a ref and have the listener read `handlerRef.current()`. The subscription stays put while the latest handler still runs.",
1427
1334
  create: (context) => ({ CallExpression(node) {
1428
- if (!isReactHookCall(node, EFFECT_HOOK_NAMES$1, context.scopes)) return;
1335
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1)) return;
1429
1336
  if ((node.arguments?.length ?? 0) < 2) return;
1430
1337
  const callback = getEffectCallback(node);
1431
1338
  if (!callback || !isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
@@ -1449,8 +1356,8 @@ const advancedEventHandlerRefs = defineRule({
1449
1356
  });
1450
1357
  if (!registeredHandlerName) return;
1451
1358
  const handlerBinding = findVariableInitializer(node, registeredHandlerName);
1452
- if (handlerBinding?.initializer && isStableHandlerInitializer(handlerBinding.initializer, context.scopes)) return;
1453
- if ([...depIdentifierNames].some((depName) => depName !== registeredHandlerName && subscriptionReceiverNames.has(depName) && !isStableRefReceiverDep(node, depName, context.scopes))) return;
1359
+ if (handlerBinding?.initializer && isStableHandlerInitializer(handlerBinding.initializer)) return;
1360
+ if ([...depIdentifierNames].some((depName) => depName !== registeredHandlerName && subscriptionReceiverNames.has(depName) && !isStableRefReceiverDep(node, depName))) return;
1454
1361
  context.report({
1455
1362
  node,
1456
1363
  message: `useEffect re-adds the "${registeredHandlerName}" listener every time the handler changes.`
@@ -1937,6 +1844,15 @@ const flattenJsxName$1 = (node) => {
1937
1844
  return null;
1938
1845
  };
1939
1846
  //#endregion
1847
+ //#region src/plugin/utils/get-static-property-name.ts
1848
+ const getStaticPropertyName = (memberExpression) => {
1849
+ const property = memberExpression.property;
1850
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
1851
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
1852
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
1853
+ return null;
1854
+ };
1855
+ //#endregion
1940
1856
  //#region src/plugin/utils/is-generated-image-renderer-call.ts
1941
1857
  const GENERATED_IMAGE_RENDERER_MODULES = [
1942
1858
  "next/og",
@@ -4944,6 +4860,23 @@ const containsDirectAwait = (node) => {
4944
4860
  return foundAwait;
4945
4861
  };
4946
4862
  //#endregion
4863
+ //#region src/plugin/utils/get-static-property-key-name.ts
4864
+ const getStaticPropertyKeyName = (node, options = {}) => {
4865
+ if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition") && !isNodeOfType(node, "MemberExpression")) return null;
4866
+ const key = isNodeOfType(node, "MemberExpression") ? node.property : node.key;
4867
+ if (node.computed) {
4868
+ if (options.allowComputedString && isNodeOfType(key, "Literal") && typeof key.value === "string") return key.value;
4869
+ if (options.allowComputedString && isNodeOfType(key, "TemplateLiteral") && key.expressions.length === 0) return key.quasis[0]?.value.cooked ?? key.quasis[0]?.value.raw ?? null;
4870
+ return null;
4871
+ }
4872
+ if (isNodeOfType(key, "Identifier")) return key.name;
4873
+ if (isNodeOfType(key, "Literal")) {
4874
+ if (typeof key.value === "string") return key.value;
4875
+ if (options.stringifyNonStringLiterals) return String(key.value);
4876
+ }
4877
+ return null;
4878
+ };
4879
+ //#endregion
4947
4880
  //#region src/plugin/utils/get-destructured-binding-property-name.ts
4948
4881
  const getDestructuredBindingPropertyName = (bindingIdentifier) => {
4949
4882
  let bindingNode = bindingIdentifier;
@@ -9008,6 +8941,65 @@ const hasVisibleBindingNamed = (node, bindingName, scopes) => {
9008
8941
  }
9009
8942
  };
9010
8943
  //#endregion
8944
+ //#region src/plugin/utils/is-react-api-call.ts
8945
+ const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
8946
+ const isImportedFromReact = (symbol) => {
8947
+ if (symbol.kind !== "import") return false;
8948
+ const importDeclaration = symbol.declarationNode.parent;
8949
+ return Boolean(importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && typeof importDeclaration.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(importDeclaration.source.value));
8950
+ };
8951
+ const isNamedReactApiImport = (identifier, apiNames, scopes, resolveAliases) => {
8952
+ if (!isNodeOfType(identifier, "Identifier")) return false;
8953
+ const symbol = resolveAliases ? resolveConstIdentifierAlias(identifier, scopes) : scopes.symbolFor(identifier);
8954
+ if (!symbol || !isImportedFromReact(symbol)) return false;
8955
+ const importedName = getImportedName(symbol.declarationNode);
8956
+ return Boolean(importedName && includesApiName(apiNames, importedName));
8957
+ };
8958
+ const isReactNamespaceImport = (identifier, scopes) => {
8959
+ const symbol = resolveConstIdentifierAlias(identifier, scopes);
8960
+ if (!symbol || !isImportedFromReact(symbol)) return false;
8961
+ return isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier") || getImportedName(symbol.declarationNode) === "default";
8962
+ };
8963
+ const isReactNamespaceReceiver$1 = (receiver, scopes, options) => {
8964
+ if (!isNodeOfType(receiver, "Identifier")) return false;
8965
+ if (isReactNamespaceImport(receiver, scopes)) return true;
8966
+ return Boolean(options.allowGlobalReactNamespace && receiver.name === "React" && scopes.isGlobalReference(receiver));
8967
+ };
8968
+ const isDestructuredReactApiBinding = (identifier, apiNames, scopes, options) => {
8969
+ const symbol = scopes.symbolFor(identifier);
8970
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
8971
+ const pattern = symbol.declarationNode.id;
8972
+ if (!isNodeOfType(pattern, "ObjectPattern")) return false;
8973
+ for (const property of pattern.properties) {
8974
+ if (!isNodeOfType(property, "Property") || property.value !== symbol.bindingIdentifier) continue;
8975
+ const propertyName = getStaticPropertyKeyName(property);
8976
+ return Boolean(propertyName && includesApiName(apiNames, propertyName) && isReactNamespaceReceiver$1(stripParenExpression(symbol.initializer), scopes, options));
8977
+ }
8978
+ return false;
8979
+ };
8980
+ const isReactApiCall = (node, apiNames, scopes, options = {}) => {
8981
+ if (!isNodeOfType(node, "CallExpression")) return false;
8982
+ return isReactApiCallee(node.callee, apiNames, scopes, options, /* @__PURE__ */ new Set());
8983
+ };
8984
+ const isReactApiCallee = (rawCallee, apiNames, scopes, options, visitedSymbolIds) => {
8985
+ const callee = stripParenExpression(rawCallee);
8986
+ if (options.resolveConditionalAliases && isNodeOfType(callee, "ConditionalExpression")) return isReactApiCallee(callee.consequent, apiNames, scopes, options, new Set(visitedSymbolIds)) && isReactApiCallee(callee.alternate, apiNames, scopes, options, new Set(visitedSymbolIds));
8987
+ if (isNodeOfType(callee, "Identifier")) {
8988
+ if (isNamedReactApiImport(callee, apiNames, scopes, Boolean(options.resolveNamedAliases))) return true;
8989
+ if (options.resolveNamedAliases && isDestructuredReactApiBinding(callee, apiNames, scopes, options)) return true;
8990
+ if (options.resolveConditionalAliases) {
8991
+ const symbol = scopes.symbolFor(callee);
8992
+ if (symbol?.kind === "const" && symbol.initializer && !visitedSymbolIds.has(symbol.id)) {
8993
+ visitedSymbolIds.add(symbol.id);
8994
+ return isReactApiCallee(symbol.initializer, apiNames, scopes, options, visitedSymbolIds);
8995
+ }
8996
+ }
8997
+ return Boolean(options.allowUnboundBareCalls && includesApiName(apiNames, callee.name) && scopes.isGlobalReference(callee));
8998
+ }
8999
+ if (!isNodeOfType(callee, "MemberExpression") || !includesApiName(apiNames, getStaticPropertyName(callee) ?? "")) return false;
9000
+ return isReactNamespaceReceiver$1(stripParenExpression(callee.object), scopes, options);
9001
+ };
9002
+ //#endregion
9011
9003
  //#region src/plugin/utils/is-proven-browser-api-receiver.ts
9012
9004
  const DOM_EVENT_TARGET_TYPE_NAMES = new Set([
9013
9005
  "AbortSignal",
@@ -13891,7 +13883,6 @@ const walkInsideStatementBlocks = (node, visitor) => {
13891
13883
  };
13892
13884
  //#endregion
13893
13885
  //#region src/plugin/rules/state-and-effects/utils/is-subscribe-like-call-expression.ts
13894
- const OBSERVER_REGISTRATION_METHOD_NAME = "observe";
13895
13886
  const getSubscribeLikeMethodName = (node) => {
13896
13887
  if (!isNodeOfType(node, "CallExpression")) return null;
13897
13888
  if (!isNodeOfType(node.callee, "MemberExpression")) return null;
@@ -13902,11 +13893,6 @@ const isSubscribeLikeCallExpression = (node) => {
13902
13893
  const methodName = getSubscribeLikeMethodName(node);
13903
13894
  return methodName !== null && SUBSCRIPTION_METHOD_NAMES.has(methodName);
13904
13895
  };
13905
- const getSubscribeOrObserveMethodName = (node) => {
13906
- const methodName = getSubscribeLikeMethodName(node);
13907
- return methodName !== null && (SUBSCRIPTION_METHOD_NAMES.has(methodName) || methodName === "observe") ? methodName : null;
13908
- };
13909
- const isSubscribeOrObserveCallExpression = (node) => getSubscribeOrObserveMethodName(node) !== null;
13910
13896
  const isCleanupReturningSubscribeLikeCallExpression = (node) => {
13911
13897
  const methodName = getSubscribeLikeMethodName(node);
13912
13898
  if (methodName === null || !CLEANUP_RETURNING_SUBSCRIPTION_METHOD_NAMES.has(methodName)) return false;
@@ -13959,6 +13945,7 @@ const isNodeReachableWithinFunction = (node, context) => {
13959
13945
  };
13960
13946
  //#endregion
13961
13947
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
13948
+ const OBSERVER_REGISTRATION_METHOD_NAME = "observe";
13962
13949
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
13963
13950
  const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
13964
13951
  const REACT_REF_EFFECT_ANALYSIS_CACHE = /* @__PURE__ */ new WeakMap();
@@ -13968,6 +13955,10 @@ const RESOURCE_NOUN_BY_KIND = {
13968
13955
  socket: "connection"
13969
13956
  };
13970
13957
  const isSocketConstruction = (node) => isNodeOfType(node, "NewExpression") && isNodeOfType(node.callee, "Identifier") && SOCKET_CONSTRUCTOR_NAMES_REQUIRING_CLEANUP.has(node.callee.name);
13958
+ const isSubscribeOrObserveCall = (node) => {
13959
+ if (isSubscribeLikeCallExpression(node)) return true;
13960
+ return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === OBSERVER_REGISTRATION_METHOD_NAME;
13961
+ };
13971
13962
  const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13972
13963
  if (!expression) return null;
13973
13964
  const unwrappedExpression = stripParenExpression(expression);
@@ -14069,13 +14060,12 @@ const findSubscribeLikeUsages = (callback, context) => {
14069
14060
  });
14070
14061
  return;
14071
14062
  }
14072
- const subscribeOrObserveMethodName = getSubscribeOrObserveMethodName(child);
14073
- if (subscribeOrObserveMethodName !== null) {
14063
+ if (isNodeOfType(child.callee, "MemberExpression") && isNodeOfType(child.callee.property, "Identifier") && (SUBSCRIPTION_METHOD_NAMES.has(child.callee.property.name) || child.callee.property.name === OBSERVER_REGISTRATION_METHOD_NAME)) {
14074
14064
  const registrationDetails = getCallRegistrationDetails(child, context);
14075
14065
  usages.push({
14076
14066
  kind: "subscribe",
14077
14067
  node: child,
14078
- resourceName: subscribeOrObserveMethodName,
14068
+ resourceName: child.callee.property.name,
14079
14069
  handleKey: findAssignedResourceKey(child, context),
14080
14070
  ...registrationDetails
14081
14071
  });
@@ -14606,7 +14596,7 @@ const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
14606
14596
  walkAst(componentFunction.body, (child) => {
14607
14597
  if (didFindUnmountCleanup) return false;
14608
14598
  if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction) return;
14609
- if (!isReactHookCall(child, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return;
14599
+ if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
14610
14600
  const dependencyList = child.arguments?.[1];
14611
14601
  if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
14612
14602
  const cleanupCallback = getEffectCallback(child);
@@ -15037,7 +15027,7 @@ const getReleaseVerbName = (node) => {
15037
15027
  const isRetainedAbortControllerRefRelease = (releaseReceiver, usage, context) => {
15038
15028
  const releaseFunction = findEnclosingFunction$1(releaseReceiver);
15039
15029
  const usageFunction = findEnclosingFunction$1(usage.node);
15040
- if (!releaseFunction || !usageFunction || !isFunctionLike$1(usageFunction) || !isReturnedEffectCleanupFunction(releaseFunction, context) || !resolveReactRefCurrentOriginSymbol(releaseReceiver, context.scopes)) return false;
15030
+ if (!releaseFunction || !usageFunction || !isFunctionLike$1(usageFunction) || !isReturnedEffectCleanupFunction(releaseFunction) || !resolveReactRefCurrentOriginSymbol(releaseReceiver, context.scopes)) return false;
15041
15031
  const controllerKey = getListenerAbortControllerKey(usage, context);
15042
15032
  const refCurrentKey = resolveExpressionKey(releaseReceiver, context);
15043
15033
  if (controllerKey === null || refCurrentKey === null) return false;
@@ -15184,7 +15174,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15184
15174
  return true;
15185
15175
  };
15186
15176
  const matchesPairedReleaseVerb = (releaseVerbName, pairedVerbNames) => pairedVerbNames.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName);
15187
- const isReturnedEffectCleanupFunction = (functionNode, context) => {
15177
+ const isReturnedEffectCleanupFunction = (functionNode) => {
15188
15178
  let currentNode = functionNode;
15189
15179
  let parentNode = currentNode.parent;
15190
15180
  while (isNodeOfType(parentNode, "ChainExpression") || isNodeOfType(parentNode, "TSAsExpression") || isNodeOfType(parentNode, "TSNonNullExpression")) {
@@ -15193,10 +15183,10 @@ const isReturnedEffectCleanupFunction = (functionNode, context) => {
15193
15183
  }
15194
15184
  const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
15195
15185
  const effectCall = effectCallback?.parent;
15196
- return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes));
15186
+ return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isHookCall$2(effectCall, CLEANUP_EFFECT_HOOK_NAMES));
15197
15187
  };
15198
15188
  const isPotentiallyReachableFunction = (functionNode, context) => {
15199
- if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode, context)) return true;
15189
+ if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode)) return true;
15200
15190
  const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
15201
15191
  if (!bindingIdentifier) return false;
15202
15192
  const symbol = context.scopes.symbolFor(bindingIdentifier);
@@ -15693,7 +15683,7 @@ const findRetainedFunctionLeak = (retainedFunction, context, options) => {
15693
15683
  return false;
15694
15684
  }
15695
15685
  }
15696
- if (isSubscribeOrObserveCallExpression(child) && (!doesResourceResultEscape(child, allowReturnedResourceEscape, allowReturnedResourceEscape, context) || options?.requireCallableReturnedResource === true && !isCleanupReturningSubscribeLikeCallExpression(child))) {
15686
+ if (isSubscribeOrObserveCall(child) && (!doesResourceResultEscape(child, allowReturnedResourceEscape, allowReturnedResourceEscape, context) || options?.requireCallableReturnedResource === true && !isCleanupReturningSubscribeLikeCallExpression(child))) {
15697
15687
  const registrationDetails = getCallRegistrationDetails(child, context);
15698
15688
  const subscriptionUsage = {
15699
15689
  kind: "subscribe",
@@ -15901,7 +15891,7 @@ const isInlineRetainedHandlerFunction = (functionNode, context) => {
15901
15891
  if (!isFunctionLike$1(functionNode)) return false;
15902
15892
  const functionRoot = findTransparentExpressionRoot(functionNode);
15903
15893
  const callbackCall = functionRoot.parent;
15904
- if (isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments?.[0] === functionRoot && isReactHookCall(callbackCall, "useCallback", context.scopes) && isDirectJsxEventHandlerValue(callbackCall)) return true;
15894
+ if (isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments?.[0] === functionRoot && isHookCall$2(callbackCall, "useCallback") && isDirectJsxEventHandlerValue(callbackCall)) return true;
15905
15895
  const parentNode = functionNode.parent;
15906
15896
  if (isDirectJsxEventHandlerValue(functionNode)) return true;
15907
15897
  if (!isNodeOfType(parentNode, "Property") || parentNode.value !== functionNode || parentNode.computed) return false;
@@ -15937,12 +15927,12 @@ const effectNeedsCleanup = defineRule({
15937
15927
  };
15938
15928
  return {
15939
15929
  CallExpression(node) {
15940
- if (isReactHookCall(node, "useCallback", context.scopes)) {
15930
+ if (isHookCall$2(node, "useCallback")) {
15941
15931
  const retainedCallback = getEffectCallback(node);
15942
15932
  if (retainedCallback && !isInlineRetainedHandlerFunction(retainedCallback, context)) reportRetainedLeak(retainedCallback);
15943
15933
  return;
15944
15934
  }
15945
- if (!isReactHookCall(node, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return;
15935
+ if (!isHookCall$2(node, CLEANUP_EFFECT_HOOK_NAMES)) return;
15946
15936
  const callback = getEffectCallback(node);
15947
15937
  if (!callback) return;
15948
15938
  const usages = removeSynchronouslyReleasedUsages(callback, findSubscribeLikeUsages(callback, context), context);
@@ -15950,7 +15940,7 @@ const effectNeedsCleanup = defineRule({
15950
15940
  const firstUsage = findFirstUsageWithoutCleanup(callback, usages, context);
15951
15941
  if (!firstUsage) return;
15952
15942
  const resourceNoun = RESOURCE_NOUN_BY_KIND[firstUsage.kind];
15953
- const hookName = getCalleeName$1(node) ?? "effect";
15943
+ const hookName = getCalleeName$2(node) ?? "effect";
15954
15944
  context.report({
15955
15945
  node,
15956
15946
  message: `\`${firstUsage.resourceName}\` creates a ${resourceNoun} in ${hookName} without guaranteed cleanup. Return a cleanup function that owns every allocation so it does not leak after unmount.`
@@ -18823,7 +18813,7 @@ const flattenCalleeName = (callee) => {
18823
18813
  const PRAGMA = "React";
18824
18814
  const isReactFunctionCall = (node, expectedCall) => {
18825
18815
  if (!isNodeOfType(node, "CallExpression")) return false;
18826
- if (getCalleeName$1(node) !== expectedCall) return false;
18816
+ if (getCalleeName$2(node) !== expectedCall) return false;
18827
18817
  if (isNodeOfType(node.callee, "MemberExpression")) {
18828
18818
  const receiver = stripParenExpression(node.callee.object);
18829
18819
  return isNodeOfType(receiver, "Identifier") && receiver.name === PRAGMA;
@@ -19021,14 +19011,10 @@ const hookUseState = defineRule({
19021
19011
  create: (context) => {
19022
19012
  const { allowDestructuredState } = resolveSettings$40(context.settings);
19023
19013
  return { CallExpression(node) {
19024
- const callExpressionNode = node;
19025
- if (!isReactFunctionCall(callExpressionNode, "useState")) return;
19026
- const expressionRoot = findTransparentExpressionRoot(callExpressionNode);
19027
- const expressionParent = expressionRoot.parent;
19028
- if (isNodeOfType(expressionParent, "ReturnStatement")) return;
19029
- if (isNodeOfType(expressionParent, "ArrowFunctionExpression") && expressionParent.body === expressionRoot) return;
19014
+ if (!isReactFunctionCall(node, "useState")) return;
19030
19015
  const parent = node.parent;
19031
19016
  if (!parent) return;
19017
+ if (isNodeOfType(parent, "ReturnStatement")) return;
19032
19018
  if (!isNodeOfType(parent, "VariableDeclarator")) {
19033
19019
  context.report({
19034
19020
  node,
@@ -19136,8 +19122,8 @@ const hooksNoNanInDeps = defineRule({
19136
19122
  severity: "warn",
19137
19123
  recommendation: "Remove `NaN` (or `Number.NaN`) from the dependency array. If a value can be NaN at runtime, normalise it (`Number.isNaN(x) ? 0 : x`) before passing it.",
19138
19124
  create: (context) => ({ CallExpression(node) {
19139
- if (!isReactHookCall(node, HOOKS_WITH_DEP_ARRAY, context.scopes)) return;
19140
- const depsIndex = isReactHookCall(node, "useImperativeHandle", context.scopes) ? 2 : 1;
19125
+ if (!isHookCall$2(node, HOOKS_WITH_DEP_ARRAY)) return;
19126
+ const depsIndex = getCalleeName$2(node) === "useImperativeHandle" ? 2 : 1;
19141
19127
  const depsArgument = node.arguments[depsIndex];
19142
19128
  if (!depsArgument || !isNodeOfType(depsArgument, "ArrayExpression")) return;
19143
19129
  for (const element of depsArgument.elements) {
@@ -20308,7 +20294,7 @@ const isImportedSelectAtom = (callExpression) => {
20308
20294
  const isDeferredCallbackPosition$1 = (functionNode) => {
20309
20295
  const parent = functionNode.parent;
20310
20296
  if (isNodeOfType(parent, "CallExpression") && parent.arguments?.[0] === functionNode) {
20311
- const hookName = getCalleeName$1(parent);
20297
+ const hookName = getCalleeName$2(parent);
20312
20298
  if (hookName && MEMOIZING_HOOK_NAMES$1.has(hookName)) return true;
20313
20299
  if (hookName && EFFECT_HOOK_NAMES$1.has(hookName) && Boolean(parent.arguments?.[1])) return true;
20314
20300
  }
@@ -30379,7 +30365,7 @@ const DEFERRING_CALLEE_NAMES$1 = new Set([
30379
30365
  "on",
30380
30366
  "once"
30381
30367
  ]);
30382
- const getCalleeName = (callee) => {
30368
+ const getCalleeName$1 = (callee) => {
30383
30369
  if (!callee) return null;
30384
30370
  if (isNodeOfType(callee, "Identifier")) return callee.name;
30385
30371
  if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
@@ -30391,11 +30377,11 @@ const isDeferredCallbackPosition = (expression) => {
30391
30377
  const parent = parentOf(expression);
30392
30378
  if (!parent) return false;
30393
30379
  if (isNodeOfType(parent, "CallExpression") && argumentsInclude(parent.arguments, expression)) {
30394
- const name = getCalleeName(parent.callee);
30380
+ const name = getCalleeName$1(parent.callee);
30395
30381
  if (name && DEFERRING_CALLEE_NAMES$1.has(name)) return true;
30396
30382
  }
30397
30383
  if (isNodeOfType(parent, "NewExpression") && argumentsInclude(parent.arguments, expression)) {
30398
- const name = getCalleeName(parent.callee);
30384
+ const name = getCalleeName$1(parent.callee);
30399
30385
  if (name && (name.endsWith("Observer") || name === "Promise")) return true;
30400
30386
  }
30401
30387
  if (isNodeOfType(parent, "AssignmentExpression") && parent.right === expression && isNodeOfType(parent.left, "MemberExpression") && isNodeOfType(parent.left.property, "Identifier") && parent.left.property.name.startsWith("on")) return true;
@@ -30815,6 +30801,14 @@ const isHookCallee$1 = (analysis, node, hookName) => {
30815
30801
  if (isNodeOfType(node, "MemberExpression")) return isReactNamespaceReceiver(analysis, node.object) && isNodeOfType(node.property, "Identifier") && node.property.name === hookName;
30816
30802
  return false;
30817
30803
  };
30804
+ const isUseEffect = (node) => {
30805
+ if (!node || !isNodeOfType(node, "CallExpression")) return false;
30806
+ const callee = node.callee;
30807
+ if (isNodeOfType(callee, "Identifier") && callee.name === "useEffect") return true;
30808
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
30809
+ const receiver = stripParenExpression(callee.object);
30810
+ return isNodeOfType(receiver, "Identifier") && receiver.name === "React" && isNodeOfType(callee.property, "Identifier") && callee.property.name === "useEffect";
30811
+ };
30818
30812
  const getEffectFn = (analysis, node) => {
30819
30813
  if (!isNodeOfType(node, "CallExpression")) return null;
30820
30814
  const fn = node.arguments?.[0];
@@ -32276,7 +32270,6 @@ const collectEffectStateWriteFacts = (analysis, context, effectNode, currentFile
32276
32270
  sourceReferences,
32277
32271
  isDeferred: frame.isDeferred,
32278
32272
  isRenderKnownCopy,
32279
- isSynchronousRenderValue: !frame.isDeferred && !valueEvidence.hasUnknownSource && !valueEvidence.hasDeferredIntroducedValue && !valueEvidence.readsExternalValue,
32280
32273
  matchesStateInitializer: doesMatchStateInitializer,
32281
32274
  resetsSourceState: false
32282
32275
  });
@@ -32292,71 +32285,27 @@ const collectEffectStateWriteFacts = (analysis, context, effectNode, currentFile
32292
32285
  });
32293
32286
  };
32294
32287
  //#endregion
32295
- //#region src/plugin/rules/state-and-effects/utils/has-deferred-or-external-effect-work.ts
32296
- const DEFERRED_MEMBER_NAMES = new Set([
32297
- "catch",
32298
- "finally",
32299
- "then"
32300
- ]);
32301
- const hasDeferredOrExternalEffectWork = (analysis, effectNode, scopes) => {
32302
- const effectFunction = getEffectFn(analysis, effectNode);
32303
- if (!effectFunction) return false;
32304
- if (containsFetchCall(effectFunction, { stopAtFunctionBoundary: true })) return true;
32305
- const effectInvokedFunctions = collectEffectInvokedFunctions(effectFunction);
32306
- let didFindDeferredOrExternalWork = false;
32307
- walkAst(effectFunction, (child) => {
32308
- if (didFindDeferredOrExternalWork) return false;
32309
- if (child !== effectFunction && isFunctionLike$1(child) && !effectInvokedFunctions.has(child)) return false;
32310
- if (isNodeOfType(child, "AssignmentExpression")) {
32311
- const assignmentTarget = child.left;
32312
- if ((isNodeOfType(assignmentTarget, "MemberExpression") ? getStaticPropertyName(assignmentTarget) : null)?.startsWith("on") && isFunctionLike$1(child.right)) {
32313
- didFindDeferredOrExternalWork = true;
32314
- return false;
32315
- }
32316
- }
32317
- if (!isNodeOfType(child, "CallExpression")) return;
32318
- if (isSubscribeOrObserveCallExpression(child)) {
32319
- didFindDeferredOrExternalWork = true;
32320
- return false;
32321
- }
32322
- const localFunction = resolveExactLocalFunction(child.callee, scopes);
32323
- if (isFunctionLike$1(localFunction) && localFunction.async) {
32324
- didFindDeferredOrExternalWork = true;
32325
- return false;
32326
- }
32327
- const callee = child.callee;
32328
- if (isNodeOfType(callee, "Identifier") && TIMER_AND_SCHEDULER_DIRECT_CALLEE_NAMES.has(callee.name)) {
32329
- didFindDeferredOrExternalWork = true;
32330
- return false;
32331
- }
32332
- const memberName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null;
32333
- if (memberName && DEFERRED_MEMBER_NAMES.has(memberName)) {
32334
- didFindDeferredOrExternalWork = true;
32335
- return false;
32336
- }
32337
- });
32338
- return didFindDeferredOrExternalWork;
32339
- };
32340
- //#endregion
32341
32288
  //#region src/plugin/rules/state-and-effects/no-adjust-state-on-prop-change.ts
32342
32289
  const noAdjustStateOnPropChange = defineRule({
32343
32290
  id: "no-adjust-state-on-prop-change",
32344
- title: "State adjusted after a prop changes",
32291
+ title: "State synced to a prop inside an effect",
32345
32292
  severity: "warn",
32346
32293
  tags: ["test-noise"],
32347
- recommendation: "Remove the adjustment effect by deriving values during render, resetting the component with a key, or updating related state in the event that changes the prop. Avoid tracking the previous prop in more state, which preserves the duplication. See https://react.dev/learn/you-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes",
32294
+ 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",
32348
32295
  create: (context) => ({ CallExpression(node) {
32349
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
32296
+ if (!isReactApiCall(node, "useEffect", context.scopes, {
32297
+ allowGlobalReactNamespace: true,
32298
+ allowUnboundBareCalls: true,
32299
+ resolveConditionalAliases: true,
32300
+ resolveNamedAliases: true
32301
+ })) return;
32350
32302
  const analysis = getProgramAnalysis(node);
32351
32303
  if (!analysis) return;
32352
32304
  const dependencyReferences = getEffectDepsRefs(analysis, node);
32353
32305
  if (!dependencyReferences) return;
32354
32306
  if (!dependencyReferences.flatMap((reference) => isState(analysis, reference) ? [] : getUpstreamRefs(analysis, reference)).some((reference) => isProp(analysis, reference))) return;
32355
- const facts = collectEffectStateWriteFacts(analysis, context, node, context.filename);
32356
- if (hasCleanup(analysis, node) || hasDeferredOrExternalEffectWork(analysis, node, context.scopes) || facts.some((fact) => fact.isDeferred)) return;
32357
- for (const fact of facts) {
32358
- if (!fact.isSynchronousRenderValue || fact.resetsSourceState) continue;
32359
- if (fact.sourceReferences.flatMap((reference) => getUpstreamRefs(analysis, reference)).some((reference) => isProp(analysis, reference))) continue;
32307
+ for (const fact of collectEffectStateWriteFacts(analysis, context, node, context.filename)) {
32308
+ if (!fact.isRenderKnownCopy || fact.resetsSourceState) continue;
32360
32309
  context.report({
32361
32310
  node: fact.callExpression,
32362
32311
  message: "This effect adjusts state after a prop changes, so users briefly see the stale value."
@@ -34369,7 +34318,7 @@ const declarationBodyContainsHookCall = (symbol) => {
34369
34318
  walkAst(componentFunction, (descendant) => {
34370
34319
  if (didFindHookCall) return false;
34371
34320
  if (!isNodeOfType(descendant, "CallExpression")) return;
34372
- const calleeName = getCalleeName$1(descendant);
34321
+ const calleeName = getCalleeName$2(descendant);
34373
34322
  if (calleeName && isReactHookName(calleeName)) {
34374
34323
  didFindHookCall = true;
34375
34324
  return false;
@@ -34394,7 +34343,7 @@ const isReturnedFromUseCallbackAdapter = (callNode) => {
34394
34343
  if (isNodeOfType(parent, "ArrowFunctionExpression")) {
34395
34344
  if (parent.body !== current) return false;
34396
34345
  const grandparent = parent.parent;
34397
- return isNodeOfType(grandparent, "CallExpression") && getCalleeName$1(grandparent) === "useCallback" && grandparent.arguments.some((argumentNode) => argumentNode === parent);
34346
+ return isNodeOfType(grandparent, "CallExpression") && getCalleeName$2(grandparent) === "useCallback" && grandparent.arguments.some((argumentNode) => argumentNode === parent);
34398
34347
  }
34399
34348
  if (!isNodeOfType(parent, "ConditionalExpression") && !isNodeOfType(parent, "LogicalExpression")) return false;
34400
34349
  current = parent;
@@ -36926,7 +36875,12 @@ const noDerivedState = defineRule({
36926
36875
  for (const fact of collectRenderStateWriteFacts(analysis, componentBody, context.filename)) reportStateWrite(fact.callExpression, fact.stateDeclarator);
36927
36876
  } }).visitors,
36928
36877
  CallExpression(node) {
36929
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
36878
+ if (!isReactApiCall(node, "useEffect", context.scopes, {
36879
+ allowGlobalReactNamespace: true,
36880
+ allowUnboundBareCalls: true,
36881
+ resolveConditionalAliases: true,
36882
+ resolveNamedAliases: true
36883
+ })) return;
36930
36884
  const analysis = getProgramAnalysis(node);
36931
36885
  if (!analysis) return;
36932
36886
  for (const fact of collectEffectStateWriteFacts(analysis, context, node, context.filename)) {
@@ -36946,7 +36900,12 @@ const noDerivedStateEffect = defineRule({
36946
36900
  tags: ["test-noise"],
36947
36901
  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",
36948
36902
  create: (context) => ({ CallExpression(node) {
36949
- if (!isReactHookCall(node, EFFECT_HOOK_NAMES$1, context.scopes)) return;
36903
+ if (!isReactApiCall(node, EFFECT_HOOK_NAMES$1, context.scopes, {
36904
+ allowGlobalReactNamespace: true,
36905
+ allowUnboundBareCalls: true,
36906
+ resolveConditionalAliases: true,
36907
+ resolveNamedAliases: true
36908
+ })) return;
36950
36909
  const analysis = getProgramAnalysis(node);
36951
36910
  if (!analysis) return;
36952
36911
  if (!collectEffectStateWriteFacts(analysis, context, node, context.filename).find((fact) => fact.isRenderKnownCopy && !fact.resetsSourceState)) return;
@@ -37069,7 +37028,7 @@ const getEnclosingEffectHookCallback = (node, componentFunction) => {
37069
37028
  if (isFunctionLike$1(cursor)) {
37070
37029
  const parent = cursor.parent ?? null;
37071
37030
  if (parent && isNodeOfType(parent, "CallExpression")) {
37072
- const calleeName = getCalleeName$1(parent);
37031
+ const calleeName = getCalleeName$2(parent);
37073
37032
  if (calleeName !== null && EFFECT_HOOK_NAME_PATTERN.test(calleeName) && (parent.arguments ?? []).some((argument) => argument === cursor)) return cursor;
37074
37033
  }
37075
37034
  }
@@ -37140,7 +37099,7 @@ const isNonHandlerHookCallback = (functionNode) => {
37140
37099
  const parent = functionNode.parent ?? null;
37141
37100
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
37142
37101
  if (!(parent.arguments ?? []).some((argument) => argument === functionNode)) return false;
37143
- const calleeName = getCalleeName$1(parent);
37102
+ const calleeName = getCalleeName$2(parent);
37144
37103
  return calleeName !== null && isReactHookName(calleeName) && calleeName !== "useCallback";
37145
37104
  };
37146
37105
  const isHandlerShapedReseed = (setterCall, componentFunction) => {
@@ -37222,7 +37181,7 @@ const noDerivedUseState = defineRule({
37222
37181
  return {
37223
37182
  ...propStackTracker.visitors,
37224
37183
  CallExpression(node) {
37225
- if (!isReactHookCall(node, "useState", context.scopes) || !node.arguments?.length) return;
37184
+ if (!isHookCall$2(node, "useState") || !node.arguments?.length) return;
37226
37185
  const seed = unwrapInitializerSeed(node.arguments[0]);
37227
37186
  const reportStalePropCopy = (propName) => {
37228
37187
  if (isIntentionalSnapshotState(node)) return;
@@ -37911,7 +37870,12 @@ const collectUseStateBindings = (componentBody, scopes) => {
37911
37870
  const setterElement = elements[1];
37912
37871
  if (!isNodeOfType(valueElement, "Identifier") || !isNodeOfType(setterElement, "Identifier") || !isSetterIdentifier(setterElement.name)) continue;
37913
37872
  if (!isNodeOfType(declarator.init, "CallExpression")) continue;
37914
- if (!isReactHookCall(declarator.init, "useState", scopes)) 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;
37915
37879
  bindings.push({
37916
37880
  valueName: valueElement.name,
37917
37881
  setterName: setterElement.name,
@@ -38078,7 +38042,7 @@ const noDirectStateMutation = defineRule({
38078
38042
  create: (context) => {
38079
38043
  const checkComponent = (componentBody) => {
38080
38044
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
38081
- const bindings = collectUseStateBindings(componentBody, context.scopes);
38045
+ const bindings = collectUseStateBindings(componentBody);
38082
38046
  if (bindings.length === 0) return;
38083
38047
  const stateValueToSetter = new Map(bindings.map((binding) => [binding.valueName, binding.setterName]));
38084
38048
  const setterValueObservations = collectSetterValueObservations(componentBody, new Set(bindings.map((binding) => binding.setterName)));
@@ -38645,7 +38609,12 @@ const findTopLevelEffectCalls = (componentBody, scopes) => {
38645
38609
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
38646
38610
  const expression = unwrapDiscardedExpression(statement);
38647
38611
  if (!isNodeOfType(expression, "CallExpression")) continue;
38648
- if (!isReactHookCall(expression, EFFECT_HOOK_NAMES$1, scopes)) continue;
38612
+ if (!isReactApiCall(expression, EFFECT_HOOK_NAMES$1, scopes, {
38613
+ allowGlobalReactNamespace: true,
38614
+ allowUnboundBareCalls: true,
38615
+ resolveConditionalAliases: true,
38616
+ resolveNamedAliases: true
38617
+ })) continue;
38649
38618
  effectCalls.push(expression);
38650
38619
  }
38651
38620
  return effectCalls;
@@ -38889,7 +38858,7 @@ const collectStorageHookSetterNames = (componentBody) => {
38889
38858
  for (const declarator of statement.declarations ?? []) {
38890
38859
  if (!isNodeOfType(declarator.id, "ArrayPattern")) continue;
38891
38860
  if (!isNodeOfType(declarator.init, "CallExpression")) continue;
38892
- const calleeName = getCalleeName$1(declarator.init);
38861
+ const calleeName = getCalleeName$2(declarator.init);
38893
38862
  if (!calleeName || !STORAGE_HOOK_PATTERN.test(calleeName)) continue;
38894
38863
  for (const element of declarator.id.elements ?? []) if (isNodeOfType(element, "Identifier") && isSetterIdentifier(element.name)) setterNames.add(element.name);
38895
38864
  }
@@ -39377,7 +39346,7 @@ const noEffectEventHandler = defineRule({
39377
39346
  return {
39378
39347
  ...propStackTracker.visitors,
39379
39348
  CallExpression(node) {
39380
- if (!isReactHookCall(node, EFFECT_HOOK_NAMES$1, context.scopes) || (node.arguments?.length ?? 0) < 2) return;
39349
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1) || (node.arguments?.length ?? 0) < 2) return;
39381
39350
  const callback = getEffectCallback(node);
39382
39351
  if (!callback) return;
39383
39352
  const analysis = getProgramAnalysis(node);
@@ -39497,14 +39466,14 @@ const noEffectEventInDeps = defineRule({
39497
39466
  if (!isNodeOfType(declaratorNode.id, "Identifier")) return;
39498
39467
  const initializer = declaratorNode.init;
39499
39468
  if (!initializer || !isNodeOfType(initializer, "CallExpression")) return;
39500
- if (!isReactHookCall(initializer, "useEffectEvent", context.scopes)) return;
39469
+ if (!isHookCall$2(initializer, "useEffectEvent")) return;
39501
39470
  if (isNonReactEffectEventCallee(initializer.callee, declaratorNode, context.scopes)) return;
39502
39471
  componentBindings.addBindingToCurrentFrame(declaratorNode.id.name);
39503
39472
  } });
39504
39473
  return {
39505
39474
  ...componentBindings.visitors,
39506
39475
  CallExpression(node) {
39507
- if (!isReactHookCall(node, HOOKS_WITH_DEPS, context.scopes) || node.arguments.length < 2) return;
39476
+ if (!isHookCall$2(node, HOOKS_WITH_DEPS) || node.arguments.length < 2) return;
39508
39477
  if (!componentBindings.isInsideComponent()) return;
39509
39478
  const depsNode = node.arguments[1];
39510
39479
  if (!isNodeOfType(depsNode, "ArrayExpression")) return;
@@ -39561,7 +39530,7 @@ const noEffectWithFreshDeps = defineRule({
39561
39530
  node: finding.reportNode,
39562
39531
  message: `A dependency inside this custom Hook changes every render because \`${finding.bindingName}\` is a new ${finding.kind} built fresh each time.`
39563
39532
  });
39564
- if (!isReactHookCall(node, HOOKS_WITH_DEPS, context.scopes)) return;
39533
+ if (!isHookCall$2(node, HOOKS_WITH_DEPS)) return;
39565
39534
  const args = node.arguments ?? [];
39566
39535
  if (args.length < 2) return;
39567
39536
  const depsNode = args[1];
@@ -39822,7 +39791,7 @@ const noEventHandler = defineRule({
39822
39791
  severity: "warn",
39823
39792
  recommendation: "Run the side effect in the event handler that triggers it, instead of watching its state from a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers",
39824
39793
  create: (context) => ({ CallExpression(node) {
39825
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
39794
+ if (!isUseEffect(node)) return;
39826
39795
  const analysis = getProgramAnalysis(node);
39827
39796
  if (!analysis || hasCleanup(analysis, node)) return;
39828
39797
  const frames = collectBoundedEffectExecutionFrames(analysis, node);
@@ -40284,25 +40253,25 @@ const addDeclarationBindings = (statement, scope) => {
40284
40253
  }
40285
40254
  if (isNodeOfType(statement, "FunctionDeclaration") && statement.id) addPatternBindings(statement.id, scope);
40286
40255
  };
40287
- const collectRenderReachableNamesFromStatements = (statements, names, scope, scopes, eventHandlerReferenceNames = /* @__PURE__ */ new Set()) => {
40256
+ const collectRenderReachableNamesFromStatements = (statements, names, scope, eventHandlerReferenceNames = /* @__PURE__ */ new Set()) => {
40288
40257
  let hasReturn = false;
40289
- for (const statement of statements ?? []) if (collectRenderReachableNamesFromStatement(statement, names, scope, scopes, eventHandlerReferenceNames)) hasReturn = true;
40258
+ for (const statement of statements ?? []) if (collectRenderReachableNamesFromStatement(statement, names, scope, eventHandlerReferenceNames)) hasReturn = true;
40290
40259
  else addDeclarationBindings(statement, scope);
40291
40260
  return hasReturn;
40292
40261
  };
40293
- const collectRenderReachableNamesFromStatement = (statement, names, scope, scopes, eventHandlerReferenceNames) => {
40262
+ const collectRenderReachableNamesFromStatement = (statement, names, scope, eventHandlerReferenceNames) => {
40294
40263
  if (isNodeOfType(statement, "ReturnStatement")) {
40295
40264
  if (statement.argument) addNames(names, collectScopedReferenceNames(statement.argument, scope, eventHandlerReferenceNames));
40296
40265
  return true;
40297
40266
  }
40298
- if (isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "CallExpression") && (isNodeOfType(statement.expression.callee, "Identifier") && isReactHookName(statement.expression.callee.name) || isReactHookCall(statement.expression, BUILTIN_HOOK_NAMES, scopes)) && !isReactHookCall(statement.expression, EFFECT_HOOK_NAMES$1, scopes)) {
40267
+ if (isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "CallExpression") && isNodeOfType(statement.expression.callee, "Identifier") && isReactHookName(statement.expression.callee.name) && !EFFECT_HOOK_NAMES$1.has(statement.expression.callee.name)) {
40299
40268
  for (const argument of statement.expression.arguments ?? []) addNames(names, collectScopedReferenceNames(argument, scope, eventHandlerReferenceNames));
40300
40269
  return false;
40301
40270
  }
40302
- if (isNodeOfType(statement, "BlockStatement")) return collectRenderReachableNamesFromStatements(statement.body, names, createBlockBindingScope(scope), scopes, eventHandlerReferenceNames);
40271
+ if (isNodeOfType(statement, "BlockStatement")) return collectRenderReachableNamesFromStatements(statement.body, names, createBlockBindingScope(scope), eventHandlerReferenceNames);
40303
40272
  if (isNodeOfType(statement, "IfStatement")) {
40304
- const consequentHasReturn = collectRenderReachableNamesFromStatement(statement.consequent, names, scope, scopes, eventHandlerReferenceNames);
40305
- const alternateHasReturn = statement.alternate ? collectRenderReachableNamesFromStatement(statement.alternate, names, scope, scopes, eventHandlerReferenceNames) : false;
40273
+ const consequentHasReturn = collectRenderReachableNamesFromStatement(statement.consequent, names, scope, eventHandlerReferenceNames);
40274
+ const alternateHasReturn = statement.alternate ? collectRenderReachableNamesFromStatement(statement.alternate, names, scope, eventHandlerReferenceNames) : false;
40306
40275
  if (consequentHasReturn || alternateHasReturn) addNames(names, collectScopedReferenceNames(statement.test, scope, eventHandlerReferenceNames));
40307
40276
  return consequentHasReturn || alternateHasReturn;
40308
40277
  }
@@ -40310,7 +40279,7 @@ const collectRenderReachableNamesFromStatement = (statement, names, scope, scope
40310
40279
  let hasReturn = false;
40311
40280
  for (const switchCase of statement.cases ?? []) {
40312
40281
  const caseScope = createBlockBindingScope(scope);
40313
- if (!collectRenderReachableNamesFromStatements(switchCase.consequent, names, caseScope, scopes, eventHandlerReferenceNames)) continue;
40282
+ if (!collectRenderReachableNamesFromStatements(switchCase.consequent, names, caseScope, eventHandlerReferenceNames)) continue;
40314
40283
  hasReturn = true;
40315
40284
  if (switchCase.test) addNames(names, collectScopedReferenceNames(switchCase.test, scope, eventHandlerReferenceNames));
40316
40285
  }
@@ -40318,25 +40287,25 @@ const collectRenderReachableNamesFromStatement = (statement, names, scope, scope
40318
40287
  return hasReturn;
40319
40288
  }
40320
40289
  if (isNodeOfType(statement, "TryStatement")) {
40321
- const blockHasReturn = collectRenderReachableNamesFromStatement(statement.block, names, scope, scopes, eventHandlerReferenceNames);
40322
- const handlerHasReturn = statement.handler ? collectRenderReachableNamesFromStatement(statement.handler, names, scope, scopes, eventHandlerReferenceNames) : false;
40323
- const finalizerHasReturn = statement.finalizer ? collectRenderReachableNamesFromStatement(statement.finalizer, names, scope, scopes, eventHandlerReferenceNames) : false;
40290
+ const blockHasReturn = collectRenderReachableNamesFromStatement(statement.block, names, scope, eventHandlerReferenceNames);
40291
+ const handlerHasReturn = statement.handler ? collectRenderReachableNamesFromStatement(statement.handler, names, scope, eventHandlerReferenceNames) : false;
40292
+ const finalizerHasReturn = statement.finalizer ? collectRenderReachableNamesFromStatement(statement.finalizer, names, scope, eventHandlerReferenceNames) : false;
40324
40293
  return blockHasReturn || handlerHasReturn || finalizerHasReturn;
40325
40294
  }
40326
40295
  if (isNodeOfType(statement, "CatchClause")) {
40327
40296
  const catchScope = createBlockBindingScope(scope);
40328
40297
  addPatternBindings(statement.param, catchScope);
40329
- return collectRenderReachableNamesFromStatement(statement.body, names, catchScope, scopes, eventHandlerReferenceNames);
40298
+ return collectRenderReachableNamesFromStatement(statement.body, names, catchScope, eventHandlerReferenceNames);
40330
40299
  }
40331
40300
  if (isNodeOfType(statement, "WhileStatement") || isNodeOfType(statement, "DoWhileStatement")) {
40332
- const bodyHasReturn = collectRenderReachableNamesFromStatement(statement.body, names, scope, scopes, eventHandlerReferenceNames);
40301
+ const bodyHasReturn = collectRenderReachableNamesFromStatement(statement.body, names, scope, eventHandlerReferenceNames);
40333
40302
  if (bodyHasReturn) addNames(names, collectScopedReferenceNames(statement.test, scope, eventHandlerReferenceNames));
40334
40303
  return bodyHasReturn;
40335
40304
  }
40336
40305
  if (isNodeOfType(statement, "ForStatement")) {
40337
40306
  const loopScope = createBlockBindingScope(scope);
40338
40307
  if (statement.init) addDeclarationBindings(statement.init, loopScope);
40339
- if (!collectRenderReachableNamesFromStatement(statement.body, names, loopScope, scopes, eventHandlerReferenceNames)) return false;
40308
+ if (!collectRenderReachableNamesFromStatement(statement.body, names, loopScope, eventHandlerReferenceNames)) return false;
40340
40309
  if (statement.init) addNames(names, collectScopedReferenceNames(statement.init, loopScope, eventHandlerReferenceNames));
40341
40310
  if (statement.test) addNames(names, collectScopedReferenceNames(statement.test, loopScope, eventHandlerReferenceNames));
40342
40311
  if (statement.update) addNames(names, collectScopedReferenceNames(statement.update, loopScope, eventHandlerReferenceNames));
@@ -40346,22 +40315,22 @@ const collectRenderReachableNamesFromStatement = (statement, names, scope, scope
40346
40315
  const rightNames = collectScopedReferenceNames(statement.right, scope, eventHandlerReferenceNames);
40347
40316
  const loopScope = createBlockBindingScope(scope);
40348
40317
  if (isNodeOfType(statement.left, "VariableDeclaration")) addDeclarationBindings(statement.left, loopScope);
40349
- if (!collectRenderReachableNamesFromStatement(statement.body, names, loopScope, scopes, eventHandlerReferenceNames)) return false;
40318
+ if (!collectRenderReachableNamesFromStatement(statement.body, names, loopScope, eventHandlerReferenceNames)) return false;
40350
40319
  addNames(names, rightNames);
40351
40320
  return true;
40352
40321
  }
40353
- if (isNodeOfType(statement, "LabeledStatement")) return collectRenderReachableNamesFromStatement(statement.body, names, scope, scopes, eventHandlerReferenceNames);
40322
+ if (isNodeOfType(statement, "LabeledStatement")) return collectRenderReachableNamesFromStatement(statement.body, names, scope, eventHandlerReferenceNames);
40354
40323
  if (isNodeOfType(statement, "WithStatement")) {
40355
- const bodyHasReturn = collectRenderReachableNamesFromStatement(statement.body, names, scope, scopes, eventHandlerReferenceNames);
40324
+ const bodyHasReturn = collectRenderReachableNamesFromStatement(statement.body, names, scope, eventHandlerReferenceNames);
40356
40325
  if (bodyHasReturn) addNames(names, collectScopedReferenceNames(statement.object, scope, eventHandlerReferenceNames));
40357
40326
  return bodyHasReturn;
40358
40327
  }
40359
40328
  return false;
40360
40329
  };
40361
- const collectRenderReachableNames = (componentBody, scopes, eventHandlerReferenceNames = /* @__PURE__ */ new Set()) => {
40330
+ const collectRenderReachableNames = (componentBody, eventHandlerReferenceNames = /* @__PURE__ */ new Set()) => {
40362
40331
  const names = /* @__PURE__ */ new Set();
40363
40332
  if (!isNodeOfType(componentBody, "BlockStatement")) return names;
40364
- collectRenderReachableNamesFromStatements(componentBody.body, names, createComponentBindingScope(), scopes, eventHandlerReferenceNames);
40333
+ collectRenderReachableNamesFromStatements(componentBody.body, names, createComponentBindingScope(), eventHandlerReferenceNames);
40365
40334
  return names;
40366
40335
  };
40367
40336
  //#endregion
@@ -40384,36 +40353,42 @@ const expandTransitiveDependencies = (seedNames, dependencyGraph) => {
40384
40353
  };
40385
40354
  //#endregion
40386
40355
  //#region src/plugin/rules/state-and-effects/utils/collect-function-like-local-names.ts
40387
- const isFunctionLikeReference = (node, functionLikeLocalNames, scope, scopes) => {
40388
- if (isInlineFunctionExpression(node) || isReactHookCall(node, "useCallback", scopes)) return true;
40356
+ const isUseCallbackCall = (node) => isNodeOfType(node, "CallExpression") && getCalleeName(node.callee) === "useCallback";
40357
+ const getCalleeName = (node) => {
40358
+ if (isNodeOfType(node, "Identifier")) return node.name;
40359
+ if (isNodeOfType(node, "MemberExpression")) return getStaticMemberPropertyName(node);
40360
+ return null;
40361
+ };
40362
+ const isFunctionLikeReference = (node, functionLikeLocalNames, scope) => {
40363
+ if (isInlineFunctionExpression(node) || isUseCallbackCall(node)) return true;
40389
40364
  if (isNodeOfType(node, "Identifier")) return functionLikeLocalNames.has(resolveBindingName(scope, node.name));
40390
40365
  const memberReferenceName = getStaticMemberReferenceName(node, (name) => resolveBindingName(scope, name));
40391
40366
  return Boolean(memberReferenceName && functionLikeLocalNames.has(memberReferenceName));
40392
40367
  };
40393
- const addObjectPropertyFunctionNames = (objectBindingName, node, functionLikeLocalNames, scope, scopes) => {
40368
+ const addObjectPropertyFunctionNames = (objectBindingName, node, functionLikeLocalNames, scope) => {
40394
40369
  if (!isNodeOfType(node, "ObjectExpression")) return;
40395
40370
  for (const property of node.properties ?? []) {
40396
40371
  if (!isNodeOfType(property, "Property")) continue;
40397
40372
  const propertyName = getStaticPropertyKeyName(property, { stringifyNonStringLiterals: true });
40398
40373
  if (!propertyName) continue;
40399
- if (!isFunctionLikeReference(property.value, functionLikeLocalNames, scope, scopes)) continue;
40374
+ if (!isFunctionLikeReference(property.value, functionLikeLocalNames, scope)) continue;
40400
40375
  functionLikeLocalNames.add(`${objectBindingName}.${propertyName}`);
40401
40376
  }
40402
40377
  };
40403
- const addVariableDeclarationFunctionNames = (statement, functionLikeLocalNames, scope, scopes) => {
40378
+ const addVariableDeclarationFunctionNames = (statement, functionLikeLocalNames, scope) => {
40404
40379
  if (!isNodeOfType(statement, "VariableDeclaration")) return;
40405
40380
  const declarationScope = getVariableDeclarationScope(statement, scope);
40406
40381
  for (const declarator of statement.declarations ?? []) {
40407
40382
  const declaredBindingNames = addPatternBindings(declarator.id, declarationScope);
40408
40383
  if (!declarator.init) continue;
40409
- const isFunctionReference = isFunctionLikeReference(declarator.init, functionLikeLocalNames, scope, scopes);
40384
+ const isFunctionReference = isFunctionLikeReference(declarator.init, functionLikeLocalNames, scope);
40410
40385
  for (const declaredBindingName of declaredBindingNames) {
40411
40386
  if (isFunctionReference) functionLikeLocalNames.add(declaredBindingName);
40412
- addObjectPropertyFunctionNames(declaredBindingName, declarator.init, functionLikeLocalNames, scope, scopes);
40387
+ addObjectPropertyFunctionNames(declaredBindingName, declarator.init, functionLikeLocalNames, scope);
40413
40388
  }
40414
40389
  }
40415
40390
  };
40416
- const collectStatementFunctionNames = (statement, functionLikeLocalNames, scope, scopes) => {
40391
+ const collectStatementFunctionNames = (statement, functionLikeLocalNames, scope) => {
40417
40392
  if (isNodeOfType(statement, "FunctionDeclaration")) {
40418
40393
  if (statement.id) {
40419
40394
  const declaredBindingNames = addPatternBindings(statement.id, scope);
@@ -40422,61 +40397,61 @@ const collectStatementFunctionNames = (statement, functionLikeLocalNames, scope,
40422
40397
  return;
40423
40398
  }
40424
40399
  if (isNodeOfType(statement, "VariableDeclaration")) {
40425
- addVariableDeclarationFunctionNames(statement, functionLikeLocalNames, scope, scopes);
40400
+ addVariableDeclarationFunctionNames(statement, functionLikeLocalNames, scope);
40426
40401
  return;
40427
40402
  }
40428
40403
  if (isNodeOfType(statement, "BlockStatement")) {
40429
- collectStatementListFunctionNames(statement.body, functionLikeLocalNames, createBlockBindingScope(scope), scopes);
40404
+ collectStatementListFunctionNames(statement.body, functionLikeLocalNames, createBlockBindingScope(scope));
40430
40405
  return;
40431
40406
  }
40432
40407
  if (isNodeOfType(statement, "IfStatement")) {
40433
- collectStatementFunctionNames(statement.consequent, functionLikeLocalNames, scope, scopes);
40434
- if (statement.alternate) collectStatementFunctionNames(statement.alternate, functionLikeLocalNames, scope, scopes);
40408
+ collectStatementFunctionNames(statement.consequent, functionLikeLocalNames, scope);
40409
+ if (statement.alternate) collectStatementFunctionNames(statement.alternate, functionLikeLocalNames, scope);
40435
40410
  return;
40436
40411
  }
40437
40412
  if (isNodeOfType(statement, "SwitchStatement")) {
40438
- for (const switchCase of statement.cases ?? []) collectStatementListFunctionNames(switchCase.consequent, functionLikeLocalNames, createBlockBindingScope(scope), scopes);
40413
+ for (const switchCase of statement.cases ?? []) collectStatementListFunctionNames(switchCase.consequent, functionLikeLocalNames, createBlockBindingScope(scope));
40439
40414
  return;
40440
40415
  }
40441
40416
  if (isNodeOfType(statement, "TryStatement")) {
40442
- collectStatementFunctionNames(statement.block, functionLikeLocalNames, scope, scopes);
40417
+ collectStatementFunctionNames(statement.block, functionLikeLocalNames, scope);
40443
40418
  if (statement.handler) {
40444
40419
  const catchScope = createBlockBindingScope(scope);
40445
40420
  addPatternBindings(statement.handler.param, catchScope);
40446
- collectStatementFunctionNames(statement.handler.body, functionLikeLocalNames, catchScope, scopes);
40421
+ collectStatementFunctionNames(statement.handler.body, functionLikeLocalNames, catchScope);
40447
40422
  }
40448
- if (statement.finalizer) collectStatementFunctionNames(statement.finalizer, functionLikeLocalNames, scope, scopes);
40423
+ if (statement.finalizer) collectStatementFunctionNames(statement.finalizer, functionLikeLocalNames, scope);
40449
40424
  return;
40450
40425
  }
40451
40426
  if (isNodeOfType(statement, "ForStatement")) {
40452
40427
  const loopScope = createBlockBindingScope(scope);
40453
- if (statement.init && isNodeOfType(statement.init, "VariableDeclaration")) addVariableDeclarationFunctionNames(statement.init, functionLikeLocalNames, loopScope, scopes);
40454
- collectStatementFunctionNames(statement.body, functionLikeLocalNames, loopScope, scopes);
40428
+ if (statement.init && isNodeOfType(statement.init, "VariableDeclaration")) addVariableDeclarationFunctionNames(statement.init, functionLikeLocalNames, loopScope);
40429
+ collectStatementFunctionNames(statement.body, functionLikeLocalNames, loopScope);
40455
40430
  return;
40456
40431
  }
40457
40432
  if (isNodeOfType(statement, "ForInStatement") || isNodeOfType(statement, "ForOfStatement")) {
40458
40433
  const loopScope = createBlockBindingScope(scope);
40459
- if (isNodeOfType(statement.left, "VariableDeclaration")) addVariableDeclarationFunctionNames(statement.left, functionLikeLocalNames, loopScope, scopes);
40434
+ if (isNodeOfType(statement.left, "VariableDeclaration")) addVariableDeclarationFunctionNames(statement.left, functionLikeLocalNames, loopScope);
40460
40435
  else addPatternBindings(statement.left, loopScope);
40461
- collectStatementFunctionNames(statement.body, functionLikeLocalNames, loopScope, scopes);
40436
+ collectStatementFunctionNames(statement.body, functionLikeLocalNames, loopScope);
40462
40437
  return;
40463
40438
  }
40464
40439
  if (isNodeOfType(statement, "WhileStatement") || isNodeOfType(statement, "DoWhileStatement")) {
40465
- collectStatementFunctionNames(statement.body, functionLikeLocalNames, scope, scopes);
40440
+ collectStatementFunctionNames(statement.body, functionLikeLocalNames, scope);
40466
40441
  return;
40467
40442
  }
40468
- if (isNodeOfType(statement, "LabeledStatement")) collectStatementFunctionNames(statement.body, functionLikeLocalNames, scope, scopes);
40443
+ if (isNodeOfType(statement, "LabeledStatement")) collectStatementFunctionNames(statement.body, functionLikeLocalNames, scope);
40469
40444
  };
40470
- const collectStatementListFunctionNames = (statements, functionLikeLocalNames, scope, scopes) => {
40471
- for (const statement of statements ?? []) collectStatementFunctionNames(statement, functionLikeLocalNames, scope, scopes);
40445
+ const collectStatementListFunctionNames = (statements, functionLikeLocalNames, scope) => {
40446
+ for (const statement of statements ?? []) collectStatementFunctionNames(statement, functionLikeLocalNames, scope);
40472
40447
  };
40473
- const collectFunctionLikeLocalNames = (componentBody, scopes) => {
40448
+ const collectFunctionLikeLocalNames = (componentBody) => {
40474
40449
  const functionLikeLocalNames = /* @__PURE__ */ new Set();
40475
40450
  if (!isNodeOfType(componentBody, "BlockStatement")) return functionLikeLocalNames;
40476
40451
  let previousSize = -1;
40477
40452
  while (previousSize !== functionLikeLocalNames.size) {
40478
40453
  previousSize = functionLikeLocalNames.size;
40479
- collectStatementListFunctionNames(componentBody.body, functionLikeLocalNames, createComponentBindingScope(), scopes);
40454
+ collectStatementListFunctionNames(componentBody.body, functionLikeLocalNames, createComponentBindingScope());
40480
40455
  }
40481
40456
  return functionLikeLocalNames;
40482
40457
  };
@@ -40516,17 +40491,17 @@ const noEventTriggerState = defineRule({
40516
40491
  create: (context) => {
40517
40492
  const checkComponent = (componentBody) => {
40518
40493
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
40519
- const useStateBindings = collectUseStateBindings(componentBody, context.scopes);
40494
+ const useStateBindings = collectUseStateBindings(componentBody);
40520
40495
  if (useStateBindings.length === 0) return;
40521
40496
  const analysis = getProgramAnalysis(componentBody);
40522
40497
  if (!analysis) return;
40523
40498
  const localStateNames = new Set(useStateBindings.map((binding) => binding.valueName));
40524
- const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody, context.scopes);
40499
+ const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody);
40525
40500
  const dependencyGraph = buildLocalDependencyGraph(componentBody, eventHandlerReferenceNames);
40526
- const renderReachableNames = expandTransitiveDependencies(collectRenderReachableNames(componentBody, context.scopes, eventHandlerReferenceNames), dependencyGraph);
40501
+ const renderReachableNames = expandTransitiveDependencies(collectRenderReachableNames(componentBody, eventHandlerReferenceNames), dependencyGraph);
40527
40502
  walkAst(componentBody, (effectCall) => {
40528
40503
  if (!isNodeOfType(effectCall, "CallExpression")) return;
40529
- if (!isReactHookCall(effectCall, EFFECT_HOOK_NAMES$1, context.scopes)) return;
40504
+ if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) return;
40530
40505
  if ((effectCall.arguments?.length ?? 0) < 2) return;
40531
40506
  const depsNode = effectCall.arguments[1];
40532
40507
  if (!isNodeOfType(depsNode, "ArrayExpression")) return;
@@ -40591,17 +40566,10 @@ const isShadowedByLocalBinding = (identifier) => {
40591
40566
  };
40592
40567
  const isRealFetchCall = (node) => {
40593
40568
  if (!isNodeOfType(node, "CallExpression")) return false;
40594
- const callee = stripParenExpression(node.callee);
40595
- if (isNodeOfType(callee, "Identifier") && FETCH_CALLEE_NAMES.has(callee.name)) return !isShadowedByLocalBinding(callee);
40596
- if (!isNodeOfType(callee, "MemberExpression")) return false;
40597
- const receiver = stripParenExpression(callee.object);
40598
- return isNodeOfType(receiver, "Identifier") && FETCH_MEMBER_OBJECTS.has(receiver.name) && !isShadowedByLocalBinding(receiver);
40599
- };
40600
- const isXmlHttpRequestConstruction = (node) => {
40601
- if (!isNodeOfType(node, "NewExpression")) return false;
40602
- const callee = stripParenExpression(node.callee);
40603
- return isNodeOfType(callee, "Identifier") && callee.name === "XMLHttpRequest";
40569
+ if (isNodeOfType(node.callee, "Identifier") && FETCH_CALLEE_NAMES.has(node.callee.name)) return !isShadowedByLocalBinding(node.callee);
40570
+ return isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.object, "Identifier") && FETCH_MEMBER_OBJECTS.has(node.callee.object.name) && !isShadowedByLocalBinding(node.callee.object);
40604
40571
  };
40572
+ const isXmlHttpRequestConstruction = (node) => isNodeOfType(node, "NewExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "XMLHttpRequest";
40605
40573
  const isNetworkRequest = (node) => isRealFetchCall(node) || isXmlHttpRequestConstruction(node);
40606
40574
  const resolveLocalFunction = (expression, context) => {
40607
40575
  if (!expression) return null;
@@ -42362,7 +42330,7 @@ const noInitializeState = defineRule({
42362
42330
  tags: ["test-noise"],
42363
42331
  recommendation: "Pass the initial value directly to useState() instead of setting it from a mount-only useEffect. For SSR hydration, prefer useSyncExternalStore().",
42364
42332
  create: (context) => ({ CallExpression(node) {
42365
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
42333
+ if (!isUseEffect(node)) return;
42366
42334
  const dependencies = node.arguments?.[1];
42367
42335
  if (!dependencies || !isNodeOfType(dependencies, "ArrayExpression") || (dependencies.elements ?? []).length !== 0) return;
42368
42336
  const analysis = getProgramAnalysis(node);
@@ -43974,7 +43942,7 @@ const noMirrorPropEffect = defineRule({
43974
43942
  const setterElement = elements[1];
43975
43943
  if (!isNodeOfType(valueElement, "Identifier") || !isNodeOfType(setterElement, "Identifier") || !isSetterIdentifier(setterElement.name)) continue;
43976
43944
  if (!isNodeOfType(declarator.init, "CallExpression")) continue;
43977
- if (!isReactHookCall(declarator.init, "useState", context.scopes)) continue;
43945
+ if (!isHookCall$2(declarator.init, "useState")) continue;
43978
43946
  const initializer = declarator.init.arguments?.[0];
43979
43947
  if (!initializer) continue;
43980
43948
  const propRootName = getPropRootName(initializer, propNames);
@@ -43992,7 +43960,7 @@ const noMirrorPropEffect = defineRule({
43992
43960
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
43993
43961
  const effectCall = unwrapDiscardedExpression(statement);
43994
43962
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
43995
- if (!isReactHookCall(effectCall, EFFECT_HOOK_NAMES$1, context.scopes)) continue;
43963
+ if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
43996
43964
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
43997
43965
  const depsNode = effectCall.arguments[1];
43998
43966
  if (!isNodeOfType(depsNode, "ArrayExpression")) continue;
@@ -44401,7 +44369,7 @@ const noMultiComp = defineRule({
44401
44369
  });
44402
44370
  //#endregion
44403
44371
  //#region src/plugin/rules/state-and-effects/no-mutable-in-deps.ts
44404
- const collectUseRefBindingNames = (componentBody, scopes) => {
44372
+ const collectUseRefBindingNames = (componentBody) => {
44405
44373
  const useRefBindings = /* @__PURE__ */ new Set();
44406
44374
  if (!isNodeOfType(componentBody, "BlockStatement")) return useRefBindings;
44407
44375
  for (const statement of componentBody.body ?? []) {
@@ -44409,7 +44377,7 @@ const collectUseRefBindingNames = (componentBody, scopes) => {
44409
44377
  for (const declarator of statement.declarations ?? []) {
44410
44378
  if (!isNodeOfType(declarator.id, "Identifier")) continue;
44411
44379
  if (!isNodeOfType(declarator.init, "CallExpression")) continue;
44412
- if (!isReactHookCall(declarator.init, "useRef", scopes)) continue;
44380
+ if (!isHookCall$2(declarator.init, "useRef")) continue;
44413
44381
  useRefBindings.add(declarator.id.name);
44414
44382
  }
44415
44383
  }
@@ -44444,12 +44412,12 @@ const noMutableInDeps = defineRule({
44444
44412
  create: (context) => {
44445
44413
  const checkComponent = (componentBody, componentParams = []) => {
44446
44414
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
44447
- const useRefBindingNames = collectUseRefBindingNames(componentBody, context.scopes);
44415
+ const useRefBindingNames = collectUseRefBindingNames(componentBody);
44448
44416
  const localBindingNames = collectLocalBindingNames(componentBody);
44449
44417
  for (const param of componentParams) collectPatternNames(param, localBindingNames);
44450
44418
  walkAst(componentBody, (child) => {
44451
44419
  if (!isNodeOfType(child, "CallExpression")) return;
44452
- if (!isReactHookCall(child, HOOKS_WITH_DEPS, context.scopes)) return;
44420
+ if (!isHookCall$2(child, HOOKS_WITH_DEPS)) return;
44453
44421
  if ((child.arguments?.length ?? 0) < 2) return;
44454
44422
  const depsNode = child.arguments[1];
44455
44423
  if (!isNodeOfType(depsNode, "ArrayExpression")) return;
@@ -46362,10 +46330,16 @@ const noPassDataToParent = defineRule({
46362
46330
  tags: ["test-noise"],
46363
46331
  recommendation: "Fetch the data in the parent and pass it down as a prop (or return it from the hook), instead of handing it back up through a prop callback in a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#passing-data-to-the-parent",
46364
46332
  create: (context) => {
46365
- const isReactUseRefCall = (node) => isReactHookCall(node, "useRef", context.scopes);
46366
- const isReactUseEffectCall = (node) => isReactHookCall(node, "useEffect", context.scopes);
46333
+ const isReactUseRefCall = (node) => isReactApiCall(node, "useRef", context.scopes, {
46334
+ allowGlobalReactNamespace: true,
46335
+ allowUnboundBareCalls: true
46336
+ });
46337
+ const isReactUseEffectCall = (node) => isReactApiCall(node, "useEffect", context.scopes, {
46338
+ allowGlobalReactNamespace: true,
46339
+ allowUnboundBareCalls: true
46340
+ });
46367
46341
  return { CallExpression(node) {
46368
- if (!isReactUseEffectCall(node)) return;
46342
+ if (!isUseEffect(node)) return;
46369
46343
  const analysis = getProgramAnalysis(node);
46370
46344
  if (!analysis) return;
46371
46345
  if (hasCleanup(analysis, node)) return;
@@ -46652,7 +46626,7 @@ const noPassLiveStateToParent = defineRule({
46652
46626
  tags: ["test-noise"],
46653
46627
  recommendation: "Move the state up to the parent (or return it from the hook), instead of handing it back up through a prop callback in a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#notifying-parent-components-about-state-changes",
46654
46628
  create: (context) => ({ CallExpression(node) {
46655
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
46629
+ if (!isUseEffect(node)) return;
46656
46630
  const analysis = getProgramAnalysis(node);
46657
46631
  if (!analysis) return;
46658
46632
  const effectFnRefs = getEffectFnRefs(analysis, node);
@@ -47067,7 +47041,7 @@ const hasPreviousValueDep = (effectNode, depElements) => {
47067
47041
  if (!isNodeOfType(element, "Identifier")) continue;
47068
47042
  const binding = findVariableInitializer(effectNode, element.name);
47069
47043
  if (!binding?.initializer || !isNodeOfType(binding.initializer, "CallExpression")) continue;
47070
- const calleeName = getCalleeName$1(binding.initializer);
47044
+ const calleeName = getCalleeName$2(binding.initializer);
47071
47045
  if (calleeName && PREVIOUS_VALUE_HOOK_PATTERN.test(calleeName)) return true;
47072
47046
  }
47073
47047
  return false;
@@ -47089,7 +47063,7 @@ const getRefHeldPropCallbackName = (callExpression, isPropName) => {
47089
47063
  if (!isNodeOfType(receiver, "Identifier")) return null;
47090
47064
  const binding = findVariableInitializer(callExpression, receiver.name);
47091
47065
  if (!binding?.initializer || !isNodeOfType(binding.initializer, "CallExpression")) return null;
47092
- if (getCalleeName$1(binding.initializer) !== "useRef") return null;
47066
+ if (getCalleeName$2(binding.initializer) !== "useRef") return null;
47093
47067
  const callbackArgument = binding.initializer.arguments?.[0];
47094
47068
  if (!callbackArgument || !isNodeOfType(callbackArgument, "Identifier")) return null;
47095
47069
  return isPropName(callbackArgument.name) ? callbackArgument.name : null;
@@ -47121,7 +47095,7 @@ const noPropCallbackInEffect = defineRule({
47121
47095
  return {
47122
47096
  ...propStackTracker.visitors,
47123
47097
  CallExpression(node) {
47124
- if (!isReactHookCall(node, EFFECT_HOOK_NAMES$1, context.scopes) || (node.arguments?.length ?? 0) < 2) return;
47098
+ if (!isHookCall$2(node, EFFECT_HOOK_NAMES$1) || (node.arguments?.length ?? 0) < 2) return;
47125
47099
  const callback = getEffectCallback(node);
47126
47100
  if (!callback || !isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return;
47127
47101
  const depsNode = node.arguments[1];
@@ -49014,7 +48988,7 @@ const noResetAllStateOnPropChange = defineRule({
49014
48988
  tags: ["test-noise"],
49015
48989
  recommendation: "Pass the prop as `key` so React resets the component for you when the prop changes, instead of clearing every state value by hand in a useEffect. See https://react.dev/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes",
49016
48990
  create: (context) => ({ CallExpression(node) {
49017
- if (!isReactHookCall(node, "useEffect", context.scopes)) return;
48991
+ if (!isUseEffect(node)) return;
49018
48992
  const analysis = getProgramAnalysis(node);
49019
48993
  if (!analysis) return;
49020
48994
  const effectFnRefs = getEffectFnRefs(analysis, node);
@@ -49283,7 +49257,7 @@ const isTanStackServerFnHandlerCall = (node) => {
49283
49257
  if (node.callee.property.name !== "handler") return false;
49284
49258
  let currentNode = node.callee.object;
49285
49259
  while (isNodeOfType(currentNode, "CallExpression")) {
49286
- const calleeName = getCalleeName$1(currentNode);
49260
+ const calleeName = getCalleeName$2(currentNode);
49287
49261
  if (calleeName && TANSTACK_SERVER_FN_NAMES.has(calleeName)) return true;
49288
49262
  if (!isNodeOfType(currentNode.callee, "MemberExpression")) return false;
49289
49263
  currentNode = currentNode.callee.object;
@@ -49784,7 +49758,7 @@ const noSelfUpdatingEffect = defineRule({
49784
49758
  create: (context) => {
49785
49759
  const checkFunctionScope = (functionBody) => {
49786
49760
  if (!functionBody || !isNodeOfType(functionBody, "BlockStatement")) return;
49787
- const useStateBindings = collectUseStateBindings(functionBody, context.scopes);
49761
+ const useStateBindings = collectUseStateBindings(functionBody);
49788
49762
  if (useStateBindings.length === 0) return;
49789
49763
  const setterNameToStateName = /* @__PURE__ */ new Map();
49790
49764
  for (const binding of useStateBindings) setterNameToStateName.set(binding.setterName, binding.valueName);
@@ -49793,7 +49767,7 @@ const noSelfUpdatingEffect = defineRule({
49793
49767
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
49794
49768
  const effectCall = unwrapDiscardedExpression(statement);
49795
49769
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
49796
- if (!isReactHookCall(effectCall, EFFECT_HOOK_NAMES$1, context.scopes)) continue;
49770
+ if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
49797
49771
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
49798
49772
  const dependencyStateNames = collectDependencyStateNames(effectCall.arguments[1]);
49799
49773
  if (dependencyStateNames.size === 0) continue;
@@ -49879,7 +49853,7 @@ const noSetStateInRender = defineRule({
49879
49853
  create: (context) => {
49880
49854
  const checkComponent = (componentBody) => {
49881
49855
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
49882
- const setterNames = new Set(collectUseStateBindings(componentBody, context.scopes).map((binding) => binding.setterName));
49856
+ const setterNames = new Set(collectUseStateBindings(componentBody).map((binding) => binding.setterName));
49883
49857
  if (setterNames.size === 0) return;
49884
49858
  for (const statement of componentBody.body ?? []) {
49885
49859
  const setterCall = isUnconditionalSetterCallStatement(statement, setterNames);
@@ -50128,10 +50102,10 @@ const collectTimerRefUsageFacts = (ownerScope, refName) => {
50128
50102
  });
50129
50103
  return facts;
50130
50104
  };
50131
- const isEffectCallbackFunction = (functionNode, scopes) => {
50105
+ const isEffectCallbackFunction = (functionNode) => {
50132
50106
  const parent = functionNode.parent;
50133
50107
  if (!parent || !isNodeOfType(parent, "CallExpression")) return false;
50134
- return isReactHookCall(parent, EFFECT_HOOK_NAMES$1, scopes) && getEffectCallback(parent) === functionNode;
50108
+ return isHookCall$2(parent, EFFECT_HOOK_NAMES$1) && getEffectCallback(parent) === functionNode;
50135
50109
  };
50136
50110
  const doesEffectCallbackReturnName = (effectCallback, name) => {
50137
50111
  if (!isFunctionLike$1(effectCallback)) return false;
@@ -50150,24 +50124,24 @@ const isFunctionReturnedFromEffectCallback = (functionNode, effectCallback) => {
50150
50124
  const cleanupBindingName = getFunctionBindingName$1(functionNode);
50151
50125
  return cleanupBindingName !== null && doesEffectCallbackReturnName(effectCallback, cleanupBindingName);
50152
50126
  };
50153
- const isReturnedFromAnyEffectInScope = (functionNode, ownerScope, scopes) => {
50127
+ const isReturnedFromAnyEffectInScope = (functionNode, ownerScope) => {
50154
50128
  const cleanupBindingName = getFunctionBindingName$1(functionNode);
50155
50129
  if (cleanupBindingName === null) return false;
50156
50130
  let isReturnedFromEffect = false;
50157
50131
  walkAst(ownerScope, (child) => {
50158
50132
  if (isReturnedFromEffect) return false;
50159
- if (!isNodeOfType(child, "CallExpression") || !isReactHookCall(child, EFFECT_HOOK_NAMES$1, scopes)) return;
50133
+ if (!isNodeOfType(child, "CallExpression") || !isHookCall$2(child, EFFECT_HOOK_NAMES$1)) return;
50160
50134
  const effectCallback = getEffectCallback(child);
50161
50135
  if (effectCallback && doesEffectCallbackReturnName(effectCallback, cleanupBindingName)) isReturnedFromEffect = true;
50162
50136
  });
50163
50137
  return isReturnedFromEffect;
50164
50138
  };
50165
- const isInsideEffectCleanupReturn = (node, ownerScope, scopes) => {
50139
+ const isInsideEffectCleanupReturn = (node, ownerScope) => {
50166
50140
  let functionNode = findEnclosingFunction$1(node);
50167
50141
  while (functionNode) {
50168
50142
  const outerFunction = findEnclosingFunction$1(functionNode);
50169
- if (outerFunction && isEffectCallbackFunction(outerFunction, scopes) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
50170
- if (isReturnedFromAnyEffectInScope(functionNode, ownerScope, scopes)) return true;
50143
+ if (outerFunction && isEffectCallbackFunction(outerFunction) && isFunctionReturnedFromEffectCallback(functionNode, outerFunction)) return true;
50144
+ if (isReturnedFromAnyEffectInScope(functionNode, ownerScope)) return true;
50171
50145
  functionNode = outerFunction;
50172
50146
  }
50173
50147
  return false;
@@ -50203,10 +50177,10 @@ const noStaleTimerRef = defineRule({
50203
50177
  if (isShadowedTimerGlobal(node)) return;
50204
50178
  const { clearCalleeName, refName } = clearCall;
50205
50179
  const refBinding = findVariableInitializer(node, refName);
50206
- if (!refBinding?.initializer || !isReactHookCall(refBinding.initializer, "useRef", context.scopes)) return;
50180
+ if (!refBinding?.initializer || !isHookCall$2(refBinding.initializer, "useRef")) return;
50207
50181
  const usageFacts = collectTimerRefUsageFacts(refBinding.scopeOwner, refName);
50208
50182
  if (!usageFacts.holdsScheduledTimerId || !usageFacts.hasPendingSignalRead) return;
50209
- if (isInsideEffectCleanupReturn(node, refBinding.scopeOwner, context.scopes)) return;
50183
+ if (isInsideEffectCleanupReturn(node, refBinding.scopeOwner)) return;
50210
50184
  if (hasRefCurrentReassignmentAfterClear(node, refName)) return;
50211
50185
  context.report({
50212
50186
  node,
@@ -56010,7 +55984,7 @@ const classifyCallableReadsInsideEffect = (callableIdentifier, effectCallback, c
56010
55984
  allReadsAreInSubHandlers = false;
56011
55985
  return;
56012
55986
  }
56013
- if (firstSubHandlerName === null) firstSubHandlerName = getCalleeName$1(subHandlerCall);
55987
+ if (firstSubHandlerName === null) firstSubHandlerName = getCalleeName$2(subHandlerCall);
56014
55988
  });
56015
55989
  return {
56016
55990
  hasAnyRead,
@@ -56033,7 +56007,7 @@ const preferUseEffectEvent = defineRule({
56033
56007
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
56034
56008
  const effectCall = statement.expression;
56035
56009
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
56036
- if (!isReactHookCall(effectCall, EFFECT_HOOK_NAMES$1, context.scopes)) continue;
56010
+ if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
56037
56011
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
56038
56012
  const depsNode = effectCall.arguments[1];
56039
56013
  if (!isNodeOfType(depsNode, "ArrayExpression")) continue;
@@ -56065,11 +56039,11 @@ const preferUseEffectEvent = defineRule({
56065
56039
  });
56066
56040
  //#endregion
56067
56041
  //#region src/plugin/rules/state-and-effects/prefer-use-sync-external-store.ts
56068
- const findUseEffectsInComponent = (componentBody, scopes) => {
56042
+ const findUseEffectsInComponent = (componentBody) => {
56069
56043
  const effectCalls = [];
56070
56044
  if (!isNodeOfType(componentBody, "BlockStatement")) return effectCalls;
56071
56045
  for (const statement of componentBody.body ?? []) walkAst(statement, (child) => {
56072
- if (isNodeOfType(child, "CallExpression") && isReactHookCall(child, EFFECT_HOOK_NAMES$1, scopes)) effectCalls.push(child);
56046
+ if (isNodeOfType(child, "CallExpression") && isHookCall$2(child, EFFECT_HOOK_NAMES$1)) effectCalls.push(child);
56073
56047
  });
56074
56048
  return effectCalls;
56075
56049
  };
@@ -56272,7 +56246,7 @@ const preferUseSyncExternalStore = defineRule({
56272
56246
  };
56273
56247
  const checkComponent = (componentBody) => {
56274
56248
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
56275
- const useStateBindings = collectUseStateBindings(componentBody, context.scopes);
56249
+ const useStateBindings = collectUseStateBindings(componentBody);
56276
56250
  if (useStateBindings.length === 0) return;
56277
56251
  const useStateInitializerByValueName = /* @__PURE__ */ new Map();
56278
56252
  for (const binding of useStateBindings) {
@@ -56285,7 +56259,7 @@ const preferUseSyncExternalStore = defineRule({
56285
56259
  }
56286
56260
  const setterNameToValueName = /* @__PURE__ */ new Map();
56287
56261
  for (const binding of useStateBindings) setterNameToValueName.set(binding.setterName, binding.valueName);
56288
- for (const effectCall of findUseEffectsInComponent(componentBody, context.scopes)) {
56262
+ for (const effectCall of findUseEffectsInComponent(componentBody)) {
56289
56263
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
56290
56264
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
56291
56265
  const depsNode = effectCall.arguments[1];
@@ -56327,7 +56301,7 @@ const preferUseSyncExternalStore = defineRule({
56327
56301
  })).filter((candidate) => candidate.storeName !== null);
56328
56302
  if (snapshotBindings.length === 0) return;
56329
56303
  const reportedDeclarators = /* @__PURE__ */ new Set();
56330
- for (const effectCall of findUseEffectsInComponent(componentBody, context.scopes)) {
56304
+ for (const effectCall of findUseEffectsInComponent(componentBody)) {
56331
56305
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
56332
56306
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
56333
56307
  const depsNode = effectCall.arguments[1];
@@ -56424,7 +56398,7 @@ const preferUseReducer = defineRule({
56424
56398
  create: (context) => {
56425
56399
  const reportCoUpdatedUseState = (body, componentName) => {
56426
56400
  if (!isNodeOfType(body, "BlockStatement")) return;
56427
- const bindings = collectUseStateBindings(body, context.scopes);
56401
+ const bindings = collectUseStateBindings(body);
56428
56402
  const setterNames = new Set(bindings.map((binding) => binding.setterName));
56429
56403
  if (setterNames.size < 5) return;
56430
56404
  const coUpdatedCount = findLargestCoUpdatedSetterGroup(body, setterNames, new Map(bindings.map((binding) => {
@@ -56640,7 +56614,7 @@ const QUERY_READ_METHOD_NAMES = new Set([
56640
56614
  ]);
56641
56615
  const isQueryCacheSourceCall = (initializer) => {
56642
56616
  if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
56643
- const hookName = getCalleeName$1(initializer);
56617
+ const hookName = getCalleeName$2(initializer);
56644
56618
  if (!hookName) return false;
56645
56619
  return hookName === "useQueryClient" || TRPC_UTILS_HOOK_PATTERN.test(hookName);
56646
56620
  };
@@ -56772,7 +56746,7 @@ const queryMutationMissingInvalidation = defineRule({
56772
56746
  },
56773
56747
  CallExpression(node) {
56774
56748
  if (!hasQueryReadUsage) {
56775
- const callName = getCalleeName$1(node);
56749
+ const callName = getCalleeName$2(node);
56776
56750
  if (callName && (QUERY_READ_HOOK_NAMES.has(callName) || QUERY_READ_METHOD_NAMES.has(callName) || TRPC_UTILS_HOOK_PATTERN.test(callName))) hasQueryReadUsage = true;
56777
56751
  }
56778
56752
  const calleeName = isNodeOfType(node.callee, "Identifier") ? node.callee.name : null;
@@ -57265,30 +57239,26 @@ const REMOVAL_MESSAGE_BY_REACT_API_NAME = new Map([
57265
57239
  ["useCallback", "This `useCallback` is dead weight, since React Compiler already caches every function here. Delete it."],
57266
57240
  ["memo", "This `memo()` is dead weight, since React Compiler already caches the component's output. Delete it."]
57267
57241
  ]);
57268
- const resolveReactApiNameForIdentifier = (callee, context) => {
57242
+ const resolveReactApiNameForIdentifier = (callee) => {
57269
57243
  if (!isNodeOfType(callee, "Identifier")) return null;
57270
- if (context.scopes.symbolFor(callee)?.kind !== "import") return null;
57271
57244
  const importedName = getImportedNameFromModule(callee, callee.name, "react");
57272
57245
  if (importedName && REMOVAL_MESSAGE_BY_REACT_API_NAME.has(importedName)) return importedName;
57273
57246
  return null;
57274
57247
  };
57275
- const resolveReactApiNameForMemberExpression = (callee, context) => {
57248
+ const resolveReactApiNameForMemberExpression = (callee) => {
57276
57249
  if (!isNodeOfType(callee, "MemberExpression")) return null;
57277
57250
  if (callee.computed) return null;
57278
- const namespaceIdentifier = stripParenExpression(callee.object);
57251
+ const namespaceIdentifier = callee.object;
57279
57252
  const propertyIdentifier = callee.property;
57280
57253
  if (!isNodeOfType(namespaceIdentifier, "Identifier")) return null;
57281
57254
  if (!isNodeOfType(propertyIdentifier, "Identifier")) return null;
57282
57255
  if (!REMOVAL_MESSAGE_BY_REACT_API_NAME.has(propertyIdentifier.name)) return null;
57283
57256
  const namespaceName = namespaceIdentifier.name;
57284
- if (context.scopes.symbolFor(namespaceIdentifier)?.kind === "import" && isImportedFromModule(namespaceIdentifier, namespaceName, "react")) return propertyIdentifier.name;
57285
- if (isCanonicalReactNamespaceName(namespaceName) && context.scopes.isGlobalReference(namespaceIdentifier)) return propertyIdentifier.name;
57257
+ if (isCanonicalReactNamespaceName(namespaceName)) return propertyIdentifier.name;
57258
+ if (isImportedFromModule(namespaceIdentifier, namespaceName, "react")) return propertyIdentifier.name;
57286
57259
  return null;
57287
57260
  };
57288
- const resolveReactApiNameForCallee = (callee, context) => {
57289
- const unwrappedCallee = stripParenExpression(callee);
57290
- return resolveReactApiNameForIdentifier(unwrappedCallee, context) ?? resolveReactApiNameForMemberExpression(unwrappedCallee, context);
57291
- };
57261
+ const resolveReactApiNameForCallee = (callee) => resolveReactApiNameForIdentifier(callee) ?? resolveReactApiNameForMemberExpression(callee);
57292
57262
  const isNullishComparatorArgument = (argumentNode) => isNodeOfType(argumentNode, "Identifier") && argumentNode.name === "undefined" || isNodeOfType(argumentNode, "Literal") && argumentNode.value === null;
57293
57263
  const COMPILER_INFERABLE_HOC_NAMES = new Set(["memo", "forwardRef"]);
57294
57264
  const calleeTrailingName = (callee) => {
@@ -57314,7 +57284,7 @@ const reactCompilerNoManualMemoization = defineRule({
57314
57284
  requires: ["react-compiler"],
57315
57285
  recommendation: "Delete the `useMemo` / `useCallback` / `memo` call and use the plain value or component. React Compiler caches it for you.",
57316
57286
  create: (context) => ({ CallExpression(node) {
57317
- const apiName = resolveReactApiNameForCallee(node.callee, context);
57287
+ const apiName = resolveReactApiNameForCallee(node.callee);
57318
57288
  if (!apiName) return;
57319
57289
  if (apiName === "memo") {
57320
57290
  const comparatorArgument = node.arguments?.[1];
@@ -59064,7 +59034,7 @@ const rerenderDependencies = defineRule({
59064
59034
  severity: "error",
59065
59035
  recommendation: "Move it into a useMemo, useRef, or a constant outside the component so it stays the same between renders.",
59066
59036
  create: (context) => ({ CallExpression(node) {
59067
- if (!isReactHookCall(node, HOOKS_WITH_DEPS, context.scopes) || node.arguments.length < 2) return;
59037
+ if (!isHookCall$2(node, HOOKS_WITH_DEPS) || node.arguments.length < 2) return;
59068
59038
  const depsNode = node.arguments[1];
59069
59039
  if (!isNodeOfType(depsNode, "ArrayExpression")) return;
59070
59040
  for (const element of depsNode.elements ?? []) {
@@ -59319,7 +59289,7 @@ const rerenderLazyRefInit = defineRule({
59319
59289
  category: "Performance",
59320
59290
  recommendation: "Initialize the ref lazily so expensive values are not rebuilt and discarded on every render.",
59321
59291
  create: (context) => ({ CallExpression(node) {
59322
- if (!isReactHookCall(node, "useRef", context.scopes) || !node.arguments?.length) return;
59292
+ if (!isHookCall$2(node, "useRef") || !node.arguments?.length) return;
59323
59293
  const initializer = stripParenExpression(node.arguments[0]);
59324
59294
  const isPlainCall = isNodeOfType(initializer, "CallExpression");
59325
59295
  const isNewCall = isNodeOfType(initializer, "NewExpression");
@@ -59383,7 +59353,7 @@ const rerenderLazyStateInit = defineRule({
59383
59353
  category: "Performance",
59384
59354
  recommendation: "Wrap expensive initial state in an arrow function so the initializer does not rerun and get thrown away on every render.",
59385
59355
  create: (context) => ({ CallExpression(node) {
59386
- if (!isReactHookCall(node, "useState", context.scopes) || !node.arguments?.length) return;
59356
+ if (!isHookCall$2(node, "useState") || !node.arguments?.length) return;
59387
59357
  const initializer = findEagerInitializerCall(node.arguments[0]);
59388
59358
  if (!initializer) return;
59389
59359
  const isConstructor = isNodeOfType(initializer, "NewExpression");
@@ -60146,11 +60116,11 @@ const isInsideConditionTest = (identifier, stopAt) => {
60146
60116
  }
60147
60117
  return false;
60148
60118
  };
60149
- const collectEffectDependencyInfos = (componentBody, setterNames, scopes) => {
60119
+ const collectEffectDependencyInfos = (componentBody, setterNames) => {
60150
60120
  const effectInfos = [];
60151
60121
  walkAst(componentBody, (child) => {
60152
60122
  if (!isNodeOfType(child, "CallExpression")) return;
60153
- if (!isReactHookCall(child, EFFECT_HOOK_NAMES$1, scopes)) return;
60123
+ if (!isHookCall$2(child, EFFECT_HOOK_NAMES$1)) return;
60154
60124
  const dependencyNames = /* @__PURE__ */ new Set();
60155
60125
  for (const argument of child.arguments ?? []) {
60156
60126
  if (!isNodeOfType(argument, "ArrayExpression")) continue;
@@ -60206,14 +60176,13 @@ const collectEffectDependencyInfos = (componentBody, setterNames, scopes) => {
60206
60176
  });
60207
60177
  return effectInfos;
60208
60178
  };
60209
- const collectCustomHookArgumentNames = (componentBody, scopes) => {
60179
+ const collectCustomHookArgumentNames = (componentBody) => {
60210
60180
  const argumentNames = /* @__PURE__ */ new Set();
60211
60181
  walkAst(componentBody, (child) => {
60212
60182
  if (!isNodeOfType(child, "CallExpression")) return;
60213
60183
  if (!isNodeOfType(child.callee, "Identifier")) return;
60214
60184
  const calleeName = child.callee.name;
60215
60185
  if (!isReactHookName(calleeName)) return;
60216
- if (isReactHookCall(child, BUILTIN_HOOK_NAMES, scopes)) return;
60217
60186
  if (BUILTIN_HOOK_NAMES.has(calleeName)) return;
60218
60187
  if (EFFECT_HOOK_NAMES$1.has(calleeName)) return;
60219
60188
  for (const argument of child.arguments ?? []) walkAst(argument, (argumentNode) => {
@@ -60254,21 +60223,21 @@ const rerenderStateOnlyInHandlers = defineRule({
60254
60223
  create: (context) => {
60255
60224
  const checkComponent = (componentBody) => {
60256
60225
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
60257
- const bindings = collectUseStateBindings(componentBody, context.scopes);
60226
+ const bindings = collectUseStateBindings(componentBody);
60258
60227
  if (bindings.length === 0) return;
60259
60228
  if (collectRenderReachableExpressions(componentBody).length === 0) return;
60260
- const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody, context.scopes);
60229
+ const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody);
60261
60230
  const dependencyGraph = buildLocalDependencyGraph(componentBody, eventHandlerReferenceNames);
60262
- const directRenderNames = collectRenderReachableNames(componentBody, context.scopes, eventHandlerReferenceNames);
60231
+ const directRenderNames = collectRenderReachableNames(componentBody, eventHandlerReferenceNames);
60263
60232
  if (hasRenderPhaseNonHookCall(componentBody)) for (const voidMarkedName of collectTopLevelVoidMarkedNames(componentBody)) directRenderNames.add(voidMarkedName);
60264
60233
  const renderReachableNames = expandTransitiveDependencies(directRenderNames, dependencyGraph);
60265
60234
  const setterNames = new Set(bindings.map((binding) => binding.setterName));
60266
- const effectInfos = collectEffectDependencyInfos(componentBody, setterNames, context.scopes);
60235
+ const effectInfos = collectEffectDependencyInfos(componentBody, setterNames);
60267
60236
  const selfEchoValueNames = /* @__PURE__ */ new Set();
60268
60237
  for (const binding of bindings) if (effectInfos.some((effectInfo) => effectInfo.dependencyNames.has(binding.valueName) && effectInfo.synchronouslyCalledFunctionNames.has(binding.setterName) && !effectInfo.payloadReadNames.has(binding.valueName) && !effectInfo.nestedCallbackCalledFunctionNames.has(binding.setterName))) selfEchoValueNames.add(binding.valueName);
60269
60238
  const effectConsumedNames = /* @__PURE__ */ new Set();
60270
60239
  for (const effectInfo of effectInfos) for (const dependencyName of effectInfo.dependencyNames) if (!selfEchoValueNames.has(dependencyName)) effectConsumedNames.add(dependencyName);
60271
- for (const hookArgumentName of collectCustomHookArgumentNames(componentBody, context.scopes)) effectConsumedNames.add(hookArgumentName);
60240
+ for (const hookArgumentName of collectCustomHookArgumentNames(componentBody)) effectConsumedNames.add(hookArgumentName);
60272
60241
  for (const reachableName of expandTransitiveDependencies(effectConsumedNames, dependencyGraph)) renderReachableNames.add(reachableName);
60273
60242
  const calledSetterNames = /* @__PURE__ */ new Set();
60274
60243
  walkAst(componentBody, (child) => {
@@ -68169,7 +68138,7 @@ const declarationAwaitsGate = (declaration, context) => {
68169
68138
  if (!isNodeOfType(argument, "CallExpression")) continue;
68170
68139
  if (hasPossibleStaticMemberCallWrite(argument, context.scopes)) return true;
68171
68140
  if (getOrderIndependentLocalFunction(argument, context.scopes) !== null) continue;
68172
- const calleeName = getCalleeName$1(argument);
68141
+ const calleeName = getCalleeName$2(argument);
68173
68142
  if (!calleeName) continue;
68174
68143
  if (isAuthGuardName(calleeName)) return true;
68175
68144
  const [leadingToken] = tokenizeIdentifierWords(calleeName);
@@ -68809,7 +68778,7 @@ const walkServerFnChain = (outerNode) => {
68809
68778
  if (!isNodeOfType(outerNode.callee, "MemberExpression")) return result;
68810
68779
  let currentNode = stripParenExpression(outerNode.callee.object);
68811
68780
  while (isNodeOfType(currentNode, "CallExpression")) {
68812
- const calleeName = getCalleeName$1(currentNode);
68781
+ const calleeName = getCalleeName$2(currentNode);
68813
68782
  if (calleeName && TANSTACK_SERVER_FN_NAMES.has(calleeName)) {
68814
68783
  result.isServerFnChain = true;
68815
68784
  const optionsArgument = currentNode.arguments?.[0];