oxlint-plugin-react-doctor 0.8.1-dev.b852961 → 0.8.1-dev.c9f2c4b

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.
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
- new Set([
765
+ const EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS = new Set([
766
766
  ...FETCH_MEMBER_OBJECTS,
767
767
  "api",
768
768
  "client",
@@ -9008,16 +9008,6 @@ 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
9021
9011
  //#region src/plugin/utils/is-proven-browser-api-receiver.ts
9022
9012
  const DOM_EVENT_TARGET_TYPE_NAMES = new Set([
9023
9013
  "AbortSignal",
@@ -9164,10 +9154,8 @@ const getProvenDomEventTargetPrototypeOwnerNames = (rawExpression, scopes, visit
9164
9154
  if (memberName === "body" || memberName === "documentElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9165
9155
  }
9166
9156
  if (isNodeOfType(expression, "CallExpression")) {
9167
- if (isGlobalMatchMediaCall(expression, scopes)) return getDomPrototypeOwnerNamesForType("MediaQueryList");
9168
9157
  const callee = stripParenExpression(expression.callee);
9169
9158
  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");
9171
9159
  if (methodName === "createElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9172
9160
  if (methodName === "createElementNS") return getDomPrototypeOwnerNamesForType("SVGElement");
9173
9161
  if (methodName && DOM_EVENT_TARGET_FACTORY_METHOD_NAMES.has(methodName)) return getDomPrototypeOwnerNamesForType("Element");
@@ -13827,116 +13815,6 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
13827
13815
  return null;
13828
13816
  };
13829
13817
  //#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
13940
13818
  //#region src/plugin/utils/get-function-binding-name.ts
13941
13819
  const getFunctionBindingIdentifier$1 = (functionNode) => {
13942
13820
  if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
@@ -13974,19 +13852,14 @@ const isAstDescendant = (inner, outer) => {
13974
13852
  };
13975
13853
  //#endregion
13976
13854
  //#region src/plugin/utils/react-ref-origin.ts
13977
- const REACT_REF_API_NAMES = new Set(["createRef", "useRef"]);
13978
- const resolveReactRefSymbol = (memberExpression, scopes, options = {}) => {
13855
+ const resolveReactRefSymbol = (memberExpression, scopes) => {
13979
13856
  const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
13980
13857
  if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
13981
13858
  const symbol = resolveConstIdentifierAlias(receiver, scopes);
13982
13859
  if (!symbol?.initializer) return null;
13983
13860
  const initializer = stripParenExpression(symbol.initializer);
13984
13861
  if (!isNodeOfType(initializer, "CallExpression")) return 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;
13862
+ return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
13990
13863
  };
13991
13864
  const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13992
13865
  const expression = stripParenExpression(node);
@@ -14085,35 +13958,9 @@ const isNodeReachableWithinFunction = (node, context) => {
14085
13958
  return false;
14086
13959
  };
14087
13960
  //#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
14102
13961
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
14103
13962
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
14104
13963
  const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
14105
- const REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES = new Set([
14106
- "pop",
14107
- "shift",
14108
- "splice",
14109
- "fill",
14110
- "copyWithin"
14111
- ]);
14112
- const REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES = new Set([
14113
- "clear",
14114
- "delete",
14115
- "set"
14116
- ]);
14117
13964
  const REACT_REF_EFFECT_ANALYSIS_CACHE = /* @__PURE__ */ new WeakMap();
14118
13965
  const RESOURCE_NOUN_BY_KIND = {
14119
13966
  subscribe: "subscription",
@@ -14154,92 +14001,6 @@ const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE
14154
14001
  }
14155
14002
  return null;
14156
14003
  };
14157
- const resolveForEachProjection = (expression, context) => {
14158
- if (!expression) return null;
14159
- let currentExpression = stripParenExpression(expression);
14160
- const memberNames = [];
14161
- while (isNodeOfType(currentExpression, "MemberExpression") && !currentExpression.computed) {
14162
- if (!isNodeOfType(currentExpression.property, "Identifier")) return null;
14163
- memberNames.unshift(currentExpression.property.name);
14164
- currentExpression = stripParenExpression(currentExpression.object);
14165
- }
14166
- if (!isNodeOfType(currentExpression, "Identifier")) return null;
14167
- const symbol = context.scopes.symbolFor(currentExpression);
14168
- if (!symbol || symbol.kind !== "parameter") return null;
14169
- let callbackNode = symbol.bindingIdentifier.parent;
14170
- while (callbackNode && !isFunctionLike$1(callbackNode)) callbackNode = callbackNode.parent;
14171
- if (!callbackNode || !isFunctionLike$1(callbackNode)) return null;
14172
- const forEachCall = findEnclosingForEachCall(callbackNode);
14173
- if (!forEachCall) return null;
14174
- const forEachCallee = stripParenExpression(forEachCall.callee);
14175
- if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
14176
- const collectionKey = resolveExpressionKey(forEachCallee.object, context);
14177
- if (!collectionKey) return null;
14178
- const firstParameter = callbackNode.params[0];
14179
- const bindingProperty = symbol.bindingIdentifier.parent;
14180
- const propertyName = isNodeOfType(firstParameter, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.parent === firstParameter && bindingProperty.value === symbol.bindingIdentifier ? getStaticPropertyKeyName(bindingProperty) : null;
14181
- const parameterProjection = firstParameter === symbol.bindingIdentifier ? "value" : propertyName;
14182
- if (!parameterProjection) return null;
14183
- return {
14184
- collectionKey,
14185
- projectionKey: [parameterProjection, ...memberNames].join(".")
14186
- };
14187
- };
14188
- const resolveForEachProjectionKey = (expression, context) => {
14189
- const projection = resolveForEachProjection(expression, context);
14190
- return projection ? `forEach:${projection.collectionKey}:${projection.projectionKey}` : null;
14191
- };
14192
- const resolveResourceIdentityKey = (expression, context) => resolveForEachProjectionKey(expression, context) ?? resolveExpressionKey(expression, context);
14193
- const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
14194
- const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
14195
- if (capture !== null) return `capture:${String(capture)}`;
14196
- if (!optionsNode) return null;
14197
- const unwrappedOptions = stripParenExpression(optionsNode);
14198
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
14199
- const optionsKey = allowOpaqueOptionsIdentity ? resolveResourceIdentityKey(unwrappedOptions, context) : null;
14200
- return optionsKey ? `options:${optionsKey}` : null;
14201
- }
14202
- let captureKey = "capture:false";
14203
- for (const property of unwrappedOptions.properties ?? []) {
14204
- if (!isNodeOfType(property, "Property")) {
14205
- captureKey = null;
14206
- continue;
14207
- }
14208
- const propertyName = getStaticPropertyKeyName(property);
14209
- if (propertyName === null || !property.computed && propertyName === "__proto__") {
14210
- captureKey = null;
14211
- continue;
14212
- }
14213
- if (propertyName === "capture") {
14214
- const propertyValueKey = resolveResourceIdentityKey(property.value, context);
14215
- captureKey = propertyValueKey ? `capture-value:${propertyValueKey}` : null;
14216
- }
14217
- }
14218
- return captureKey;
14219
- };
14220
- const resolveEventListenerCaptureProjection = (optionsNode, context) => {
14221
- if (!optionsNode) return null;
14222
- const unwrappedOptions = stripParenExpression(optionsNode);
14223
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return resolveForEachProjection(unwrappedOptions, context);
14224
- let captureProjection = null;
14225
- for (const property of unwrappedOptions.properties ?? []) {
14226
- if (!isNodeOfType(property, "Property")) {
14227
- captureProjection = null;
14228
- continue;
14229
- }
14230
- const propertyName = getStaticPropertyKeyName(property);
14231
- if (propertyName === null || !property.computed && propertyName === "__proto__") {
14232
- captureProjection = null;
14233
- continue;
14234
- }
14235
- if (propertyName === "capture") captureProjection = resolveForEachProjection(property.value, context);
14236
- }
14237
- return captureProjection;
14238
- };
14239
- const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, context, allowOpaqueOptionsIdentity = false) => {
14240
- const registrationCaptureKey = resolveEventListenerCaptureIdentityKey(registrationOptions, context, allowOpaqueOptionsIdentity);
14241
- return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
14242
- };
14243
14004
  const findAssignedResourceKey = (resourceNode, context) => {
14244
14005
  let currentNode = resourceNode;
14245
14006
  let parentNode = currentNode.parent;
@@ -14251,30 +14012,6 @@ const findAssignedResourceKey = (resourceNode, context) => {
14251
14012
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
14252
14013
  return null;
14253
14014
  };
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
- };
14278
14015
  const getCallRegistrationDetails = (callNode, context) => {
14279
14016
  const callee = stripParenExpression(callNode.callee);
14280
14017
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return {
@@ -14283,17 +14020,11 @@ const getCallRegistrationDetails = (callNode, context) => {
14283
14020
  eventKey: null,
14284
14021
  handlerKey: null
14285
14022
  };
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
- };
14292
14023
  return {
14293
- receiverKey: resolveResourceIdentityKey(callee.object, context),
14024
+ receiverKey: resolveExpressionKey(callee.object, context),
14294
14025
  registrationVerbName: callee.property.name,
14295
- eventKey: resolveResourceIdentityKey(callNode.arguments?.[0], context),
14296
- handlerKey: resolveResourceIdentityKey(callNode.arguments?.[1], context)
14026
+ eventKey: resolveExpressionKey(callNode.arguments?.[0], context),
14027
+ handlerKey: resolveExpressionKey(callNode.arguments?.[1], context)
14297
14028
  };
14298
14029
  };
14299
14030
  const findSubscribeLikeUsages = (callback, context) => {
@@ -14393,6 +14124,30 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
14393
14124
  }
14394
14125
  return matchingBlocks.size > 0;
14395
14126
  };
14127
+ const doMatchingNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context) => {
14128
+ const functionCfg = context.cfg.cfgFor(owner);
14129
+ if (!functionCfg) return false;
14130
+ const matchingBlocks = new Set(matchingNodes.flatMap((matchingNode) => {
14131
+ if (context.cfg.enclosingFunction(matchingNode) !== owner) return [];
14132
+ const matchingBlock = functionCfg.blockOf(matchingNode);
14133
+ return matchingBlock ? [matchingBlock] : [];
14134
+ }));
14135
+ if (matchingBlocks.size === 0) return false;
14136
+ const visitedBlocks = new Set([functionCfg.entry]);
14137
+ const pendingBlocks = [functionCfg.entry];
14138
+ while (pendingBlocks.length > 0) {
14139
+ const currentBlock = pendingBlocks.pop();
14140
+ if (!currentBlock) break;
14141
+ if (matchingBlocks.has(currentBlock)) continue;
14142
+ for (const edge of currentBlock.successors) {
14143
+ if (edge.to === functionCfg.exit) return false;
14144
+ if (visitedBlocks.has(edge.to)) continue;
14145
+ visitedBlocks.add(edge.to);
14146
+ pendingBlocks.push(edge.to);
14147
+ }
14148
+ }
14149
+ return true;
14150
+ };
14396
14151
  const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
14397
14152
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
14398
14153
  if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
@@ -14635,6 +14390,18 @@ const findPushedResourceCollectionKey = (usage, context) => {
14635
14390
  return memberNode.property.name === "forEach" || memberNode.property.name === "push";
14636
14391
  }) ? resolveExpressionKey(pushCallee.object, context) : null;
14637
14392
  };
14393
+ const isWithinAssignmentTarget = (identifier) => {
14394
+ let currentNode = identifier;
14395
+ let parentNode = currentNode.parent;
14396
+ while (parentNode) {
14397
+ if (isNodeOfType(parentNode, "AssignmentExpression")) return parentNode.left === currentNode;
14398
+ if (isNodeOfType(parentNode, "UpdateExpression") || isNodeOfType(parentNode, "UnaryExpression") && parentNode.operator === "delete") return parentNode.argument === currentNode;
14399
+ if (isNodeOfType(parentNode, "ForInStatement") || isNodeOfType(parentNode, "ForOfStatement")) return parentNode.left === currentNode;
14400
+ currentNode = parentNode;
14401
+ parentNode = currentNode.parent;
14402
+ }
14403
+ return false;
14404
+ };
14638
14405
  const resolveStableValue = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
14639
14406
  if (!expression) return null;
14640
14407
  const unwrappedExpression = stripParenExpression(expression);
@@ -14682,8 +14449,8 @@ const isSynchronousIteratorCallback = (functionNode) => {
14682
14449
  return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments?.[0] === functionNode;
14683
14450
  };
14684
14451
  const findEnclosingForEachCall = (node) => {
14685
- const callbackNode = isFunctionLike$1(node) ? node : findEnclosingFunction$1(node);
14686
- if (!callbackNode || !isFunctionLike$1(callbackNode) || callbackNode.async || callbackNode.generator) return null;
14452
+ const callbackNode = findEnclosingFunction$1(node);
14453
+ if (!callbackNode) return null;
14687
14454
  const callNode = callbackNode.parent;
14688
14455
  if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments?.[0] !== callbackNode) return null;
14689
14456
  const callee = stripParenExpression(callNode.callee);
@@ -14758,7 +14525,7 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
14758
14525
  const helperFunction = isNodeOfType(stableHelperFunction, "Identifier") ? resolveSingleAssignedCleanupFunction(stableHelperFunction, usage, context) : stableHelperFunction;
14759
14526
  if (helperFunction && isFunctionLike$1(helperFunction) && !helperFunction.async && !helperFunction.generator && doesCleanupFunctionReleaseUsage(helperFunction, usage, context, new Set(visitedFunctions))) matchingLoopOrHelperAnchors.push(cleanupCall);
14760
14527
  });
14761
- return didCleanupFunctionMatch || doNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
14528
+ return didCleanupFunctionMatch || doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, matchingLoopOrHelperAnchors, context);
14762
14529
  };
