oxlint-plugin-react-doctor 0.8.1-dev.a2f9bf4 → 0.8.1-dev.a667b45

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 +1892 -217
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -762,7 +762,7 @@ const EXTERNAL_SYNC_MEMBER_METHOD_NAMES = new Set([
762
762
  "put",
763
763
  "patch"
764
764
  ]);
765
- const EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS = new Set([
765
+ new Set([
766
766
  ...FETCH_MEMBER_OBJECTS,
767
767
  "api",
768
768
  "client",
@@ -9008,6 +9008,16 @@ const hasVisibleBindingNamed = (node, bindingName, scopes) => {
9008
9008
  }
9009
9009
  };
9010
9010
  //#endregion
9011
+ //#region src/plugin/utils/is-global-match-media-call.ts
9012
+ const isGlobalMatchMediaCall = (node, scopes) => {
9013
+ if (!isNodeOfType(node, "CallExpression")) return false;
9014
+ const callee = stripParenExpression(node.callee);
9015
+ if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
9016
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
9017
+ const receiver = stripParenExpression(callee.object);
9018
+ return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
9019
+ };
9020
+ //#endregion
9011
9021
  //#region src/plugin/utils/is-proven-browser-api-receiver.ts
9012
9022
  const DOM_EVENT_TARGET_TYPE_NAMES = new Set([
9013
9023
  "AbortSignal",
@@ -9154,8 +9164,10 @@ const getProvenDomEventTargetPrototypeOwnerNames = (rawExpression, scopes, visit
9154
9164
  if (memberName === "body" || memberName === "documentElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9155
9165
  }
9156
9166
  if (isNodeOfType(expression, "CallExpression")) {
9167
+ if (isGlobalMatchMediaCall(expression, scopes)) return getDomPrototypeOwnerNamesForType("MediaQueryList");
9157
9168
  const callee = stripParenExpression(expression.callee);
9158
9169
  const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null;
9170
+ if (methodName === "matchMedia" && isNodeOfType(callee, "MemberExpression") && getProvenDomEventTargetPrototypeOwnerNames(callee.object, scopes, visitedSymbolIds).includes("Window")) return getDomPrototypeOwnerNamesForType("MediaQueryList");
9159
9171
  if (methodName === "createElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9160
9172
  if (methodName === "createElementNS") return getDomPrototypeOwnerNamesForType("SVGElement");
9161
9173
  if (methodName && DOM_EVENT_TARGET_FACTORY_METHOD_NAMES.has(methodName)) return getDomPrototypeOwnerNamesForType("Element");
@@ -13815,6 +13827,116 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
13815
13827
  return null;
13816
13828
  };
13817
13829
  //#endregion
13830
+ //#region src/plugin/utils/get-final-sequence-expression-value.ts
13831
+ const getFinalSequenceExpressionValue = (expression) => {
13832
+ let finalExpression = stripParenExpression(expression);
13833
+ while (isNodeOfType(finalExpression, "SequenceExpression")) {
13834
+ const sequenceResult = finalExpression.expressions.at(-1);
13835
+ if (!sequenceResult) break;
13836
+ finalExpression = stripParenExpression(sequenceResult);
13837
+ }
13838
+ return finalExpression;
13839
+ };
13840
+ //#endregion
13841
+ //#region src/plugin/utils/collect-expression-path-coverage-nodes.ts
13842
+ const collectExpressionPathCoverageNodes = (owner, matchingNodes, context, expressionBoundary = owner) => {
13843
+ const coverageNodes = /* @__PURE__ */ new Set();
13844
+ const pendingNodes = matchingNodes.filter((matchingNode) => {
13845
+ if (context.cfg.enclosingFunction(matchingNode) !== owner) return false;
13846
+ let currentNode = matchingNode;
13847
+ while (currentNode && currentNode !== expressionBoundary) currentNode = currentNode.parent ?? null;
13848
+ return currentNode === expressionBoundary;
13849
+ });
13850
+ const visitedNodes = new Set(pendingNodes);
13851
+ const coveredBranchesByConditional = /* @__PURE__ */ new Map();
13852
+ while (pendingNodes.length > 0) {
13853
+ const coverageCandidate = pendingNodes.pop();
13854
+ if (!coverageCandidate) break;
13855
+ if (coverageCandidate === expressionBoundary) {
13856
+ coverageNodes.add(coverageCandidate);
13857
+ continue;
13858
+ }
13859
+ let currentChild = coverageCandidate;
13860
+ let currentParent = currentChild.parent ?? null;
13861
+ let conditionalExpression = null;
13862
+ let conditionalBranch = null;
13863
+ let isBlockedByNonExhaustiveExpression = false;
13864
+ while (currentParent && currentParent !== expressionBoundary) {
13865
+ if (isNodeOfType(currentParent, "ConditionalExpression")) {
13866
+ const isConsequent = currentParent.consequent === currentChild;
13867
+ const isAlternate = currentParent.alternate === currentChild;
13868
+ if (isConsequent || isAlternate) {
13869
+ const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.test));
13870
+ if (staticTestValue !== null) {
13871
+ if (staticTestValue !== isConsequent) {
13872
+ isBlockedByNonExhaustiveExpression = true;
13873
+ break;
13874
+ }
13875
+ currentChild = currentParent;
13876
+ currentParent = currentChild.parent ?? null;
13877
+ continue;
13878
+ }
13879
+ conditionalExpression = currentParent;
13880
+ conditionalBranch = isConsequent ? "consequent" : "alternate";
13881
+ break;
13882
+ }
13883
+ }
13884
+ if (isNodeOfType(currentParent, "LogicalExpression") && currentParent.right === currentChild) {
13885
+ const staticLeftValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.left));
13886
+ if (!(currentParent.operator === "&&" && staticLeftValue === true || currentParent.operator === "||" && staticLeftValue === false)) {
13887
+ isBlockedByNonExhaustiveExpression = true;
13888
+ break;
13889
+ }
13890
+ }
13891
+ if (isNodeOfType(currentParent, "AssignmentPattern") && currentParent.right === currentChild) {
13892
+ isBlockedByNonExhaustiveExpression = true;
13893
+ break;
13894
+ }
13895
+ currentChild = currentParent;
13896
+ currentParent = currentChild.parent ?? null;
13897
+ }
13898
+ if (isBlockedByNonExhaustiveExpression) continue;
13899
+ if (!conditionalExpression || !conditionalBranch) {
13900
+ coverageNodes.add(coverageCandidate);
13901
+ continue;
13902
+ }
13903
+ const coveredBranches = coveredBranchesByConditional.get(conditionalExpression) ?? /* @__PURE__ */ new Set();
13904
+ coveredBranches.add(conditionalBranch);
13905
+ coveredBranchesByConditional.set(conditionalExpression, coveredBranches);
13906
+ if (coveredBranches.has("alternate") && coveredBranches.has("consequent") && !visitedNodes.has(conditionalExpression)) {
13907
+ visitedNodes.add(conditionalExpression);
13908
+ pendingNodes.push(conditionalExpression);
13909
+ }
13910
+ }
13911
+ return coverageNodes;
13912
+ };
13913
+ //#endregion
13914
+ //#region src/plugin/utils/do-nodes-cover-every-path-from-function-entry.ts
13915
+ const doNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context, options = {}) => {
13916
+ const functionCfg = context.cfg.cfgFor(owner);
13917
+ if (!functionCfg) return false;
13918
+ const matchingBlocks = new Set([...collectExpressionPathCoverageNodes(owner, matchingNodes, context)].flatMap((matchingNode) => {
13919
+ const matchingBlock = functionCfg.blockOf(matchingNode);
13920
+ return matchingBlock ? [matchingBlock] : [];
13921
+ }));
13922
+ if (matchingBlocks.size === 0) return false;
13923
+ const visitedBlocks = new Set([functionCfg.entry]);
13924
+ const pendingBlocks = [functionCfg.entry];
13925
+ while (pendingBlocks.length > 0) {
13926
+ const currentBlock = pendingBlocks.pop();
13927
+ if (!currentBlock) break;
13928
+ if (matchingBlocks.has(currentBlock)) continue;
13929
+ for (const edge of currentBlock.successors) {
13930
+ if (options.ignoreThrowEdges && edge.kind === "throw") continue;
13931
+ if (edge.to === functionCfg.exit) return false;
13932
+ if (visitedBlocks.has(edge.to)) continue;
13933
+ visitedBlocks.add(edge.to);
13934
+ pendingBlocks.push(edge.to);
13935
+ }
13936
+ }
13937
+ return true;
13938
+ };
13939
+ //#endregion
13818
13940
  //#region src/plugin/utils/get-function-binding-name.ts
13819
13941
  const getFunctionBindingIdentifier$1 = (functionNode) => {
13820
13942
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
@@ -13852,14 +13974,19 @@ const isAstDescendant = (inner, outer) => {
13852
13974
  };
13853
13975
  //#endregion
13854
13976
  //#region src/plugin/utils/react-ref-origin.ts
13855
- const resolveReactRefSymbol = (memberExpression, scopes) => {
13977
+ const REACT_REF_API_NAMES = new Set(["createRef", "useRef"]);
13978
+ const resolveReactRefSymbol = (memberExpression, scopes, options = {}) => {
13856
13979
  const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
13857
13980
  if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
13858
13981
  const symbol = resolveConstIdentifierAlias(receiver, scopes);
13859
13982
  if (!symbol?.initializer) return null;
13860
13983
  const initializer = stripParenExpression(symbol.initializer);
13861
13984
  if (!isNodeOfType(initializer, "CallExpression")) return null;
13862
- return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
13985
+ return isReactApiCall(initializer, options.includeCreateRef ? REACT_REF_API_NAMES : "useRef", scopes, {
13986
+ allowGlobalReactNamespace: true,
13987
+ allowUnboundBareCalls: options.allowUnboundBareCalls,
13988
+ resolveNamedAliases: options.resolveNamedAliases
13989
+ }) ? symbol : null;
13863
13990
  };
13864
13991
  const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13865
13992
  const expression = stripParenExpression(node);
@@ -13958,6 +14085,20 @@ const isNodeReachableWithinFunction = (node, context) => {
13958
14085
  return false;
13959
14086
  };
13960
14087
  //#endregion
14088
+ //#region src/plugin/utils/is-within-assignment-target.ts
14089
+ const isWithinAssignmentTarget = (identifier) => {
14090
+ let currentNode = identifier;
14091
+ let parentNode = currentNode.parent;
14092
+ while (parentNode) {
14093
+ if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
14094
+ if (isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete") return parentNode.argument === currentNode;
14095
+ if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
14096
+ currentNode = parentNode;
14097
+ parentNode = currentNode.parent;
14098
+ }
14099
+ return false;
14100
+ };
14101
+ //#endregion
13961
14102
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
13962
14103
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
13963
14104
  const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
@@ -14110,6 +14251,30 @@ const findAssignedResourceKey = (resourceNode, context) => {
14110
14251
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
14111
14252
  return null;
14112
14253
  };
14254
+ const resolveStableMediaQueryListenerIdentityKey = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
14255
+ if (!expression) return null;
14256
+ const unwrappedExpression = stripParenExpression(expression);
14257
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
14258
+ const symbol = context.scopes.symbolFor(unwrappedExpression);
14259
+ if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier))) return null;
14260
+ const initializer = getDirectUnreassignedInitializer(symbol);
14261
+ if (!initializer) return `symbol:${symbol.id}`;
14262
+ const unwrappedInitializer = stripParenExpression(initializer);
14263
+ if (!isNodeOfType(unwrappedInitializer, "Identifier")) return `symbol:${symbol.id}`;
14264
+ const nextVisitedSymbolIds = new Set(visitedSymbolIds);
14265
+ nextVisitedSymbolIds.add(symbol.id);
14266
+ return resolveStableMediaQueryListenerIdentityKey(unwrappedInitializer, context, nextVisitedSymbolIds) ?? `symbol:${symbol.id}`;
14267
+ }
14268
+ if (isFunctionLike$1(unwrappedExpression)) {
14269
+ const rangeStart = getRangeStart(unwrappedExpression);
14270
+ return rangeStart === null ? null : `function:${rangeStart}`;
14271
+ }
14272
+ return null;
14273
+ };
14274
+ const isProvenLegacyMediaQueryListMethodCall = (callNode, methodName, context) => {
14275
+ const callee = stripParenExpression(callNode.callee);
14276
+ return callNode.arguments?.length === 1 && isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName && getProvenDomEventTargetPrototypeOwnerNames(callee.object, context.scopes).includes("MediaQueryList");
14277
+ };
14113
14278
  const getCallRegistrationDetails = (callNode, context) => {
14114
14279
  const callee = stripParenExpression(callNode.callee);
14115
14280
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return {
@@ -14118,6 +14283,12 @@ const getCallRegistrationDetails = (callNode, context) => {
14118
14283
  eventKey: null,
14119
14284
  handlerKey: null
14120
14285
  };
14286
+ if (isProvenLegacyMediaQueryListMethodCall(callNode, "addListener", context)) return {
14287
+ receiverKey: resolveStableMediaQueryListenerIdentityKey(callee.object, context),
14288
+ registrationVerbName: callee.property.name,
14289
+ eventKey: null,
14290
+ handlerKey: resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context)
14291
+ };
14121
14292
  return {
14122
14293
  receiverKey: resolveResourceIdentityKey(callee.object, context),
14123
14294
  registrationVerbName: callee.property.name,
@@ -14222,30 +14393,6 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
14222
14393
  }
14223
14394
  return matchingBlocks.size > 0;
14224
14395
  };
14225
- const doMatchingNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context) => {
14226
- const functionCfg = context.cfg.cfgFor(owner);
14227
- if (!functionCfg) return false;
14228
- const matchingBlocks = new Set(matchingNodes.flatMap((matchingNode) => {
14229
- if (context.cfg.enclosingFunction(matchingNode) !== owner) return [];
14230
- const matchingBlock = functionCfg.blockOf(matchingNode);
14231
- return matchingBlock ? [matchingBlock] : [];
14232
- }));
14233
- if (matchingBlocks.size === 0) return false;
14234
- const visitedBlocks = new Set([functionCfg.entry]);
14235
- const pendingBlocks = [functionCfg.entry];
14236
- while (pendingBlocks.length > 0) {
14237
- const currentBlock = pendingBlocks.pop();
14238
- if (!currentBlock) break;
14239
- if (matchingBlocks.has(currentBlock)) continue;
14240
- for (const edge of currentBlock.successors) {
14241
- if (edge.to === functionCfg.exit) return false;
14242
- if (visitedBlocks.has(edge.to)) continue;
14243
- visitedBlocks.add(edge.to);
14244
- pendingBlocks.push(edge.to);
14245
- }
14246
- }
14247
- return true;
14248
- };
14249
14396
  const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
14250
14397
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
14251
14398
  if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
@@ -14488,18 +14635,6 @@ const findPushedResourceCollectionKey = (usage, context) => {
14488
14635
  return memberNode.property.name === "forEach" || memberNode.property.name === "push";
14489
14636
  }) ? resolveExpressionKey(pushCallee.object, context) : null;
14490
14637
  };
14491
- const isWithinAssignmentTarget = (identifier) => {
14492
- let currentNode = identifier;
14493
- let parentNode = currentNode.parent;
14494
- while (parentNode) {
14495
- if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
14496
- if (isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete") return parentNode.argument === currentNode;
14497
- if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
14498
- currentNode = parentNode;
14499
- parentNode = currentNode.parent;
14500
- }
14501
- return false;
14502
- };
14503
14638
  const resolveStableValue = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
14504
14639
  if (!expression) return null;
14505
14640
  const unwrappedExpression = stripParenExpression(expression);
@@ -14623,7 +14758,7 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
14623
14758
  const helperFunction = isNodeOfType(stableHelperFunction, "Identifier") ? resolveSingleAssignedCleanupFunction(stableHelperFunction, usage, context) : stableHelperFunction;
14624
14759
  if (helperFunction && isFunctionLike$1(helperFunction) && !helperFunction.async && !helperFunction.generator && doesCleanupFunctionReleaseUsage(helperFunction, usage, context, new Set(visitedFunctions))) matchingLoopOrHelperAnchors.push(cleanupCall);
14625
14760
  });
14626
- return didCleanupFunctionMatch || doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
14761
+ return didCleanupFunctionMatch || doNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
14627
14762
  };
14628
14763
  const doesBoundCleanupReleaseUsage = (expression, usage, context) => {
14629
14764
  const callExpression = stripParenExpression(expression);
@@ -14652,7 +14787,7 @@ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
14652
14787
  walkInsideStatementBlocks(callback.body, (child) => {
14653
14788
  if (isNodeOfType(child, "ReturnStatement") && child.argument && doesReturnedValueReleaseUsage(stripParenExpression(child.argument))) matchingCleanupReturns.push(child);
14654
14789
  });
14655
- return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
14790
+ return doNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
14656
14791
  };
14657
14792
  const doesTestRequireLiveExpressionKey = (test, expressionKey, context) => {
14658
14793
  if (resolveExpressionKey(test, context) === expressionKey) return true;
@@ -14695,7 +14830,7 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
14695
14830
  const helperFunction = resolveStableValue(child.callee, context);
14696
14831
  if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
14697
14832
  });
14698
- return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
14833
+ return doNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
14699
14834
  };
14700
14835
  const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
14701
14836
  const componentFunction = findEnclosingFunction$1(callback);
@@ -14946,7 +15081,7 @@ const hasGuardedRefOwnedNestedCleanup = (callback, usage, cleanupReturns, contex
14946
15081
  const usageFunction = findEnclosingFunction$1(usage.node);
14947
15082
  const usageExpression = findTransparentExpressionRoot(usage.node);
14948
15083
  const usageAssignment = usageExpression.parent;
14949
- if (usage.kind !== "subscribe" && usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usageAssignment, "AssignmentExpression") || usageAssignment.operator !== "=" || usageAssignment.right !== usageExpression || !resolveReactRefSymbol(stripParenExpression(usageAssignment.left), context.scopes) || !collectSynchronouslyEffectInvokedFunctions(callback).has(usageFunction) || !cleanupReturnsReleaseUsage(cleanupReturns, usage, context) || !doMatchingNodesCoverEveryPathFromFunctionEntry(callback, cleanupReturns, context)) return false;
15084
+ if (usage.kind !== "subscribe" && usage.kind !== "timer" || usage.handleKey === null || !usageFunction || !isFunctionLike$1(usageFunction) || usageFunction === callback || usageFunction.async || usageFunction.generator || !isNodeOfType(usageAssignment, "AssignmentExpression") || usageAssignment.operator !== "=" || usageAssignment.right !== usageExpression || !resolveReactRefSymbol(stripParenExpression(usageAssignment.left), context.scopes) || !collectSynchronouslyEffectInvokedFunctions(callback).has(usageFunction) || !cleanupReturnsReleaseUsage(cleanupReturns, usage, context) || !doNodesCoverEveryPathFromFunctionEntry(callback, cleanupReturns, context)) return false;
14950
15085
  const cleanupFunctions = cleanupReturns.flatMap((cleanupReturn) => {
14951
15086
  if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return [];
14952
15087
  const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
@@ -14994,7 +15129,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
14994
15129
  });
14995
15130
  }
14996
15131
  });
14997
- if (!doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, globalReleaseProofs.map((releaseProof) => releaseProof.anchor), context)) return false;
15132
+ if (!doNodesCoverEveryPathFromFunctionEntry(cleanupFunction, globalReleaseProofs.map((releaseProof) => releaseProof.anchor), context)) return false;
14998
15133
  globalReleaseProofsByCleanup.set(cleanupFunction, globalReleaseProofs);
14999
15134
  }
15000
15135
  const handleAssignments = handleSymbol.references.filter((reference) => isWithinAssignmentTarget(reference.identifier));
@@ -15207,7 +15342,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15207
15342
  });
15208
15343
  const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
15209
15344
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
15210
- return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15345
+ return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15211
15346
  };