14763
14530
  const doesBoundCleanupReleaseUsage = (expression, usage, context) => {
14764
14531
  const callExpression = stripParenExpression(expression);
@@ -14787,7 +14554,7 @@ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
14787
14554
  walkInsideStatementBlocks(callback.body, (child) => {
14788
14555
  if (isNodeOfType(child, "ReturnStatement") && child.argument && doesReturnedValueReleaseUsage(stripParenExpression(child.argument))) matchingCleanupReturns.push(child);
14789
14556
  });
14790
- return doNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
14557
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
14791
14558
  };
14792
14559
  const doesTestRequireLiveExpressionKey = (test, expressionKey, context) => {
14793
14560
  if (resolveExpressionKey(test, context) === expressionKey) return true;
@@ -14830,7 +14597,7 @@ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
14830
14597
  const helperFunction = resolveStableValue(child.callee, context);
14831
14598
  if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
14832
14599
  });
14833
- return doNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
14600
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
14834
14601
  };
14835
14602
  const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
14836
14603
  const componentFunction = findEnclosingFunction$1(callback);
@@ -15081,7 +14848,7 @@ const hasGuardedRefOwnedNestedCleanup = (callback, usage, cleanupReturns, contex
15081
14848
  const usageFunction = findEnclosingFunction$1(usage.node);
15082
14849
  const usageExpression = findTransparentExpressionRoot(usage.node);
15083
14850
  const usageAssignment = usageExpression.parent;
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;
14851
+ 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;
15085
14852
  const cleanupFunctions = cleanupReturns.flatMap((cleanupReturn) => {
15086
14853
  if (!isNodeOfType(cleanupReturn, "ReturnStatement") || !cleanupReturn.argument) return [];
15087
14854
  const cleanupFunction = resolveStableValue(cleanupReturn.argument, context);
@@ -15129,7 +14896,7 @@ const hasGuardedDeferredCleanup = (callback, usage, cleanupReturns, context) =>
15129
14896
  });
15130
14897
  }
15131
14898
  });
15132
- if (!doNodesCoverEveryPathFromFunctionEntry(cleanupFunction, globalReleaseProofs.map((releaseProof) => releaseProof.anchor), context)) return false;
14899
+ if (!doMatchingNodesCoverEveryPathFromFunctionEntry(cleanupFunction, globalReleaseProofs.map((releaseProof) => releaseProof.anchor), context)) return false;
15133
14900
  globalReleaseProofsByCleanup.set(cleanupFunction, globalReleaseProofs);
15134
14901
  }
15135
14902
  const handleAssignments = handleSymbol.references.filter((reference) => isWithinAssignmentTarget(reference.identifier));
@@ -15332,7 +15099,9 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15332
15099
  const nodeParameterKey = resolveExpressionKey(usageFunction.params?.[0], context);
15333
15100
  const releaseReceiverKey = resolveExpressionKey(releaseCallee.object, context);
15334
15101
  if (registrationReceiverKey === null || registrationReceiverKey !== nodeParameterKey || releaseReceiverKey === null || usage.eventKey === null || usage.eventKey !== resolveExpressionKey(releaseCall.arguments?.[0], context) || usage.handlerKey === null || usage.handlerKey !== resolveExpressionKey(releaseCall.arguments?.[1], context)) return false;
15335
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], releaseCall.arguments?.[2], context)) return false;
15102
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15103
+ const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15104
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15336
15105
  const releaseStart = getRangeStart(releaseCall);
15337
15106
  const matchingOwnershipAssignments = [];
15338
15107
  const usageFunctionBody = usageFunction.body;
@@ -15342,127 +15111,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15342
15111
  });
15343
15112
  const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
15344
15113
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
15345
- return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15346
- };
15347
- const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollectionKeys, context) => {
15348
- let currentNode = findTransparentExpressionRoot(releaseNode);
15349
- const visitedFunctions = /* @__PURE__ */ new Set();
15350
- const replayedCollectionKeys = /* @__PURE__ */ new Set();
15351
- while (true) {
15352
- const ownerFunction = findEnclosingFunction$1(currentNode);
15353
- if (!ownerFunction || !isFunctionLike$1(ownerFunction) || visitedFunctions.has(ownerFunction)) return null;
15354
- visitedFunctions.add(ownerFunction);
15355
- const isDirectConciseBody = ownerFunction.body === currentNode;
15356
- const statementNode = currentNode.parent;
15357
- const isDirectBlockStatement = isNodeOfType(ownerFunction.body, "BlockStatement") && isNodeOfType(statementNode, "ExpressionStatement") && statementNode.parent === ownerFunction.body;
15358
- if (!isDirectConciseBody && !isDirectBlockStatement || !doNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
15359
- const forEachCall = findEnclosingForEachCall(ownerFunction);
15360
- if (!forEachCall) return replayedCollectionKeys.size === requiredCollectionKeys.size && isReturnedEffectCleanupFunction(ownerFunction, context) ? ownerFunction : null;
15361
- const forEachCallee = stripParenExpression(forEachCall.callee);
15362
- if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
15363
- const collectionKey = resolveExpressionKey(forEachCallee.object, context);
15364
- if (!collectionKey || !requiredCollectionKeys.has(collectionKey)) return null;
15365
- replayedCollectionKeys.add(collectionKey);
15366
- currentNode = findTransparentExpressionRoot(forEachCall);
15367
- }
15368
- };
15369
- const collectReplayOwnerFunctions = (usageNode) => {
15370
- const ownerFunctions = /* @__PURE__ */ new Set();
15371
- let currentNode = usageNode;
15372
- while (true) {
15373
- const ownerFunction = findEnclosingFunction$1(currentNode);
15374
- if (!ownerFunction || !isFunctionLike$1(ownerFunction) || ownerFunctions.has(ownerFunction)) break;
15375
- ownerFunctions.add(ownerFunction);
15376
- const forEachCall = findEnclosingForEachCall(ownerFunction);
15377
- if (!forEachCall) break;
15378
- currentNode = forEachCall;
15379
- }
15380
- return ownerFunctions;
15381
- };
15382
- const hasCollectionMutationBeforeRelease = (usageNode, releaseNode, collectionKeys, context) => {
15383
- const usageStart = getRangeStart(usageNode);
15384
- const releaseStart = getRangeStart(releaseNode);
15385
- if (usageStart === null || releaseStart === null) return true;
15386
- const setupOwnerFunctions = collectReplayOwnerFunctions(usageNode);
15387
- const cleanupOwnerFunctions = collectReplayOwnerFunctions(releaseNode);
15388
- let programNode = usageNode;
15389
- while (programNode.parent) programNode = programNode.parent;
15390
- let didFindMutation = false;
15391
- walkAst(programNode, (child) => {
15392
- if (didFindMutation) return false;
15393
- const childStart = getRangeStart(child);
15394
- if (childStart === null) return;
15395
- const ownerFunction = context.cfg.enclosingFunction(child);
15396
- if (!ownerFunction) return;
15397
- const isAfterRegistration = setupOwnerFunctions.has(ownerFunction) && childStart > usageStart;
15398
- const isBeforeRelease = cleanupOwnerFunctions.has(ownerFunction) && childStart < releaseStart;
15399
- if (!isAfterRegistration && !isBeforeRelease) return;
15400
- if (isNodeOfType(child, "AssignmentExpression")) {
15401
- const assignmentKey = resolveExpressionKey(child.left, context);
15402
- const assignmentTarget = stripParenExpression(child.left);
15403
- if (assignmentKey && [...collectionKeys].some((collectionKey) => assignmentKey === collectionKey || assignmentKey === `${collectionKey}.length`) || isNodeOfType(assignmentTarget, "MemberExpression") && assignmentTarget.computed && collectionKeys.has(resolveExpressionKey(assignmentTarget.object, context) ?? "")) {
15404
- didFindMutation = true;
15405
- return false;
15406
- }
15407
- return;
15408
- }
15409
- if (isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
15410
- const deletedMember = stripParenExpression(child.argument);
15411
- if (!isNodeOfType(deletedMember, "MemberExpression")) return;
15412
- if (collectionKeys.has(resolveExpressionKey(deletedMember.object, context) ?? "")) {
15413
- didFindMutation = true;
15414
- return false;
15415
- }
15416
- return;
15417
- }
15418
- if (isNodeOfType(child, "UpdateExpression")) {
15419
- const updatedKey = resolveExpressionKey(child.argument, context);
15420
- if (updatedKey && [...collectionKeys].some((collectionKey) => updatedKey === `${collectionKey}.length`)) {
15421
- didFindMutation = true;
15422
- return false;
15423
- }
15424
- return;
15425
- }
15426
- if (!isNodeOfType(child, "CallExpression")) return;
15427
- const callee = stripParenExpression(child.callee);
15428
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES.has(callee.property.name) && !REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES.has(callee.property.name) || !collectionKeys.has(resolveExpressionKey(callee.object, context) ?? "")) return;
15429
- didFindMutation = true;
15430
- return false;
15431
- });
15432
- return didFindMutation;
15433
- };
15434
- const usesUnaryListenerSignature = (registrationCall, releaseCall) => getCalleeName$1(registrationCall) === "addListener" && registrationCall.arguments?.length === 1 && releaseCall.arguments?.length === 1;
15435
- const hasSafeForEachProjectionCleanup = (registrationCall, releaseCall, context) => {
15436
- const registrationCallee = stripParenExpression(registrationCall.callee);
15437
- const releaseCallee = stripParenExpression(releaseCall.callee);
15438
- if (!isNodeOfType(registrationCallee, "MemberExpression") || !isNodeOfType(releaseCallee, "MemberExpression")) return true;
15439
- const registrationVerbName = getCalleeName$1(registrationCall);
15440
- const releaseVerbName = getCalleeName$1(releaseCall);
15441
- const releaseHandler = releaseCall.arguments?.[usesUnaryListenerSignature(registrationCall, releaseCall) ? 0 : 1];
15442
- const releaseFunction = findEnclosingFunction$1(releaseCall);
15443
- const registrationEventKey = resolveResourceIdentityKey(registrationCall.arguments?.[0], context);
15444
- const releaseEventKey = resolveResourceIdentityKey(releaseCall.arguments?.[0], context);
15445
- const doesHandlerlessOffReleaseEveryRegistration = releaseVerbName === "off" && !releaseHandler && (releaseCall.arguments?.length === 0 || registrationEventKey !== null && registrationEventKey === releaseEventKey);
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;
15447
- const projections = [
15448
- registrationCallee.object,
15449
- registrationCall.arguments?.[0],
15450
- registrationCall.arguments?.[1],
15451
- releaseCallee.object,
15452
- releaseCall.arguments?.[0],
15453
- releaseCall.arguments?.[1]
15454
- ].flatMap((expression) => {
15455
- const projection = resolveForEachProjection(expression, context);
15456
- return projection ? [projection] : [];
15457
- });
15458
- if (registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener") for (const optionsNode of [registrationCall.arguments?.[2], releaseCall.arguments?.[2]]) {
15459
- const captureProjection = resolveEventListenerCaptureProjection(optionsNode, context);
15460
- if (captureProjection) projections.push(captureProjection);
15461
- }
15462
- if (projections.length === 0) return true;
15463
- const collectionKeys = new Set(projections.map((projection) => projection.collectionKey));
15464
- if (!findDirectExhaustiveForEachCleanupFunction(releaseCall, collectionKeys, context)) return false;
15465
- return !hasCollectionMutationBeforeRelease(registrationCall, releaseCall, collectionKeys, context);
15114
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15466
15115
  };
15467
15116
  const doesReleaseCallMatchUsage = (node, usage, context) => {
15468
15117
  const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
@@ -15479,8 +15128,8 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15479
15128
  const releaseVerbName = getReleaseVerbName(callNode);
15480
15129
  if (!releaseVerbName) return false;
15481
15130
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
15482
- const releaseReceiverKey = resolveResourceIdentityKey(callee.object, context);
15483
- const releaseEventKey = resolveResourceIdentityKey(callNode.arguments?.[0], context);
15131
+ const releaseReceiverKey = resolveExpressionKey(callee.object, context);
15132
+ const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
15484
15133
  const pairedReleaseVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
15485
15134
  const pushedResourceCollectionKey = findPushedResourceCollectionKey(usage, context);
15486
15135
  const releaseReceiverForOfStatement = findForOfStatementForIteratorExpression(callee.object, context);
@@ -15490,13 +15139,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15490
15139
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
15491
15140
  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;
15492
15141
  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;
15494
15142
  if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
15495
- if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
15496
- if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
15497
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
15498
- }
15499
- if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
15500
15143
  if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
15501
15144
  if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
15502
15145
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
@@ -15506,9 +15149,6 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15506
15149
  if (isAssignmentFormForOfIteratorReference(usageEventArgument, context) || isAssignmentFormForOfIteratorReference(releaseEventArgument, context)) return false;
15507
15150
  if (usage.eventKey !== null && releaseEventKey !== null && usage.eventKey !== releaseEventKey) {
15508
15151
  if (!isNodeOfType(usage.node, "CallExpression")) return false;
15509
- const registrationEventProjectionKey = resolveForEachProjectionKey(usage.node.arguments?.[0], context);
15510
- const releaseEventProjectionKey = resolveForEachProjectionKey(callNode.arguments?.[0], context);
15511
- if ((registrationEventProjectionKey !== null || releaseEventProjectionKey !== null) && registrationEventProjectionKey !== releaseEventProjectionKey) return false;
15512
15152
  const usageForOfStatement = findForOfStatementForIteratorExpression(usageEventArgument, context);
15513
15153
  const releaseForOfStatement = findForOfStatementForIteratorExpression(releaseEventArgument, context);
15514
15154
  if (usageForOfStatement === null !== (releaseForOfStatement === null)) return false;
@@ -15522,7 +15162,9 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15522
15162
  const registrationCallee = stripParenExpression(usage.node.callee);
15523
15163
  if (!isNodeOfType(registrationCallee, "MemberExpression")) return false;
15524
15164
  if (!isStableLoopReceiver(registrationCallee.object, context) || !isStableLoopReceiver(callee.object, context) || registrationHandlerSymbolId === null || registrationHandlerSymbolId !== releaseHandlerSymbolId) return false;
15525
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context)) return false;
15165
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15166
+ const releaseCapture = resolveEventListenerCapture(callNode.arguments?.[2], { allowIndeterminateEntries: true });
15167
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15526
15168
  if (!isDirectExhaustiveForOfRelease(callNode, releaseForOfStatement)) return false;