15212
15347
  const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollectionKeys, context) => {
15213
15348
  let currentNode = findTransparentExpressionRoot(releaseNode);
@@ -15220,7 +15355,7 @@ const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollect
15220
15355
  const isDirectConciseBody = ownerFunction.body === currentNode;
15221
15356
  const statementNode = currentNode.parent;
15222
15357
  const isDirectBlockStatement = isNodeOfType(ownerFunction.body, "BlockStatement") && isNodeOfType(statementNode, "ExpressionStatement") && statementNode.parent === ownerFunction.body;
15223
- if (!isDirectConciseBody && !isDirectBlockStatement || !doMatchingNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
15358
+ if (!isDirectConciseBody && !isDirectBlockStatement || !doNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
15224
15359
  const forEachCall = findEnclosingForEachCall(ownerFunction);
15225
15360
  if (!forEachCall) return replayedCollectionKeys.size === requiredCollectionKeys.size && isReturnedEffectCleanupFunction(ownerFunction, context) ? ownerFunction : null;
15226
15361
  const forEachCallee = stripParenExpression(forEachCall.callee);
@@ -15308,7 +15443,7 @@ const hasSafeForEachProjectionCleanup = (registrationCall, releaseCall, context)
15308
15443
  const registrationEventKey = resolveResourceIdentityKey(registrationCall.arguments?.[0], context);
15309
15444
  const releaseEventKey = resolveResourceIdentityKey(releaseCall.arguments?.[0], context);
15310
15445
  const doesHandlerlessOffReleaseEveryRegistration = releaseVerbName === "off" && !releaseHandler && (releaseCall.arguments?.length === 0 || registrationEventKey !== null && registrationEventKey === releaseEventKey);
15311
- if (Boolean(releaseFunction && isFunctionLike$1(releaseFunction) && isReturnedEffectCleanupFunction(releaseFunction, context) && doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseCall], context)) && (releaseVerbName !== null && UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName) || doesHandlerlessOffReleaseEveryRegistration)) return true;
15446
+ if (Boolean(releaseFunction && isFunctionLike$1(releaseFunction) && isReturnedEffectCleanupFunction(releaseFunction, context) && doNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseCall], context)) && (releaseVerbName !== null && UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName) || doesHandlerlessOffReleaseEveryRegistration)) return true;
15312
15447
  const projections = [
15313
15448
  registrationCallee.object,
15314
15449
  registrationCall.arguments?.[0],
@@ -15355,6 +15490,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15355
15490
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
15356
15491
  if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
15357
15492
  if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
15493
+ if (usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) return isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && releaseVerbName === "removeListener" && isProvenLegacyMediaQueryListMethodCall(callNode, "removeListener", context) && usage.receiverKey !== null && resolveStableMediaQueryListenerIdentityKey(callee.object, context) === usage.receiverKey && usage.handlerKey !== null && resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context) === usage.handlerKey;
15358
15494
  if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
15359
15495
  if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
15360
15496
  if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
@@ -15506,7 +15642,7 @@ const hasEffectCleanupInvocation = (storage, usage, context) => {
15506
15642
  if (!cleanupFunction || !cleanupFunctionInvokesRef(cleanupFunction)) return;
15507
15643
  matchingReturns.push(child);
15508
15644
  });
15509
- return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15645
+ return doNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15510
15646
  };
15511
15647
  let didFindInvocation = false;
15512
15648
  walkAst(componentFunction.body, (child) => {
@@ -15558,7 +15694,7 @@ const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
15558
15694
  return findRetainedDisposerStorages(disposerFunction, usage, context).some((storage) => isRetainedDisposerStorageEstablished(storage, usage, context) && !hasUnsafeRetainedDisposerOverwrite(storage, usage, context) && (hasEffectCleanupInvocation(storage, usage, context) || hasCallbackRefReplacementInvocation(storage, usage, context)));
15559
15695
  };
15560
15696
  const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
15561
- if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseNode], context)) return false;
15697
+ if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !doNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseNode], context)) return false;
15562
15698
  const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
15563
15699
  if (!isNodeOfType(releaseCall, "CallExpression")) return false;
15564
15700
  if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], releaseCall.arguments?.[2], context)) return false;
@@ -15786,7 +15922,7 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
15786
15922
  walkInsideStatementBlocks(effectCallback.body, (child) => {
15787
15923
  if (isNodeOfType(child, "ReturnStatement") && child.argument && matchesReturnedCleanup(stripParenExpression(child.argument))) matchingReturns.push(child);
15788
15924
  });
15789
- return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15925
+ return doNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15790
15926
  };
15791
15927
  const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
15792
15928
  const componentFunction = findEnclosingFunction$1(retainedFunction);
@@ -15829,15 +15965,6 @@ const findUnconditionalReturnStatement = (expression, ownerFunction) => {
15829
15965
  const returnStatement = expressionRoot.parent;
15830
15966
  return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === expressionRoot && findEnclosingFunction$1(returnStatement) === ownerFunction ? returnStatement : null;
15831
15967
  };
15832
- const getFinalSequenceExpressionValue = (expression) => {
15833
- let finalExpression = stripParenExpression(expression);
15834
- while (isNodeOfType(finalExpression, "SequenceExpression")) {
15835
- const sequenceResult = finalExpression.expressions.at(-1);
15836
- if (!sequenceResult) break;
15837
- finalExpression = stripParenExpression(sequenceResult);
15838
- }
15839
- return finalExpression;
15840
- };
15841
15968
  const doesResourceResultEscape = (resourceNode, allowReturnedResourceEscape, allowConciseReturnEscape, context) => {
15842
15969
  if (!allowReturnedResourceEscape) return false;
15843
15970
  let currentNode = resourceNode;
@@ -16086,7 +16213,7 @@ const isReactRefCallbackCleanupOwnedByEffect = (retainedFunction, cleanupFunctio
16086
16213
  if (child !== returnedCleanupFunction.body && isFunctionLike$1(child)) return false;
16087
16214
  if (isNodeOfType(child, "CallExpression") && resolveRefOwnedCleanupFunction(child.callee, context) === cleanupFunction) matchingCalls.push(child);
16088
16215
  });
16089
- return doMatchingNodesCoverEveryPathFromFunctionEntry(returnedCleanupFunction, matchingCalls, context);
16216
+ return doNodesCoverEveryPathFromFunctionEntry(returnedCleanupFunction, matchingCalls, context);
16090
16217
  };
16091
16218
  const matchingReturns = [];
16092
16219
  walkInsideStatementBlocks(retainedFunction.body, (child) => {
@@ -22436,25 +22563,49 @@ const jsLengthCheckFirst = defineRule({
22436
22563
  });
22437
22564
  //#endregion
22438
22565
  //#region src/plugin/utils/is-proven-global-namespace-reference.ts
22566
+ const GLOBAL_SELF_PROPERTY_NAMES = new Set([
22567
+ "global",
22568
+ "globalThis",
22569
+ "self",
22570
+ "window"
22571
+ ]);
22439
22572
  const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
22440
- const strippedExpression = stripParenExpression(expression);
22441
- if (!isNodeOfType(strippedExpression, "Identifier")) return false;
22442
- if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
22443
- const symbol = scopes.symbolFor(strippedExpression);
22444
- if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22445
- visitedSymbolIds.add(symbol.id);
22446
- return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
22573
+ let currentExpression = expression;
22574
+ while (true) {
22575
+ const strippedExpression = stripParenExpression(currentExpression);
22576
+ if (!isNodeOfType(strippedExpression, "Identifier")) return false;
22577
+ if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
22578
+ const symbol = scopes.symbolFor(strippedExpression);
22579
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
22580
+ const declaration = symbol.declarationNode;
22581
+ const initializer = declaration.init;
22582
+ if (!initializer) return false;
22583
+ const isDirectIdentifierAlias = isNodeOfType(declaration.id, "Identifier") && declaration.id === symbol.bindingIdentifier && initializer === symbol.initializer;
22584
+ const bindingProperty = symbol.bindingIdentifier.parent;
22585
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
22586
+ const isDirectGlobalSelfAlias = isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === symbol.bindingIdentifier && bindingProperty.parent === declaration.id && destructuredPropertyName !== null && GLOBAL_SELF_PROPERTY_NAMES.has(destructuredPropertyName);
22587
+ if (!isDirectIdentifierAlias && !isDirectGlobalSelfAlias) return false;
22588
+ visitedSymbolIds.add(symbol.id);
22589
+ currentExpression = initializer;
22590
+ }
22447
22591
  };
22448
22592
  const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
22449
- const strippedExpression = stripParenExpression(expression);
22450
- if (isNodeOfType(strippedExpression, "Identifier")) {
22593
+ let currentExpression = expression;
22594
+ while (true) {
22595
+ const strippedExpression = stripParenExpression(currentExpression);
22596
+ if (!isNodeOfType(strippedExpression, "Identifier")) return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
22451
22597
  if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
22452
22598
  const symbol = scopes.symbolFor(strippedExpression);
22453
- if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22599
+ if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22454
22600
  visitedSymbolIds.add(symbol.id);
22455
- return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
22601
+ const declaration = symbol.declarationNode;
22602
+ const bindingIdentifier = symbol.bindingIdentifier;
22603
+ const bindingProperty = bindingIdentifier.parent;
22604
+ if (getDestructuredBindingPropertyName(bindingIdentifier) === namespaceName && isNodeOfType(declaration, "VariableDeclarator") && isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === bindingIdentifier && bindingProperty.parent === declaration.id && declaration.init) return isProvenGlobalObjectReference(declaration.init, scopes);
22605
+ const directInitializer = getDirectConstInitializer(symbol);
22606
+ if (!directInitializer) return false;
22607
+ currentExpression = directInitializer;
22456
22608
  }
22457
- return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
22458
22609
  };
22459
22610
  //#endregion
22460
22611
  //#region src/plugin/rules/js-performance/js-min-max-loop.ts
@@ -34716,6 +34867,19 @@ const noCascadingSetState = defineRetiredRule({
34716
34867
  recommendation: "Retired: React batches synchronous state updates from one effect into the same follow-up commit, so setter count does not prove repeated redraws."
34717
34868
  });
34718
34869
  //#endregion
34870
+ //#region src/plugin/utils/get-react-use-callback-call.ts
34871
+ const getReactUseCallbackCall = (expression, scopes) => {
34872
+ const identifier = stripParenExpression(expression);
34873
+ if (!isNodeOfType(identifier, "Identifier")) return null;
34874
+ const symbol = resolveConstIdentifierAlias(identifier, scopes);
34875
+ if (symbol?.kind !== "const" || !symbol.initializer) return null;
34876
+ const initializer = stripParenExpression(symbol.initializer);
34877
+ return isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", scopes, {
34878
+ allowGlobalReactNamespace: true,
34879
+ resolveNamedAliases: true
34880
+ }) ? initializer : null;
34881
+ };
34882
+ //#endregion
34719
34883
  //#region src/plugin/rules/state-and-effects/utils/create-state-trigger-reachability.ts
34720
34884
  const getRefCurrentSymbol = (node, context) => {
34721
34885
  const expression = stripParenExpression(node);
@@ -35244,19 +35408,8 @@ const isBuiltinNamespaceCallee = (callee) => {
35244
35408
  }
35245
35409
  return false;
35246
35410
  };
35247
- const getReactUseCallbackSource = (reference, context) => {
35248
- const identifier = reference.identifier;
35249
- const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
35250
- if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
35251
- const initializer = stripParenExpression(symbol.initializer);
35252
- if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
35253
- allowGlobalReactNamespace: true,
35254
- resolveNamedAliases: true
35255
- })) return initializer;
35256
- return null;
35257
- };
35258
35411
  const getDependencyStateRefs = (analysis, context, dependencyReference) => {
35259
- const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
35412
+ const useCallbackCall = getReactUseCallbackCall(dependencyReference.identifier, context.scopes);
35260
35413
  if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
35261
35414
  const dependencyList = useCallbackCall.arguments?.[1];
35262
35415
  if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
@@ -38770,6 +38923,17 @@ const noDynamicImportPath = defineRule({
38770
38923
  })
38771
38924
  });
38772
38925
  //#endregion
38926
+ //#region src/plugin/utils/get-require-call-source.ts
38927
+ const getRequireCallSource = (expression) => {
38928
+ const unwrappedExpression = stripParenExpression(expression);
38929
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
38930
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
38931
+ if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
38932
+ const [firstArgument] = unwrappedExpression.arguments ?? [];
38933
+ if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
38934
+ return typeof firstArgument.value === "string" ? firstArgument.value : null;
38935
+ };
38936
+ //#endregion
38773
38937
  //#region src/plugin/rules/state-and-effects/utils/is-cleanup-return.ts
38774
38938
  const ITERATOR_CALLBACK_METHOD_NAMES = new Set([
38775
38939
  "each",
@@ -38914,7 +39078,16 @@ const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) =>
38914
39078
  }
38915
39079
  return dependencyStateSymbolIds;
38916
39080
  };
38917
- const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
39081
+ const resolveSynchronouslyInvokedFunction = (expression, scopes) => {
39082
+ const localFunction = resolveExactLocalFunction(expression, scopes);
39083
+ if (localFunction) return localFunction;
39084
+ const useCallbackCall = getReactUseCallbackCall(expression, scopes);
39085
+ if (!useCallbackCall) return null;
39086
+ const callback = useCallbackCall.arguments[0];
39087
+ if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
39088
+ return resolveExactLocalFunction(callback, scopes);
39089
+ };
39090
+ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeStableCallbacks = true) => {
38918
39091
  const analysisFunctions = new Set([effectCallback]);
38919
39092
  const pendingFunctions = [effectCallback];
38920
39093
  while (pendingFunctions.length > 0) {
@@ -38922,9 +39095,9 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
38922
39095
  if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
38923
39096
  walkInsideStatementBlocks(currentFunction.body, (child) => {
38924
39097
  if (!isNodeOfType(child, "CallExpression")) return;
38925
- const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
39098
+ const invokedFunction = includeStableCallbacks ? resolveSynchronouslyInvokedFunction(child.callee, scopes) : resolveExactLocalFunction(child.callee, scopes);
38926
39099
  if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
38927
- if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
39100
+ if (isFunctionLike$1(invokedFunction) && (invokedFunction.async || invokedFunction.generator)) return;
38928
39101
  analysisFunctions.add(invokedFunction);
38929
39102
  pendingFunctions.push(invokedFunction);
38930
39103
  });
@@ -38937,7 +39110,51 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
38937
39110
  walkInsideStatementBlocks(analysisFunction.body, visitor);
38938
39111
  }
38939
39112
  };
38940
- const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39113
+ const getStateNameForSetterCall = (node, setterSymbolIdToStateName, scopes) => {
39114
+ if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return null;
39115
+ const setterSymbol = resolveConstIdentifierAlias(node.callee, scopes, true);
39116
+ return setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) ?? null : null;
39117
+ };
39118
+ const collectStateWriteAnalysisFunctions = (effectCallback, scopes, setterSymbolIdToStateName) => {
39119
+ const ordinaryAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes, false);
39120
+ const fullAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes);
39121
+ const stableAnalysisFunctions = [...fullAnalysisFunctions].filter((analysisFunction) => !ordinaryAnalysisFunctions.has(analysisFunction));
39122
+ if (stableAnalysisFunctions.length === 0) return ordinaryAnalysisFunctions;
39123
+ let hasUnprovenObservableWork = stableAnalysisFunctions.some((analysisFunction) => {
39124
+ if (!isFunctionLike$1(analysisFunction)) return false;
39125
+ let hasExecutableDefault = false;
39126
+ for (const parameter of analysisFunction.params) {
39127
+ walkInsideStatementBlocks(parameter, (child) => {
39128
+ if (isNodeOfType(child, "CallExpression") || isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasExecutableDefault = true;
39129
+ });
39130
+ if (hasExecutableDefault) return true;
39131
+ }
39132
+ return false;
39133
+ });
39134
+ const stableWrittenStateNames = /* @__PURE__ */ new Set();
39135
+ const allWrittenStateNames = /* @__PURE__ */ new Set();
39136
+ visitSynchronousFunctionBodies(fullAnalysisFunctions, (child) => {
39137
+ const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
39138
+ if (writtenStateName) allWrittenStateNames.add(writtenStateName);
39139
+ });
39140
+ visitSynchronousFunctionBodies(new Set(stableAnalysisFunctions), (child) => {
39141
+ if (hasUnprovenObservableWork) return;
39142
+ if (isNodeOfType(child, "CallExpression")) {
39143
+ const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
39144
+ if (writtenStateName) {
39145
+ stableWrittenStateNames.add(writtenStateName);
39146
+ return;
39147
+ }
39148
+ const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
39149
+ if (invokedFunction && fullAnalysisFunctions.has(invokedFunction)) return;
39150
+ hasUnprovenObservableWork = true;
39151
+ return;
39152
+ }
39153
+ if (isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasUnprovenObservableWork = true;
39154
+ });
39155
+ return !hasUnprovenObservableWork && stableWrittenStateNames.size === 1 && allWrittenStateNames.size === 1 ? fullAnalysisFunctions : ordinaryAnalysisFunctions;
39156
+ };
39157
+ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set(), additionalStateValues = /* @__PURE__ */ new Map()) => {
38941
39158
  const unwrappedExpression = stripParenExpression(expression);
38942
39159
  if (isNodeOfType(unwrappedExpression, "Literal")) {
38943
39160
  const literalValue = unwrappedExpression.value;
@@ -38945,46 +39162,49 @@ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, vi
38945
39162
  return null;
38946
39163
  }
38947
39164
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
38948
- if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
39165
+ const symbol = scopes.symbolFor(unwrappedExpression);
39166
+ if (symbol?.id === stateSymbolId) return stateValue;
39167
+ if (symbol && additionalStateValues.has(symbol.id)) return additionalStateValues.get(symbol.id) ?? null;
38949
39168
  if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
39169
+ if (unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression)) return { value: NaN };
38950
39170
  const immutableSymbol = scopes.symbolFor(unwrappedExpression);
38951
39171
  if (immutableSymbol?.kind !== "const" || !immutableSymbol.initializer || !isNodeOfType(immutableSymbol.declarationNode, "VariableDeclarator") || immutableSymbol.declarationNode.id !== immutableSymbol.bindingIdentifier || immutableSymbol.declarationNode.init !== immutableSymbol.initializer || immutableSymbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(immutableSymbol.id)) return null;
38952
- return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
39172
+ return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id), additionalStateValues);
38953
39173
  }
38954
39174
  if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
38955
39175
  if (unwrappedExpression.operator === "void") return { value: void 0 };
38956
39176
  if (unwrappedExpression.operator !== "!") return null;
38957
- const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39177
+ const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38958
39178
  return argumentValue ? { value: !argumentValue.value } : null;
38959
39179
  }
38960
39180
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
38961
39181
  if (isNodeOfType(unwrappedExpression.callee, "Identifier") && unwrappedExpression.callee.name === "Boolean" && scopes.isGlobalReference(unwrappedExpression.callee) && unwrappedExpression.arguments.length === 1 && unwrappedExpression.arguments[0] && !isNodeOfType(unwrappedExpression.arguments[0], "SpreadElement")) {
38962
- const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
39182
+ const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38963
39183
  return argumentValue ? { value: Boolean(argumentValue.value) } : null;
38964
39184
  }
38965
39185
  return null;
38966
39186
  }
38967
39187
  if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
38968
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39188
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38969
39189
  if (!leftValue) return null;
38970
39190
  if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
38971
39191
  if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
38972
39192
  if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
38973
- return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39193
+ return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38974
39194
  }
38975
39195
  if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
38976
- const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39196
+ const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38977
39197
  if (!testValue) return null;
38978
- return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39198
+ return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38979
39199
  }
38980
39200
  if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
38981
- const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39201
+ const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38982
39202
  if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
38983
39203
  return null;
38984
39204
  }
38985
39205
  if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
38986
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
38987
- const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39206
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
39207
+ const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38988
39208
  if (!leftValue || !rightValue) return null;
38989
39209
  if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
38990
39210
  const areEqual = leftValue.value === rightValue.value;