15527
15169
  }
15528
15170
  }
@@ -15531,28 +15173,27 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15531
15173
  return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
15532
15174
  }
15533
15175
  if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
15534
- const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
15535
- const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
15176
+ const usesUnaryListenerSignature = usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1 && callNode.arguments?.length === 1;
15177
+ const releaseHandler = usesUnaryListenerSignature ? callNode.arguments?.[0] : callNode.arguments?.[1];
15536
15178
  if (!releaseHandler) return releaseVerbName === "off";
15537
- const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
15538
- const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
15539
- return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
15179
+ const expectedHandlerKey = usesUnaryListenerSignature ? usage.eventKey : usage.handlerKey;
15180
+ const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignature ? 0 : 1] : null;
15181
+ return expectedHandlerKey !== null && resolveExpressionKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
15540
15182
  }
15541
15183
  if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
15542
15184
  return true;
15543
15185
  };
15544
15186
  const matchesPairedReleaseVerb = (releaseVerbName, pairedVerbNames) => pairedVerbNames.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName);
15545
15187
  const isReturnedEffectCleanupFunction = (functionNode, context) => {
15546
- const effectCallback = findEnclosingFunction$1(functionNode);
15547
- if (!effectCallback || !isFunctionLike$1(effectCallback)) return false;
15548
- const effectCall = effectCallback.parent;
15549
- if (!isNodeOfType(effectCall, "CallExpression") || !isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return false;
15550
- if (!isNodeOfType(effectCallback.body, "BlockStatement")) return resolveStableValue(effectCallback.body, context) === functionNode;
15551
- let isReturned = false;
15552
- walkInsideStatementBlocks(effectCallback.body, (child) => {
15553
- if (isNodeOfType(child, "ReturnStatement") && child.argument && resolveStableValue(child.argument, context) === functionNode) isReturned = true;
15554
- });
15555
- return isReturned;
15188
+ let currentNode = functionNode;
15189
+ let parentNode = currentNode.parent;
15190
+ while (isNodeOfType(parentNode, "ChainExpression") || isNodeOfType(parentNode, "TSAsExpression") || isNodeOfType(parentNode, "TSNonNullExpression")) {
15191
+ currentNode = parentNode;
15192
+ parentNode = currentNode.parent;
15193
+ }
15194
+ const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
15195
+ const effectCall = effectCallback?.parent;
15196
+ return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes));
15556
15197
  };
15557
15198
  const isPotentiallyReachableFunction = (functionNode, context) => {
15558
15199
  if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode, context)) return true;
@@ -15642,7 +15283,7 @@ const hasEffectCleanupInvocation = (storage, usage, context) => {
15642
15283
  if (!cleanupFunction || !cleanupFunctionInvokesRef(cleanupFunction)) return;
15643
15284
  matchingReturns.push(child);
15644
15285
  });
15645
- return doNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15286
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15646
15287
  };
15647
15288
  let didFindInvocation = false;
15648
15289
  walkAst(componentFunction.body, (child) => {
@@ -15694,10 +15335,12 @@ const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
15694
15335
  return findRetainedDisposerStorages(disposerFunction, usage, context).some((storage) => isRetainedDisposerStorageEstablished(storage, usage, context) && !hasUnsafeRetainedDisposerOverwrite(storage, usage, context) && (hasEffectCleanupInvocation(storage, usage, context) || hasCallbackRefReplacementInvocation(storage, usage, context)));
15695
15336
  };
15696
15337
  const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
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;
15338
+ 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;
15339
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15698
15340
  const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
15699
15341
  if (!isNodeOfType(releaseCall, "CallExpression")) return false;
15700
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], releaseCall.arguments?.[2], context)) return false;
15342
+ const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15343
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15701
15344
  const ownerFunction = findEnclosingFunction$1(releaseFunction);
15702
15345
  if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
15703
15346
  const triggerRegistrations = [];
@@ -15922,7 +15565,7 @@ const effectReturnsRefOwnedCleanup = (effectCallback, componentFunction, retaine
15922
15565
  walkInsideStatementBlocks(effectCallback.body, (child) => {
15923
15566
  if (isNodeOfType(child, "ReturnStatement") && child.argument && matchesReturnedCleanup(stripParenExpression(child.argument))) matchingReturns.push(child);
15924
15567
  });
15925
- return doNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15568
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(effectCallback, matchingReturns, context);
15926
15569
  };
15927
15570
  const hasGuaranteedRefOwnedUnmountCleanup = (retainedFunction, usage, context) => {
15928
15571
  const componentFunction = findEnclosingFunction$1(retainedFunction);
@@ -15965,6 +15608,15 @@ const findUnconditionalReturnStatement = (expression, ownerFunction) => {
15965
15608
  const returnStatement = expressionRoot.parent;
15966
15609
  return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === expressionRoot && findEnclosingFunction$1(returnStatement) === ownerFunction ? returnStatement : null;
15967
15610
  };
15611
+ const getFinalSequenceExpressionValue = (expression) => {
15612
+ let finalExpression = stripParenExpression(expression);
15613
+ while (isNodeOfType(finalExpression, "SequenceExpression")) {
15614
+ const sequenceResult = finalExpression.expressions.at(-1);
15615
+ if (!sequenceResult) break;
15616
+ finalExpression = stripParenExpression(sequenceResult);
15617
+ }
15618
+ return finalExpression;
15619
+ };
15968
15620
  const doesResourceResultEscape = (resourceNode, allowReturnedResourceEscape, allowConciseReturnEscape, context) => {
15969
15621
  if (!allowReturnedResourceEscape) return false;
15970
15622
  let currentNode = resourceNode;
@@ -16213,7 +15865,7 @@ const isReactRefCallbackCleanupOwnedByEffect = (retainedFunction, cleanupFunctio
16213
15865
  if (child !== returnedCleanupFunction.body && isFunctionLike$1(child)) return false;
16214
15866
  if (isNodeOfType(child, "CallExpression") && resolveRefOwnedCleanupFunction(child.callee, context) === cleanupFunction) matchingCalls.push(child);
16215
15867
  });
16216
- return doNodesCoverEveryPathFromFunctionEntry(returnedCleanupFunction, matchingCalls, context);
15868
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(returnedCleanupFunction, matchingCalls, context);
16217
15869
  };
16218
15870
  const matchingReturns = [];
16219
15871
  walkInsideStatementBlocks(retainedFunction.body, (child) => {
@@ -22563,49 +22215,25 @@ const jsLengthCheckFirst = defineRule({
22563
22215
  });
22564
22216
  //#endregion
22565
22217
  //#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
- ]);
22572
22218
  const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
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
- }
22219
+ const strippedExpression = stripParenExpression(expression);
22220
+ if (!isNodeOfType(strippedExpression, "Identifier")) return false;
22221
+ if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
22222
+ const symbol = scopes.symbolFor(strippedExpression);
22223
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22224
+ visitedSymbolIds.add(symbol.id);
22225
+ return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
22591
22226
  };
22592
22227
  const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
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);
22228
+ const strippedExpression = stripParenExpression(expression);
22229
+ if (isNodeOfType(strippedExpression, "Identifier")) {
22597
22230
  if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
22598
22231
  const symbol = scopes.symbolFor(strippedExpression);
22599
- if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22232
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22600
22233
  visitedSymbolIds.add(symbol.id);
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;
22234
+ return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
22608
22235
  }
22236
+ return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
22609
22237
  };
22610
22238
  //#endregion
22611
22239
  //#region src/plugin/rules/js-performance/js-min-max-loop.ts
@@ -34867,19 +34495,6 @@ const noCascadingSetState = defineRetiredRule({
34867
34495
  recommendation: "Retired: React batches synchronous state updates from one effect into the same follow-up commit, so setter count does not prove repeated redraws."
34868
34496
  });
34869
34497
  //#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
34883
34498
  //#region src/plugin/rules/state-and-effects/utils/create-state-trigger-reachability.ts
34884
34499
  const getRefCurrentSymbol = (node, context) => {
34885
34500
  const expression = stripParenExpression(node);
@@ -35408,8 +35023,19 @@ const isBuiltinNamespaceCallee = (callee) => {
35408
35023
  }
35409
35024
  return false;
35410
35025
  };
35026
+ const getReactUseCallbackSource = (reference, context) => {
35027
+ const identifier = reference.identifier;
35028
+ const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
35029
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
35030
+ const initializer = stripParenExpression(symbol.initializer);
35031
+ if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
35032
+ allowGlobalReactNamespace: true,
35033
+ resolveNamedAliases: true
35034
+ })) return initializer;
35035
+ return null;
35036
+ };
35411
35037
  const getDependencyStateRefs = (analysis, context, dependencyReference) => {
35412
- const useCallbackCall = getReactUseCallbackCall(dependencyReference.identifier, context.scopes);
35038
+ const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
35413
35039
  if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
35414
35040
  const dependencyList = useCallbackCall.arguments?.[1];
35415
35041
  if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
@@ -38923,17 +38549,6 @@ const noDynamicImportPath = defineRule({
38923
38549
  })
38924
38550
  });
38925
38551
  //#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
38937
38552
  //#region src/plugin/rules/state-and-effects/utils/is-cleanup-return.ts
38938
38553
  const ITERATOR_CALLBACK_METHOD_NAMES = new Set([
38939
38554
  "each",
@@ -39078,16 +38693,7 @@ const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) =>
39078
38693
  }
39079
38694
  return dependencyStateSymbolIds;
39080
38695
  };
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) => {
38696
+ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
39091
38697
  const analysisFunctions = new Set([effectCallback]);
39092
38698
  const pendingFunctions = [effectCallback];
39093
38699
  while (pendingFunctions.length > 0) {
@@ -39095,9 +38701,9 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeSta
39095
38701
  if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
39096
38702
  walkInsideStatementBlocks(currentFunction.body, (child) => {
39097
38703
  if (!isNodeOfType(child, "CallExpression")) return;
39098
- const invokedFunction = includeStableCallbacks ? resolveSynchronouslyInvokedFunction(child.callee, scopes) : resolveExactLocalFunction(child.callee, scopes);
38704
+ const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
39099
38705
  if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
39100
- if (isFunctionLike$1(invokedFunction) && (invokedFunction.async || invokedFunction.generator)) return;
38706
+ if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
39101
38707
  analysisFunctions.add(invokedFunction);
39102
38708
  pendingFunctions.push(invokedFunction);
39103
38709
  });
@@ -39110,51 +38716,7 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
39110
38716
  walkInsideStatementBlocks(analysisFunction.body, visitor);
39111
38717
  }
39112
38718
  };
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()) => {
38719
+ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39158
38720
  const unwrappedExpression = stripParenExpression(expression);
39159
38721
  if (isNodeOfType(unwrappedExpression, "Literal")) {
39160
38722
  const literalValue = unwrappedExpression.value;
@@ -39162,49 +38724,46 @@ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, vi
39162
38724
  return null;
39163
38725
  }
39164
38726
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
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;
38727
+ if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
39168
38728
  if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
39169
- if (unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression)) return { value: NaN };
39170
38729
  const immutableSymbol = scopes.symbolFor(unwrappedExpression);
39171
38730
  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;
39172
- return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id), additionalStateValues);
38731
+ return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
39173
38732
  }
39174
38733
  if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
39175
38734
  if (unwrappedExpression.operator === "void") return { value: void 0 };
39176
38735
  if (unwrappedExpression.operator !== "!") return null;
39177
- const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38736
+ const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39178
38737
  return argumentValue ? { value: !argumentValue.value } : null;
39179
38738
  }
39180
38739
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
39181
38740
  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")) {
39182
- const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38741
+ const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
39183
38742
  return argumentValue ? { value: Boolean(argumentValue.value) } : null;
39184
38743
  }
39185
38744
  return null;
39186
38745
  }
39187
38746
  if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
39188
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38747
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39189
38748
  if (!leftValue) return null;
39190
38749
  if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
39191
38750
  if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
39192
38751
  if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
39193
- return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38752
+ return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39194
38753
  }
39195
38754
  if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
39196
- const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38755
+ const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39197
38756
  if (!testValue) return null;
39198
- return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38757
+ return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39199
38758
  }
39200
38759
  if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
39201
- const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38760
+ const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39202
38761
  if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
39203
38762
  return null;
39204
38763
  }
39205
38764
  if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
39206
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
39207
- const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38765
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
38766
+ const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39208
38767
  if (!leftValue || !rightValue) return null;
39209
38768
  if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
39210
38769
  const areEqual = leftValue.value === rightValue.value;
@@ -39243,7 +38802,8 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39243
38802
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
39244
38803
  if (!isNodeOfType(child, "CallExpression")) return;
39245
38804
  if (!isNodeOfType(child.callee, "Identifier")) return;
39246
- const stateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
38805
+ const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
38806
+ const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
39247
38807
  if (!stateName) return;
39248
38808
  const writeInfo = stateWrites.get(stateName) ?? {
39249
38809
  values: /* @__PURE__ */ new Set(),
@@ -39259,27 +38819,27 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39259
38819
  const isGlobalBooleanCall = (node, scopes) => {
39260
38820
  return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
39261
38821
  };
39262
- const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
38822
+ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
39263
38823
  let currentNode = workNode;
39264
38824
  while (currentNode.parent) {
39265
38825
  const parentNode = currentNode.parent;
39266
38826
  if (isFunctionLike$1(parentNode)) break;
39267
38827
  if (isNodeOfType(parentNode, "IfStatement")) {
39268
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38828
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39269
38829
  if (testValue) {
39270
38830
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39271
38831
  if (currentNode === parentNode.alternate && testValue.value) return false;
39272
38832
  }
39273
38833
  }
39274
38834
  if (isNodeOfType(parentNode, "ConditionalExpression")) {
39275
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38835
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39276
38836
  if (testValue) {
39277
38837
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39278
38838
  if (currentNode === parentNode.alternate && testValue.value) return false;
39279
38839
  }
39280
38840
  }
39281
38841
  if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
39282
- const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38842
+ const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
39283
38843
  if (leftValue) {
39284
38844
  if (parentNode.operator === "&&" && !leftValue.value) return false;
39285
38845
  if (parentNode.operator === "||" && leftValue.value) return false;
@@ -39291,7 +38851,7 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39291
38851
  if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
39292
38852
  const earlierStatement = parentNode.body[index];
39293
38853
  if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
39294
- if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues)?.value) return false;
38854
+ if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
39295
38855
  }