@@ -39023,8 +39243,7 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39023
39243
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
39024
39244
  if (!isNodeOfType(child, "CallExpression")) return;
39025
39245
  if (!isNodeOfType(child.callee, "Identifier")) return;
39026
- const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
39027
- const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
39246
+ const stateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
39028
39247
  if (!stateName) return;
39029
39248
  const writeInfo = stateWrites.get(stateName) ?? {
39030
39249
  values: /* @__PURE__ */ new Set(),
@@ -39040,27 +39259,27 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39040
39259
  const isGlobalBooleanCall = (node, scopes) => {
39041
39260
  return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
39042
39261
  };
39043
- const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
39262
+ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
39044
39263
  let currentNode = workNode;
39045
39264
  while (currentNode.parent) {
39046
39265
  const parentNode = currentNode.parent;
39047
39266
  if (isFunctionLike$1(parentNode)) break;
39048
39267
  if (isNodeOfType(parentNode, "IfStatement")) {
39049
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39268
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
39050
39269
  if (testValue) {
39051
39270
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39052
39271
  if (currentNode === parentNode.alternate && testValue.value) return false;
39053
39272
  }
39054
39273
  }
39055
39274
  if (isNodeOfType(parentNode, "ConditionalExpression")) {
39056
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39275
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
39057
39276
  if (testValue) {
39058
39277
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39059
39278
  if (currentNode === parentNode.alternate && testValue.value) return false;
39060
39279
  }
39061
39280
  }
39062
39281
  if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
39063
- const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
39282
+ const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
39064
39283
  if (leftValue) {
39065
39284
  if (parentNode.operator === "&&" && !leftValue.value) return false;
39066
39285
  if (parentNode.operator === "||" && leftValue.value) return false;
@@ -39072,7 +39291,7 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39072
39291
  if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
39073
39292
  const earlierStatement = parentNode.body[index];
39074
39293
  if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
39075
- if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
39294
+ if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues)?.value) return false;
39076
39295
  }
39077
39296
  }
39078
39297
  currentNode = parentNode;
@@ -39082,24 +39301,93 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39082
39301
  const isReaderWorkNode = (node, analysisFunctions, scopes) => {
39083
39302
  if (isNodeOfType(node, "CallExpression")) {
39084
39303
  if (isGlobalBooleanCall(node, scopes)) return false;
39085
- const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
39304
+ const invokedFunction = resolveSynchronouslyInvokedFunction(node.callee, scopes);
39086
39305
  return !invokedFunction || !analysisFunctions.has(invokedFunction);
39087
39306
  }
39088
39307
  return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
39089
39308
  };
39090
- const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
39091
- if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
39092
- for (const writtenValue of writeInfo.values) {
39093
- const stateValue = { value: writtenValue };
39309
+ const mergeKnownStateValues = (additionalStateValues, parameterStateValues) => {
39310
+ if (parameterStateValues.size === 0) return additionalStateValues;
39311
+ const knownStateValues = new Map(additionalStateValues);
39312
+ for (const [symbolId, value] of parameterStateValues) knownStateValues.set(symbolId, value);
39313
+ return knownStateValues;
39314
+ };
39315
+ const buildInvokedFunctionParameterStateValues = (invokedFunction, invocation, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map(), callerParameterStateValues = /* @__PURE__ */ new Map()) => {
39316
+ if (!isFunctionLike$1(invokedFunction)) return /* @__PURE__ */ new Map();
39317
+ const parameterStateValues = /* @__PURE__ */ new Map();
39318
+ const callerKnownStateValues = mergeKnownStateValues(additionalStateValues, callerParameterStateValues);
39319
+ for (let parameterIndex = 0; parameterIndex < invokedFunction.params.length; parameterIndex += 1) {
39320
+ const rawParameter = invokedFunction.params[parameterIndex];
39321
+ const rawArgument = invocation.arguments[parameterIndex];
39322
+ if (rawArgument && isNodeOfType(rawArgument, "SpreadElement")) break;
39323
+ const parameter = isNodeOfType(rawParameter, "AssignmentPattern") ? rawParameter.left : rawParameter;
39324
+ if (!isNodeOfType(parameter, "Identifier")) continue;
39325
+ const parameterSymbol = scopes.symbolFor(parameter);
39326
+ if (!parameterSymbol) continue;
39327
+ let argumentValue = null;
39328
+ if (rawArgument) {
39329
+ argumentValue = readStaticEffectValue(rawArgument, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), callerKnownStateValues);
39330
+ if (argumentValue !== null && argumentValue.value === void 0 && isNodeOfType(rawParameter, "AssignmentPattern")) {
39331
+ const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
39332
+ argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
39333
+ }
39334
+ } else if (isNodeOfType(rawParameter, "AssignmentPattern")) {
39335
+ const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
39336
+ argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
39337
+ } else argumentValue = { value: void 0 };
39338
+ if (argumentValue) parameterStateValues.set(parameterSymbol.id, argumentValue);
39339
+ }
39340
+ return parameterStateValues;
39341
+ };
39342
+ const haveEqualParameterStateValues = (leftValues, rightValues) => {
39343
+ if (leftValues.size !== rightValues.size) return false;
39344
+ for (const [symbolId, leftValue] of leftValues) {
39345
+ const rightValue = rightValues.get(symbolId);
39346
+ if (!rightValue || !Object.is(leftValue.value, rightValue.value)) return false;
39347
+ }
39348
+ return true;
39349
+ };
39350
+ const canReaderWorkRunForStateValues = (readerEffect, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
39351
+ const pendingFrames = [{
39352
+ functionNode: readerEffect.callback,
39353
+ parameterStateValues: /* @__PURE__ */ new Map()
39354
+ }];
39355
+ const visitedParameterStateValues = /* @__PURE__ */ new Map();
39356
+ while (pendingFrames.length > 0) {
39357
+ const frame = pendingFrames.pop();
39358
+ if (!frame || !isFunctionLike$1(frame.functionNode)) continue;
39359
+ const functionParameterStateValues = visitedParameterStateValues.get(frame.functionNode) ?? [];
39360
+ if (functionParameterStateValues.some((previousValues) => haveEqualParameterStateValues(previousValues, frame.parameterStateValues))) continue;
39361
+ functionParameterStateValues.push(frame.parameterStateValues);
39362
+ visitedParameterStateValues.set(frame.functionNode, functionParameterStateValues);
39363
+ const knownStateValues = mergeKnownStateValues(additionalStateValues, frame.parameterStateValues);
39094
39364
  let didFindReachableWork = false;
39095
- visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
39096
- if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
39097
- if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
39365
+ walkInsideStatementBlocks(frame.functionNode.body, (child) => {
39366
+ if (didFindReachableWork) return;
39367
+ if (isNodeOfType(child, "CallExpression")) {
39368
+ const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
39369
+ if (invokedFunction && readerEffect.analysisFunctions.has(invokedFunction)) {
39370
+ if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) pendingFrames.push({
39371
+ functionNode: invokedFunction,
39372
+ parameterStateValues: buildInvokedFunctionParameterStateValues(invokedFunction, child, stateSymbolId, stateValue, scopes, additionalStateValues, frame.parameterStateValues)
39373
+ });
39374
+ return;
39375
+ }
39376
+ }
39377
+ if (isReaderWorkNode(child, readerEffect.analysisFunctions, scopes) && isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) didFindReachableWork = true;
39098
39378
  });
39099
39379
  if (didFindReachableWork) return true;
39100
39380
  }
39101
39381
  return false;
39102
39382
  };
39383
+ const canStateWriteReachReaderWork = (writtenStateName, writerEffect, readerEffect, stateSymbolIds, scopes) => {
39384
+ const writeInfo = writerEffect.stateWrites.get(writtenStateName);
39385
+ const stateSymbolId = stateSymbolIds.get(writtenStateName);
39386
+ if (!writeInfo || stateSymbolId === void 0) return true;
39387
+ if (writeInfo.hasUnknownValue) return true;
39388
+ for (const writtenValue of writeInfo.values) if (canReaderWorkRunForStateValues(readerEffect, stateSymbolId, { value: writtenValue }, scopes)) return true;
39389
+ return false;
39390
+ };
39103
39391
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
39104
39392
  const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39105
39393
  "clear",
@@ -39110,26 +39398,499 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39110
39398
  "keys",
39111
39399
  "values"
39112
39400
  ]);
39113
- const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
39114
- if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
39115
- if (isNodeOfType(returnedValue, "CallExpression")) {
39116
- if (isNodeOfType(returnedValue.callee, "Identifier")) {
39117
- const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
39118
- if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
39119
- if (isSetterIdentifier(returnedValue.callee.name)) return true;
39401
+ const EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES = new Map([
39402
+ ["fetch", new Set([
39403
+ "cross-fetch",
39404
+ "node-fetch",
39405
+ "undici"
39406
+ ])],
39407
+ ["ky", new Set(["ky"])],
39408
+ ["got", new Set(["got"])],
39409
+ ["wretch", new Set(["wretch"])],
39410
+ ["ofetch", new Set(["ofetch"])],
39411
+ ["setTimeout", new Set([
39412
+ "node:timers",
39413
+ "node:timers/promises",
39414
+ "timers",
39415
+ "timers/promises"
39416
+ ])],
39417
+ ["setInterval", new Set([
39418
+ "node:timers",
39419
+ "node:timers/promises",
39420
+ "timers",
39421
+ "timers/promises"
39422
+ ])]
39423
+ ]);
39424
+ const EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES = new Map([
39425
+ ["axios", "axios"],
39426
+ ["cross-fetch", "fetch"],
39427
+ ["got", "got"],
39428
+ ["ky", "ky"],
39429
+ ["node-fetch", "fetch"],
39430
+ ["wretch", "wretch"]
39431
+ ]);
39432
+ const EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES = new Set([
39433
+ "axios",
39434
+ "cross-fetch",
39435
+ "got",
39436
+ "ky",
39437
+ "node-fetch",
39438
+ "ofetch",
39439
+ "undici",
39440
+ "wretch"
39441
+ ]);
39442
+ const EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES = new Set([
39443
+ "axios",
39444
+ "got",
39445
+ "ky",
39446
+ "ofetch",
39447
+ "wretch"
39448
+ ]);
39449
+ const EXTERNAL_SYNC_HTTP_METHOD_NAMES = new Set([
39450
+ ...EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES,
39451
+ "fetch",
39452
+ "patch",
39453
+ "post",
39454
+ "put",
39455
+ "request"
39456
+ ]);
39457
+ const TANSTACK_QUERY_CLIENT_MODULE_SOURCES = new Set(["@tanstack/query-core", "@tanstack/react-query"]);
39458
+ const TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES = new Set(["fetchQuery", "prefetchQuery"]);
39459
+ const EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES = new Set([
39460
+ ...EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS,
39461
+ ...SOCKET_CONSTRUCTOR_NAMES_REQUIRING_CLEANUP,
39462
+ "EventTarget",
39463
+ "XMLHttpRequest"
39464
+ ]);
39465
+ const getDestructuredBindingDepth = (bindingIdentifier) => {
39466
+ let bindingNode = bindingIdentifier;
39467
+ let depth = 0;
39468
+ while (true) {
39469
+ if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
39470
+ const property = bindingNode.parent;
39471
+ if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return depth;
39472
+ depth += 1;
39473
+ bindingNode = property.parent;
39474
+ }
39475
+ };
39476
+ const isMutatedMemberExpression = (rawMemberExpression) => {
39477
+ let expression = findTransparentExpressionRoot(rawMemberExpression);
39478
+ while (expression.parent) {
39479
+ const parent = expression.parent;
39480
+ if (isNodeOfType(parent, "MemberExpression") && parent.object === expression) {
39481
+ expression = findTransparentExpressionRoot(parent);
39482
+ continue;
39120
39483
  }
39121
- return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
39484
+ if (isNodeOfType(parent, "Property") && parent.value === expression && isNodeOfType(parent.parent, "ObjectPattern")) {
39485
+ expression = findTransparentExpressionRoot(parent);
39486
+ continue;
39487
+ }
39488
+ if (isNodeOfType(parent, "ObjectPattern") && parent.properties.some((property) => property === expression)) {
39489
+ expression = findTransparentExpressionRoot(parent);
39490
+ continue;
39491
+ }
39492
+ if (isNodeOfType(parent, "ArrayPattern") && parent.elements.some((element) => element === expression)) {
39493
+ expression = findTransparentExpressionRoot(parent);
39494
+ continue;
39495
+ }
39496
+ if (isNodeOfType(parent, "RestElement") && parent.argument === expression || isNodeOfType(parent, "AssignmentPattern") && parent.left === expression) {
39497
+ expression = findTransparentExpressionRoot(parent);
39498
+ continue;
39499
+ }
39500
+ break;
39501
+ }
39502
+ const parent = expression.parent;
39503
+ return Boolean(isNodeOfType(parent, "AssignmentExpression") && parent.left === expression || isNodeOfType(parent, "UpdateExpression") && parent.argument === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expression || (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) && parent.left === expression);
39504
+ };
39505
+ const isReactHookDependencyArgument = (expression, scopes) => {
39506
+ const call = expression.parent;
39507
+ return Boolean(isNodeOfType(call, "CallExpression") && call.arguments[1] === expression && isReactApiCall(call, HOOKS_WITH_DEPS, scopes, {
39508
+ allowGlobalReactNamespace: true,
39509
+ allowUnboundBareCalls: true,
39510
+ resolveNamedAliases: true
39511
+ }));
39512
+ };
39513
+ const isPlainDependencyObjectExpression = (expression) => isNodeOfType(expression, "ObjectExpression") && expression.properties.every((property) => isNodeOfType(property, "Property") && property.kind === "init" && !property.computed && !property.method);
39514
+ const getContainingReactDependencyValue = (rawExpression, containerKind) => {
39515
+ const expression = findTransparentExpressionRoot(rawExpression);
39516
+ const parent = expression.parent;
39517
+ if (isNodeOfType(parent, "ArrayExpression")) return {
39518
+ expression: parent,
39519
+ containerKind: "array"
39520
+ };
39521
+ if (containerKind === "array" && isNodeOfType(parent, "SpreadElement") && parent.argument === expression && isNodeOfType(parent.parent, "ArrayExpression")) return {
39522
+ expression: parent.parent,
39523
+ containerKind: "array"
39524
+ };
39525
+ if (isNodeOfType(parent, "Property") && (parent.value === expression || parent.shorthand && parent.key === expression) && parent.parent && isPlainDependencyObjectExpression(parent.parent)) return {
39526
+ expression: parent.parent,
39527
+ containerKind: "object"
39528
+ };
39529
+ return null;
39530
+ };
39531
+ const getFullArrayRestCopySymbol = (initializer, scopes) => {
39532
+ const declaration = initializer.parent;
39533
+ if (!isNodeOfType(declaration, "VariableDeclarator") || declaration.init !== initializer || !isNodeOfType(declaration.id, "ArrayPattern") || declaration.id.elements.length !== 1) return null;
39534
+ const restElement = declaration.id.elements[0];
39535
+ if (!isNodeOfType(restElement, "RestElement") || !isNodeOfType(restElement.argument, "Identifier")) return null;
39536
+ const symbol = scopes.symbolFor(restElement.argument);
39537
+ return symbol?.kind === "const" && symbol.declarationNode === declaration && symbol.references.every((reference) => reference.flag === "read") ? symbol : null;
39538
+ };
39539
+ const isReactDependencyArrayReference = (initialExpression, scopes) => {
39540
+ const pendingPaths = [{
39541
+ expression: initialExpression,
39542
+ containerKind: null
39543
+ }];
39544
+ const visitedExpressions = /* @__PURE__ */ new Set();
39545
+ let didFindReactDependencyArgument = false;
39546
+ while (pendingPaths.length > 0) {
39547
+ const pendingPath = pendingPaths.pop();
39548
+ if (!pendingPath) continue;
39549
+ const expression = findTransparentExpressionRoot(pendingPath.expression);
39550
+ if (visitedExpressions.has(expression)) continue;
39551
+ visitedExpressions.add(expression);
39552
+ if (pendingPath.containerKind === "array" && isReactHookDependencyArgument(expression, scopes)) {
39553
+ didFindReactDependencyArgument = true;
39554
+ continue;
39555
+ }
39556
+ const containingValue = getContainingReactDependencyValue(expression, pendingPath.containerKind);
39557
+ if (containingValue) {
39558
+ pendingPaths.push(containingValue);
39559
+ continue;
39560
+ }
39561
+ const declaration = expression.parent;
39562
+ if (isNodeOfType(declaration, "VariableDeclarator") && declaration.init === expression && isNodeOfType(declaration.id, "Identifier")) {
39563
+ const symbol = scopes.symbolFor(declaration.id);
39564
+ if (!symbol || getDirectUnreassignedInitializer(symbol) !== expression) return false;
39565
+ if (symbol.references.length === 0) return false;
39566
+ for (const reference of symbol.references) pendingPaths.push({
39567
+ expression: reference.identifier,
39568
+ containerKind: pendingPath.containerKind
39569
+ });
39570
+ continue;
39571
+ }
39572
+ if (pendingPath.containerKind === "array") {
39573
+ const restCopySymbol = getFullArrayRestCopySymbol(expression, scopes);
39574
+ if (restCopySymbol) {
39575
+ if (restCopySymbol.references.length === 0) return false;
39576
+ for (const reference of restCopySymbol.references) pendingPaths.push({
39577
+ expression: reference.identifier,
39578
+ containerKind: "array"
39579
+ });
39580
+ continue;
39581
+ }
39582
+ }
39583
+ return false;
39122
39584
  }
39123
- if (isNodeOfType(returnedValue, "Identifier")) return true;
39585
+ return didFindReactDependencyArgument;
39586
+ };
39587
+ const hasSafeReceiverAliases = (rootSymbols, scopes) => {
39588
+ const pendingSymbols = [...rootSymbols];
39589
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
39590
+ while (pendingSymbols.length > 0) {
39591
+ const symbol = pendingSymbols.pop();
39592
+ if (!symbol || visitedSymbolIds.has(symbol.id)) continue;
39593
+ visitedSymbolIds.add(symbol.id);
39594
+ for (const reference of symbol.references) {
39595
+ const expression = findTransparentExpressionRoot(reference.identifier);
39596
+ const container = expression.parent;
39597
+ if (isReactDependencyArrayReference(expression, scopes)) continue;
39598
+ if (isNodeOfType(container, "MemberExpression") && container.object === expression) {
39599
+ if (isMutatedMemberExpression(container)) return false;
39600
+ continue;
39601
+ }
39602
+ if (isNodeOfType(container, "CallExpression") && container.callee === expression) continue;
39603
+ if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "ObjectPattern")) continue;
39604
+ if (isNodeOfType(container, "AssignmentExpression") && container.right === expression && isNodeOfType(container.left, "ObjectPattern")) continue;
39605
+ if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
39606
+ const aliasSymbol = scopes.symbolFor(container.id);
39607
+ const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
39608
+ if (!aliasSymbol || aliasInitializer !== expression) return false;
39609
+ pendingSymbols.push(aliasSymbol);
39610
+ continue;
39611
+ }
39612
+ return false;
39613
+ }
39614
+ }
39615
+ return true;
39616
+ };
39617
+ const hasProvenGlobalNamespaceReference = (expression, namespaceNames, scopes) => {
39618
+ for (const namespaceName of namespaceNames) if (isProvenGlobalNamespaceReference(expression, namespaceName, scopes)) return true;
39124
39619
  return false;
39125
39620
  };
39126
- const STORAGE_GLOBAL_NAMES = new Set(["localStorage", "sessionStorage"]);
39127
- const isBrowserStorageReceiver = (receiver) => {
39128
- if (!receiver) return false;
39129
- if (isNodeOfType(receiver, "Identifier")) return STORAGE_GLOBAL_NAMES.has(receiver.name);
39130
- if (isNodeOfType(receiver, "MemberExpression")) return isNodeOfType(receiver.property, "Identifier") && STORAGE_GLOBAL_NAMES.has(receiver.property.name);
39621
+ const getTypeScriptImportEqualsSource = (symbol) => {
39622
+ if (symbol.kind !== "ts-import-equals" || !isNodeOfType(symbol.declarationNode, "TSImportEqualsDeclaration")) return null;
39623
+ const moduleReference = symbol.declarationNode.moduleReference;
39624
+ if (!isNodeOfType(moduleReference, "TSExternalModuleReference") || !isNodeOfType(moduleReference.expression, "Literal")) return null;
39625
+ return typeof moduleReference.expression.value === "string" ? moduleReference.expression.value : null;
39626
+ };
39627
+ const getUnshadowedRequireSource = (rawExpression, scopes) => {
39628
+ let expression = stripParenExpression(rawExpression);
39629
+ while (isNodeOfType(expression, "MemberExpression")) expression = stripParenExpression(expression.object);
39630
+ if (!isNodeOfType(expression, "CallExpression") || !isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require" || !scopes.isGlobalReference(expression.callee)) return null;
39631
+ return getRequireCallSource(rawExpression);
39632
+ };
39633
+ const resolveExternalModuleBinding = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), ignoreCommonJsMutation = false) => {
39634
+ let expression = stripParenExpression(rawExpression);
39635
+ let outermostExportedName = null;
39636
+ let exportedPropertyDepth = 0;
39637
+ let resolvedBinding = null;
39638
+ let isCommonJsModuleObject = false;
39639
+ const traversedSymbolIds = new Set(visitedSymbolIds);
39640
+ const commonJsModuleObjectSymbols = /* @__PURE__ */ new Set();
39641
+ while (true) {
39642
+ if (isNodeOfType(expression, "MemberExpression")) {
39643
+ const propertyName = getStaticPropertyName(expression);
39644
+ if (propertyName === null) return null;
39645
+ outermostExportedName ??= propertyName;
39646
+ exportedPropertyDepth += 1;
39647
+ expression = stripParenExpression(expression.object);
39648
+ continue;
39649
+ }
39650
+ const requireSource = getUnshadowedRequireSource(expression, scopes);
39651
+ if (requireSource !== null) {
39652
+ resolvedBinding = {
39653
+ source: requireSource,
39654
+ exportedName: null,
39655
+ isModuleObject: true
39656
+ };
39657
+ isCommonJsModuleObject = true;
39658
+ break;
39659
+ }
39660
+ if (!isNodeOfType(expression, "Identifier")) return null;
39661
+ const symbol = scopes.symbolFor(expression);
39662
+ if (!symbol || traversedSymbolIds.has(symbol.id)) return null;
39663
+ if (symbol.kind === "import") {
39664
+ const importDeclaration = symbol.declarationNode.parent;
39665
+ if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string") return null;
39666
+ if (isNodeOfType(symbol.declarationNode, "ImportSpecifier")) resolvedBinding = {
39667
+ source: importDeclaration.source.value,
39668
+ exportedName: getImportedName(symbol.declarationNode) ?? null,
39669
+ isModuleObject: false
39670
+ };
39671
+ else if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) resolvedBinding = {
39672
+ source: importDeclaration.source.value,
39673
+ exportedName: "default",
39674
+ isModuleObject: false
39675
+ };
39676
+ else if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) resolvedBinding = {
39677
+ source: importDeclaration.source.value,
39678
+ exportedName: null,
39679
+ isModuleObject: true
39680
+ };
39681
+ break;
39682
+ }
39683
+ const importEqualsSource = getTypeScriptImportEqualsSource(symbol);
39684
+ if (importEqualsSource !== null) {
39685
+ resolvedBinding = {
39686
+ source: importEqualsSource,
39687
+ exportedName: null,
39688
+ isModuleObject: true
39689
+ };
39690
+ isCommonJsModuleObject = true;
39691
+ commonJsModuleObjectSymbols.add(symbol);
39692
+ break;
39693
+ }
39694
+ const directInitializer = getDirectUnreassignedInitializer(symbol);
39695
+ const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
39696
+ const initializer = directInitializer ?? (destructuredPropertyName !== null && symbol.kind === "const" && isNodeOfType(symbol.declarationNode, "VariableDeclarator") ? symbol.declarationNode.init : null);
39697
+ if (!initializer) return null;
39698
+ traversedSymbolIds.add(symbol.id);
39699
+ outermostExportedName ??= destructuredPropertyName;
39700
+ if (destructuredPropertyName !== null) exportedPropertyDepth += getDestructuredBindingDepth(symbol.bindingIdentifier);
39701
+ const unwrappedInitializer = stripParenExpression(initializer);
39702
+ if (destructuredPropertyName === null && !isNodeOfType(unwrappedInitializer, "MemberExpression")) commonJsModuleObjectSymbols.add(symbol);
39703
+ expression = unwrappedInitializer;
39704
+ }
39705
+ if (!resolvedBinding) return null;
39706
+ if (!ignoreCommonJsMutation && isCommonJsModuleObject && !hasSafeReceiverAliases(commonJsModuleObjectSymbols, scopes)) return null;
39707
+ if (outermostExportedName === null) return resolvedBinding;
39708
+ if (!resolvedBinding.isModuleObject || exportedPropertyDepth !== 1) return null;
39709
+ return {
39710
+ source: resolvedBinding.source,
39711
+ exportedName: outermostExportedName,
39712
+ isModuleObject: false
39713
+ };
39714
+ };
39715
+ const getCanonicalExternalSyncExportName = (binding) => {
39716
+ if (binding.exportedName && binding.exportedName !== "default") return binding.exportedName;
39717
+ return EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.get(binding.source) ?? null;
39718
+ };
39719
+ const isKnownExternalSyncDirectModuleBinding = (binding) => {
39720
+ const exportedName = getCanonicalExternalSyncExportName(binding);
39721
+ return Boolean(exportedName && EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES.get(exportedName)?.has(binding.source));
39722
+ };
39723
+ const DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES = new Set([
39724
+ "Array",
39725
+ "BigInt",
39726
+ "Boolean",
39727
+ "Date",
39728
+ "Number",
39729
+ "String",
39730
+ "Symbol"
39731
+ ]);
39732
+ const DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES = new Set([
39733
+ "Array",
39734
+ "Boolean",
39735
+ "Date",
39736
+ "Map",
39737
+ "Number",
39738
+ "Promise",
39739
+ "RegExp",
39740
+ "Set",
39741
+ "String",
39742
+ "URL",
39743
+ "URLSearchParams",
39744
+ "WeakMap",
39745
+ "WeakSet"
39746
+ ]);
39747
+ const PROMISE_STATIC_RESULT_METHOD_NAMES = new Set([
39748
+ "all",
39749
+ "allSettled",
39750
+ "any",
39751
+ "race",
39752
+ "reject",
39753
+ "resolve",
39754
+ "withResolvers"
39755
+ ]);
39756
+ const returnsOnlyNonCleanupValues = (functionNode, setterToStateName, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
39757
+ if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return false;
39758
+ const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
39759
+ const isNonCleanupValue = (returnValue) => {
39760
+ const expression = stripParenExpression(returnValue);
39761
+ if (isNodeOfType(expression, "Identifier") && expression.name === "undefined" && scopes.isGlobalReference(expression)) return true;
39762
+ if (isNodeOfType(expression, "Literal") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "JSXElement") || isNodeOfType(expression, "JSXFragment") || isNodeOfType(expression, "TemplateLiteral") || isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return true;
39763
+ if (isNodeOfType(expression, "ConditionalExpression")) return isNonCleanupValue(expression.consequent) && isNonCleanupValue(expression.alternate);
39764
+ if (isNodeOfType(expression, "LogicalExpression")) return isNonCleanupValue(expression.left) && isNonCleanupValue(expression.right);
39765
+ if (isNodeOfType(expression, "SequenceExpression")) {
39766
+ const finalExpression = expression.expressions.at(-1);
39767
+ return Boolean(finalExpression && isNonCleanupValue(finalExpression));
39768
+ }
39769
+ if (isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
39770
+ if (!isNodeOfType(expression, "CallExpression")) return false;
39771
+ if (isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
39772
+ if (isNodeOfType(expression.callee, "MemberExpression") && isNodeOfType(expression.callee.object, "Identifier") && expression.callee.object.name === "Promise" && scopes.isGlobalReference(expression.callee.object) && PROMISE_STATIC_RESULT_METHOD_NAMES.has(getStaticPropertyName(expression.callee) ?? "")) return true;
39773
+ if (isNodeOfType(expression.callee, "Identifier") && setterToStateName.has(expression.callee.name)) return true;
39774
+ const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
39775
+ return Boolean(invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes, nextVisitedFunctions));
39776
+ };
39777
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNonCleanupValue(functionNode.body);
39778
+ let returnsOnlyNonCleanup = true;
39779
+ walkAst(functionNode.body, (child) => {
39780
+ if (!returnsOnlyNonCleanup) return false;
39781
+ if (child !== functionNode.body && isFunctionLike$1(child)) return false;
39782
+ if (!isNodeOfType(child, "ReturnStatement")) return;
39783
+ if (child.argument && !isNonCleanupValue(child.argument)) {
39784
+ returnsOnlyNonCleanup = false;
39785
+ return false;
39786
+ }
39787
+ });
39788
+ return returnsOnlyNonCleanup;
39789
+ };
39790
+ const getResolvedFunctionCleanupReturnProof = (functionNode, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
39791
+ if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return {
39792
+ hasCleanup: false,
39793
+ isValid: false
39794
+ };
39795
+ const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
39796
+ const getCleanupReturnProof = (returnValue) => {
39797
+ const expression = stripParenExpression(returnValue);
39798
+ if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return {
39799
+ hasCleanup: true,
39800
+ isValid: true
39801
+ };
39802
+ if (isNodeOfType(expression, "Identifier")) {
39803
+ if (expression.name === "undefined" && scopes.isGlobalReference(expression)) return {
39804
+ hasCleanup: false,
39805
+ isValid: true
39806
+ };
39807
+ const localFunction = resolveExactLocalFunction(expression, scopes);
39808
+ return {
39809
+ hasCleanup: Boolean(localFunction),
39810
+ isValid: Boolean(localFunction)
39811
+ };
39812
+ }
39813
+ if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return {
39814
+ hasCleanup: false,
39815
+ isValid: true
39816
+ };
39817
+ if (isNodeOfType(expression, "ConditionalExpression")) {
39818
+ const consequentProof = getCleanupReturnProof(expression.consequent);
39819
+ const alternateProof = getCleanupReturnProof(expression.alternate);
39820
+ return {
39821
+ hasCleanup: consequentProof.hasCleanup || alternateProof.hasCleanup,
39822
+ isValid: consequentProof.isValid && alternateProof.isValid
39823
+ };
39824
+ }
39825
+ if (isNodeOfType(expression, "LogicalExpression")) {
39826
+ const leftProof = getCleanupReturnProof(expression.left);
39827
+ const rightProof = getCleanupReturnProof(expression.right);
39828
+ return {
39829
+ hasCleanup: leftProof.hasCleanup || rightProof.hasCleanup,
39830
+ isValid: leftProof.isValid && rightProof.isValid
39831
+ };
39832
+ }
39833
+ if (isNodeOfType(expression, "SequenceExpression")) {
39834
+ const finalExpression = expression.expressions.at(-1);
39835
+ return finalExpression ? getCleanupReturnProof(finalExpression) : {
39836
+ hasCleanup: false,
39837
+ isValid: false
39838
+ };
39839
+ }
39840
+ if (!isNodeOfType(expression, "CallExpression")) return {
39841
+ hasCleanup: false,
39842
+ isValid: false
39843
+ };
39844
+ const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
39845
+ return invokedFunction ? getResolvedFunctionCleanupReturnProof(invokedFunction, scopes, nextVisitedFunctions) : {
39846
+ hasCleanup: false,
39847
+ isValid: false
39848
+ };
39849
+ };
39850
+ if (!isNodeOfType(functionNode.body, "BlockStatement")) return getCleanupReturnProof(functionNode.body);
39851
+ let cleanupReturnProof = {
39852
+ hasCleanup: false,
39853
+ isValid: true
39854
+ };
39855
+ walkAst(functionNode.body, (child) => {
39856
+ if (!cleanupReturnProof.isValid) return false;
39857
+ if (child !== functionNode.body && isFunctionLike$1(child)) return false;
39858
+ if (!isNodeOfType(child, "ReturnStatement")) return;
39859
+ if (!child.argument) return;
39860
+ const returnProof = getCleanupReturnProof(child.argument);
39861
+ if (!returnProof.isValid) {
39862
+ cleanupReturnProof = {
39863
+ hasCleanup: false,
39864
+ isValid: false
39865
+ };
39866
+ return false;
39867
+ }
39868
+ cleanupReturnProof.hasCleanup ||= returnProof.hasCleanup;
39869
+ });
39870
+ return cleanupReturnProof;
39871
+ };
39872
+ const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
39873
+ const unwrappedReturnedValue = stripParenExpression(returnedValue);
39874
+ if (isNodeOfType(unwrappedReturnedValue, "ArrowFunctionExpression") || isNodeOfType(unwrappedReturnedValue, "FunctionExpression")) return true;
39875
+ if (isNodeOfType(unwrappedReturnedValue, "CallExpression")) {
39876
+ if (isNodeOfType(unwrappedReturnedValue.callee, "Identifier")) {
39877
+ const setterSymbol = resolveConstIdentifierAlias(unwrappedReturnedValue.callee, scopes, true);
39878
+ if (setterToStateName.has(unwrappedReturnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
39879
+ }
39880
+ const invokedFunction = resolveSynchronouslyInvokedFunction(unwrappedReturnedValue.callee, scopes);
39881
+ if (invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes)) return false;
39882
+ if (invokedFunction) {
39883
+ const cleanupReturnProof = getResolvedFunctionCleanupReturnProof(invokedFunction, scopes);
39884
+ if (cleanupReturnProof.isValid) return cleanupReturnProof.hasCleanup;
39885
+ }
39886
+ if (!invokedFunction && isNodeOfType(unwrappedReturnedValue.callee, "Identifier") && isSetterIdentifier(unwrappedReturnedValue.callee.name)) return true;
39887
+ return isCleanupReturn(unwrappedReturnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
39888
+ }
39889
+ if (isNodeOfType(unwrappedReturnedValue, "Identifier")) return true;
39131
39890
  return false;
39132
39891
  };
39892
+ const STORAGE_GLOBAL_NAMES = ["localStorage", "sessionStorage"];
39893
+ const isBrowserStorageReceiver = (receiver, scopes) => Boolean(receiver && STORAGE_GLOBAL_NAMES.some((storageName) => isProvenGlobalNamespaceReference(receiver, storageName, scopes)));
39133
39894
  const STORAGE_HOOK_PATTERN = /^use\w*Storage/i;
39134
39895
  const collectStorageHookSetterNames = (componentBody) => {
39135
39896
  const setterNames = /* @__PURE__ */ new Set();
@@ -39161,23 +39922,16 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
39161
39922
  });
39162
39923
  return didFindOpaqueSetterCall;
39163
39924
  };
39164
- const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
39925
+ const isReactUseRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "useRef", scopes, {
39165
39926
  allowGlobalReactNamespace: true,
39166
39927
  allowUnboundBareCalls: true,
39167
39928
  resolveNamedAliases: true
39168
- }) || isReactApiCall(expression, "createRef", scopes, {
39929
+ });
39930
+ const isReactRefCall = (expression, scopes) => isReactUseRefCall(expression, scopes) || isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "createRef", scopes, {
39169
39931
  allowGlobalReactNamespace: true,
39170
39932
  allowUnboundBareCalls: true,
39171
39933
  resolveNamedAliases: true
39172
- }));
39173
- const getDirectReactRefSymbol = (rawExpression, scopes) => {
39174
- const expression = stripParenExpression(rawExpression);
39175
- if (!isNodeOfType(expression, "Identifier")) return null;
39176
- const symbol = scopes.symbolFor(expression);
39177
- if (!symbol) return null;
39178
- const initializer = getDirectUnreassignedInitializer(symbol);
39179
- return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
39180
- };
39934
+ });
39181
39935
  const isReactNativeJsxElement = (openingElement, scopes) => {
39182
39936
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
39183
39937
  const symbol = scopes.symbolFor(openingElement.name);
@@ -39185,19 +39939,33 @@ const isReactNativeJsxElement = (openingElement, scopes) => {
39185
39939
  return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
39186
39940
  };
39187
39941
  const isDirectHostJsxRef = (symbol, scopes) => {
39188
- let hostRefCount = 0;
39189
- for (const reference of symbol.references) {
39190
- const expression = findTransparentExpressionRoot(reference.identifier);
39191
- const container = expression.parent;
39192
- if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
39193
- if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
39194
- const attribute = container.parent;
39195
- if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
39196
- const openingElement = attribute.parent;
39197
- if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
39198
- hostRefCount += 1;
39199
- }
39200
- return hostRefCount > 0;
39942
+ let didFindHostRef = false;
39943
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
39944
+ const pendingSymbols = [symbol];
39945
+ while (pendingSymbols.length > 0) {
39946
+ const currentSymbol = pendingSymbols.pop();
39947
+ if (!currentSymbol || visitedSymbolIds.has(currentSymbol.id)) continue;
39948
+ visitedSymbolIds.add(currentSymbol.id);
39949
+ for (const reference of currentSymbol.references) {
39950
+ const expression = findTransparentExpressionRoot(reference.identifier);
39951
+ const container = expression.parent;
39952
+ if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
39953
+ if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
39954
+ const aliasSymbol = scopes.symbolFor(container.id);
39955
+ const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
39956
+ if (!aliasSymbol || aliasInitializer !== expression) return false;
39957
+ pendingSymbols.push(aliasSymbol);
39958
+ continue;
39959
+ }
39960
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
39961
+ const attribute = container.parent;
39962
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
39963
+ const openingElement = attribute.parent;
39964
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
39965
+ didFindHostRef = true;
39966
+ }
39967
+ }
39968
+ return didFindHostRef;
39201
39969
  };
39202
39970
  const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39203
39971
  const identifier = stripParenExpression(expression);
@@ -39207,7 +39975,18 @@ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39207
39975
  const rawFirstParameter = callback.params?.[0];
39208
39976
  const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
39209
39977
  const symbol = scopes.symbolFor(identifier);
39210
- return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
39978
+ return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter && symbol.references.every((reference) => {
39979
+ if (reference.flag !== "read") return false;
39980
+ let referenceRoot = reference.identifier;
39981
+ while (referenceRoot.parent) {
39982
+ const parent = referenceRoot.parent;
39983
+ if (isNodeOfType(parent, "AssignmentExpression")) return parent.left !== referenceRoot;
39984
+ if (isNodeOfType(parent, "UpdateExpression")) return parent.argument !== referenceRoot;
39985
+ if (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) return parent.left !== referenceRoot;
39986
+ referenceRoot = parent;
39987
+ }
39988
+ return true;
39989
+ }));
39211
39990
  };
39212
39991
  const getDirectReactRefCall = (symbol, scopes) => {
39213
39992
  const initializer = getDirectUnreassignedInitializer(symbol);
@@ -39215,15 +39994,38 @@ const getDirectReactRefCall = (symbol, scopes) => {
39215
39994
  const expression = stripParenExpression(initializer);
39216
39995
  return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
39217
39996
  };
39997
+ const isEmptyGlobalMapConstruction = (rawExpression, scopes) => {
39998
+ const expression = stripParenExpression(rawExpression);
39999
+ return isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Map" && scopes.isGlobalReference(expression.callee) && expression.arguments.length === 0;
40000
+ };
40001
+ const hasEmptyRefSentinelInitializer = (refCall, scopes) => {
40002
+ if (!isReactUseRefCall(refCall, scopes) || refCall.arguments.length > 1) return false;
40003
+ const [initialValue] = refCall.arguments;
40004
+ return !initialValue || isNodeOfType(initialValue, "Literal") && initialValue.value === null || isNodeOfType(initialValue, "Identifier") && initialValue.name === "undefined" && scopes.isGlobalReference(initialValue);
40005
+ };
40006
+ const isDirectLazyEmptyMapInitialization = (currentExpression, symbol, scopes) => {
40007
+ const assignment = currentExpression.parent;
40008
+ if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== currentExpression || assignment.operator !== "??=" || !isEmptyGlobalMapConstruction(assignment.right, scopes)) return false;
40009
+ const refOwner = findEnclosingFunction$1(symbol.bindingIdentifier);
40010
+ return refOwner !== null && findEnclosingFunction$1(assignment) === refOwner;
40011
+ };
39218
40012
  const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