39296
38856
  }
39297
38857
  currentNode = parentNode;
@@ -39301,93 +38861,24 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39301
38861
  const isReaderWorkNode = (node, analysisFunctions, scopes) => {
39302
38862
  if (isNodeOfType(node, "CallExpression")) {
39303
38863
  if (isGlobalBooleanCall(node, scopes)) return false;
39304
- const invokedFunction = resolveSynchronouslyInvokedFunction(node.callee, scopes);
38864
+ const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
39305
38865
  return !invokedFunction || !analysisFunctions.has(invokedFunction);
39306
38866
  }
39307
38867
  return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
39308
38868
  };
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);
38869
+ const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
38870
+ if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
38871
+ for (const writtenValue of writeInfo.values) {
38872
+ const stateValue = { value: writtenValue };
39364
38873
  let didFindReachableWork = false;
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;
38874
+ visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
38875
+ if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
38876
+ if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
39378
38877
  });
39379
38878
  if (didFindReachableWork) return true;
39380
38879
  }
39381
38880
  return false;
39382
38881
  };
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
- };
39391
38882
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
39392
38883
  const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39393
38884
  "clear",
@@ -39398,499 +38889,26 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39398
38889
  "keys",
39399
38890
  "values"
39400
38891
  ]);
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;
39483
- }
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;
39584
- }
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;
38892
+ const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
38893
+ if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
38894
+ if (isNodeOfType(returnedValue, "CallExpression")) {
38895
+ if (isNodeOfType(returnedValue.callee, "Identifier")) {
38896
+ const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
38897
+ if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
38898
+ if (isSetterIdentifier(returnedValue.callee.name)) return true;
39613
38899
  }
38900
+ return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
39614
38901
  }
39615
- return true;
39616
- };
39617
- const hasProvenGlobalNamespaceReference = (expression, namespaceNames, scopes) => {
39618
- for (const namespaceName of namespaceNames) if (isProvenGlobalNamespaceReference(expression, namespaceName, scopes)) return true;
38902
+ if (isNodeOfType(returnedValue, "Identifier")) return true;
39619
38903
  return false;
39620
38904
  };
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;
38905
+ const STORAGE_GLOBAL_NAMES = new Set(["localStorage", "sessionStorage"]);
38906
+ const isBrowserStorageReceiver = (receiver) => {
38907
+ if (!receiver) return false;
38908
+ if (isNodeOfType(receiver, "Identifier")) return STORAGE_GLOBAL_NAMES.has(receiver.name);
38909
+ if (isNodeOfType(receiver, "MemberExpression")) return isNodeOfType(receiver.property, "Identifier") && STORAGE_GLOBAL_NAMES.has(receiver.property.name);
39890
38910
  return false;
39891
38911
  };
39892
- const STORAGE_GLOBAL_NAMES = ["localStorage", "sessionStorage"];
39893
- const isBrowserStorageReceiver = (receiver, scopes) => Boolean(receiver && STORAGE_GLOBAL_NAMES.some((storageName) => isProvenGlobalNamespaceReference(receiver, storageName, scopes)));
39894
38912
  const STORAGE_HOOK_PATTERN = /^use\w*Storage/i;
39895
38913
  const collectStorageHookSetterNames = (componentBody) => {
39896
38914
  const setterNames = /* @__PURE__ */ new Set();
@@ -39922,16 +38940,23 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
39922
38940
  });
39923
38941
  return didFindOpaqueSetterCall;
39924
38942
  };
39925
- const isReactUseRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "useRef", scopes, {
38943
+ const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
39926
38944
  allowGlobalReactNamespace: true,
39927
38945
  allowUnboundBareCalls: true,
39928
38946
  resolveNamedAliases: true
39929
- });
39930
- const isReactRefCall = (expression, scopes) => isReactUseRefCall(expression, scopes) || isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "createRef", scopes, {
38947
+ }) || isReactApiCall(expression, "createRef", scopes, {
39931
38948
  allowGlobalReactNamespace: true,
39932
38949
  allowUnboundBareCalls: true,
39933
38950
  resolveNamedAliases: true
39934
- });
38951
+ }));
38952
+ const getDirectReactRefSymbol = (rawExpression, scopes) => {
38953
+ const expression = stripParenExpression(rawExpression);
38954
+ if (!isNodeOfType(expression, "Identifier")) return null;
38955
+ const symbol = scopes.symbolFor(expression);
38956
+ if (!symbol) return null;
38957
+ const initializer = getDirectUnreassignedInitializer(symbol);
38958
+ return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
38959
+ };
39935
38960
  const isReactNativeJsxElement = (openingElement, scopes) => {
39936
38961
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
39937
38962
  const symbol = scopes.symbolFor(openingElement.name);
@@ -39939,33 +38964,19 @@ const isReactNativeJsxElement = (openingElement, scopes) => {
39939
38964
  return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
39940
38965
  };
39941
38966
  const isDirectHostJsxRef = (symbol, scopes) => {
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;
38967
+ let hostRefCount = 0;
38968
+ for (const reference of symbol.references) {
38969
+ const expression = findTransparentExpressionRoot(reference.identifier);
38970
+ const container = expression.parent;
38971
+ if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
38972
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
38973
+ const attribute = container.parent;
38974
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
38975
+ const openingElement = attribute.parent;
38976
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
38977
+ hostRefCount += 1;
38978
+ }
38979
+ return hostRefCount > 0;
39969
38980
  };
39970
38981
  const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39971
38982
  const identifier = stripParenExpression(expression);
@@ -39975,18 +38986,7 @@ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39975
38986
  const rawFirstParameter = callback.params?.[0];
39976
38987
  const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
39977
38988
  const symbol = scopes.symbolFor(identifier);
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
- }));
38989
+ return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
39990
38990
  };
39991
38991
  const getDirectReactRefCall = (symbol, scopes) => {
39992
38992
  const initializer = getDirectUnreassignedInitializer(symbol);
@@ -39994,38 +38994,15 @@ const getDirectReactRefCall = (symbol, scopes) => {
39994
38994
  const expression = stripParenExpression(initializer);
39995
38995
  return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
39996
38996
  };
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
- };
40012
38997
  const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
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;
38998
+ const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
38999
+ if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
40018
39000
  let intrinsicValueWriteCount = 0;
40019
- let lazyEmptyMapInitializationCount = 0;
40020
39001
  for (const reference of symbol.references) {
40021
39002
  const identifier = findTransparentExpressionRoot(reference.identifier);
40022
39003
  const currentMember = identifier.parent;
40023
39004
  if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
40024
39005
  const currentExpression = findTransparentExpressionRoot(currentMember);
40025
- if (hasLazyEmptyMapInitializer && isDirectLazyEmptyMapInitialization(currentExpression, symbol, scopes)) {
40026
- lazyEmptyMapInitializationCount += 1;
40027
- continue;
40028
- }
40029
39006
  const methodMember = currentExpression.parent;
40030
39007
  if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
40031
39008
  const methodName = getStaticPropertyName(methodMember);
@@ -40038,7 +39015,7 @@ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
40038
39015
  if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
40039
39016
  intrinsicValueWriteCount += 1;
40040
39017
  }
40041
- return intrinsicValueWriteCount > 0 && (hasDirectEmptyMapInitializer || lazyEmptyMapInitializationCount === 1);
39018
+ return intrinsicValueWriteCount > 0;
40042
39019
  };
40043
39020
  const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40044
39021
  const expression = stripParenExpression(rawExpression);
@@ -40052,11 +39029,7 @@ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollecti
40052
39029
  }
40053
39030
  if (isNodeOfType(expression, "MemberExpression")) {
40054
39031
  if (getStaticPropertyName(expression) === "current") {
40055
- const symbol = resolveReactRefSymbol(expression, scopes, {
40056
- allowUnboundBareCalls: true,
40057
- includeCreateRef: true,
40058
- resolveNamedAliases: true
40059
- });
39032
+ const symbol = getDirectReactRefSymbol(expression.object, scopes);
40060
39033
  return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
40061
39034
  }
40062
39035
  return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
@@ -40074,80 +39047,19 @@ const isCommittedDomSyncNode = (node, scopes) => {
40074
39047
  if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
40075
39048
  return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
40076
39049
  };
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
- }
39050
+ const isExternalSyncNode = (node) => {
39051
+ if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
39052
+ if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
40142
39053
  if (!isNodeOfType(node, "CallExpression")) return false;
40143
- if (isProvenExternalSyncDirectCallee(node.callee, scopes)) return true;
39054
+ if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
40144
39055
  if (!isNodeOfType(node.callee, "MemberExpression")) return false;
40145
39056
  const propertyName = getStaticPropertyName(node.callee);
40146
39057
  if (propertyName === null) return false;
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);
39058
+ if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
39059
+ if (isBrowserStorageReceiver(node.callee.object)) return true;
39060
+ if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
39061
+ const receiverRootName = getRootIdentifierName(node.callee.object);
39062
+ return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
40151
39063
  };
40152
39064
  const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
40153
39065
  if (!isFunctionLike$1(effectCallback)) return false;
@@ -40156,7 +39068,7 @@ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateNa
40156
39068
  } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
40157
39069
  let didFindExternalCall = false;
40158
39070
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
40159
- if (isExternalSyncNode(child, scopes) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
39071
+ if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
40160
39072
  });
40161
39073
  return didFindExternalCall;
40162
39074
  };
@@ -40178,11 +39090,11 @@ const noEffectChain = defineRule({
40178
39090
  setterToStateName.set(binding.setterName, binding.valueName);
40179
39091
  if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
40180
39092
  const stateIdentifier = binding.declarator.id.elements[0];
40181
- const setterIdentifier = binding.declarator.id.elements[1];
40182
39093
  if (isNodeOfType(stateIdentifier, "Identifier")) {
40183
39094
  const stateSymbol = context.scopes.symbolFor(stateIdentifier);
40184
39095
  if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
40185
39096
  }
39097
+ const setterIdentifier = binding.declarator.id.elements[1];
40186
39098
  if (isNodeOfType(setterIdentifier, "Identifier")) {
40187
39099
  const setterSymbol = context.scopes.symbolFor(setterIdentifier);
40188
39100
  if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
@@ -40195,11 +39107,10 @@ const noEffectChain = defineRule({
40195
39107
  const callback = getEffectCallback(effectCall, context.scopes);
40196
39108
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
40197
39109
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
40198
- const stateWrites = collectStateWritesInEffect(collectStateWriteAnalysisFunctions(callback, context.scopes, setterSymbolIdToStateName), setterSymbolIdToStateName, context.scopes);
39110
+ const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
40199
39111
  const writtenStateNames = new Set(stateWrites.keys());
40200
39112
  effectInfos.push({
40201
39113
  node: effectCall,
40202
- callback,
40203
39114
  dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
40204
39115
  stateWrites,
40205
39116
  analysisFunctions,
@@ -40216,10 +39127,10 @@ const noEffectChain = defineRule({
40216
39127
  if (readerEffect.isExternalSync) continue;
40217
39128
  if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
40218
39129
  let chainedStateName = null;
40219
- for (const writtenName of writerEffect.stateWrites.keys()) {
39130
+ for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
40220
39131
  const writtenStateSymbolId = stateSymbolIds.get(writtenName);
40221
39132
  if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
40222
- if (!canStateWriteReachReaderWork(writtenName, writerEffect, readerEffect, stateSymbolIds, context.scopes)) continue;
39133
+ if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
40223
39134
  chainedStateName = writtenName;
40224
39135
  break;
40225
39136
  }
@@ -45004,6 +43915,16 @@ const noManyBooleanProps = defineRule({
45004
43915
  }
45005
43916
  });
45006
43917
  //#endregion
43918
+ //#region src/plugin/utils/is-global-match-media-call.ts
43919
+ const isGlobalMatchMediaCall = (node, scopes) => {
43920
+ if (!isNodeOfType(node, "CallExpression")) return false;
43921
+ const callee = stripParenExpression(node.callee);
43922
+ if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
43923
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
43924
+ const receiver = stripParenExpression(callee.object);
43925
+ return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
43926
+ };
43927
+ //#endregion
45007
43928
  //#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
45008
43929
  const REACT_USE_STATE_OPTIONS = {
45009
43930
  allowGlobalReactNamespace: true,
@@ -56529,6 +55450,17 @@ const preferModuleScopePureFunction = defineRule({
56529
55450
  }
56530
55451
  });
56531
55452
  //#endregion
55453
+ //#region src/plugin/utils/get-require-call-source.ts
55454
+ const getRequireCallSource = (expression) => {
55455
+ const unwrappedExpression = stripParenExpression(expression);
55456
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
55457
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
55458
+ if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
55459
+ const [firstArgument] = unwrappedExpression.arguments ?? [];
55460
+ if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
55461
+ return typeof firstArgument.value === "string" ? firstArgument.value : null;
55462
+ };
55463
+ //#endregion
56532
55464
  //#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
56533
55465
  const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
56534
55466
  const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
@@ -61236,717 +60168,6 @@ const isSetterCalledDuringRender = (root, setterName) => {
61236
60168
  return found;
61237
60169
  };
61238
60170
  //#endregion
61239
- //#region src/plugin/rules/state-and-effects/utils/create-external-location-invalidation-checker.ts
61240
- const HISTORY_LOCATION_MUTATION_METHOD_NAMES = new Set(["pushState", "replaceState"]);
61241
- const AGGREGATE_MUTATION_METHOD_NAMES = new Set([...MUTATING_ARRAY_METHODS, ...MUTATING_COLLECTION_METHODS]);
61242
- const OBJECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
61243
- "assign",
61244
- "defineProperties",
61245
- "defineProperty",
61246
- "setPrototypeOf"
61247
- ]);
61248
- const REFLECT_AGGREGATE_MUTATION_METHOD_NAMES = new Set([
61249
- "defineProperty",
61250
- "deleteProperty",
61251
- "set",
61252
- "setPrototypeOf"
61253
- ]);
61254
- const LOCATION_CHANGE_EVENT_NAMES = new Set(["hashchange", "popstate"]);
61255
- const containsGlobalLocationSnapshotRead = (node, scopes) => {
61256
- let didFindLocationSnapshotRead = false;
61257
- walkAst(node, (child) => {
61258
- if (didFindLocationSnapshotRead) return false;
61259
- if (child !== node && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
61260
- if (!isProvenGlobalNamespaceReference(child, "location", scopes)) return;
61261
- didFindLocationSnapshotRead = true;
61262
- return false;
61263
- });
61264
- return didFindLocationSnapshotRead;
61265
- };
61266
- const resolveExactLocalOrReactCallbackFunction = (expression, scopes) => {
61267
- const localFunction = resolveExactLocalFunction(expression, scopes);
61268
- if (isFunctionLike$1(localFunction)) return localFunction;
61269
- const unwrappedExpression = stripParenExpression(expression);
61270
- let callbackSource = unwrappedExpression;
61271
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
61272
- const callbackSymbol = resolveConstIdentifierAlias(unwrappedExpression, scopes);
61273
- if (callbackSymbol?.kind !== "const" || !callbackSymbol.initializer) return null;
61274
- callbackSource = callbackSymbol.initializer;
61275
- }
61276
- const unwrappedCallbackSource = stripParenExpression(callbackSource);
61277
- if (!isNodeOfType(unwrappedCallbackSource, "CallExpression") || !isReactApiCall(unwrappedCallbackSource, "useCallback", scopes, { resolveNamedAliases: true })) return null;
61278
- const callback = unwrappedCallbackSource.arguments?.[0];
61279
- if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
61280
- return resolveExactLocalFunction(stripParenExpression(callback), scopes);
61281
- };
61282
- const bindingReadsExactGlobalLocationSnapshot = (bindingIdentifier, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
61283
- const symbol = scopes.symbolFor(bindingIdentifier);
61284
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
61285
- 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;
61286
- const resolvedFunction = resolveExactLocalOrReactCallbackFunction(bindingIdentifier, scopes);
61287
- const initializer = isFunctionLike$1(resolvedFunction) ? resolvedFunction.body : getDirectUnreassignedInitializer(symbol);
61288
- if (!initializer) return false;
61289
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
61290
- nextVisitedSymbolIds.add(symbol.id);
61291
- if (containsGlobalLocationSnapshotRead(initializer, scopes)) return true;
61292
- let didFindAliasedLocationSnapshotRead = false;
61293
- walkAst(initializer, (child) => {
61294
- if (didFindAliasedLocationSnapshotRead) return false;
61295
- if (child !== initializer && isFunctionLike$1(child) && !executesDuringRender(findTransparentExpressionRoot(child), scopes)) return false;
61296
- if (!isNodeOfType(child, "Identifier")) return;
61297
- if (bindingReadsExactGlobalLocationSnapshot(child, scopes, nextVisitedSymbolIds)) {
61298
- didFindAliasedLocationSnapshotRead = true;
61299
- return false;
61300
- }
61301
- });
61302
- return didFindAliasedLocationSnapshotRead;
61303
- };
61304
- const hasRenderReachableLocationSnapshotRead = (componentBody, renderReachableExpressions, directRenderNames, scopes) => {
61305
- if (renderReachableExpressions.some((expression) => containsGlobalLocationSnapshotRead(expression, scopes))) return true;
61306
- for (const statement of componentBody.body ?? []) {
61307
- if (isNodeOfType(statement, "FunctionDeclaration") && statement.id) {
61308
- if (directRenderNames.has(statement.id.name) && bindingReadsExactGlobalLocationSnapshot(statement.id, scopes)) return true;
61309
- continue;
61310
- }
61311
- if (!isNodeOfType(statement, "VariableDeclaration")) continue;
61312
- for (const declarator of statement.declarations ?? []) {
61313
- if (!isNodeOfType(declarator.id, "Identifier") || !declarator.init) continue;
61314
- if (!directRenderNames.has(declarator.id.name)) continue;
61315
- if (bindingReadsExactGlobalLocationSnapshot(declarator.id, scopes)) return true;
61316
- }
61317
- }
61318
- return false;
61319
- };
61320
- const isGlobalHistoryLocationMutation = (node, scopes) => {
61321
- if (!isNodeOfType(node, "CallExpression")) return false;
61322
- const callee = stripParenExpression(node.callee);
61323
- if (!isNodeOfType(callee, "MemberExpression")) return false;
61324
- const methodName = getStaticPropertyName(callee);
61325
- return Boolean(methodName && HISTORY_LOCATION_MUTATION_METHOD_NAMES.has(methodName) && isProvenGlobalNamespaceReference(callee.object, "history", scopes));
61326
- };
61327
- const getStaticLocationChangeEvent = (node) => {
61328
- if (!node) return null;
61329
- const eventNameNode = stripParenExpression(node);
61330
- if (!isNodeOfType(eventNameNode, "Literal") || typeof eventNameNode.value !== "string") return null;
61331
- return LOCATION_CHANGE_EVENT_NAMES.has(eventNameNode.value) ? eventNameNode.value : null;
61332
- };
61333
- const addToSetIndex = (index, key, value) => {
61334
- const indexedValues = index.get(key) ?? /* @__PURE__ */ new Set();
61335
- indexedValues.add(value);
61336
- index.set(key, indexedValues);
61337
- };
61338
- const getSynchronousInvocationExpression = (functionNode, scopes) => {
61339
- const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
61340
- if (!executesDuringRender(functionExpressionRoot, scopes)) return null;
61341
- const parent = functionExpressionRoot.parent;
61342
- return isNodeOfType(parent, "CallExpression") || isNodeOfType(parent, "NewExpression") ? parent : null;
61343
- };
61344
- const getLocationListenerOperation = (callExpression, scopes) => {
61345
- if (!isNodeOfType(callExpression, "CallExpression")) return null;
61346
- const callee = stripParenExpression(callExpression.callee);
61347
- const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : isNodeOfType(callee, "Identifier") ? callee.name : null;
61348
- if (methodName !== "addEventListener" && methodName !== "removeEventListener") return null;
61349
- if (!(isNodeOfType(callee, "MemberExpression") ? isProvenGlobalObjectReference(callee.object, scopes) : isNodeOfType(callee, "Identifier") && scopes.isGlobalReference(callee))) return null;
61350
- const eventName = getStaticLocationChangeEvent(callExpression.arguments?.[0]);
61351
- if (!eventName) return null;
61352
- const listenerExpression = callExpression.arguments?.[1];
61353
- if (!listenerExpression || isNodeOfType(listenerExpression, "SpreadElement")) return null;
61354
- const listenerFunction = resolveExactLocalOrReactCallbackFunction(listenerExpression, scopes);
61355
- if (!isFunctionLike$1(listenerFunction)) return null;
61356
- const captureArgument = callExpression.arguments?.[2];
61357
- const capture = isNodeOfType(captureArgument, "SpreadElement") ? null : resolveEventListenerCapture(captureArgument, {
61358
- allowComputedString: true,
61359
- allowIndeterminateEntries: true
61360
- });
61361
- return {
61362
- operation: methodName === "addEventListener" ? "add" : "remove",
61363
- registration: {
61364
- callExpression,
61365
- listenerFunction,
61366
- capture,
61367
- eventName
61368
- }
61369
- };
61370
- };
61371
- const buildLocationInvalidationIndex = (componentBody, componentFunction, context) => {
61372
- const index = {
61373
- componentFunction,
61374
- context,
61375
- effectCallbacks: /* @__PURE__ */ new Set(),
61376
- expressionsByOwner: /* @__PURE__ */ new Map(),
61377
- historyMutationsByOwner: /* @__PURE__ */ new Map(),
61378
- awaitExpressionsByOwner: /* @__PURE__ */ new Map(),
61379
- callSitesByFunction: /* @__PURE__ */ new Map(),
61380
- calledFunctionByExpression: /* @__PURE__ */ new Map(),
61381
- synchronousInvocationsByFunction: /* @__PURE__ */ new Map(),
61382
- synchronousCallbacksByExpression: /* @__PURE__ */ new Map(),
61383
- callsByCalleeSymbolId: /* @__PURE__ */ new Map(),
61384
- identifierCalls: /* @__PURE__ */ new Set(),
61385
- listenerRegistrations: [],
61386
- listenerRemovals: [],
61387
- mountedListenerFunctions: /* @__PURE__ */ new Set(),
61388
- mutationExecutionsByOwner: /* @__PURE__ */ new Map(),
61389
- synchronousMutationResultByFunction: /* @__PURE__ */ new Map()
61390
- };
61391
- walkAst(componentBody, (child) => {
61392
- if (isFunctionLike$1(child)) {
61393
- const invocationExpression = getSynchronousInvocationExpression(child, context.scopes);
61394
- if (invocationExpression) {
61395
- addToSetIndex(index.synchronousInvocationsByFunction, child, invocationExpression);
61396
- addToSetIndex(index.synchronousCallbacksByExpression, invocationExpression, child);
61397
- }
61398
- return;
61399
- }
61400
- const owner = context.cfg.enclosingFunction(child);
61401
- if (!owner) return;
61402
- if (isNodeOfType(child, "AwaitExpression")) {
61403
- addToSetIndex(index.awaitExpressionsByOwner, owner, child);
61404
- return;
61405
- }
61406
- if (!isNodeOfType(child, "CallExpression") && !isNodeOfType(child, "NewExpression")) return;
61407
- addToSetIndex(index.expressionsByOwner, owner, child);
61408
- if (isNodeOfType(child, "CallExpression")) {
61409
- if (isGlobalHistoryLocationMutation(child, context.scopes)) addToSetIndex(index.historyMutationsByOwner, owner, child);
61410
- if (isNodeOfType(stripParenExpression(child.callee), "Identifier")) index.identifierCalls.add(child);
61411
- const calledFunction = resolveExactLocalOrReactCallbackFunction(child.callee, context.scopes);
61412
- if (isFunctionLike$1(calledFunction)) {
61413
- addToSetIndex(index.callSitesByFunction, calledFunction, child);
61414
- index.calledFunctionByExpression.set(child, calledFunction);
61415
- }
61416
- if (context.cfg.enclosingFunction(child) === componentFunction && isReactApiCall(child, EFFECT_HOOK_NAMES$1, context.scopes, {
61417
- allowGlobalReactNamespace: true,
61418
- allowUnboundBareCalls: true,
61419
- resolveNamedAliases: true
61420
- })) {
61421
- const effectCallback = getEffectCallback(child, context.scopes);
61422
- if (isFunctionLike$1(effectCallback)) index.effectCallbacks.add(effectCallback);
61423
- }
61424
- const listenerOperation = getLocationListenerOperation(child, context.scopes);
61425
- if (listenerOperation?.operation === "add") index.listenerRegistrations.push(listenerOperation.registration);
61426
- else if (listenerOperation) index.listenerRemovals.push(listenerOperation.registration);
61427
- }
61428
- });
61429
- return index;
61430
- };
61431
- const isDescendantWithoutFunctionBoundary = (descendant, ancestor) => {
61432
- let current = descendant;
61433
- while (current && current !== ancestor) {
61434
- if (current !== descendant && isFunctionLike$1(current)) return false;
61435
- current = current.parent;
61436
- }
61437
- return current === ancestor;
61438
- };
61439
- const areInMutuallyExclusiveConditionalBranches = (firstNode, secondNode) => {
61440
- const firstBranches = /* @__PURE__ */ new Map();
61441
- let current = firstNode;
61442
- while (current?.parent) {
61443
- const parent = current.parent;
61444
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) firstBranches.set(parent, current);
61445
- if (current !== firstNode && isFunctionLike$1(current)) break;
61446
- current = parent;
61447
- }
61448
- current = secondNode;
61449
- while (current?.parent) {
61450
- const parent = current.parent;
61451
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === current || parent.alternate === current)) {
61452
- const firstBranch = firstBranches.get(parent);
61453
- if (firstBranch && firstBranch !== current) return true;
61454
- }
61455
- if (current !== secondNode && isFunctionLike$1(current)) break;
61456
- current = parent;
61457
- }
61458
- return false;
61459
- };
61460
- const collectStableBooleanGuardConstraints = (node, functionBoundary, scopes) => {
61461
- const constraintsBySymbolId = /* @__PURE__ */ new Map();
61462
- const recordConstraint = (test, requiredTestValue) => {
61463
- let expression = stripParenExpression(test);
61464
- let requiredValue = requiredTestValue;
61465
- while (isNodeOfType(expression, "UnaryExpression") && expression.operator === "!") {
61466
- requiredValue = !requiredValue;
61467
- expression = stripParenExpression(expression.argument);
61468
- }
61469
- if (!isNodeOfType(expression, "Identifier")) return;
61470
- const symbol = scopes.symbolFor(expression);
61471
- if (!symbol || symbol.references.some((reference) => reference.flag !== "read")) return;
61472
- const existingConstraint = constraintsBySymbolId.get(symbol.id);
61473
- constraintsBySymbolId.set(symbol.id, existingConstraint === void 0 || existingConstraint === requiredValue ? requiredValue : null);
61474
- };
61475
- let current = node;
61476
- while (current?.parent && current !== functionBoundary) {
61477
- const parent = current.parent;
61478
- if (isNodeOfType(parent, "IfStatement")) {
61479
- if (parent.consequent === current) recordConstraint(parent.test, true);
61480
- if (parent.alternate === current) recordConstraint(parent.test, false);
61481
- } else if (isNodeOfType(parent, "ConditionalExpression")) {
61482
- if (parent.consequent === current) recordConstraint(parent.test, true);
61483
- if (parent.alternate === current) recordConstraint(parent.test, false);
61484
- } else if (isNodeOfType(parent, "LogicalExpression") && parent.right === current) {
61485
- if (parent.operator === "&&") recordConstraint(parent.left, true);
61486
- if (parent.operator === "||") recordConstraint(parent.left, false);
61487
- }
61488
- if (current !== node && isFunctionLike$1(current)) break;
61489
- current = parent;
61490
- }
61491
- return constraintsBySymbolId;
61492
- };
61493
- const haveContradictoryStableBooleanGuards = (firstNode, secondNode, functionBoundary, scopes) => {
61494
- const firstConstraints = collectStableBooleanGuardConstraints(firstNode, functionBoundary, scopes);
61495
- const secondConstraints = collectStableBooleanGuardConstraints(secondNode, functionBoundary, scopes);
61496
- for (const requiredValue of firstConstraints.values()) if (requiredValue === null) return true;
61497
- for (const requiredValue of secondConstraints.values()) if (requiredValue === null) return true;
61498
- for (const [symbolId, firstRequiredValue] of firstConstraints) {
61499
- const secondRequiredValue = secondConstraints.get(symbolId);
61500
- if (firstRequiredValue !== null && secondRequiredValue !== void 0 && secondRequiredValue !== null && firstRequiredValue !== secondRequiredValue) return true;
61501
- }
61502
- return false;
61503
- };
61504
- const isInsideIntrinsicReactEventHandlerAttribute = (node, functionBoundary) => {
61505
- let current = node;
61506
- while (current && current !== functionBoundary) {
61507
- if (isEventHandlerAttribute(current) && isJsxAttributeOnIntrinsicHtmlElement(current)) return true;
61508
- current = current.parent;
61509
- }
61510
- return false;
61511
- };
61512
- const getTransparentExpressionBindingIdentifier = (expression) => {
61513
- const expressionRoot = findTransparentExpressionRoot(expression);
61514
- const parent = expressionRoot.parent;
61515
- if (isNodeOfType(parent, "VariableDeclarator") && isNodeOfType(parent.id, "Identifier")) return parent.id;
61516
- if (isNodeOfType(parent, "AssignmentExpression") && parent.right === expressionRoot && isNodeOfType(parent.left, "Identifier")) return parent.left;
61517
- return null;
61518
- };
61519
- const getIntrinsicReactEventHandlerBindingIdentifier = (functionNode, index) => {
61520
- if (isNodeOfType(functionNode, "FunctionDeclaration") && isNodeOfType(functionNode.id, "Identifier")) return functionNode.id;
61521
- const functionExpressionRoot = findTransparentExpressionRoot(functionNode);
61522
- const directTransparentBindingIdentifier = getTransparentExpressionBindingIdentifier(functionExpressionRoot);
61523
- if (directTransparentBindingIdentifier) return directTransparentBindingIdentifier;
61524
- const callbackCall = functionExpressionRoot.parent;
61525
- if (!isNodeOfType(callbackCall, "CallExpression") || callbackCall.arguments?.[0] !== functionExpressionRoot || !isReactApiCall(callbackCall, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
61526
- return getTransparentExpressionBindingIdentifier(callbackCall);
61527
- };
61528
- const isExclusiveIntrinsicReactEventHandler = (functionNode, index) => {
61529
- const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
61530
- if (!bindingIdentifier) return isInsideIntrinsicReactEventHandlerAttribute(functionNode, index.componentFunction);
61531
- const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
61532
- return Boolean(bindingSymbol && bindingSymbol.references.length > 0 && bindingSymbol.references.every((reference) => isInsideIntrinsicReactEventHandlerAttribute(reference.identifier, index.componentFunction)));
61533
- };
61534
- const canNodeReachNode = (sourceNode, targetNode, index) => {
61535
- const { context } = index;
61536
- if (!isNodeReachableWithinFunction(sourceNode, context)) return false;
61537
- if (!isNodeReachableWithinFunction(targetNode, context)) return false;
61538
- const sourceOwner = context.cfg.enclosingFunction(sourceNode);
61539
- const targetOwner = context.cfg.enclosingFunction(targetNode);
61540
- if (!sourceOwner || sourceOwner !== targetOwner) return false;
61541
- if (areInMutuallyExclusiveConditionalBranches(sourceNode, targetNode)) return false;
61542
- if (haveContradictoryStableBooleanGuards(sourceNode, targetNode, sourceOwner, context.scopes)) return false;
61543
- if (isDescendantWithoutFunctionBoundary(targetNode, sourceNode)) return false;
61544
- if (isDescendantWithoutFunctionBoundary(sourceNode, targetNode)) return true;
61545
- const functionCfg = context.cfg.cfgFor(sourceOwner);
61546
- const sourceBlock = functionCfg?.blockOf(sourceNode);
61547
- const targetBlock = functionCfg?.blockOf(targetNode);
61548
- if (!functionCfg || !sourceBlock || !targetBlock) return false;
61549
- if (sourceBlock === targetBlock) {
61550
- const sourceStart = getRangeStart(sourceNode);
61551
- const targetStart = getRangeStart(targetNode);
61552
- return sourceStart !== null && targetStart !== null && sourceStart < targetStart;
61553
- }
61554
- const visitedBlocks = new Set([sourceBlock]);
61555
- const pendingBlocks = sourceBlock.successors.filter((edge) => edge.kind !== "throw").map((edge) => edge.to);
61556
- while (pendingBlocks.length > 0) {
61557
- const block = pendingBlocks.pop();
61558
- if (!block || visitedBlocks.has(block)) continue;
61559
- if (block === targetBlock) return true;
61560
- visitedBlocks.add(block);
61561
- for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
61562
- }
61563
- return false;
61564
- };
61565
- const getInlineIntrinsicHandlerJsxElement = (functionNode) => {
61566
- let currentNode = functionNode;
61567
- while (currentNode) {
61568
- if (isNodeOfType(currentNode, "JSXAttribute") && isEventHandlerAttribute(currentNode) && isJsxAttributeOnIntrinsicHtmlElement(currentNode)) {
61569
- const openingElement = currentNode.parent;
61570
- return isNodeOfType(openingElement?.parent, "JSXElement") ? openingElement.parent : null;
61571
- }
61572
- if (currentNode !== functionNode && isFunctionLike$1(currentNode)) return null;
61573
- currentNode = currentNode.parent;
61574
- }
61575
- return null;
61576
- };
61577
- const isProvenNonEscapingVoidRead = (identifier) => {
61578
- const expressionRoot = findTransparentExpressionRoot(identifier);
61579
- return isNodeOfType(expressionRoot.parent, "UnaryExpression") && expressionRoot.parent.operator === "void" && expressionRoot.parent.argument === expressionRoot;
61580
- };
61581
- const getDirectReadonlyValueAliasSymbol = (referenceIdentifier, sourceSymbol, index) => {
61582
- const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
61583
- const parent = referenceRoot.parent;
61584
- if (isNodeOfType(parent, "VariableDeclarator") && parent.init === referenceRoot && isNodeOfType(parent.id, "Identifier")) {
61585
- const aliasSymbol = index.context.scopes.symbolFor(parent.id);
61586
- const aliasInitializer = aliasSymbol ? getDirectConstInitializer(aliasSymbol) : null;
61587
- const unwrappedInitializer = aliasInitializer ? stripParenExpression(aliasInitializer) : null;
61588
- return aliasSymbol && isNodeOfType(unwrappedInitializer, "Identifier") && index.context.scopes.symbolFor(unwrappedInitializer) === sourceSymbol ? aliasSymbol : null;
61589
- }
61590
- if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot || !isReactApiCall(parent, "useCallback", index.context.scopes, { resolveNamedAliases: true })) return null;
61591
- const callbackResultRoot = findTransparentExpressionRoot(parent);
61592
- const resultParent = callbackResultRoot.parent;
61593
- if (!isNodeOfType(resultParent, "VariableDeclarator") || resultParent.init !== callbackResultRoot || !isNodeOfType(resultParent.id, "Identifier")) return null;
61594
- const callbackResultSymbol = index.context.scopes.symbolFor(resultParent.id);
61595
- return callbackResultSymbol && getDirectConstInitializer(callbackResultSymbol) === callbackResultRoot ? callbackResultSymbol : null;
61596
- };
61597
- const isKnownAggregateMutationReference = (referenceIdentifier, index) => {
61598
- const referenceRoot = findTransparentExpressionRoot(referenceIdentifier);
61599
- const parent = referenceRoot.parent;
61600
- if (isNodeOfType(parent, "MemberExpression") && parent.object === referenceRoot) {
61601
- const memberRoot = findTransparentExpressionRoot(parent);
61602
- const callExpression = memberRoot.parent;
61603
- const methodName = getStaticPropertyName(parent);
61604
- if (isNodeOfType(callExpression, "CallExpression") && callExpression.callee === memberRoot && methodName !== null && AGGREGATE_MUTATION_METHOD_NAMES.has(methodName)) return true;
61605
- }
61606
- if (!isNodeOfType(parent, "CallExpression") || parent.arguments?.[0] !== referenceRoot) return false;
61607
- const callee = stripParenExpression(parent.callee);
61608
- if (!isNodeOfType(callee, "MemberExpression")) return false;
61609
- const methodName = getStaticPropertyName(callee);
61610
- if (methodName === null) return false;
61611
- 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);
61612
- };
61613
- const collectExactReadonlyValueEscapeAnchors = (symbol, index, valueFunctionNode, options = {}, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
61614
- if (visitedSymbolIds.has(symbol.id)) return null;
61615
- if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier) || options.rejectKnownMutations && isKnownAggregateMutationReference(reference.identifier, index))) return null;
61616
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
61617
- nextVisitedSymbolIds.add(symbol.id);
61618
- const escapeAnchors = [];
61619
- for (const reference of symbol.references) {
61620
- if (valueFunctionNode && index.context.cfg.enclosingFunction(reference.identifier) === valueFunctionNode || isProvenNonEscapingVoidRead(reference.identifier)) continue;
61621
- const aliasSymbol = getDirectReadonlyValueAliasSymbol(reference.identifier, symbol, index);
61622
- if (!aliasSymbol) {
61623
- escapeAnchors.push(reference.identifier);
61624
- continue;
61625
- }
61626
- const aliasEscapeAnchors = collectExactReadonlyValueEscapeAnchors(aliasSymbol, index, valueFunctionNode, options, nextVisitedSymbolIds);
61627
- if (!aliasEscapeAnchors) return null;
61628
- escapeAnchors.push(...aliasEscapeAnchors);
61629
- }
61630
- return escapeAnchors.length > 0 ? escapeAnchors : null;
61631
- };
61632
- const findReadonlyJsxAggregateRoot = (jsxElement) => {
61633
- let aggregateRoot = findTransparentExpressionRoot(jsxElement);
61634
- while (aggregateRoot.parent) {
61635
- const parent = aggregateRoot.parent;
61636
- if ((isNodeOfType(parent, "JSXElement") || isNodeOfType(parent, "JSXFragment")) && parent.children?.some((child) => child === aggregateRoot)) {
61637
- aggregateRoot = parent;
61638
- continue;
61639
- }
61640
- if (isNodeOfType(parent, "ArrayExpression") && parent.elements?.some((element) => element === aggregateRoot)) {
61641
- aggregateRoot = findTransparentExpressionRoot(parent);
61642
- continue;
61643
- }
61644
- if (isNodeOfType(parent, "Property") && parent.value === aggregateRoot && parent.kind === "init" && !parent.computed && isNodeOfType(parent.parent, "ObjectExpression")) {
61645
- aggregateRoot = findTransparentExpressionRoot(parent.parent);
61646
- continue;
61647
- }
61648
- if (isNodeOfType(parent, "ConditionalExpression") && (parent.consequent === aggregateRoot || parent.alternate === aggregateRoot)) {
61649
- aggregateRoot = findTransparentExpressionRoot(parent);
61650
- continue;
61651
- }
61652
- if (isNodeOfType(parent, "LogicalExpression") && (parent.right === aggregateRoot || parent.left === aggregateRoot && parent.operator !== "&&")) {
61653
- aggregateRoot = findTransparentExpressionRoot(parent);
61654
- continue;
61655
- }
61656
- if (isNodeOfType(parent, "SequenceExpression") && getFinalSequenceExpressionValue(parent) === getFinalSequenceExpressionValue(aggregateRoot)) {
61657
- aggregateRoot = findTransparentExpressionRoot(parent);
61658
- continue;
61659
- }
61660
- break;
61661
- }
61662
- return aggregateRoot;
61663
- };
61664
- const getReadonlyJsxValueEscapeAnchors = (jsxElement, index) => {
61665
- const jsxValueRoot = findReadonlyJsxAggregateRoot(jsxElement);
61666
- const parent = jsxValueRoot.parent;
61667
- if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== jsxValueRoot) return [jsxValueRoot];
61668
- if (!isNodeOfType(parent.id, "Identifier")) return null;
61669
- const bindingSymbol = index.context.scopes.symbolFor(parent.id);
61670
- if (!bindingSymbol || getDirectConstInitializer(bindingSymbol) !== jsxValueRoot) return null;
61671
- return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, null, { rejectKnownMutations: true });
61672
- };
61673
- const getExclusiveIntrinsicReactEventHandlerAnchors = (functionNode, index) => {
61674
- if (!isExclusiveIntrinsicReactEventHandler(functionNode, index)) return null;
61675
- const bindingIdentifier = getIntrinsicReactEventHandlerBindingIdentifier(functionNode, index);
61676
- if (!bindingIdentifier) {
61677
- const jsxElement = getInlineIntrinsicHandlerJsxElement(functionNode);
61678
- return jsxElement ? getReadonlyJsxValueEscapeAnchors(jsxElement, index) : null;
61679
- }
61680
- return index.context.scopes.symbolFor(bindingIdentifier)?.references.map((reference) => reference.identifier) ?? null;
61681
- };
61682
- const getReadonlyFunctionEscapeAnchors = (functionNode, index) => {
61683
- const bindingIdentifier = getFunctionBindingIdentifier$1(functionNode);
61684
- if (!bindingIdentifier) return null;
61685
- const bindingSymbol = index.context.scopes.symbolFor(bindingIdentifier);
61686
- if (!bindingSymbol || resolveExactLocalFunction(bindingIdentifier, index.context.scopes) !== functionNode) return null;
61687
- return collectExactReadonlyValueEscapeAnchors(bindingSymbol, index, functionNode);
61688
- };
61689
- const isAliasInitializedBeforeExecution = (aliasDeclaration, executionNode, index, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
61690
- if (!isNodeReachableWithinFunction(aliasDeclaration, index.context)) return false;
61691
- const aliasOwner = index.context.cfg.enclosingFunction(aliasDeclaration);
61692
- const executionOwner = index.context.cfg.enclosingFunction(executionNode);
61693
- if (!aliasOwner || !executionOwner) return false;
61694
- if (aliasOwner === executionOwner) return canNodeReachNode(aliasDeclaration, executionNode, index);
61695
- if (visitedFunctionNodes.has(executionOwner)) return false;
61696
- const nextVisitedFunctionNodes = new Set(visitedFunctionNodes);
61697
- nextVisitedFunctionNodes.add(executionOwner);
61698
- if (aliasOwner === index.componentFunction && (index.effectCallbacks.has(executionOwner) || index.mountedListenerFunctions.has(executionOwner))) return doNodesCoverEveryPathFromFunctionEntry(index.componentFunction, [aliasDeclaration], index.context, { ignoreThrowEdges: true });
61699
- const invocations = new Set([...index.callSitesByFunction.get(executionOwner) ?? [], ...index.synchronousInvocationsByFunction.get(executionOwner) ?? []]);
61700
- if (invocations.size > 0) return [...invocations].every((invocation) => isAliasInitializedBeforeExecution(aliasDeclaration, invocation, index, nextVisitedFunctionNodes));
61701
- const deferredExecutionAnchors = getExclusiveIntrinsicReactEventHandlerAnchors(executionOwner, index);
61702
- if (deferredExecutionAnchors) return deferredExecutionAnchors.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes));
61703
- const functionEscapeAnchors = getReadonlyFunctionEscapeAnchors(executionOwner, index);
61704
- return Boolean(functionEscapeAnchors?.every((anchor) => isAliasInitializedBeforeExecution(aliasDeclaration, anchor, index, nextVisitedFunctionNodes)));
61705
- };
61706
- const resolveExactReadonlyCalleeSymbolId = (callExpression, index) => {
61707
- if (!isNodeOfType(callExpression, "CallExpression")) return null;
61708
- const callee = stripParenExpression(callExpression.callee);
61709
- if (!isNodeOfType(callee, "Identifier")) return null;
61710
- const visitedSymbolIds = /* @__PURE__ */ new Set();
61711
- let symbol = index.context.scopes.symbolFor(callee);
61712
- while (symbol) {
61713
- if (visitedSymbolIds.has(symbol.id)) return null;
61714
- visitedSymbolIds.add(symbol.id);
61715
- const initializer = getDirectConstInitializer(symbol);
61716
- if (!initializer) return symbol.id;
61717
- if (symbol.references.some((reference) => reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) || !isAliasInitializedBeforeExecution(symbol.declarationNode, callExpression, index)) return null;
61718
- const unwrappedInitializer = stripParenExpression(initializer);
61719
- if (!isNodeOfType(unwrappedInitializer, "Identifier")) return symbol.id;
61720
- const initializerSymbol = index.context.scopes.symbolFor(unwrappedInitializer);
61721
- if (!initializerSymbol || !isAliasInitializedBeforeExecution(initializerSymbol.declarationNode, symbol.declarationNode, index)) return null;
61722
- symbol = initializerSymbol;
61723
- }
61724
- return null;
61725
- };
61726
- const collectExactReadonlyCalleeCalls = (index) => {
61727
- for (const callExpression of index.identifierCalls) {
61728
- const calleeSymbolId = resolveExactReadonlyCalleeSymbolId(callExpression, index);
61729
- if (calleeSymbolId !== null) addToSetIndex(index.callsByCalleeSymbolId, calleeSymbolId, callExpression);
61730
- }
61731
- };
61732
- const canNodeReachNormalFunctionExit = (node, functionNode, index) => {
61733
- const functionCfg = index.context.cfg.cfgFor(functionNode);
61734
- const sourceBlock = functionCfg?.blockOf(node);
61735
- if (!functionCfg || !sourceBlock) return false;
61736
- const visitedBlocks = /* @__PURE__ */ new Set();
61737
- const pendingBlocks = [sourceBlock];
61738
- while (pendingBlocks.length > 0) {
61739
- const block = pendingBlocks.pop();
61740
- if (!block || visitedBlocks.has(block)) continue;
61741
- visitedBlocks.add(block);
61742
- for (const edge of block.successors) {
61743
- if (edge.kind === "throw") continue;
61744
- if (edge.to === functionCfg.exit) return true;
61745
- pendingBlocks.push(edge.to);
61746
- }
61747
- }
61748
- return false;
61749
- };
61750
- const canExecuteBeforeAsyncSuspension = (node, functionNode, index) => {
61751
- if (!isFunctionLike$1(functionNode) || !functionNode.async) return isNodeReachableWithinFunction(node, index.context);
61752
- const functionCfg = index.context.cfg.cfgFor(functionNode);
61753
- const targetBlock = functionCfg?.blockOf(node);
61754
- if (!functionCfg || !targetBlock) return false;
61755
- const awaitsByBlock = /* @__PURE__ */ new Map();
61756
- for (const awaitExpression of index.awaitExpressionsByOwner.get(functionNode) ?? []) {
61757
- const awaitBlock = functionCfg.blockOf(awaitExpression);
61758
- if (!awaitBlock) continue;
61759
- const blockAwaits = awaitsByBlock.get(awaitBlock) ?? [];
61760
- blockAwaits.push(awaitExpression);
61761
- awaitsByBlock.set(awaitBlock, blockAwaits);
61762
- }
61763
- const visitedBlocks = /* @__PURE__ */ new Set();
61764
- const pendingBlocks = [functionCfg.entry];
61765
- const targetStart = getRangeStart(node);
61766
- while (pendingBlocks.length > 0) {
61767
- const block = pendingBlocks.pop();
61768
- if (!block || visitedBlocks.has(block)) continue;
61769
- visitedBlocks.add(block);
61770
- const blockAwaits = awaitsByBlock.get(block) ?? [];
61771
- if (block === targetBlock) return !blockAwaits.some((awaitExpression) => {
61772
- if (isNodeOfType(awaitExpression, "AwaitExpression") && isDescendantWithoutFunctionBoundary(node, awaitExpression.argument)) return false;
61773
- if (isDescendantWithoutFunctionBoundary(awaitExpression, node)) return true;
61774
- const awaitStart = getRangeStart(awaitExpression);
61775
- return awaitStart !== null && targetStart !== null && awaitStart < targetStart;
61776
- });
61777
- if (blockAwaits.length > 0) continue;
61778
- for (const edge of block.successors) if (edge.kind !== "throw") pendingBlocks.push(edge.to);
61779
- }
61780
- return false;
61781
- };
61782
- 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);
61783
- const functionMaySynchronouslyMutateLocation = (functionNode, index, visitingFunctions, cycleAffectedFunctions) => {
61784
- const cachedResult = index.synchronousMutationResultByFunction.get(functionNode);
61785
- if (cachedResult !== void 0) return cachedResult;
61786
- if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
61787
- if (visitingFunctions.has(functionNode)) {
61788
- let didReachCycleEntry = false;
61789
- for (const visitingFunction of visitingFunctions) {
61790
- if (visitingFunction === functionNode) didReachCycleEntry = true;
61791
- if (didReachCycleEntry) cycleAffectedFunctions.add(visitingFunction);
61792
- }
61793
- return false;
61794
- }
61795
- visitingFunctions.add(functionNode);
61796
- const doesMutateSynchronously = [...collectLocationMutationExecutions(functionNode, index, visitingFunctions, cycleAffectedFunctions)].some((mutationExecution) => canExecuteBeforeAsyncSuspension(mutationExecution, functionNode, index) && canNodeReachNormalFunctionExit(mutationExecution, functionNode, index));
61797
- visitingFunctions.delete(functionNode);
61798
- if (doesMutateSynchronously || !cycleAffectedFunctions.has(functionNode)) index.synchronousMutationResultByFunction.set(functionNode, doesMutateSynchronously);
61799
- return doesMutateSynchronously;
61800
- };
61801
- const collectLocationMutationExecutions = (functionNode, index, visitingFunctions = /* @__PURE__ */ new Set(), cycleAffectedFunctions = /* @__PURE__ */ new Set()) => {
61802
- const cachedExecutions = index.mutationExecutionsByOwner.get(functionNode);
61803
- if (cachedExecutions) return cachedExecutions;
61804
- const mutationExecutions = new Set(index.historyMutationsByOwner.get(functionNode) ?? []);
61805
- for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61806
- const calledFunction = index.calledFunctionByExpression.get(expression);
61807
- if (calledFunction && functionMaySynchronouslyMutateLocation(calledFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
61808
- for (const callbackFunction of index.synchronousCallbacksByExpression.get(expression) ?? []) if (functionMaySynchronouslyMutateLocation(callbackFunction, index, visitingFunctions, cycleAffectedFunctions)) mutationExecutions.add(expression);
61809
- }
61810
- if (!cycleAffectedFunctions.has(functionNode)) index.mutationExecutionsByOwner.set(functionNode, mutationExecutions);
61811
- return mutationExecutions;
61812
- };
61813
- const isDefinitelyMatchingLocationListenerRemoval = (registration, removal) => registration.eventName === removal.eventName && registration.listenerFunction === removal.listenerFunction && registration.capture !== null && removal.capture !== null && registration.capture === removal.capture;
61814
- const functionMustSynchronouslyRemoveLocationListener = (functionNode, registration, index, visitingFunctions) => {
61815
- if (!isFunctionLike$1(functionNode) || functionNode.generator) return false;
61816
- if (visitingFunctions.has(functionNode)) return false;
61817
- const nextVisitingFunctions = new Set(visitingFunctions);
61818
- nextVisitingFunctions.add(functionNode);
61819
- const removalExecutions = [];
61820
- for (const removal of index.listenerRemovals) {
61821
- if (!isDefinitelyMatchingLocationListenerRemoval(registration, removal)) continue;
61822
- if (index.context.cfg.enclosingFunction(removal.callExpression) !== functionNode) continue;
61823
- if (canExecuteBeforeAsyncSuspension(removal.callExpression, functionNode, index)) removalExecutions.push(removal.callExpression);
61824
- }
61825
- for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61826
- const calledFunction = index.calledFunctionByExpression.get(expression);
61827
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, nextVisitingFunctions)) removalExecutions.push(expression);
61828
- }
61829
- return doNodesCoverEveryPathFromFunctionEntry(functionNode, removalExecutions, index.context);
61830
- };
61831
- const collectSynchronousLocationListenerRemovalExecutions = (functionNode, registration, index) => {
61832
- const removalExecutions = /* @__PURE__ */ new Set();
61833
- 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);
61834
- for (const expression of index.expressionsByOwner.get(functionNode) ?? []) {
61835
- const calledFunction = index.calledFunctionByExpression.get(expression);
61836
- if (calledFunction && canExecuteBeforeAsyncSuspension(expression, functionNode, index) && functionMustSynchronouslyRemoveLocationListener(calledFunction, registration, index, /* @__PURE__ */ new Set())) removalExecutions.add(expression);
61837
- }
61838
- return removalExecutions;
61839
- };
61840
- const collectImpliedExpressionExecutionBoundaries = (node, owner) => {
61841
- const expressionBoundaries = [];
61842
- let currentChild = node;
61843
- let currentParent = currentChild.parent ?? null;
61844
- while (currentParent && currentParent !== owner) {
61845
- if (isNodeOfType(currentParent, "ConditionalExpression") && currentParent.test === currentChild) {
61846
- const staticTestValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.test));
61847
- if (staticTestValue !== null) expressionBoundaries.push(staticTestValue ? currentParent.consequent : currentParent.alternate);
61848
- }
61849
- if (isNodeOfType(currentParent, "LogicalExpression") && currentParent.left === currentChild) {
61850
- const staticLeftValue = readStaticBoolean(getFinalSequenceExpressionValue(currentParent.left));
61851
- if (currentParent.operator === "&&" && staticLeftValue === true || currentParent.operator === "||" && staticLeftValue === false) expressionBoundaries.push(currentParent.right);
61852
- }
61853
- 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);
61854
- currentChild = currentParent;
61855
- currentParent = currentChild.parent ?? null;
61856
- }
61857
- return expressionBoundaries;
61858
- };
61859
- const canExecutionReachFunctionExitWithoutListenerRemoval = (executionNode, registration, index) => {
61860
- if (!isNodeReachableWithinFunction(executionNode, index.context)) return false;
61861
- const owner = index.context.cfg.enclosingFunction(executionNode);
61862
- if (!owner) return false;
61863
- const functionCfg = index.context.cfg.cfgFor(owner);
61864
- const sourceBlock = functionCfg?.blockOf(executionNode);
61865
- if (!functionCfg || !sourceBlock) return false;
61866
- const expressionBoundaries = collectImpliedExpressionExecutionBoundaries(executionNode, owner);
61867
- const matchingRemovalsByBlock = /* @__PURE__ */ new Map();
61868
- for (const removalExecution of collectSynchronousLocationListenerRemovalExecutions(owner, registration, index)) {
61869
- const removalBlock = functionCfg.blockOf(removalExecution);
61870
- if (!removalBlock) continue;
61871
- const blockRemovals = matchingRemovalsByBlock.get(removalBlock) ?? [];
61872
- blockRemovals.push(removalExecution);
61873
- matchingRemovalsByBlock.set(removalBlock, blockRemovals);
61874
- }
61875
- const sourceStart = getRangeStart(executionNode);
61876
- const hasRemovalAfterExecution = (block) => {
61877
- const removals = (matchingRemovalsByBlock.get(block) ?? []).filter((removal) => {
61878
- if (block !== sourceBlock) return true;
61879
- const removalStart = getRangeStart(removal);
61880
- return sourceStart !== null && removalStart !== null && sourceStart < removalStart;
61881
- });
61882
- if (collectExpressionPathCoverageNodes(owner, removals, index.context).size > 0) return true;
61883
- if (block !== sourceBlock) return false;
61884
- return expressionBoundaries.some((expressionBoundary) => collectExpressionPathCoverageNodes(owner, removals, index.context, expressionBoundary).size > 0);
61885
- };
61886
- const visitedBlocks = /* @__PURE__ */ new Set();
61887
- const pendingBlocks = [sourceBlock];
61888
- while (pendingBlocks.length > 0) {
61889
- const block = pendingBlocks.pop();
61890
- if (!block || visitedBlocks.has(block)) continue;
61891
- visitedBlocks.add(block);
61892
- if (hasRemovalAfterExecution(block)) continue;
61893
- for (const edge of block.successors) {
61894
- if (edge.kind === "throw") continue;
61895
- if (edge.to === functionCfg.exit) return true;
61896
- pendingBlocks.push(edge.to);
61897
- }
61898
- }
61899
- return false;
61900
- };
61901
- const isListenerActiveAtMountedExit = (executionNode, registration, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
61902
- if (visitedExecutions.has(executionNode)) return false;
61903
- if (!canExecutionReachFunctionExitWithoutListenerRemoval(executionNode, registration, index)) return false;
61904
- const owner = index.context.cfg.enclosingFunction(executionNode);
61905
- if (!owner) return false;
61906
- if (owner === index.componentFunction || index.effectCallbacks.has(owner)) return true;
61907
- const nextVisitedExecutions = new Set(visitedExecutions);
61908
- nextVisitedExecutions.add(executionNode);
61909
- for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(callSite, registration, index, nextVisitedExecutions)) return true;
61910
- for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (isListenerActiveAtMountedExit(invocation, registration, index, nextVisitedExecutions)) return true;
61911
- return false;
61912
- };
61913
- const collectMountedListenerFunctions = (index) => {
61914
- for (const registration of index.listenerRegistrations) if (isListenerActiveAtMountedExit(registration.callExpression, registration, index)) index.mountedListenerFunctions.add(registration.listenerFunction);
61915
- };
61916
- const setterArgumentMutatesLocation = (setterCall, index) => {
61917
- if (!isNodeOfType(setterCall, "CallExpression")) return false;
61918
- return (setterCall.arguments ?? []).some((argument) => {
61919
- if (isNodeOfType(argument, "SpreadElement")) return false;
61920
- const updaterFunction = resolveExactLocalFunction(argument, index.context.scopes);
61921
- return Boolean(isFunctionLike$1(updaterFunction) && functionMaySynchronouslyMutateLocation(updaterFunction, index, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set()));
61922
- });
61923
- };
61924
- const executionAnchorInvalidatesLocationSnapshot = (executionAnchor, index, visitedExecutions = /* @__PURE__ */ new Set()) => {
61925
- if (visitedExecutions.has(executionAnchor)) return false;
61926
- if (!isNodeReachableWithinFunction(executionAnchor, index.context)) return false;
61927
- const owner = index.context.cfg.enclosingFunction(executionAnchor);
61928
- if (!owner) return false;
61929
- if (index.mountedListenerFunctions.has(owner)) return true;
61930
- if (setterArgumentMutatesLocation(executionAnchor, index) || [...collectLocationMutationExecutions(owner, index)].some((mutationExecution) => canNodeReachNode(mutationExecution, executionAnchor, index) || canReactBatchMutationAfterExecution(executionAnchor, mutationExecution, owner, index))) return true;
61931
- const nextVisitedExecutions = new Set(visitedExecutions);
61932
- nextVisitedExecutions.add(executionAnchor);
61933
- for (const callSite of index.callSitesByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(callSite, index, nextVisitedExecutions)) return true;
61934
- for (const invocation of index.synchronousInvocationsByFunction.get(owner) ?? []) if (executionAnchorInvalidatesLocationSnapshot(invocation, index, nextVisitedExecutions)) return true;
61935
- return false;
61936
- };
61937
- const setterInvalidatesGlobalLocationSnapshot = (setterBindingIdentifier, index) => {
61938
- const setterSymbol = index.context.scopes.symbolFor(setterBindingIdentifier);
61939
- if (!setterSymbol) return false;
61940
- return [...index.callsByCalleeSymbolId.get(setterSymbol.id) ?? []].some((setterCall) => executionAnchorInvalidatesLocationSnapshot(setterCall, index));
61941
- };
61942
- const createExternalLocationInvalidationChecker = ({ componentBody, componentFunction, context, directRenderNames, renderReachableExpressions }) => {
61943
- if (!hasRenderReachableLocationSnapshotRead(componentBody, renderReachableExpressions, directRenderNames, context.scopes)) return () => false;
61944
- const locationInvalidationIndex = buildLocationInvalidationIndex(componentBody, componentFunction, context);
61945
- collectMountedListenerFunctions(locationInvalidationIndex);
61946
- collectExactReadonlyCalleeCalls(locationInvalidationIndex);
61947
- return (setterBindingIdentifier) => setterInvalidatesGlobalLocationSnapshot(setterBindingIdentifier, locationInvalidationIndex);
61948
- };
61949
- //#endregion
61950
60171
  //#region src/plugin/rules/state-and-effects/rerender-state-only-in-handlers.ts