39219
- const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
39220
- if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
40013
+ const refCall = getDirectReactRefCall(symbol, scopes);
40014
+ const initialValue = refCall?.arguments[0];
40015
+ const hasDirectEmptyMapInitializer = Boolean(initialValue && isEmptyGlobalMapConstruction(initialValue, scopes));
40016
+ const hasLazyEmptyMapInitializer = Boolean(refCall && hasEmptyRefSentinelInitializer(refCall, scopes));
40017
+ if (!hasDirectEmptyMapInitializer && !hasLazyEmptyMapInitializer) return false;
39221
40018
  let intrinsicValueWriteCount = 0;
40019
+ let lazyEmptyMapInitializationCount = 0;
39222
40020
  for (const reference of symbol.references) {
39223
40021
  const identifier = findTransparentExpressionRoot(reference.identifier);
39224
40022
  const currentMember = identifier.parent;
39225
40023
  if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
39226
40024
  const currentExpression = findTransparentExpressionRoot(currentMember);
40025
+ if (hasLazyEmptyMapInitializer && isDirectLazyEmptyMapInitialization(currentExpression, symbol, scopes)) {
40026
+ lazyEmptyMapInitializationCount += 1;
40027
+ continue;
40028
+ }
39227
40029
  const methodMember = currentExpression.parent;
39228
40030
  if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
39229
40031
  const methodName = getStaticPropertyName(methodMember);
@@ -39236,7 +40038,7 @@ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
39236
40038
  if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
39237
40039
  intrinsicValueWriteCount += 1;
39238
40040
  }
39239
- return intrinsicValueWriteCount > 0;
40041
+ return intrinsicValueWriteCount > 0 && (hasDirectEmptyMapInitializer || lazyEmptyMapInitializationCount === 1);
39240
40042
  };
39241
40043
  const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39242
40044
  const expression = stripParenExpression(rawExpression);
@@ -39250,7 +40052,11 @@ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollecti
39250
40052
  }
39251
40053
  if (isNodeOfType(expression, "MemberExpression")) {
39252
40054
  if (getStaticPropertyName(expression) === "current") {
39253
- const symbol = getDirectReactRefSymbol(expression.object, scopes);
40055
+ const symbol = resolveReactRefSymbol(expression, scopes, {
40056
+ allowUnboundBareCalls: true,
40057
+ includeCreateRef: true,
40058
+ resolveNamedAliases: true
40059
+ });
39254
40060
  return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
39255
40061
  }
39256
40062
  return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
@@ -39268,19 +40074,80 @@ const isCommittedDomSyncNode = (node, scopes) => {
39268
40074
  if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
39269
40075
  return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
39270
40076
  };
39271
- const isExternalSyncNode = (node) => {
39272
- if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
39273
- if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
40077
+ const isProvenHttpClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40078
+ const expression = stripParenExpression(rawExpression);
40079
+ if (isNodeOfType(expression, "Identifier")) {
40080
+ const receiverSymbol = scopes.symbolFor(expression);
40081
+ if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
40082
+ }
40083
+ if (hasProvenGlobalNamespaceReference(expression, EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES, scopes)) return true;
40084
+ const moduleBinding = resolveExternalModuleBinding(expression, scopes);
40085
+ if (moduleBinding && EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES.has(moduleBinding.source) && (moduleBinding.isModuleObject || moduleBinding.exportedName === "default" && EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.has(moduleBinding.source) || isKnownExternalSyncDirectModuleBinding(moduleBinding))) return true;
40086
+ if (isNodeOfType(expression, "CallExpression")) {
40087
+ const callee = stripParenExpression(expression.callee);
40088
+ return Boolean(isNodeOfType(callee, "MemberExpression") && getStaticPropertyName(callee) === "create" && isProvenHttpClientReceiver(callee.object, scopes, visitedSymbolIds));
40089
+ }
40090
+ if (!isNodeOfType(expression, "Identifier")) return false;
40091
+ const symbol = scopes.symbolFor(expression);
40092
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40093
+ if (resolveExternalModuleBinding(expression, scopes, /* @__PURE__ */ new Set(), true)) return false;
40094
+ const initializer = getDirectUnreassignedInitializer(symbol);
40095
+ return Boolean(initializer && isProvenHttpClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40096
+ };
40097
+ const isProvenTanStackQueryClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40098
+ const expression = stripParenExpression(rawExpression);
40099
+ if (isNodeOfType(expression, "Identifier")) {
40100
+ const receiverSymbol = scopes.symbolFor(expression);
40101
+ if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
40102
+ }
40103
+ if (isNodeOfType(expression, "CallExpression")) {
40104
+ const binding = resolveExternalModuleBinding(expression.callee, scopes);
40105
+ return Boolean(binding && binding.source === "@tanstack/react-query" && binding.exportedName === "useQueryClient");
40106
+ }
40107
+ if (isNodeOfType(expression, "NewExpression")) {
40108
+ const binding = resolveExternalModuleBinding(expression.callee, scopes);
40109
+ return Boolean(binding && TANSTACK_QUERY_CLIENT_MODULE_SOURCES.has(binding.source) && binding.exportedName === "QueryClient");
40110
+ }
40111
+ if (!isNodeOfType(expression, "Identifier")) return false;
40112
+ const symbol = scopes.symbolFor(expression);
40113
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40114
+ const initializer = getDirectUnreassignedInitializer(symbol);
40115
+ return Boolean(initializer && isProvenTanStackQueryClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40116
+ };
40117
+ const isProvenExternalResourceReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40118
+ const expression = stripParenExpression(rawExpression);
40119
+ if (isDerivedFromProvenDomRefCurrent(expression, scopes) || isProvenBrowserApiReceiver(expression, "dom-event-target", scopes) || isProvenBrowserApiReceiver(expression, "xml-http-request", scopes) || isProvenHttpClientReceiver(expression, scopes)) return true;
40120
+ if (isNodeOfType(expression, "NewExpression")) return hasProvenGlobalNamespaceReference(expression.callee, EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES, scopes);
40121
+ if (!isNodeOfType(expression, "Identifier")) return false;
40122
+ const symbol = scopes.symbolFor(expression);
40123
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40124
+ const initializer = getDirectUnreassignedInitializer(symbol);
40125
+ return Boolean(initializer && isProvenExternalResourceReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40126
+ };
40127
+ const isProvenExternalSyncDirectCallee = (callee, scopes) => {
40128
+ if (hasProvenGlobalNamespaceReference(callee, EXTERNAL_SYNC_DIRECT_CALLEE_NAMES, scopes)) return true;
40129
+ const moduleBinding = resolveExternalModuleBinding(callee, scopes);
40130
+ return Boolean(moduleBinding && isKnownExternalSyncDirectModuleBinding(moduleBinding));
40131
+ };
40132
+ const isExternalSyncNode = (node, scopes) => {
40133
+ if (isNodeOfType(node, "NewExpression")) return hasProvenGlobalNamespaceReference(node.callee, EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS, scopes);
40134
+ if (isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression")) {
40135
+ const mutationTarget = isNodeOfType(node, "AssignmentExpression") ? node.left : node.argument;
40136
+ return isNodeOfType(mutationTarget, "MemberExpression") && getStaticPropertyName(mutationTarget) === "current" && Boolean(resolveReactRefSymbol(mutationTarget, scopes, {
40137
+ allowUnboundBareCalls: true,
40138
+ includeCreateRef: true,
40139
+ resolveNamedAliases: true
40140
+ }));
40141
+ }
39274
40142
  if (!isNodeOfType(node, "CallExpression")) return false;
39275
- if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
40143
+ if (isProvenExternalSyncDirectCallee(node.callee, scopes)) return true;
39276
40144
  if (!isNodeOfType(node.callee, "MemberExpression")) return false;
39277
40145
  const propertyName = getStaticPropertyName(node.callee);
39278
40146
  if (propertyName === null) return false;
39279
- if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
39280
- if (isBrowserStorageReceiver(node.callee.object)) return true;
39281
- if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
39282
- const receiverRootName = getRootIdentifierName(node.callee.object);
39283
- return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
40147
+ if (isBrowserStorageReceiver(node.callee.object, scopes)) return true;
40148
+ if (TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES.has(propertyName)) return isProvenTanStackQueryClientReceiver(node.callee.object, scopes);
40149
+ if (EXTERNAL_SYNC_HTTP_METHOD_NAMES.has(propertyName)) return isProvenHttpClientReceiver(node.callee.object, scopes);
40150
+ return EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName) && isProvenExternalResourceReceiver(node.callee.object, scopes);
39284
40151
  };
39285
40152
  const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
39286
40153
  if (!isFunctionLike$1(effectCallback)) return false;
@@ -39289,7 +40156,7 @@ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateNa
39289
40156
  } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
39290
40157
  let didFindExternalCall = false;
39291
40158
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
39292
- if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
40159
+ if (isExternalSyncNode(child, scopes) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
39293
40160
  });
39294
40161
  return didFindExternalCall;
39295
40162
  };