61951
60172
  const isInsideConditionTest = (identifier, stopAt) => {
61952
60173
  let current = identifier;
@@ -62068,8 +60289,7 @@ const rerenderStateOnlyInHandlers = defineRule({
62068
60289
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
62069
60290
  const bindings = collectUseStateBindings(componentBody, context.scopes);
62070
60291
  if (bindings.length === 0) return;
62071
- const renderReachableExpressions = collectRenderReachableExpressions(componentBody);
62072
- if (renderReachableExpressions.length === 0) return;
60292
+ if (collectRenderReachableExpressions(componentBody).length === 0) return;
62073
60293
  const eventHandlerReferenceNames = collectFunctionLikeLocalNames(componentBody, context.scopes);
62074
60294
  const dependencyGraph = buildLocalDependencyGraph(componentBody, eventHandlerReferenceNames);
62075
60295
  const directRenderNames = collectRenderReachableNames(componentBody, context.scopes, eventHandlerReferenceNames);
@@ -62083,22 +60303,12 @@ const rerenderStateOnlyInHandlers = defineRule({
62083
60303
  for (const effectInfo of effectInfos) for (const dependencyName of effectInfo.dependencyNames) if (!selfEchoValueNames.has(dependencyName)) effectConsumedNames.add(dependencyName);
62084
60304
  for (const hookArgumentName of collectCustomHookArgumentNames(componentBody, context.scopes)) effectConsumedNames.add(hookArgumentName);
62085
60305
  for (const reachableName of expandTransitiveDependencies(effectConsumedNames, dependencyGraph)) renderReachableNames.add(reachableName);
62086
- const componentFunction = context.cfg.enclosingFunction(componentBody);
62087
- const doesSetterInvalidateExternalLocation = isFunctionLike$1(componentFunction) ? createExternalLocationInvalidationChecker({
62088
- componentBody,
62089
- componentFunction,
62090
- context,
62091
- directRenderNames,
62092
- renderReachableExpressions
62093
- }) : () => false;
62094
60306
  const calledSetterNames = /* @__PURE__ */ new Set();
62095
60307
  walkAst(componentBody, (child) => {
62096
60308
  if (isNodeOfType(child, "CallExpression") && isNodeOfType(child.callee, "Identifier") && setterNames.has(child.callee.name)) calledSetterNames.add(child.callee.name);
62097
60309
  });
62098
60310
  for (const binding of bindings) {
62099
60311
  if (renderReachableNames.has(binding.valueName)) continue;
62100
- const setterBindingIdentifier = isNodeOfType(binding.declarator.id, "ArrayPattern") ? binding.declarator.id.elements?.[1] : null;
62101
- if (setterBindingIdentifier && doesSetterInvalidateExternalLocation(setterBindingIdentifier)) continue;
62102
60312
  if (binding.valueName === "_" || binding.valueName.startsWith("_")) continue;
62103
60313
  const setterSuffix = binding.setterName.slice(3);
62104
60314
  if (/^(TriggerRender|ForceUpdate|Rerender|ForceRender|Tick|Bump|BumpVersion|InvalidateRender|Refresh|Repaint)$/i.test(setterSuffix)) continue;
@@ -65005,66 +63215,6 @@ const roleHasRequiredAriaProps = defineRule({
65005
63215
  } })
65006
63216
  });
65007
63217
  //#endregion
65008
- //#region src/plugin/constants/global-aria-properties.ts
65009
- const GLOBAL_ARIA_PROPERTIES = new Set([
65010
- "aria-atomic",
65011
- "aria-braillelabel",
65012
- "aria-brailleroledescription",
65013
- "aria-busy",
65014
- "aria-controls",
65015
- "aria-current",
65016
- "aria-describedby",
65017
- "aria-description",
65018
- "aria-details",
65019
- "aria-disabled",
65020
- "aria-dropeffect",
65021
- "aria-errormessage",
65022
- "aria-flowto",
65023
- "aria-grabbed",
65024
- "aria-haspopup",
65025
- "aria-hidden",
65026
- "aria-invalid",
65027
- "aria-keyshortcuts",
65028
- "aria-label",
65029
- "aria-labelledby",
65030
- "aria-live",
65031
- "aria-owns",
65032
- "aria-relevant",
65033
- "aria-roledescription"
65034
- ]);
65035
- //#endregion
65036
- //#region src/plugin/constants/prohibited-aria-properties-by-role.ts
65037
- const ACCESSIBLE_NAME_PROPERTIES = new Set([
65038
- "aria-braillelabel",
65039
- "aria-label",
65040
- "aria-labelledby"
65041
- ]);
65042
- const PROHIBITED_ARIA_PROPERTIES_BY_ROLE = {
65043
- caption: ACCESSIBLE_NAME_PROPERTIES,
65044
- code: ACCESSIBLE_NAME_PROPERTIES,
65045
- definition: ACCESSIBLE_NAME_PROPERTIES,
65046
- deletion: ACCESSIBLE_NAME_PROPERTIES,
65047
- emphasis: ACCESSIBLE_NAME_PROPERTIES,
65048
- generic: new Set([
65049
- "aria-braillelabel",
65050
- "aria-brailleroledescription",
65051
- "aria-label",
65052
- "aria-labelledby",
65053
- "aria-roledescription"
65054
- ]),
65055
- insertion: ACCESSIBLE_NAME_PROPERTIES,
65056
- mark: ACCESSIBLE_NAME_PROPERTIES,
65057
- none: ACCESSIBLE_NAME_PROPERTIES,
65058
- paragraph: ACCESSIBLE_NAME_PROPERTIES,
65059
- presentation: ACCESSIBLE_NAME_PROPERTIES,
65060
- strong: ACCESSIBLE_NAME_PROPERTIES,
65061
- subscript: ACCESSIBLE_NAME_PROPERTIES,
65062
- superscript: ACCESSIBLE_NAME_PROPERTIES,
65063
- term: ACCESSIBLE_NAME_PROPERTIES,
65064
- time: ACCESSIBLE_NAME_PROPERTIES,
65065
- tooltip: ACCESSIBLE_NAME_PROPERTIES
65066
- };
65067
- //#endregion
65068
63218
  //#region src/plugin/constants/role-supports-aria-props.ts
65069
63219
  const ROLE_SUPPORTS_ARIA_PROPS = {
65070
63220
  alert: new Set([
@@ -68165,12 +66315,6 @@ const ROLE_SUPPORTS_ARIA_PROPS = {
68165
66315
  ])
68166
66316
  };
68167
66317
  //#endregion
68168
- //#region src/plugin/utils/is-aria-property-supported-by-role.ts
68169
- const isAriaPropertySupportedByRole = (role, property) => {
68170
- if (PROHIBITED_ARIA_PROPERTIES_BY_ROLE[role]?.has(property)) return false;
68171
- return ROLE_SUPPORTS_ARIA_PROPS[role]?.has(property) === true || GLOBAL_ARIA_PROPERTIES.has(property);
68172
- };
68173
- //#endregion
68174
66318
  //#region src/plugin/rules/a11y/role-supports-aria-props.ts
68175
66319
  const buildMessageDefault = (roles, propName) => {
68176
66320
  return `Screen reader users get no help from \`${propName}\` because role ${roles.map((role) => `\`${role}\``).join(" / ")} ignores it, so remove it or change the role.`;
@@ -68207,10 +66351,16 @@ const roleSupportsAriaProps = defineRule({
68207
66351
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
68208
66352
  const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType, context.scopes)].filter((role) => role !== null);
68209
66353
  if (roleCandidates === null || roleCandidates.length === 0) return;
68210
- for (const role of roleCandidates) if (!VALID_ARIA_ROLES.has(role)) return;
66354
+ const supportedSets = [];
66355
+ for (const role of roleCandidates) {
66356
+ if (!VALID_ARIA_ROLES.has(role)) return;
66357
+ const supported = ROLE_SUPPORTS_ARIA_PROPS[role];
66358
+ if (!supported) return;
66359
+ supportedSets.push(supported);
66360
+ }
68211
66361
  const isImplicit = !roleAttribute;
68212
66362
  for (const { attribute, propName } of ariaAttributes) {
68213
- if (roleCandidates.some((role) => isAriaPropertySupportedByRole(role, propName))) continue;
66363
+ if (supportedSets.some((supported) => supported.has(propName))) continue;
68214
66364
  context.report({
68215
66365
  node: attribute,
68216
66366
  message: isImplicit ? buildMessageImplicit(roleCandidates[0], propName, elementType) : buildMessageDefault(roleCandidates, propName)
@@ -76992,7 +75142,7 @@ const EXTERNAL_RULES = [
76992
75142
  {
76993
75143
  key: "react-hooks-js/set-state-in-effect",
76994
75144
  source: "react-compiler",
76995
- severity: "warn"
75145
+ severity: "error"
76996
75146
  },
76997
75147
  {
76998
75148
  key: "react-hooks-js/globals",