@@ -39311,11 +40178,11 @@ const noEffectChain = defineRule({
39311
40178
  setterToStateName.set(binding.setterName, binding.valueName);
39312
40179
  if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
39313
40180
  const stateIdentifier = binding.declarator.id.elements[0];
40181
+ const setterIdentifier = binding.declarator.id.elements[1];
39314
40182
  if (isNodeOfType(stateIdentifier, "Identifier")) {
39315
40183
  const stateSymbol = context.scopes.symbolFor(stateIdentifier);
39316
40184
  if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
39317
40185
  }
39318
- const setterIdentifier = binding.declarator.id.elements[1];
39319
40186
  if (isNodeOfType(setterIdentifier, "Identifier")) {
39320
40187
  const setterSymbol = context.scopes.symbolFor(setterIdentifier);
39321
40188
  if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
@@ -39328,10 +40195,11 @@ const noEffectChain = defineRule({
39328
40195
  const callback = getEffectCallback(effectCall, context.scopes);
39329
40196
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
39330
40197
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
39331
- const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
40198
+ const stateWrites = collectStateWritesInEffect(collectStateWriteAnalysisFunctions(callback, context.scopes, setterSymbolIdToStateName), setterSymbolIdToStateName, context.scopes);
39332
40199
  const writtenStateNames = new Set(stateWrites.keys());
39333
40200
  effectInfos.push({
39334
40201
  node: effectCall,
40202
+ callback,
39335
40203
  dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
39336
40204
  stateWrites,
39337
40205
  analysisFunctions,
@@ -39348,10 +40216,10 @@ const noEffectChain = defineRule({
39348
40216
  if (readerEffect.isExternalSync) continue;
39349
40217
  if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
39350
40218
  let chainedStateName = null;
39351
- for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
40219
+ for (const writtenName of writerEffect.stateWrites.keys()) {
39352
40220
  const writtenStateSymbolId = stateSymbolIds.get(writtenName);
39353
40221
  if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
39354
- if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
40222
+ if (!canStateWriteReachReaderWork(writtenName, writerEffect, readerEffect, stateSymbolIds, context.scopes)) continue;
39355
40223
  chainedStateName = writtenName;
39356
40224
  break;
39357
40225
  }
@@ -44136,16 +45004,6 @@ const noManyBooleanProps = defineRule({
44136
45004
  }
44137
45005
  });
44138
45006
  //#endregion
44139
- //#region src/plugin/utils/is-global-match-media-call.ts
44140
- const isGlobalMatchMediaCall = (node, scopes) => {
44141
- if (!isNodeOfType(node, "CallExpression")) return false;
44142
- const callee = stripParenExpression(node.callee);
44143
- if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
44144
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
44145
- const receiver = stripParenExpression(callee.object);
44146
- return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
44147
- };
44148
- //#endregion
44149
45007
  //#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
44150
45008
  const REACT_USE_STATE_OPTIONS = {
44151
45009
  allowGlobalReactNamespace: true,
@@ -46535,6 +47393,103 @@ const EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1 = new Set([
46535
47393
  "useVisibility",
46536
47394
  "useWindowSize"
46537
47395
  ]);
47396
+ const EXTERNAL_SUBSCRIPTION_PRIMITIVE_RESULT_HOOK_NAMES = new Set([
47397
+ "useMatchMedia",
47398
+ "useMediaQuery",
47399
+ "useVisibility"
47400
+ ]);
47401
+ const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
47402
+ const getImportedExternalSubscriptionHookName = (analysis, rawCallee) => {
47403
+ const callee = stripParenExpression(rawCallee);
47404
+ if (isNodeOfType(callee, "Identifier")) {
47405
+ const calleeRef = getRef(analysis, callee);
47406
+ if (!calleeRef || !isImportBindingRef(calleeRef)) return null;
47407
+ const importBinding = getImportBindingForName(callee, callee.name);
47408
+ if (!importBinding || importBinding.isNamespace) return null;
47409
+ if (importBinding.exportedName && EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(importBinding.exportedName)) return importBinding.exportedName;
47410
+ return importBinding.exportedName === "default" && EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(callee.name) ? callee.name : null;
47411
+ }
47412
+ if (!isNodeOfType(callee, "MemberExpression")) return null;
47413
+ const hookName = getStaticMemberPropertyName(callee);
47414
+ const namespaceIdentifier = stripParenExpression(callee.object);
47415
+ if (!hookName || !EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(hookName) || !isNodeOfType(namespaceIdentifier, "Identifier")) return null;
47416
+ const namespaceRef = getRef(analysis, namespaceIdentifier);
47417
+ if (!namespaceRef || !isImportBindingRef(namespaceRef)) return null;
47418
+ return getImportBindingForName(namespaceIdentifier, namespaceIdentifier.name)?.isNamespace ? hookName : null;
47419
+ };
47420
+ const isSafeExternalSubscriptionResultBinding = (bindingIdentifier, bindingPattern) => {
47421
+ let current = bindingIdentifier;
47422
+ let didCrossDestructuringBoundary = false;
47423
+ while (current !== bindingPattern) {
47424
+ const parent = current.parent;
47425
+ if (!parent || isNodeOfType(parent, "AssignmentPattern") || isNodeOfType(parent, "RestElement")) return false;
47426
+ if (isNodeOfType(parent, "Property")) {
47427
+ if (parent.value !== current) return false;
47428
+ didCrossDestructuringBoundary = true;
47429
+ }
47430
+ if (isNodeOfType(parent, "ArrayPattern")) didCrossDestructuringBoundary = true;
47431
+ current = parent;
47432
+ }
47433
+ return didCrossDestructuringBoundary;
47434
+ };
47435
+ const getVariablesDefinedByDeclarator = (analysis, declarator) => analysis.scopeManager.scopes.flatMap((scope) => scope.variables).filter((variable) => variable.defs.some((definition) => definition.node === declarator));
47436
+ const hasUnsafeExternalSubscriptionBindingUse = (analysis, variable, visitedVariables = /* @__PURE__ */ new Set()) => {
47437
+ if (visitedVariables.has(variable)) return false;
47438
+ visitedVariables.add(variable);
47439
+ return variable.references.some((candidateReference) => {
47440
+ if (candidateReference.init) return false;
47441
+ if (candidateReference.isWrite()) return true;
47442
+ let usageRoot = findTransparentExpressionRoot(candidateReference.identifier);
47443
+ while (isNodeOfType(usageRoot.parent, "MemberExpression") && usageRoot.parent.object === usageRoot) usageRoot = findTransparentExpressionRoot(usageRoot.parent);
47444
+ const usageParent = usageRoot.parent;
47445
+ if (isNodeOfType(usageParent, "AssignmentExpression") && usageParent.left === usageRoot || isNodeOfType(usageParent, "UpdateExpression") && usageParent.argument === usageRoot || isNodeOfType(usageParent, "UnaryExpression") && usageParent.operator === "delete" && usageParent.argument === usageRoot) return true;
47446
+ let usageAncestor = usageRoot.parent;
47447
+ while (usageAncestor && !isNodeOfType(usageAncestor, "VariableDeclarator") && !isFunctionLike$1(usageAncestor) && !isNodeOfType(usageAncestor, "Program")) usageAncestor = usageAncestor.parent;
47448
+ if (isNodeOfType(usageAncestor, "VariableDeclarator") && usageAncestor.init) {
47449
+ const aliasVariables = getVariablesDefinedByDeclarator(analysis, usageAncestor);
47450
+ return aliasVariables.length === 0 || aliasVariables.some((aliasVariable) => hasUnsafeExternalSubscriptionBindingUse(analysis, aliasVariable, visitedVariables));
47451
+ }
47452
+ return false;
47453
+ });
47454
+ };
47455
+ const hasOnlySafeExternalSubscriptionResultBindings = (analysis, declarator, variables = getVariablesDefinedByDeclarator(analysis, declarator)) => {
47456
+ return variables.length > 0 && variables.every((variable) => {
47457
+ const bindingIdentifier = variable.defs.find((candidateDefinition) => candidateDefinition.node === declarator)?.name;
47458
+ return Boolean(bindingIdentifier && isNodeOfType(bindingIdentifier, "Identifier") && isSafeExternalSubscriptionResultBinding(bindingIdentifier, declarator.id) && !hasUnsafeExternalSubscriptionBindingUse(analysis, variable));
47459
+ });
47460
+ };
47461
+ const hasImmutableExternalSubscriptionCallResult = (analysis, rawCallee, relatedRefs, allowWholeResult) => {
47462
+ const callee = stripParenExpression(rawCallee);
47463
+ const callExpression = callee.parent;
47464
+ if (!callExpression || !isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== callee) return false;
47465
+ const initializer = findTransparentExpressionRoot(callExpression);
47466
+ const declarator = initializer.parent;
47467
+ if (!declarator || !isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== initializer || !isNodeOfType(declarator.parent, "VariableDeclaration") || declarator.parent.kind !== "const") return false;
47468
+ const relatedVariables = getVariablesDefinedByDeclarator(analysis, declarator).filter((variable) => relatedRefs.some((relatedRef) => relatedRef.resolved === variable));
47469
+ if (relatedVariables.length === 0) return false;
47470
+ return isNodeOfType(declarator.id, "Identifier") ? allowWholeResult && relatedVariables.every((variable) => !hasUnsafeExternalSubscriptionBindingUse(analysis, variable)) : hasOnlySafeExternalSubscriptionResultBindings(analysis, declarator, relatedVariables);
47471
+ };
47472
+ const isImmutableImportedExternalSubscriptionHookCallee = (analysis, rawCallee, relatedRefs) => {
47473
+ const hookName = getImportedExternalSubscriptionHookName(analysis, rawCallee);
47474
+ return Boolean(hookName && hasImmutableExternalSubscriptionCallResult(analysis, rawCallee, relatedRefs, EXTERNAL_SUBSCRIPTION_PRIMITIVE_RESULT_HOOK_NAMES.has(hookName)));
47475
+ };
47476
+ const isExternalSubscriptionHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
47477
+ const declarator = def.node;
47478
+ if (!isNodeOfType(declarator, "VariableDeclarator") || !declarator.init || !isNodeOfType(declarator.parent, "VariableDeclaration") || declarator.parent.kind !== "const") return false;
47479
+ const initializer = stripParenExpression(declarator.init);
47480
+ if (!isNodeOfType(initializer, "CallExpression")) return false;
47481
+ const hookName = getImportedExternalSubscriptionHookName(analysis, initializer.callee);
47482
+ if (!hookName) return false;
47483
+ if (isNodeOfType(declarator.id, "Identifier")) return EXTERNAL_SUBSCRIPTION_PRIMITIVE_RESULT_HOOK_NAMES.has(hookName) && ref.resolved && !hasUnsafeExternalSubscriptionBindingUse(analysis, ref.resolved);
47484
+ const bindingIdentifier = def.name;
47485
+ return isNodeOfType(bindingIdentifier, "Identifier") && isSafeExternalSubscriptionResultBinding(bindingIdentifier, declarator.id) && ref.resolved && !hasUnsafeExternalSubscriptionBindingUse(analysis, ref.resolved);
47486
+ }));
47487
+ const isExternalSubscriptionHookResultArgument = (analysis, argument) => {
47488
+ const unwrappedArgument = stripParenExpression(argument);
47489
+ if (!isNodeOfType(unwrappedArgument, "Identifier")) return false;
47490
+ const argumentRef = getRef(analysis, unwrappedArgument);
47491
+ return Boolean(argumentRef && isExternalSubscriptionHookResultRef(analysis, argumentRef));
47492
+ };
46538
47493
  const isCallbackPropReference = (analysis, ref) => {
46539
47494
  if (!isProp(analysis, ref)) return false;
46540
47495
  const identifier = ref.identifier;
@@ -46567,8 +47522,9 @@ const isParentWiredHookCalleeRef = (analysis, ref) => {
46567
47522
  if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== identifier) return false;
46568
47523
  return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isCallbackPropReference(analysis, downstreamRef)));
46569
47524
  };
46570
- const getLocalHookExternalStateProof = (analysis, ref) => {
47525
+ const getLocalHookExternalStateProof = (analysis, ref, scopes) => {
46571
47526
  let hookFunction = resolveToFunction(ref);
47527
+ let didResolveThroughResultBinding = false;
46572
47528
  if (!hookFunction) for (const definition of ref.resolved?.defs ?? []) {
46573
47529
  const definitionNode = definition.node;
46574
47530
  if (!isNodeOfType(definitionNode, "VariableDeclarator") || !definitionNode.init) continue;
@@ -46579,29 +47535,27 @@ const getLocalHookExternalStateProof = (analysis, ref) => {
46579
47535
  const calleeReference = getRef(analysis, callee);
46580
47536
  if (!calleeReference) continue;
46581
47537
  hookFunction = resolveToFunction(calleeReference);
46582
- if (hookFunction) break;
47538
+ if (hookFunction) {
47539
+ didResolveThroughResultBinding = true;
47540
+ break;
47541
+ }
46583
47542
  }
46584
47543
  if (!hookFunction) return null;
46585
- const returnedReferences = collectFunctionReturnStatements(hookFunction).flatMap((returnStatement) => returnStatement.argument ? getDownstreamRefs(analysis, returnStatement.argument) : []);
47544
+ if (didResolveThroughResultBinding && (!ref.resolved || hasUnsafeExternalSubscriptionBindingUse(analysis, ref.resolved))) return false;
47545
+ if (isNodeOfType(hookFunction, "ArrowFunctionExpression") && !isNodeOfType(hookFunction.body, "BlockStatement") && isReactHookCall(stripParenExpression(hookFunction.body), "useSyncExternalStore", scopes)) return true;
47546
+ const returnStatements = collectFunctionReturnStatements(hookFunction);
47547
+ if (returnStatements.length > 0 && returnStatements.every((returnStatement) => returnStatement.argument && isReactHookCall(stripParenExpression(returnStatement.argument), "useSyncExternalStore", scopes))) return true;
47548
+ const returnedReferences = returnStatements.flatMap((returnStatement) => returnStatement.argument ? getDownstreamRefs(analysis, returnStatement.argument) : []);
46586
47549
  if (returnedReferences.length === 0) return null;
46587
47550
  return returnedReferences.every((returnedReference) => isState(analysis, returnedReference) && isExternallyDrivenState(analysis, returnedReference));
46588
47551
  };
46589
- const isExternalSubscriptionHookRef = (analysis, ref) => {
46590
- const identifier = ref.identifier;
46591
- if (!isNodeOfType(identifier, "Identifier")) return false;
46592
- const localHookProof = getLocalHookExternalStateProof(analysis, ref);
46593
- if (localHookProof !== null) return localHookProof;
46594
- if (EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(identifier.name) && isCalleePosition(identifier)) return true;
46595
- return Boolean(ref.resolved?.defs.some((def) => {
46596
- const node = def.node;
46597
- if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
46598
- const initializer = stripParenExpression(node.init);
46599
- if (!isNodeOfType(initializer, "CallExpression")) return false;
46600
- const callee = stripParenExpression(initializer.callee);
46601
- return isNodeOfType(callee, "Identifier") && EXTERNAL_SUBSCRIPTION_HOOK_NAMES$1.has(callee.name);
46602
- }));
47552
+ const isImmutableLocalExternalStoreHookCallee = (analysis, rawCallee, relatedRefs, scopes) => {
47553
+ const callee = stripParenExpression(rawCallee);
47554
+ if (!isNodeOfType(callee, "Identifier")) return false;
47555
+ const calleeReference = getRef(analysis, callee);
47556
+ if (!calleeReference || getLocalHookExternalStateProof(analysis, calleeReference, scopes) !== true) return false;
47557
+ return hasImmutableExternalSubscriptionCallResult(analysis, callee, relatedRefs, true);
46603
47558
  };
46604
- const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
46605
47559
  const isCalleePosition = (identifier) => {
46606
47560
  const parent = identifier.parent;
46607
47561
  return Boolean(parent && (isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression")) && parent.callee === identifier);
@@ -46662,25 +47616,35 @@ const noPassDataToParent = defineRule({
46662
47616
  return getFunctionalUpdaterDataRefs(analysis, argument);
46663
47617
  }
46664
47618
  if (isHandlerBagArgument(analysis, argument)) return [];
47619
+ if (isExternalSubscriptionHookResultArgument(analysis, argument)) return [];
46665
47620
  if (isParentWiredHookResultArgument(analysis, argument)) return [];
46666
47621
  if (isNodeOfType(argument, "Identifier")) {
46667
47622
  const argumentRef = getRef(analysis, argument);
46668
47623
  if (argumentRef && resolveToFunction(argumentRef)) return [];
46669
47624
  }
46670
47625
  return getDownstreamRefs(analysis, argument);
46671
- }).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) || isExternalSubscriptionHookRef(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
46672
- if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref, context.scopes)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
47626
+ }).flatMap((argumentRef) => {
47627
+ if (isExternallyDrivenState(analysis, argumentRef) || getLocalHookExternalStateProof(analysis, argumentRef, context.scopes) === true) return [];
47628
+ const upstreamRefs = getUpstreamRefs(analysis, argumentRef);
47629
+ return upstreamRefs.filter((upstreamRef) => {
47630
+ if (!isLeafRef(upstreamRef)) return false;
47631
+ return !isImmutableImportedExternalSubscriptionHookCallee(analysis, upstreamRef.identifier, upstreamRefs) && !isImmutableLocalExternalStoreHookCallee(analysis, upstreamRef.identifier, upstreamRefs, context.scopes);
47632
+ });
47633
+ });
47634
+ if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref, context.scopes)) {
47635
+ const wrapperUpstreamRefs = getArgsUpstreamRefs(analysis, ref);
47636
+ argsUpstreamRefs.push(...wrapperUpstreamRefs.filter((upstreamRef) => isLeafRef(upstreamRef) && !isImmutableImportedExternalSubscriptionHookCallee(analysis, upstreamRef.identifier, wrapperUpstreamRefs) && !isImmutableLocalExternalStoreHookCallee(analysis, upstreamRef.identifier, wrapperUpstreamRefs, context.scopes)));
47637
+ }
46673
47638
  if (!argsUpstreamRefs.some((argRef) => {
46674
- if (isUseStateIdentifier(argRef.identifier)) return false;
46675
- if (isExternalSubscriptionHookRef(analysis, argRef)) return false;
47639
+ const argIdentifier = argRef.identifier;
47640
+ if (isUseStateIdentifier(argIdentifier)) return false;
46676
47641
  if (isProp(analysis, argRef)) return false;
46677
- if (isUseRefIdentifier(argRef.identifier)) return false;
47642
+ if (isUseRefIdentifier(argIdentifier)) return false;
46678
47643
  if (isRefCurrent(argRef)) return false;
46679
47644
  if (isConstant(argRef)) return false;
46680
47645
  if (isParentWiredHookResultRef(analysis, argRef)) return false;
46681
47646
  if (isParentWiredHookCalleeRef(analysis, argRef)) return false;
46682
47647
  if (resolveToFunction(argRef)) return false;
46683
- const argIdentifier = argRef.identifier;
46684
47648
  if (isImportBindingRef(argRef) && !isCalleePosition(argIdentifier)) return false;
46685
47649
  if (isNodeOfType(argIdentifier, "Identifier") && argIdentifier.name === "undefined") return false;
46686
47650
  return true;
@@ -55671,17 +56635,6 @@ const preferModuleScopePureFunction = defineRule({
55671
56635
  }
55672
56636
  });
55673
56637
  //#endregion
55674
- //#region src/plugin/utils/get-require-call-source.ts
55675
- const getRequireCallSource = (expression) => {
55676
- const unwrappedExpression = stripParenExpression(expression);
55677
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
55678
- if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
55679
- if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
55680
- const [firstArgument] = unwrappedExpression.arguments ?? [];
55681
- if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
55682
- return typeof firstArgument.value === "string" ? firstArgument.value : null;
55683
- };
55684
- //#endregion
55685
56638
  //#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
55686
56639
  const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
55687
56640
  const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
@@ -60389,6 +61342,717 @@ const isSetterCalledDuringRender = (root, setterName) => {
60389
61342
  return found;
60390
61343
  };
60391
61344
  //#endregion
61345
+ //#region src/plugin/rules/state-and-effects/utils/create-external-location-invalidation-checker.ts
61346
+ const HISTORY_LOCATION_MUTATION_METHOD_NAMES = new Set(["pushState", "replaceState"]);
61347
+ const AGGREGATE_MUTATION_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
61348
+ const OBJECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
61349
+ "assign",
61350
+ "defineProperties",
61351
+ "defineProperty",
61352
+ "setPrototypeOf"
61353
+ ]);
61354
+ const REFLECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
61355
+ "defineProperty",
61356
+ "deleteProperty",
61357
+ "set",
61358
+ "setPrototypeOf"
61359
+ ]);
61360
+ const LOCATION_CHANGE_EVENT_NAMES = new Set(["hashchange", "popstate"]);
61361
+ const containsGlobalLocationSnapshotRead = (node, scopes) => {
61362
+ let didFindLocationSnapshotRead = false;
61363
+ walkAst(node, (child) => {
61364
+ if (didFindLocationSnapshotRead) return false;
61365
+ if (child !== node && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
61366
+ if (!isProvenGlobalNamespaceReference(child, "location", scopes)) return;
61367
+ didFindLocationSnapshotRead = true;
61368
+ return false;
61369
+ });
61370
+ return didFindLocationSnapshotRead;
61371
+ };
61372
+ const resolveExactLocalOrReactCallbackFunction = (expression, scopes) => {
61373
+ const localFunction = resolveExactLocalFunction(expression, scopes);
61374
+ if (isFunctionLike$1(localFunction)) return localFunction;
61375
+ const unwrappedExpression = stripParenExpression(expression);
61376
+ let callbackSource = unwrappedExpression;
61377
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
61378
+ const callbackSymbol = resolveConstIdentifierAlias(unwrappedExpression, scopes);
61379
+ if (callbackSymbol?.kind !== "const" || !callbackSymbol.initializer) return null;
61380
+ callbackSource = callbackSymbol.initializer;
61381
+ }
61382
+ const unwrappedCallbackSource = stripParenExpression(callbackSource);
61383
+ if (!isNodeOfType(unwrappedCallbackSource, "CallExpression") || !isReactApiCall(unwrappedCallbackSource, "useCallback", scopes, { resolveNamedAliases: true })) return null;
61384
+ const callback = unwrappedCallbackSource.arguments?.[0];
61385
+ if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
61386
+ return resolveExactLocalFunction(stripParenExpression(callback), scopes);
61387
+ };
61388
+ const bindingReadsExactGlobalLocationSnapshot = (bindingIdentifier, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
61389
+ const symbol = scopes.symbolFor(bindingIdentifier);
61390
+ if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
61391
+ if ((symbol.kind === "function" ? scopes.scopeFor(symbol.declarationNode) : symbol.scope).symbols.some((candidateSymbol) => candidateSymbol.name === symbol.name && candidateSymbol.references.some((reference) => reference.flag !== "read"))) return false;
61392
+ const resolvedFunction = resolveExactLocalOrReactCallbackFunction(bindingIdentifier, scopes);
61393
+ const initializer = isFunctionLike$1(resolvedFunction) ? resolvedFunction.body : getDirectUnreassignedInitializer(symbol);
61394
+ if (!initializer) return false;
61395
+ const nextVisitedSymbolIds = new Set(visitedSymbolIds);
61396
+ nextVisitedSymbolIds.add(symbol.id);
61397
+ if (containsGlobalLocationSnapshotRead(initializer, scopes)) return true;
61398
+ let didFindAliasedLocationSnapshotRead = false;
61399
+ walkAst(initializer, (child) => {
61400
+ if (didFindAliasedLocationSnapshotRead) return false;
61401
+ if (child !== initializer && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
61402
+ if (!isNodeOfType(child, "Identifier")) return;
61403
+ if (bindingReadsExactGlobalLocationSnapshot(child, scopes, nextVisitedSymbolIds)) {
61404
+ didFindAliasedLocationSnapshotRead = true;
61405
+ return false;
61406
+ }
61407
+ });
61408
+ return didFindAliasedLocationSnapshotRead;
61409
+ };
61410
+ const hasRenderReachableLocationSnapshotRead = (componentBody, renderReachableExpressions, directRenderNames, scopes) => {
61411
+ if (renderReachableExpressions.some((expression) => containsGlobalLocationSnapshotRead(expression, scopes))) return true;
61412
+ for (const statement of componentBody.body ?? []) {
61413
+ if (isNodeOfType(statement, "FunctionDeclaration") && statement.id) {
61414
+ if (directRenderNames.has(statement.id.name) && bindingReadsExactGlobalLocationSnapshot(statement.id, scopes)) return true;
61415
+ continue;
61416
+ }
61417
+ if (!isNodeOfType(statement, "VariableDeclaration")) continue;
61418
+ for (const declarator of statement.declarations ?? []) {
61419
+ if (!isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
61420
+ if (!directRenderNames.has(declarator.id.name)) continue;
61421
+ if (bindingReadsExactGlobalLocationSnapshot(declarator.id, scopes)) return true;
61422
+ }
61423
+ }
61424
+ return false;
61425
+ };
61426
+ const isGlobalHistoryLocationMutation = (node, scopes) => {
61427
+ if (!isNodeOfType(node, "CallExpression")) return false;
61428
+ const callee = stripParenExpression(node.callee);
61429
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
61430
+ const methodName = getStaticPropertyName(callee);
61431
+ return Boolean(methodName && HISTORY_LOCATION_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "history", scopes));
61432
+ };
61433
+ const getStaticLocationChangeEvent = (node) => {
61434
+ if (!node) return null;
61435
+ const eventNameNode = stripParenExpression(node);
61436
+ if (!isNodeOfType(eventNameNode, "Literal") || typeof eventNameNode.value !== "string") return null;
61437
+ return LOCATION_CHANGE_EVENT_NAMES.has(eventNameNode.value) ? eventNameNode.value : null;
61438
+ };
61439
+ const addToSetIndex = (index, key, value) => {
61440
+ const indexedValues = index.get(key) ?? /* @__PURE__ */ new Set();
61441
+ indexedValues.add(value);
61442
+ index.set(key, indexedValues);
61443
+ };
61444
+ const getSynchronousInvocationExpression = (functionNode, scopes) => {
61445
+ const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
61446
+ if (!executesDuringRender(functionExpressionRoot, scopes)) return null;
61447
+ const parent = functionExpressionRoot.parent;
61448
+ return isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression") ? parent : null;
61449
+ };
61450
+ const getLocationListenerOperation = (callExpression, scopes) => {
61451
+ if (!isNodeOfType(callExpression, "CallExpression")) return null;
61452
+ const callee = stripParenExpression(callExpression.callee);
61453
+ const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : isNodeOfType(callee, "Identifier") ? callee.name : null;
61454
+ if (methodName !== "addEventListener" && methodName !== "removeEventListener") return null;
61455
+ if (!(isNodeOfType(callee, "MemberExpression") ? isProvenGlobalObjectReference(callee.object, scopes) : isNodeOfType(callee, "Identifier") && scopes.isGlobalReference(callee))) return null;
61456
+ const eventName = getStaticLocationChangeEvent(callExpression.arguments?.[0]);
61457
+ if (!eventName) return null;
61458
+ const listenerExpression = callExpression.arguments?.[1];
61459
+ if (!listenerExpression || isNodeOfType(listenerExpression, "SpreadElement")) return null;
61460
+ const listenerFunction = resolveExactLocalOrReactCallbackFunction(listenerExpression, scopes);
61461
+ if (!isFunctionLike$1(listenerFunction)) return null;
61462
+ const captureArgument = callExpression.arguments?.[2];
61463
+ const capture = isNodeOfType(captureArgument, "SpreadElement") ? null : resolveEventListenerCapture(captureArgument, {
61464
+ allowComputedString: true,
61465
+ allowIndeterminateEntries: true
61466
+ });
61467
+ return {
61468
+ operation: methodName === "addEventListener" ? "add" : "remove",
61469
+ registration: {
61470
+ callExpression,
61471
+ listenerFunction,
61472
+ capture,
61473
+ eventName
61474
+ }
61475
+ };
61476
+ };
61477
+ const buildLocationInvalidationIndex = (componentBody, componentFunction, context) => {
61478
+ const index = {
61479
+ componentFunction,
61480
+ context,
61481
+ effectCallbacks: /* @__PURE__ */ new Set(),
61482
+ expressionsByOwner: /* @__PURE__ */ new Map(),
61483
+ historyMutationsByOwner: /* @__PURE__ */ new Map(),
61484
+ awaitExpressionsByOwner: /* @__PURE__ */ new Map(),
61485
+ callSitesByFunction: /* @__PURE__ */ new Map(),
61486
+ calledFunctionByExpression: /* @__PURE__ */ new Map(),
61487
+ synchronousInvocationsByFunction: /* @__PURE__ */ new Map(),
61488
+ synchronousCallbacksByExpression: /* @__PURE__ */ new Map(),
61489
+ callsByCalleeSymbolId: /* @__PURE__ */ new Map(),
61490
+ identifierCalls: /* @__PURE__ */ new Set(),
61491
+ listenerRegistrations: [],
61492
+ listenerRemovals: [],
61493
+ mountedListenerFunctions: /* @__PURE__ */ new Set(),
61494
+ mutationExecutionsByOwner: /* @__PURE__ */ new Map(),
61495
+ synchronousMutationResultByFunction: /* @__PURE__ */ new Map()
61496
+ };
61497
+ walkAst(componentBody, (child) => {
61498
+ if (isFunctionLike$1(child)) {
61499
+ const invocationExpression = getSynchronousInvocationExpression(child, context.scopes);
61500
+ if (invocationExpression) {
61501
+ addToSetIndex(index.synchronousInvocationsByFunction, child, invocationExpression);
61502
+ addToSetIndex(index.synchronousCallbacksByExpression, invocationExpression, child);
61503
+ }
61504
+ return;
61505
+ }
61506
+ const owner = context.cfg.enclosingFunction(child);
61507
+ if (!owner) return;
61508
+ if (isNodeOfType(child, "AwaitExpression")) {
61509
+ addToSetIndex(index.awaitExpressionsByOwner, owner, child);
61510
+ return;
61511
+ }
61512
+ if (!isNodeOfType(child, "CallExpression") && !isNodeOfType(child, "NewExpression")) return;
61513
+ addToSetIndex(index.expressionsByOwner, owner, child);
61514
+ if (isNodeOfType(child, "CallExpression")) {
61515
+ if (isGlobalHistoryLocationMutation(child, context.scopes)) addToSetIndex(index.historyMutationsByOwner, owner, child);
61516
+ if (isNodeOfType(stripParenExpression(child.callee), "Identifier")) index.identifierCalls.add(child);
61517
+ const calledFunction = resolveExactLocalOrReactCallbackFunction(child.callee, context.scopes);
61518
+ if (isFunctionLike$1(calledFunction)) {
61519
+ addToSetIndex(index.callSitesByFunction, calledFunction, child);
61520
+ index.calledFunctionByExpression.set(child, calledFunction);
61521
+ }
61522
+ if (context.cfg.enclosingFunction(child) === componentFunction && isReactApiCall(child, EFFECT_HOOK_NAMES$1, context.scopes, {
61523
+ allowGlobalReactNamespace: true,
61524
+ allowUnboundBareCalls: true,
61525
+ resolveNamedAliases: true
61526
+ })) {
61527
+ const effectCallback = getEffectCallback(child, context.scopes);
61528
+ if (isFunctionLike$1(effectCallback)) index.effectCallbacks.add(effectCallback);
61529
+ }
61530
+ const listenerOperation = getLocationListenerOperation(child, context.scopes);
61531
+ if (listenerOperation?.operation === "add") index.listenerRegistrations.push(listenerOperation.registration);
61532
+ else if (listenerOperation) index.listenerRemovals.push(listenerOperation.registration);
61533
+ }
61534
+ });
61535
+ return index;
61536
+ };
61537
+ const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
61538
+ let current = descendant;
61539
+ while (current && current !== ancestor) {
61540
+ if (current !== descendant && isFunctionLike$1(current)) return false;
61541
+ current = current.parent;
61542
+ }
61543
+ return current === ancestor;
61544
+ };
61545
+ const areInMutuallyExclusiveConditionalBranches = (firstNode, secondNode) => {
61546
+ const firstBranches = /* @__PURE__ */ new Map();
61547
+ let current = firstNode;
61548
+ while (current?.parent) {
61549
+ const parent = current.parent;
61550
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) firstBranches.set(parent, current);
61551
+ if (current !== firstNode && isFunctionLike$1(current)) break;
61552
+ current = parent;
61553
+ }
61554
+ current = secondNode;
61555
+ while (current?.parent) {
61556
+ const parent = current.parent;
61557
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) {
61558
+ const firstBranch = firstBranches.get(parent);
61559
+ if (firstBranch && firstBranch !== current) return true;
61560
+ }
61561
+ if (current !== secondNode && isFunctionLike$1(current)) break;
61562
+ current = parent;
61563
+ }
61564
+ return false;
61565
+ };
61566
+ const collectStableBooleanGuardConstraints = (node, functionBoundary, scopes) => {
61567
+ const constraintsBySymbolId = /* @__PURE__ */ new Map();
61568
+ const recordConstraint = (test, requiredTestValue) => {
61569
+ let expression = stripParenExpression(test);
61570
+ let requiredValue = requiredTestValue;
61571
+ while (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
61572
+ requiredValue = !requiredValue;
61573
+ expression = stripParenExpression(expression.argument);
61574
+ }
61575
+ if (!isNodeOfType(expression, "Identifier")) return;
61576
+ const symbol = scopes.symbolFor(expression);
61577
+ if (!symbol || symbol.references.some((reference) => reference.flag !== "read")) return;
61578
+ const existingConstraint = constraintsBySymbolId.get(symbol.id);
61579
+ constraintsBySymbolId.set(symbol.id, existingConstraint === void 0 || existingConstraint === requiredValue ? requiredValue : null);
61580
+ };
61581
+ let current = node;
61582
+ while (current?.parent && current !== functionBoundary) {
61583
+ const parent = current.parent;
61584
+ if (isNodeOfType(parent, "IfStatement")) {
61585
+ if (parent.consequent === current) recordConstraint(parent.test, true);
61586
+ if (parent.alternate === current) recordConstraint(parent.test, false);
61587
+ } else if (isNodeOfType(parent, "ConditionalExpression")) {
61588
+ if (parent.consequent === current) recordConstraint(parent.test, true);
61589
+ if (parent.alternate === current) recordConstraint(parent.test, false);
61590
+ } else if (isNodeOfType(parent, "LogicalExpression") && parent.right === current) {
61591
+ if (parent.operator === "&&") recordConstraint(parent.left, true);
61592
+ if (parent.operator === "||") recordConstraint(parent.left, false);
61593
+ }
61594
+ if (current !== node && isFunctionLike$1(current)) break;
61595
+ current = parent;
61596
+ }
61597
+ return constraintsBySymbolId;
61598
+ };
61599
+ const haveContradictoryStableBooleanGuards = (firstNode, secondNode, functionBoundary, scopes) => {
61600
+ const firstConstraints = collectStableBooleanGuardConstraints(firstNode, functionBoundary, scopes);
61601
+ const secondConstraints = collectStableBooleanGuardConstraints(secondNode, functionBoundary, scopes);
61602
+ for (const requiredValue of firstConstraints.values()) if (requiredValue === null) return true;
61603
+ for (const requiredValue of secondConstraints.values()) if (requiredValue === null) return true;
61604
+ for (const [symbolId, firstRequiredValue] of firstConstraints) {
61605
+ const secondRequiredValue = secondConstraints.get(symbolId);
61606
+ if (firstRequiredValue !== null && secondRequiredValue !== void 0 && secondRequiredValue !== null && firstRequiredValue !== secondRequiredValue) return true;
61607
+ }
61608
+ return false;
61609
+ };
61610
+ const isInsideIntrinsicReactEventHandlerAttribute = (node, functionBoundary) => {
61611
+ let current = node;
61612
+ while (current && current !== functionBoundary) {
61613
+ if (isEventHandlerAttribute(current) && isJsxAttributeOnIntrinsicHtmlElement(current)) return true;
61614
+ current = current.parent;
61615
+ }
61616
+ return false;
61617
+ };
61618
+ const getTransparentExpressionBindingIdentifier = (expression) => {
61619
+ const expressionRoot = findTransparentExpressionRoot(expression);
61620
+ const parent = expressionRoot.parent;
61621
+ if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
61622
+ if (isNodeOfType(parent, "AssignmentExpression") && parent.right === expressionRoot && isNodeOfType(parent.left, "Identifier")) return parent.left;
61623
+ return null;
61624
+ };
61625
+ const getIntrinsicReactEventHandlerBindingIdentifier = (functionNode, index) => {
61626
+ if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
61627
+ const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
61628
+ const directTransparentBindingIdentifier = getTransparentExpressionBindingIdentifier(functionExpressionRoot);
61629
+ if (directTransparentBindingIdentifier) return directTransparentBindingIdentifier;
61630
+ const callbackCall = functionExpressionRoot.parent;
61631
+ if (!isNodeOfType(callbackCall, "CallExpression") || callbackCall.arguments?.[0] !== functionExpressionRoot || !isReactApiCall(callbackCall, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
61632
+ return getTransparentExpressionBindingIdentifier(callbackCall);
61633
+ };
61634
+ const isExclusiveIntrinsicReactEventHandler = (functionNode, index) => {
61635
+ const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
61636
+ if (!bindingIdentifier) return isInsideIntrinsicReactEventHandlerAttribute(functionNode, index.componentFunction);
61637
+ const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
61638
+ return Boolean(bindingSymbol && bindingSymbol.references.length > 0 && bindingSymbol.references.every((reference) => isInsideIntrinsicReactEventHandlerAttribute(reference.identifier, index.componentFunction)));
61639
+ };
61640
+ const canNodeReachNode = (sourceNode, targetNode, index) => {
61641
+ const { context } = index;
61642
+ if (!isNodeReachableWithinFunction(sourceNode, context)) return false;
61643
+ if (!isNodeReachableWithinFunction(targetNode, context)) return false;
61644
+ const sourceOwner = context.cfg.enclosingFunction(sourceNode);
61645
+ const targetOwner = context.cfg.enclosingFunction(targetNode);
61646
+ if (!sourceOwner || sourceOwner !== targetOwner) return false;
61647
+ if (areInMutuallyExclusiveConditionalBranches(sourceNode, targetNode)) return false;
61648
+ if (haveContradictoryStableBooleanGuards(sourceNode, targetNode, sourceOwner, context.scopes)) return false;
61649
+ if (isDescendantWithoutFunctionBoundary(targetNode, sourceNode)) return false;
61650
+ if (isDescendantWithoutFunctionBoundary(sourceNode, targetNode)) return true;
61651
+ const functionCfg = context.cfg.cfgFor(sourceOwner);
61652
+ const sourceBlock = functionCfg?.blockOf(sourceNode);
61653
+ const targetBlock = functionCfg?.blockOf(targetNode);
61654
+ if (!functionCfg || !sourceBlock || !targetBlock) return false;
61655
+ if (sourceBlock === targetBlock) {
61656
+ const sourceStart = getRangeStart(sourceNode);
61657
+ const targetStart = getRangeStart(targetNode);
61658
+ return sourceStart !== null && targetStart !== null && sourceStart < targetStart;
61659
+ }
61660
+ const visitedBlocks = new Set([sourceBlock]);
61661
+ const pendingBlocks = sourceBlock.successors.filter((edge) => edge.kind !== "throw").map((edge) => edge.to);
61662
+ while (pendingBlocks.length > 0) {
61663
+ const block = pendingBlocks.pop();
61664
+ if (!block || visitedBlocks.has(block)) continue;
61665
+ if (block === targetBlock) return true;
61666
+ visitedBlocks.add(block);
61667
+ for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
61668
+ }
61669
+ return false;
61670
+ };
61671
+ const getInlineIntrinsicHandlerJsxElement = (functionNode) => {
61672
+ let currentNode = functionNode;
61673
+ while (currentNode) {
61674
+ if (isNodeOfType(currentNode, "JSXAttribute") && isEventHandlerAttribute(currentNode) && isJsxAttributeOnIntrinsicHtmlElement(currentNode)) {
61675
+ const openingElement = currentNode.parent;
61676
+ return isNodeOfType(openingElement?.parent, "JSXElement") ? openingElement.parent : null;
61677
+ }
61678
+ if (currentNode !== functionNode && isFunctionLike$1(currentNode)) return null;
61679
+ currentNode = currentNode.parent;
61680
+ }
61681
+ return null;
61682
+ };
61683
+ const isProvenNonEscapingVoidRead = (identifier) => {
61684
+ const expressionRoot = findTransparentExpressionRoot(identifier);
61685
+ return isNodeOfType(expressionRoot.parent, "UnaryExpression") && expressionRoot.parent.operator === "void" && expressionRoot.parent.argument === expressionRoot;
61686
+ };
61687
+ const getDirectReadonlyValueAliasSymbol = (referenceIdentifier, sourceSymbol, index) => {
61688
+ const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
61689
+ const parent = referenceRoot.parent;
61690
+ if (isNodeOfType(parent, "VariableDeclarator") && parent.init === referenceRoot && isNodeOfType(parent.id, "Identifier")) {
61691
+ const aliasSymbol = index.context.scopes.symbolFor(parent.id);
61692
+ const aliasInitializer = aliasSymbol ? getDirectConstInitializer(aliasSymbol) : null;
61693
+ const unwrappedInitializer = aliasInitializer ? stripParenExpression(aliasInitializer) : null;
61694
+ return aliasSymbol && isNodeOfType(unwrappedInitializer, "Identifier") && index.context.scopes.symbolFor(unwrappedInitializer) === sourceSymbol ? aliasSymbol : null;
61695
+ }
61696
+ if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot || !isReactApiCall(parent, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
61697
+ const callbackResultRoot = findTransparentExpressionRoot(parent);
61698
+ const resultParent = callbackResultRoot.parent;
61699
+ if (!isNodeOfType(resultParent, "VariableDeclarator") || resultParent.init !== callbackResultRoot || !isNodeOfType(resultParent.id, "Identifier")) return null;
61700
+ const callbackResultSymbol = index.context.scopes.symbolFor(resultParent.id);
61701
+ return callbackResultSymbol && getDirectConstInitializer(callbackResultSymbol) === callbackResultRoot ? callbackResultSymbol : null;
61702
+ };
61703
+ const isKnownAggregateMutationReference = (referenceIdentifier, index) => {
61704
+ const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
61705
+ const parent = referenceRoot.parent;
61706
+ if (isNodeOfType(parent, "MemberExpression") && parent.object === referenceRoot) {
61707
+ const memberRoot = findTransparentExpressionRoot(parent);
61708
+ const callExpression = memberRoot.parent;
61709
+ const methodName = getStaticPropertyName(parent);
61710
+ if (isNodeOfType(callExpression, "CallExpression") && callExpression.callee === memberRoot && methodName !== null && AGGREGATE_MUTATION_METHOD_NAMES.has(methodName)) return true;
61711
+ }
61712
+ if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot) return false;
61713
+ const callee = stripParenExpression(parent.callee);
61714
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
61715
+ const methodName = getStaticPropertyName(callee);
61716
+ if (methodName === null) return false;
61717
+ return OBJECT_AGGREGATE_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "Object", index.context.scopes) || REFLECT_AGGREGATE_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "Reflect", index.context.scopes);
61718
+ };
61719
+ const collectExactReadonlyValueEscapeAnchors = (symbol, index, valueFunctionNode, options = {}, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
61720
+ if (visitedSymbolIds.has(symbol.id)) return null;
61721
+ if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier) || options.rejectKnownMutations && isKnownAggregateMutationReference(reference.identifier, index))) return null;
61722
+ const nextVisitedSymbolIds = new Set(visitedSymbolIds);
61723
+ nextVisitedSymbolIds.add(symbol.id);
61724
+ const escapeAnchors = [];
61725
+ for (const reference of symbol.references) {
61726
+ if (valueFunctionNode && index.context.cfg.enclosingFunction(reference.identifier) === valueFunctionNode || isProvenNonEscapingVoidRead(reference.identifier)) continue;
61727
+ const aliasSymbol = getDirectReadonlyValueAliasSymbol(reference.identifier, symbol, index);
61728
+ if (!aliasSymbol) {
61729
+ escapeAnchors.push(reference.identifier);
61730
+ continue;
61731
+ }
61732
+ const aliasEscapeAnchors = collectExactReadonlyValueEscapeAnchors(aliasSymbol, index, valueFunctionNode, options, nextVisitedSymbolIds);
61733
+ if (!aliasEscapeAnchors) return null;
61734
+ escapeAnchors.push(...aliasEscapeAnchors);
61735
+ }
61736
+ return escapeAnchors.length > 0 ? escapeAnchors : null;
61737
+ };
61738
+ const findReadonlyJsxAggregateRoot = (jsxElement) => {
61739
+ let aggregateRoot = findTransparentExpressionRoot(jsxElement);
61740
+ while (aggregateRoot.parent) {
61741
+ const parent = aggregateRoot.parent;
61742
+ if ((isNodeOfType(parent, "JSXElement") || isNodeOfType(parent, "JSXFragment")) && parent.children?.some((child) => child === aggregateRoot)) {
61743
+ aggregateRoot = parent;
61744
+ continue;
61745
+ }
61746
+ if (isNodeOfType(parent, "ArrayExpression") && parent.elements?.some((element) => element === aggregateRoot)) {
61747
+ aggregateRoot = findTransparentExpressionRoot(parent);
61748
+ continue;
61749
+ }
61750
+ if (isNodeOfType(parent, "Property") && parent.value === aggregateRoot && parent.kind === "init" && !parent.computed && isNodeOfType(parent.parent, "ObjectExpression")) {
61751
+ aggregateRoot = findTransparentExpressionRoot(parent.parent);
61752
+ continue;
61753
+ }
61754
+ if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === aggregateRoot || parent.alternate === aggregateRoot)) {
61755
+ aggregateRoot = findTransparentExpressionRoot(parent);
61756
+ continue;
61757
+ }
61758
+ if (isNodeOfType(parent, "LogicalExpression") && (parent.right === aggregateRoot || parent.left === aggregateRoot && parent.operator !== "&&")) {
61759
+ aggregateRoot = findTransparentExpressionRoot(parent);
61760
+ continue;
61761
+ }
61762
+ if (isNodeOfType(parent, "SequenceExpression") && getFinalSequenceExpressionValue(parent) === getFinalSequenceExpressionValue(aggregateRoot)) {
61763
+ aggregateRoot = findTransparentExpressionRoot(parent);
61764
+ continue;
61765
+ }
61766
+ break;
61767
+ }
61768
+ return aggregateRoot;
61769
+ };
61770
+ const getReadonlyJsxValueEscapeAnchors = (jsxElement, index) => {
61771
+ const jsxValueRoot = findReadonlyJsxAggregateRoot(jsxElement);
61772
+ const parent = jsxValueRoot.parent;
61773
+ if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== jsxValueRoot) return [jsxValueRoot];
61774
+ if (!isNodeOfType(parent.id, "Identifier")) return null;
61775
+ const bindingSymbol = index.context.scopes.symbolFor(parent.id);
61776
+ if (!bindingSymbol || getDirectConstInitializer(bindingSymbol) !== jsxValueRoot) return null;
61777
+ return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, null, { rejectKnownMutations: true });
61778
+ };
61779
+ const getExclusiveIntrinsicReactEventHandlerAnchors = (functionNode, index) => {
61780
+ if (!isExclusiveIntrinsicReactEventHandler(functionNode, index)) return null;
61781
+ const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
61782
+ if (!bindingIdentifier) {
61783
+ const jsxElement = getInlineIntrinsicHandlerJsxElement(functionNode);
61784
+ return jsxElement ? getReadonlyJsxValueEscapeAnchors(jsxElement, index) : null;
61785
+ }
61786
+ return index.context.scopes.symbolFor(bindingIdentifier)?.references.map((reference) => reference.identifier) ?? null;
61787
+ };
61788
+ const getReadonlyFunctionEscapeAnchors = (functionNode, index) => {
61789
+ const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
61790
+ if (!bindingIdentifier) return null;
61791
+ const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
61792
+ if (!bindingSymbol || resolveExactLocalFunction(bindingIdentifier, index.context.scopes) !== functionNode) return null;
61793
+ return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, functionNode);
61794
+ };
61795
+ const isAliasInitializedBeforeExecution = (aliasDeclaration, executionNode, index, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
61796
+ if (!isNodeReachableWithinFunction(aliasDeclaration, index.context)) return false;
61797
+ const aliasOwner = index.context.cfg.enclosingFunction(aliasDeclaration);
61798
+ const executionOwner = index.context.cfg.enclosingFunction(executionNode);
61799
+ if (!aliasOwner || !executionOwner) return false;
61800
+ if (aliasOwner === executionOwner) return canNodeReachNode(aliasDeclaration, executionNode, index);
61801
+ if (visitedFunctionNodes.has(executionOwner)) return false;
61802
+ const nextVisitedFunctionNodes = new Set(visitedFunctionNodes);
61803
+ nextVisitedFunctionNodes.add(executionOwner);
61804
+ if (aliasOwner === index.componentFunction && (index.effectCallbacks.has(executionOwner) || index.mountedListenerFunctions.has(executionOwner))) return doNodesCoverEveryPathFromFunctionEntry(index.componentFunction, [aliasDeclaration], index.context, { ignoreThrowEdges: true });
61805
+ const invocations = new Set([...index.callSitesByFunction.get(executionOwner) ?? [], ...index.synchronousInvocationsByFunction.get(executionOwner) ?? []]);
61806
+ if (invocations.size > 0) return [...invocations].every((invocation) => isAliasInitializedBeforeExecution(aliasDeclaration, invocation, index, nextVisitedFunctionNodes));
61807
+ const deferredExecutionAnchors = getExclusiveIntrinsicReactEventHandlerAnchors(executionOwner, index);
61808
+ if (deferredExecutionAnchors) return deferredExecutionAnchors.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes));
61809
+ const functionEscapeAnchors = getReadonlyFunctionEscapeAnchors(executionOwner, index);
61810
+ return Boolean(functionEscapeAnchors?.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes)));
61811
+ };
61812
+ const resolveExactReadonlyCalleeSymbolId = (callExpression, index) => {
61813
+ if (!isNodeOfType(callExpression, "CallExpression")) return null;
61814
+ const callee = stripParenExpression(callExpression.callee);
61815
+ if (!isNodeOfType(callee, "Identifier")) return null;
61816
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
61817
+ let symbol = index.context.scopes.symbolFor(callee);
61818
+ while (symbol) {
61819
+ if (visitedSymbolIds.has(symbol.id)) return null;
61820
+ visitedSymbolIds.add(symbol.id);
61821
+ const initializer = getDirectConstInitializer(symbol);
61822
+ if (!initializer) return symbol.id;
61823
+ if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) || !isAliasInitializedBeforeExecution(symbol.declarationNode, callExpression, index)) return null;
61824
+ const unwrappedInitializer = stripParenExpression(initializer);
61825
+ if (!isNodeOfType(unwrappedInitializer, "Identifier")) return symbol.id;
61826
+ const initializerSymbol = index.context.scopes.symbolFor(unwrappedInitializer);
61827
+ if (!initializerSymbol || !isAliasInitializedBeforeExecution(initializerSymbol.declarationNode, symbol.declarationNode, index)) return null;
61828
+ symbol = initializerSymbol;
61829
+ }
61830
+ return null;
61831
+ };
61832
+ const collectExactReadonlyCalleeCalls = (index) => {
61833
+ for (const callExpression of index.identifierCalls) {
61834
+ const calleeSymbolId = resolveExactReadonlyCalleeSymbolId(callExpression, index);
61835
+ if (calleeSymbolId !== null) addToSetIndex(index.callsByCalleeSymbolId, calleeSymbolId, callExpression);
61836
+ }
61837
+ };
61838
+ const canNodeReachNormalFunctionExit = (node, functionNode, index) => {
61839
+ const functionCfg = index.context.cfg.cfgFor(functionNode);
61840
+ const sourceBlock = functionCfg?.blockOf(node);
61841
+ if (!functionCfg || !sourceBlock) return false;
61842
+ const visitedBlocks = /* @__PURE__ */ new Set();
61843
+ const pendingBlocks = [sourceBlock];
61844
+ while (pendingBlocks.length > 0) {
61845
+ const block = pendingBlocks.pop();
61846
+ if (!block || visitedBlocks.has(block)) continue;
61847
+ visitedBlocks.add(block);
61848
+ for (const edge of block.successors) {
61849
+ if (edge.kind === "throw") continue;
61850
+ if (edge.to === functionCfg.exit) return true;
61851
+ pendingBlocks.push(edge.to);
61852
+ }
61853
+ }
61854
+ return false;
61855
+ };
61856
+ const canExecuteBeforeAsyncSuspension = (node, functionNode, index) => {
61857
+ if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, index.context);
61858
+ const functionCfg = index.context.cfg.cfgFor(functionNode);
61859
+ const targetBlock = functionCfg?.blockOf(node);
61860
+ if (!functionCfg || !targetBlock) return false;
61861
+ const awaitsByBlock = /* @__PURE__ */ new Map();
61862
+ for (const awaitExpression of index.awaitExpressionsByOwner.get(functionNode) ?? []) {
61863
+ const awaitBlock = functionCfg.blockOf(awaitExpression);
61864
+ if (!awaitBlock) continue;
61865
+ const blockAwaits = awaitsByBlock.get(awaitBlock) ?? [];
61866
+ blockAwaits.push(awaitExpression);
61867
+ awaitsByBlock.set(awaitBlock, blockAwaits);
61868
+ }
61869
+ const visitedBlocks = /* @__PURE__ */ new Set();
61870
+ const pendingBlocks = [functionCfg.entry];
61871
+ const targetStart = getRangeStart(node);
61872
+ while (pendingBlocks.length > 0) {
61873
+ const block = pendingBlocks.pop();
61874
+ if (!block || visitedBlocks.has(block)) continue;
61875
+ visitedBlocks.add(block);
61876
+ const blockAwaits = awaitsByBlock.get(block) ?? [];
61877
+ if (block === targetBlock) return !blockAwaits.some((awaitExpression) => {
61878
+ if (isNodeOfType(awaitExpression, "AwaitExpression") && isDescendantWithoutFunctionBoundary(node, awaitExpression.argument)) return false;
61879
+ if (isDescendantWithoutFunctionBoundary(awaitExpression, node)) return true;
61880
+ const awaitStart = getRangeStart(awaitExpression);
61881
+ return awaitStart !== null && targetStart !== null && awaitStart < targetStart;
61882
+ });
61883
+ if (blockAwaits.length > 0) continue;
61884
+ for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
61885
+ }
61886
+ return false;
61887
+ };
61888
+ const canReactBatchMutationAfterExecution = (executionNode, mutationNode, owner, index) => (isExclusiveIntrinsicReactEventHandler(owner, index) || index.effectCallbacks.has(owner)) && canNodeReachNode(executionNode, mutationNode, index) && canExecuteBeforeAsyncSuspension(mutationNode, owner, index) && canNodeReachNormalFunctionExit(mutationNode, owner, index);
61889
+ const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFunctions, cycleAffectedFunctions) => {
61890
+ const cachedResult = index.synchronousMutationResultByFunction.get(functionNode);
61891
+ if (cachedResult !== void 0) return cachedResult;
61892
+ if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
61893
+ if (visitingFunctions.has(functionNode)) {
61894
+ let didReachCycleEntry = false;
61895
+ for (const visitingFunction of visitingFunctions) {
61896
+ if (visitingFunction === functionNode) didReachCycleEntry = true;
61897
+ if (didReachCycleEntry) cycleAffectedFunctions.add(visitingFunction);
61898
+ }
61899
+ return false;
61900
+ }
61901
+ visitingFunctions.add(functionNode);
61902
+ const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
61903
+ visitingFunctions.delete(functionNode);
61904
+ if (doesMutateSynchronously || !cycleAffectedFunctions.has(functionNode)) index.synchronousMutationResultByFunction.set(functionNode, doesMutateSynchronously);
61905
+ return doesMutateSynchronously;
61906
+ };
61907
+ const collectLocationMutationExecutions = (functionNode, index, visitingFunctions = /* @__PURE__ */ new Set(), cycleAffectedFunctions = /* @__PURE__ */ new Set()) => {
61908
+ const cachedExecutions = index.mutationExecutionsByOwner.get(functionNode);
61909
+ if (cachedExecutions) return cachedExecutions;
61910
+ const mutationExecutions = new Set(index.historyMutationsByOwner.get(functionNode) ?? []);
61911
+ for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61912
+ const calledFunction = index.calledFunctionByExpression.get(expression);
61913
+ if (calledFunction && functionMaySynchronouslyMutateLocation(calledFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
61914
+ for (const callbackFunction of index.synchronousCallbacksByExpression.get(expression) ?? []) if (functionMaySynchronouslyMutateLocation(callbackFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
61915
+ }
61916
+ if (!cycleAffectedFunctions.has(functionNode)) index.mutationExecutionsByOwner.set(functionNode, mutationExecutions);
61917
+ return mutationExecutions;
61918
+ };
61919
+ const isDefinitelyMatchingLocationListenerRemoval = (registration, removal) => registration.eventName === removal.eventName && registration.listenerFunction === removal.listenerFunction && registration.capture !== null && removal.capture !== null && registration.capture === removal.capture;
61920
+ const functionMustSynchronouslyRemoveLocationListener = (functionNode, registration, index, visitingFunctions) => {
61921
+ if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
61922
+ if (visitingFunctions.has(functionNode)) return false;
61923
+ const nextVisitingFunctions = new Set(visitingFunctions);
61924
+ nextVisitingFunctions.add(functionNode);
61925
+ const removalExecutions = [];
61926
+ for (const removal of index.listenerRemovals) {
61927
+ if (!isDefinitelyMatchingLocationListenerRemoval(registration, removal)) continue;
61928
+ if (index.context.cfg.enclosingFunction(removal.callExpression) !== functionNode) continue;
61929
+ if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.push(removal.callExpression);
61930
+ }
61931
+ for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61932
+ const calledFunction = index.calledFunctionByExpression.get(expression);
61933
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
61934
+ }
61935
+ return doNodesCoverEveryPathFromFunctionEntry(functionNode, removalExecutions, index.context);
61936
+ };
61937
+ const collectSynchronousLocationListenerRemovalExecutions = (functionNode, registration, index) => {
61938
+ const removalExecutions = /* @__PURE__ */ new Set();
61939
+ for (const removal of index.listenerRemovals) if (isDefinitelyMatchingLocationListenerRemoval(registration, removal) && index.context.cfg.enclosingFunction(removal.callExpression) === functionNode && canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.add(removal.callExpression);
61940
+ for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61941
+ const calledFunction = index.calledFunctionByExpression.get(expression);
61942
+ if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
61943
+ }
61944
+ return removalExecutions;
61945
+ };
61946
+ const collectImpliedExpressionExecutionBoundaries = (node, owner) => {
61947
+ const expressionBoundaries = [];
61948
+ let currentChild = node;
61949
+ let currentParent = currentChild.parent ?? null;
61950
+ while (currentParent && currentParent !== owner) {
61951
+ if (isNodeOfType(currentParent, "ConditionalExpression") && currentParent.test === currentChild) {
61952
+ const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.test));
61953
+ if (staticTestValue !== null) expressionBoundaries.push(staticTestValue ? currentParent.consequent : currentParent.alternate);
61954
+ }
61955
+ if (isNodeOfType(currentParent, "LogicalExpression") && currentParent.left === currentChild) {
61956
+ const staticLeftValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.left));
61957
+ if (currentParent.operator === "&&" && staticLeftValue === true || currentParent.operator === "||" && staticLeftValue === false) expressionBoundaries.push(currentParent.right);
61958
+ }
61959
+ if (isNodeOfType(currentParent, "ConditionalExpression") && (currentParent.consequent === currentChild || currentParent.alternate === currentChild) || isNodeOfType(currentParent, "LogicalExpression") && currentParent.right === currentChild || isNodeOfType(currentParent, "AssignmentPattern") && currentParent.right === currentChild) expressionBoundaries.push(currentChild);
61960
+ currentChild = currentParent;
61961
+ currentParent = currentChild.parent ?? null;
61962
+ }
61963
+ return expressionBoundaries;
61964
+ };
61965
+ const canExecutionReachFunctionExitWithoutListenerRemoval = (executionNode, registration, index) => {
61966
+ if (!isNodeReachableWithinFunction(executionNode, index.context)) return false;
61967
+ const owner = index.context.cfg.enclosingFunction(executionNode);
61968
+ if (!owner) return false;
61969
+ const functionCfg = index.context.cfg.cfgFor(owner);
61970
+ const sourceBlock = functionCfg?.blockOf(executionNode);
61971
+ if (!functionCfg || !sourceBlock) return false;
61972
+ const expressionBoundaries = collectImpliedExpressionExecutionBoundaries(executionNode, owner);
61973
+ const matchingRemovalsByBlock = /* @__PURE__ */ new Map();
61974
+ for (const removalExecution of collectSynchronousLocationListenerRemovalExecutions(owner, registration, index)) {
61975
+ const removalBlock = functionCfg.blockOf(removalExecution);
61976
+ if (!removalBlock) continue;
61977
+ const blockRemovals = matchingRemovalsByBlock.get(removalBlock) ?? [];
61978
+ blockRemovals.push(removalExecution);
61979
+ matchingRemovalsByBlock.set(removalBlock, blockRemovals);
61980
+ }
61981
+ const sourceStart = getRangeStart(executionNode);
61982
+ const hasRemovalAfterExecution = (block) => {
61983
+ const removals = (matchingRemovalsByBlock.get(block) ?? []).filter((removal) => {
61984
+ if (block !== sourceBlock) return true;
61985
+ const removalStart = getRangeStart(removal);
61986
+ return sourceStart !== null && removalStart !== null && sourceStart < removalStart;
61987
+ });
61988
+ if (collectExpressionPathCoverageNodes(owner, removals, index.context).size > 0) return true;
61989
+ if (block !== sourceBlock) return false;
61990
+ return expressionBoundaries.some((expressionBoundary) => collectExpressionPathCoverageNodes(owner, removals, index.context, expressionBoundary).size > 0);
61991
+ };
61992
+ const visitedBlocks = /* @__PURE__ */ new Set();
61993
+ const pendingBlocks = [sourceBlock];
61994
+ while (pendingBlocks.length > 0) {
61995
+ const block = pendingBlocks.pop();
61996
+ if (!block || visitedBlocks.has(block)) continue;
61997
+ visitedBlocks.add(block);
61998
+ if (hasRemovalAfterExecution(block)) continue;
61999
+ for (const edge of block.successors) {
62000
+ if (edge.kind === "throw") continue;
62001
+ if (edge.to === functionCfg.exit) return true;
62002
+ pendingBlocks.push(edge.to);
62003
+ }
62004
+ }
62005
+ return false;
62006
+ };
62007
+ const isListenerActiveAtMountedExit = (executionNode, registration, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
62008
+ if (visitedExecutions.has(executionNode)) return false;
62009
+ if (!canExecutionReachFunctionExitWithoutListenerRemoval(executionNode, registration, index)) return false;
62010
+ const owner = index.context.cfg.enclosingFunction(executionNode);
62011
+ if (!owner) return false;
62012
+ if (owner === index.componentFunction || index.effectCallbacks.has(owner)) return true;
62013
+ const nextVisitedExecutions = new Set(visitedExecutions);
62014
+ nextVisitedExecutions.add(executionNode);
62015
+ for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(callSite, registration, index, nextVisitedExecutions)) return true;
62016
+ for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(invocation, registration, index, nextVisitedExecutions)) return true;
62017
+ return false;
62018
+ };
62019
+ const collectMountedListenerFunctions = (index) => {
62020
+ for (const registration of index.listenerRegistrations) if (isListenerActiveAtMountedExit(registration.callExpression, registration, index)) index.mountedListenerFunctions.add(registration.listenerFunction);
62021
+ };
62022
+ const setterArgumentMutatesLocation = (setterCall, index) => {
62023
+ if (!isNodeOfType(setterCall, "CallExpression")) return false;
62024
+ return (setterCall.arguments ?? []).some((argument) => {
62025
+ if (isNodeOfType(argument, "SpreadElement")) return false;
62026
+ const updaterFunction = resolveExactLocalFunction(argument, index.context.scopes);
62027
+ return Boolean(isFunctionLike$1(updaterFunction) && functionMaySynchronouslyMutateLocation(updaterFunction, index, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()));
62028
+ });
62029
+ };
62030
+ const executionAnchorInvalidatesLocationSnapshot = (executionAnchor, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
62031
+ if (visitedExecutions.has(executionAnchor)) return false;
62032
+ if (!isNodeReachableWithinFunction(executionAnchor, index.context)) return false;
62033
+ const owner = index.context.cfg.enclosingFunction(executionAnchor);
62034
+ if (!owner) return false;
62035
+ if (index.mountedListenerFunctions.has(owner)) return true;
62036
+ if (setterArgumentMutatesLocation(executionAnchor, index) || [...collectLocationMutationExecutions(owner, index)].some((mutationExecution) => canNodeReachNode(mutationExecution, executionAnchor, index) || canReactBatchMutationAfterExecution(executionAnchor, mutationExecution, owner, index))) return true;
62037
+ const nextVisitedExecutions = new Set(visitedExecutions);
62038
+ nextVisitedExecutions.add(executionAnchor);
62039
+ for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(callSite, index, nextVisitedExecutions)) return true;
62040
+ for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(invocation, index, nextVisitedExecutions)) return true;
62041
+ return false;
62042
+ };
62043
+ const setterInvalidatesGlobalLocationSnapshot = (setterBindingIdentifier, index) => {
62044
+ const setterSymbol = index.context.scopes.symbolFor(setterBindingIdentifier);
62045
+ if (!setterSymbol) return false;
62046
+ return [...index.callsByCalleeSymbolId.get(setterSymbol.id) ?? []].some((setterCall) => executionAnchorInvalidatesLocationSnapshot(setterCall, index));
62047
+ };
62048
+ const createExternalLocationInvalidationChecker = ({ componentBody, componentFunction, context, directRenderNames, renderReachableExpressions }) => {
62049
+ if (!hasRenderReachableLocationSnapshotRead(componentBody, renderReachableExpressions, directRenderNames, context.scopes)) return () => false;
62050
+ const locationInvalidationIndex = buildLocationInvalidationIndex(componentBody, componentFunction, context);
62051
+ collectMountedListenerFunctions(locationInvalidationIndex);
62052
+ collectExactReadonlyCalleeCalls(locationInvalidationIndex);
62053
+ return (setterBindingIdentifier) => setterInvalidatesGlobalLocationSnapshot(setterBindingIdentifier, locationInvalidationIndex);
62054
+ };
62055
+ //#endregion
60392
62056
  //#region src/plugin/rules/state-and-effects/rerender-state-only-in-handlers.ts
60393
62057
  const isInsideConditionTest = (identifier, stopAt) => {
60394
62058
  let current = identifier;
@@ -60510,7 +62174,8 @@ const rerenderStateOnlyInHandlers = defineRule({
60510
62174
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
60511
62175
  const bindings = collectUseStateBindings(componentBody, context.scopes);
60512
62176
  if (bindings.length === 0) return;
60513
- if (collectRenderReachableExpressions(componentBody).length === 0) return;
62177
+ const renderReachableExpressions = collectRenderReachableExpressions(componentBody);
62178
+ if (renderReachableExpressions.length === 0) return;
60514
62179
  const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody, context.scopes);
60515
62180
  const dependencyGraph = buildLocalDependencyGraph(componentBody, eventHandlerReferenceNames);
60516
62181
  const directRenderNames = collectRenderReachableNames(componentBody, context.scopes, eventHandlerReferenceNames);
@@ -60524,12 +62189,22 @@ const rerenderStateOnlyInHandlers = defineRule({
60524
62189
  for (const effectInfo of effectInfos) for (const dependencyName of effectInfo.dependencyNames) if (!selfEchoValueNames.has(dependencyName)) effectConsumedNames.add(dependencyName);
60525
62190
  for (const hookArgumentName of collectCustomHookArgumentNames(componentBody, context.scopes)) effectConsumedNames.add(hookArgumentName);
60526
62191
  for (const reachableName of expandTransitiveDependencies(effectConsumedNames, dependencyGraph)) renderReachableNames.add(reachableName);
62192
+ const componentFunction = context.cfg.enclosingFunction(componentBody);
62193
+ const doesSetterInvalidateExternalLocation = isFunctionLike$1(componentFunction) ? createExternalLocationInvalidationChecker({
62194
+ componentBody,
62195
+ componentFunction,
62196
+ context,
62197
+ directRenderNames,
62198
+ renderReachableExpressions
62199
+ }) : () => false;
60527
62200
  const calledSetterNames = /* @__PURE__ */ new Set();
60528
62201
  walkAst(componentBody, (child) => {
60529
62202
  if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && setterNames.has(child.callee.name)) calledSetterNames.add(child.callee.name);
60530
62203
  });
60531
62204
  for (const binding of bindings) {
60532
62205
  if (renderReachableNames.has(binding.valueName)) continue;
62206
+ const setterBindingIdentifier = isNodeOfType(binding.declarator.id, "ArrayPattern") ? binding.declarator.id.elements?.[1] : null;
62207
+ if (setterBindingIdentifier && doesSetterInvalidateExternalLocation(setterBindingIdentifier)) continue;
60533
62208
  if (binding.valueName === "_" || binding.valueName.startsWith("_")) continue;
60534
62209
  const setterSuffix = binding.setterName.slice(3);
60535
62210
  if (/^(TriggerRender|ForceUpdate|Rerender|ForceRender|Tick|Bump|BumpVersion|InvalidateRender|Refresh|Repaint)$/i.test(setterSuffix)) continue;