oxlint-plugin-react-doctor 0.9.2-dev.5f23826 → 0.9.2-dev.811a2ff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +141 -1549
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -665,6 +665,19 @@ const TRIVIAL_INITIALIZER_NAMES = new Set([
665
665
  "parseInt",
666
666
  "parseFloat"
667
667
  ]);
668
+ const TRIVIAL_CONSTRUCTOR_NAMES = new Set([
669
+ "Date",
670
+ "Map",
671
+ "Set",
672
+ "WeakMap",
673
+ "WeakSet",
674
+ "WeakRef",
675
+ "RegExp",
676
+ "Error",
677
+ "URL",
678
+ "URLSearchParams",
679
+ "AbortController"
680
+ ]);
668
681
  const SETTER_PATTERN = /^set[A-Z]/;
669
682
  const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
670
683
  const UPPERCASE_PATTERN = /^[A-Z]/;
@@ -5874,7 +5887,6 @@ const isInlineFunctionExpression = (node) => Boolean(node && (isNodeOfType(node,
5874
5887
  //#endregion
5875
5888
  //#region src/plugin/rules/js-performance/async-await-in-loop.ts
5876
5889
  const LOOP_STATEMENT_TYPES$1 = new Set(LOOP_TYPES);
5877
- const ORDERED_OUTPUT_INSERTION_METHOD_NAMES = new Set(["push", "unshift"]);
5878
5890
  const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) => {
5879
5891
  let firstAwait = null;
5880
5892
  walkAst(block, (child) => {
@@ -5941,151 +5953,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
5941
5953
  });
5942
5954
  return isWaitLike;
5943
5955
  };
5944
- const getRootObjectIdentifierName = (node) => {
5945
- let current = node;
5946
- while (isNodeOfType(current, "MemberExpression")) current = current.object;
5947
- return isNodeOfType(current, "Identifier") ? current.name : null;
5948
- };
5949
- const isScopeWithinFunction = (candidateScope, functionScope) => {
5950
- let currentScope = candidateScope;
5951
- while (currentScope) {
5952
- if (currentScope === functionScope) return true;
5953
- currentScope = currentScope.parent;
5954
- }
5955
- return false;
5956
- };
5957
- const isSymbolDirectlyReturned = (symbol, callerFunction) => Boolean(callerFunction) && symbol.references.some((reference) => {
5958
- const expressionRoot = findTransparentExpressionRoot(reference.identifier);
5959
- const parent = expressionRoot.parent;
5960
- return isNodeOfType(parent, "ReturnStatement") && parent.argument === expressionRoot && findEnclosingFunction$1(parent) === callerFunction;
5961
- });
5962
- const collectPatternBindingSymbolIds = (pattern, scopes, target) => {
5963
- if (isNodeOfType(pattern, "Identifier")) {
5964
- const symbol = scopes.symbolFor(pattern);
5965
- if (symbol) target.add(symbol.id);
5966
- return;
5967
- }
5968
- if (isNodeOfType(pattern, "ObjectPattern")) {
5969
- for (const property of pattern.properties ?? []) if (isNodeOfType(property, "Property") && property.value) collectPatternBindingSymbolIds(property.value, scopes, target);
5970
- else if (isNodeOfType(property, "RestElement") && property.argument) collectPatternBindingSymbolIds(property.argument, scopes, target);
5971
- return;
5972
- }
5973
- if (isNodeOfType(pattern, "ArrayPattern")) {
5974
- for (const element of pattern.elements ?? []) if (element) collectPatternBindingSymbolIds(element, scopes, target);
5975
- return;
5976
- }
5977
- if (isNodeOfType(pattern, "AssignmentPattern") && pattern.left) collectPatternBindingSymbolIds(pattern.left, scopes, target);
5978
- };
5979
- const collectReferencedSymbolIds = (expression, scopes) => {
5980
- const referencedSymbolIds = /* @__PURE__ */ new Set();
5981
- walkAst(expression, (child) => {
5982
- if (child !== expression && isFunctionLike$1(child)) return false;
5983
- if (!isNodeOfType(child, "Identifier")) return;
5984
- const symbol = scopes.symbolFor(child);
5985
- if (symbol) referencedSymbolIds.add(symbol.id);
5986
- });
5987
- return referencedSymbolIds;
5988
- };
5989
- const collectAwaitDerivedSymbolIds = (block, scopes) => {
5990
- const awaitDerivedSymbolIds = /* @__PURE__ */ new Set();
5991
- const bindingDependencies = [];
5992
- walkAst(block, (child) => {
5993
- if (child !== block && isFunctionLike$1(child)) return false;
5994
- if (isNodeOfType(child, "VariableDeclarator") && child.id && child.init) {
5995
- const declaredSymbolIds = /* @__PURE__ */ new Set();
5996
- collectPatternBindingSymbolIds(child.id, scopes, declaredSymbolIds);
5997
- if (containsDirectAwait(child.init)) for (const symbolId of declaredSymbolIds) awaitDerivedSymbolIds.add(symbolId);
5998
- const referencedSymbolIds = collectReferencedSymbolIds(child.init, scopes);
5999
- for (const declaredSymbolId of declaredSymbolIds) bindingDependencies.push({
6000
- declaredSymbolId,
6001
- referencedSymbolIds
6002
- });
6003
- return;
6004
- }
6005
- if (isNodeOfType(child, "AssignmentExpression") && child.left) {
6006
- const assignedSymbolIds = /* @__PURE__ */ new Set();
6007
- collectPatternBindingSymbolIds(child.left, scopes, assignedSymbolIds);
6008
- if (containsDirectAwait(child.right)) for (const symbolId of assignedSymbolIds) awaitDerivedSymbolIds.add(symbolId);
6009
- const referencedSymbolIds = collectReferencedSymbolIds(child.right, scopes);
6010
- for (const assignedSymbolId of assignedSymbolIds) bindingDependencies.push({
6011
- declaredSymbolId: assignedSymbolId,
6012
- referencedSymbolIds
6013
- });
6014
- }
6015
- });
6016
- let didGrow = true;
6017
- while (didGrow) {
6018
- didGrow = false;
6019
- for (const { declaredSymbolId, referencedSymbolIds } of bindingDependencies) {
6020
- if (awaitDerivedSymbolIds.has(declaredSymbolId)) continue;
6021
- for (const referencedSymbolId of referencedSymbolIds) {
6022
- if (!awaitDerivedSymbolIds.has(referencedSymbolId)) continue;
6023
- awaitDerivedSymbolIds.add(declaredSymbolId);
6024
- didGrow = true;
6025
- break;
6026
- }
6027
- }
6028
- }
6029
- return awaitDerivedSymbolIds;
6030
- };
6031
- const getSimpleParameterIdentifier = (parameter) => {
6032
- if (isNodeOfType(parameter, "Identifier")) return parameter;
6033
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
6034
- return null;
6035
- };
6036
- const doesAwaitedLocalCallInsertAwaitDerivedOutput = (awaitNode, context) => {
6037
- if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
6038
- const callExpression = awaitNode.argument;
6039
- if (!isNodeOfType(callExpression, "CallExpression")) return false;
6040
- const localFunction = resolveStaticLocalCallFunction(callExpression, context.scopes);
6041
- if (!isFunctionLike$1(localFunction)) return false;
6042
- const callerFunction = findEnclosingFunction$1(callExpression);
6043
- const functionScope = context.scopes.ownScopeFor(localFunction);
6044
- if (!functionScope) return false;
6045
- const awaitDerivedSymbolIds = collectAwaitDerivedSymbolIds(localFunction.body, context.scopes);
6046
- const externallyReachableParameterSymbolIds = /* @__PURE__ */ new Set();
6047
- for (const [parameterIndex, parameter] of localFunction.params.entries()) {
6048
- const parameterIdentifier = getSimpleParameterIdentifier(parameter);
6049
- if (!parameterIdentifier) continue;
6050
- const argument = callExpression.arguments[parameterIndex];
6051
- if (!isNodeOfType(argument, "Identifier")) continue;
6052
- const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
6053
- const argumentSymbol = context.scopes.symbolFor(argument);
6054
- if (parameterSymbol && argumentSymbol && (isSymbolDirectlyReturned(argumentSymbol, callerFunction) || argumentSymbol.id === parameterSymbol.id)) externallyReachableParameterSymbolIds.add(parameterSymbol.id);
6055
- }
6056
- let doesInsertAwaitDerivedOutput = false;
6057
- walkAst(localFunction.body, (child) => {
6058
- if (doesInsertAwaitDerivedOutput) return false;
6059
- if (child !== localFunction.body && isFunctionLike$1(child)) return false;
6060
- if (!isNodeOfType(child, "CallExpression")) return;
6061
- const callee = child.callee;
6062
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !ORDERED_OUTPUT_INSERTION_METHOD_NAMES.has(callee.property.name)) return;
6063
- let doesMutationConsumeAwaitedValue = false;
6064
- for (const mutationArgument of child.arguments ?? []) {
6065
- if (containsDirectAwait(mutationArgument)) {
6066
- doesMutationConsumeAwaitedValue = true;
6067
- break;
6068
- }
6069
- const referencedSymbolIds = collectReferencedSymbolIds(mutationArgument, context.scopes);
6070
- for (const referencedSymbolId of referencedSymbolIds) if (awaitDerivedSymbolIds.has(referencedSymbolId)) {
6071
- doesMutationConsumeAwaitedValue = true;
6072
- break;
6073
- }
6074
- if (doesMutationConsumeAwaitedValue) break;
6075
- }
6076
- if (!doesMutationConsumeAwaitedValue) return;
6077
- let receiverIdentifier = callee.object;
6078
- while (isNodeOfType(receiverIdentifier, "MemberExpression")) receiverIdentifier = receiverIdentifier.object;
6079
- if (!isNodeOfType(receiverIdentifier, "Identifier")) return;
6080
- const receiverSymbol = context.scopes.symbolFor(receiverIdentifier);
6081
- if (receiverSymbol && (externallyReachableParameterSymbolIds.has(receiverSymbol.id) || !isScopeWithinFunction(receiverSymbol.scope, functionScope) && isSymbolDirectlyReturned(receiverSymbol, callerFunction))) {
6082
- doesInsertAwaitDerivedOutput = true;
6083
- return false;
6084
- }
6085
- });
6086
- return doesInsertAwaitDerivedOutput;
6087
- };
6088
- const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode) || doesAwaitedLocalCallInsertAwaitDerivedOutput(awaitNode, context);
5956
+ const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
6089
5957
  const collectPatternIdentifiers = (pattern, target) => {
6090
5958
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
6091
5959
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -6231,6 +6099,11 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
6231
6099
  });
6232
6100
  return hasAwaitDependentExit;
6233
6101
  };
6102
+ const getRootObjectIdentifierName = (node) => {
6103
+ let current = node;
6104
+ while (isNodeOfType(current, "MemberExpression")) current = current.object;
6105
+ return isNodeOfType(current, "Identifier") ? current.name : null;
6106
+ };
6234
6107
  const MUTATING_ARRAY_METHOD_NAMES$2 = new Set([
6235
6108
  ...ARRAY_MUTATION_METHOD_NAMES,
6236
6109
  "pop",
@@ -17999,14 +17872,6 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
17999
17872
  //#region src/plugin/utils/is-event-handler-attribute.ts
18000
17873
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
18001
17874
  //#endregion
18002
- //#region src/plugin/utils/is-early-exit-statement.ts
18003
- const isEarlyExitStatement$1 = (statement) => {
18004
- if (!statement) return false;
18005
- if (statementAlwaysExits$1(statement)) return true;
18006
- if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
18007
- return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
18008
- };
18009
- //#endregion
18010
17875
  //#region src/plugin/utils/is-ast-descendant.ts
18011
17876
  /**
18012
17877
  * True when `inner` is `outer` itself or any descendant in the AST
@@ -18225,15 +18090,13 @@ const isSynchronousIteratorCall = (callNode, callbackArgument, scopes) => {
18225
18090
  }
18226
18091
  return Boolean(methodName && EAGER_ITERATOR_METHOD_NAMES.has(methodName) && callNode.arguments[0] === callbackArgument && !isProvablyEmptyEagerCollection(callee.object, scopes) && (methodName === "forEach" ? isProvablyEagerForEachCollection(callee.object, scopes) : isProvablyEagerCollection(callee.object, scopes)));
18227
18092
  };
18228
- const isSynchronousIteratorCallbackCall = (callNode, callbackArgument) => {
18229
- const callee = stripParenExpression(callNode.callee);
18230
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
18231
- if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === callbackArgument;
18232
- return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === callbackArgument;
18233
- };
18234
18093
  const isSynchronousIteratorCallback = (functionNode) => {
18235
18094
  const callNode = functionNode.parent;
18236
- return Boolean(isNodeOfType(callNode, "CallExpression") && isSynchronousIteratorCallbackCall(callNode, functionNode));
18095
+ if (!isNodeOfType(callNode, "CallExpression")) return false;
18096
+ const callee = stripParenExpression(callNode.callee);
18097
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
18098
+ if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === functionNode;
18099
+ return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === functionNode;
18237
18100
  };
18238
18101
  //#endregion
18239
18102
  //#region src/plugin/utils/is-within-assignment-target.ts
@@ -18353,35 +18216,11 @@ const resolveEventListenerCaptureValueIdentityKey = (expression, context) => {
18353
18216
  const rightIdentityKey = resolveEventListenerCaptureValueIdentityKey(unwrappedExpression.right, context);
18354
18217
  return leftIdentityKey && rightIdentityKey ? `${unwrappedExpression.type}:${unwrappedExpression.operator}:${leftIdentityKey}:${rightIdentityKey}` : null;
18355
18218
  };
18356
- const resolveReadOnlyEventListenerOptions = (optionsNode, context) => {
18357
- const unwrappedOptions = stripParenExpression(optionsNode);
18358
- if (!isNodeOfType(unwrappedOptions, "Identifier")) return resolveStableValue(unwrappedOptions, context);
18359
- const optionsSymbol = context.scopes.symbolFor(unwrappedOptions);
18360
- const initializer = optionsSymbol?.initializer ? stripParenExpression(optionsSymbol.initializer) : null;
18361
- if (!optionsSymbol || !initializer) return resolveStableValue(unwrappedOptions, context);
18362
- if (!isNodeOfType(initializer, "ObjectExpression")) {
18363
- if (isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression")) return null;
18364
- return resolveStableValue(unwrappedOptions, context);
18365
- }
18366
- if (optionsSymbol.kind !== "const") return null;
18367
- return optionsSymbol.references.every((reference) => {
18368
- if (reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) return false;
18369
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
18370
- const callNode = referenceRoot.parent;
18371
- if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments[2] !== referenceRoot) return false;
18372
- const callee = stripParenExpression(callNode.callee);
18373
- if (!isNodeOfType(callee, "MemberExpression")) return false;
18374
- const methodName = getStaticPropertyKeyName(callee);
18375
- return methodName === "addEventListener" || methodName === "removeEventListener";
18376
- }) ? initializer : null;
18377
- };
18378
18219
  const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
18379
- const stableOptionsNode = optionsNode ? resolveReadOnlyEventListenerOptions(optionsNode, context) : null;
18380
- if (optionsNode && !stableOptionsNode) return null;
18381
- const capture = resolveEventListenerCapture(stableOptionsNode, { allowIndeterminateEntries: true });
18220
+ const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
18382
18221
  if (capture !== null) return `capture:${String(capture)}`;
18383
- if (!stableOptionsNode) return null;
18384
- const unwrappedOptions = stripParenExpression(stableOptionsNode);
18222
+ if (!optionsNode) return null;
18223
+ const unwrappedOptions = stripParenExpression(optionsNode);
18385
18224
  if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
18386
18225
  const optionsKey = allowOpaqueOptionsIdentity ? resolveEventListenerCaptureValueIdentityKey(unwrappedOptions, context) : null;
18387
18226
  return optionsKey ? `options:${optionsKey}` : null;
@@ -18428,8 +18267,12 @@ const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, conte
18428
18267
  return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
18429
18268
  };
18430
18269
  const findAssignedResourceKey = (resourceNode, context) => {
18431
- const currentNode = findTransparentExpressionRoot(resourceNode);
18432
- const parentNode = currentNode.parent;
18270
+ let currentNode = resourceNode;
18271
+ let parentNode = currentNode.parent;
18272
+ while (isNodeOfType(parentNode, "ChainExpression")) {
18273
+ currentNode = parentNode;
18274
+ parentNode = currentNode.parent;
18275
+ }
18433
18276
  if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode) return resolveExpressionKey(parentNode.id, context);
18434
18277
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
18435
18278
  return null;
@@ -18700,18 +18543,6 @@ const resolveIteratorCollectionKey = (expression, context) => {
18700
18543
  }
18701
18544
  return null;
18702
18545
  };
18703
- const resolveReceiverIteratorCollectionKey = (expression, context) => {
18704
- if (!expression) return null;
18705
- const unwrappedExpression = stripParenExpression(expression);
18706
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
18707
- const collectionExpression = findForOfStatementForIteratorExpression(unwrappedExpression, context)?.right;
18708
- if (!collectionExpression) return null;
18709
- const collectionIdentifier = stripParenExpression(collectionExpression);
18710
- if (!isNodeOfType(collectionIdentifier, "Identifier") || !isPrivatePlainConstIdentifier(collectionIdentifier, context)) return null;
18711
- const collectionSymbol = context.scopes.symbolFor(collectionIdentifier);
18712
- const initializer = collectionSymbol?.initializer ? stripParenExpression(collectionSymbol.initializer) : null;
18713
- return collectionSymbol && isNodeOfType(initializer, "ArrayExpression") && hasOnlyReplayableCollectionReferences(collectionIdentifier, context, /* @__PURE__ */ new Set()) ? `symbol:${collectionSymbol.id}` : null;
18714
- };
18715
18546
  const isStableLoopReceiver = (expression, context) => {
18716
18547
  if (!expression) return false;
18717
18548
  const unwrappedExpression = stripParenExpression(expression);
@@ -19482,28 +19313,6 @@ const isFunctionReturnedFromReactHook = (functionNode, context, requireRefProper
19482
19313
  });
19483
19314
  };
19484
19315
  const isFunctionUsedAsReactRef = (functionNode, context) => isFunctionForwardedToReactRef(functionNode, context) || isFunctionReturnedFromReactHook(functionNode, context, true);
19485
- const findCallbackRefReplacementReleaseGuard = (releaseCall, ownerFunction, releaseReceiverKey, registrationReceiverKey, context) => {
19486
- let descendant = releaseCall;
19487
- let ancestor = descendant.parent;
19488
- while (ancestor && ancestor !== ownerFunction) {
19489
- if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant && ancestor.alternate === null) {
19490
- const test = stripParenExpression(ancestor.test);
19491
- if (!isNodeOfType(test, "LogicalExpression") || test.operator !== "&&") return null;
19492
- const operands = [stripParenExpression(test.left), stripParenExpression(test.right)];
19493
- const hasLiveReceiverTest = operands.some((operand) => doesTestRequireLiveExpressionKey(operand, releaseReceiverKey, context));
19494
- const hasDifferentReceiverTest = operands.some((operand) => {
19495
- if (!isNodeOfType(operand, "BinaryExpression") || operand.operator !== "!==" && operand.operator !== "!=") return false;
19496
- const leftKey = resolveExpressionKey(operand.left, context);
19497
- const rightKey = resolveExpressionKey(operand.right, context);
19498
- return leftKey === releaseReceiverKey && rightKey === registrationReceiverKey || rightKey === releaseReceiverKey && leftKey === registrationReceiverKey;
19499
- });
19500
- return hasLiveReceiverTest && hasDifferentReceiverTest ? ancestor : null;
19501
- }
19502
- descendant = ancestor;
19503
- ancestor = descendant.parent;
19504
- }
19505
- return null;
19506
- };
19507
19316
  const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19508
19317
  if (!isNodeOfType(usage.node, "CallExpression")) return false;
19509
19318
  const usageFunction = findEnclosingFunction$1(usage.node);
@@ -19524,7 +19333,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19524
19333
  if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
19525
19334
  if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveReactRefSymbol(stripParenExpression(child.left), context.scopes)?.id === releaseRefSymbol.id && resolveExpressionKey(child.right, context) === registrationReceiverKey && releaseStart !== null && (getRangeStart(child) ?? -1) > releaseStart) matchingOwnershipAssignments.push(child);
19526
19335
  });
19527
- const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? findCallbackRefReplacementReleaseGuard(releaseCall, usageFunction, releaseReceiverKey, registrationReceiverKey, context) ?? releaseCall;
19336
+ const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
19528
19337
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
19529
19338
  return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
19530
19339
  };
@@ -19674,21 +19483,14 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19674
19483
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
19675
19484
  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;
19676
19485
  if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
19486
+ 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;
19677
19487
  if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
19678
- if (usage.registrationVerbName === "addListener" && releaseVerbName === "removeListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) {
19679
- if (callNode.arguments?.length !== 1) return false;
19680
- const registrationHandler = resolveStableValue(usage.node.arguments[0], context);
19681
- if (!isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && !isFunctionLike$1(registrationHandler)) return false;
19682
- }
19683
19488
  if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
19684
19489
  if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
19685
19490
  if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
19686
19491
  }
19687
19492
  if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
19688
- const registrationCallee = isNodeOfType(usage.node, "CallExpression") ? stripParenExpression(usage.node.callee) : null;
19689
- const registrationReceiverCollectionKey = isNodeOfType(registrationCallee, "MemberExpression") ? resolveReceiverIteratorCollectionKey(registrationCallee.object, context) : null;
19690
- const releaseReceiverCollectionKeyForPair = resolveReceiverIteratorCollectionKey(callee.object, context);
19691
- if (!(registrationReceiverCollectionKey !== null && registrationReceiverCollectionKey === releaseReceiverCollectionKeyForPair) && (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey)) return false;
19493
+ if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
19692
19494
  if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
19693
19495
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
19694
19496
  if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
@@ -19725,7 +19527,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19725
19527
  const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
19726
19528
  const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
19727
19529
  if (!releaseHandler) return releaseVerbName === "off";
19728
- const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.handlerKey ?? usage.eventKey : usage.handlerKey;
19530
+ const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
19729
19531
  const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
19730
19532
  return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
19731
19533
  }
@@ -20199,7 +20001,6 @@ const findRetainedFunctionLeak = (retainedFunction, context, options) => {
20199
20001
  walkAst(body, (child) => {
20200
20002
  if (leak !== null) return false;
20201
20003
  if (isFunctionLike$1(child)) return false;
20202
- if (!isNodeReachableWithinFunction(child, context)) return false;
20203
20004
  if (isSocketConstruction(child) && !doesResourceResultEscape(child, allowReturnedSocketEscape, false, context)) {
20204
20005
  const socketUsage = {
20205
20006
  kind: "socket",
@@ -20452,164 +20253,6 @@ const isInlineRetainedHandlerFunction = (functionNode, context) => {
20452
20253
  const objectParent = objectExpression.parent;
20453
20254
  return (isNodeOfType(objectParent, "CallExpression") && objectParent.arguments.some((argument) => argument === objectExpression) || isNodeOfType(objectParent, "JSXExpressionContainer")) && findRenderPhaseComponentOrHook(parentNode, context.scopes) !== null;
20454
20255
  };
20455
- const readInvocationArgumentValue = (expression, context) => {
20456
- if (!expression) return {
20457
- isDefinitelyUndefined: true,
20458
- truthiness: "falsy"
20459
- };
20460
- const target = stripParenExpression(expression);
20461
- if (isNodeOfType(target, "Literal")) return {
20462
- isDefinitelyUndefined: false,
20463
- truthiness: target.value ? "truthy" : "falsy"
20464
- };
20465
- if (isNodeOfType(target, "Identifier") && target.name === "undefined" && context.scopes.isGlobalReference(target)) return {
20466
- isDefinitelyUndefined: true,
20467
- truthiness: "falsy"
20468
- };
20469
- if (isNodeOfType(target, "UnaryExpression") && target.operator === "void") return {
20470
- isDefinitelyUndefined: true,
20471
- truthiness: "falsy"
20472
- };
20473
- if (isNodeOfType(target, "ArrayExpression") || isNodeOfType(target, "ArrowFunctionExpression") || isNodeOfType(target, "ClassExpression") || isNodeOfType(target, "FunctionExpression") || isNodeOfType(target, "NewExpression") || isNodeOfType(target, "ObjectExpression")) return {
20474
- isDefinitelyUndefined: false,
20475
- truthiness: "truthy"
20476
- };
20477
- return {
20478
- isDefinitelyUndefined: false,
20479
- truthiness: "unknown"
20480
- };
20481
- };
20482
- const readInvocationConditionTruthiness = (expression, parameterValues, context) => {
20483
- const target = stripParenExpression(expression);
20484
- const atomicValue = readInvocationArgumentValue(target, context);
20485
- if (atomicValue.truthiness !== "unknown") return atomicValue.truthiness;
20486
- if (isNodeOfType(target, "Identifier")) {
20487
- const symbol = context.scopes.symbolFor(target);
20488
- return symbol ? parameterValues.get(symbol.id)?.truthiness ?? "unknown" : "unknown";
20489
- }
20490
- if (isNodeOfType(target, "UnaryExpression") && target.operator === "!") {
20491
- const argumentTruthiness = readInvocationConditionTruthiness(target.argument, parameterValues, context);
20492
- return argumentTruthiness === "truthy" ? "falsy" : argumentTruthiness === "falsy" ? "truthy" : "unknown";
20493
- }
20494
- if (isNodeOfType(target, "LogicalExpression")) {
20495
- const leftTruthiness = readInvocationConditionTruthiness(target.left, parameterValues, context);
20496
- const rightTruthiness = readInvocationConditionTruthiness(target.right, parameterValues, context);
20497
- if (target.operator === "&&") {
20498
- if (leftTruthiness === "falsy" || rightTruthiness === "falsy") return "falsy";
20499
- return leftTruthiness === "truthy" && rightTruthiness === "truthy" ? "truthy" : "unknown";
20500
- }
20501
- if (target.operator === "||") {
20502
- if (leftTruthiness === "truthy" || rightTruthiness === "truthy") return "truthy";
20503
- return leftTruthiness === "falsy" && rightTruthiness === "falsy" ? "falsy" : "unknown";
20504
- }
20505
- return "unknown";
20506
- }
20507
- if (isNodeOfType(target, "ConditionalExpression")) {
20508
- const testTruthiness = readInvocationConditionTruthiness(target.test, parameterValues, context);
20509
- if (testTruthiness === "truthy") return readInvocationConditionTruthiness(target.consequent, parameterValues, context);
20510
- if (testTruthiness === "falsy") return readInvocationConditionTruthiness(target.alternate, parameterValues, context);
20511
- const consequentTruthiness = readInvocationConditionTruthiness(target.consequent, parameterValues, context);
20512
- return consequentTruthiness === readInvocationConditionTruthiness(target.alternate, parameterValues, context) ? consequentTruthiness : "unknown";
20513
- }
20514
- if (isNodeOfType(target, "CallExpression") && isNodeOfType(target.callee, "Identifier") && target.callee.name === "Boolean" && context.scopes.isGlobalReference(target.callee) && target.arguments[0] && isAstNode(target.arguments[0])) return readInvocationConditionTruthiness(target.arguments[0], parameterValues, context);
20515
- return "unknown";
20516
- };
20517
- const getInvocationParameterValues = (retainedFunction, invocation, leakNode, context) => {
20518
- const parameterValues = /* @__PURE__ */ new Map();
20519
- if (!isFunctionLike$1(retainedFunction) || !invocation.isDirect) return parameterValues;
20520
- for (const [parameterIndex, parameter] of retainedFunction.params.entries()) {
20521
- const argument = invocation.call.arguments[parameterIndex];
20522
- const argumentExpression = argument && isAstNode(argument) ? argument : null;
20523
- let parameterIdentifier = null;
20524
- let parameterValue = readInvocationArgumentValue(argumentExpression, context);
20525
- if (isNodeOfType(parameter, "Identifier")) parameterIdentifier = parameter;
20526
- else if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) {
20527
- parameterIdentifier = parameter.left;
20528
- if (parameterValue.isDefinitelyUndefined) parameterValue = readInvocationArgumentValue(parameter.right, context);
20529
- } else if (isNodeOfType(parameter, "RestElement") && isNodeOfType(parameter.argument, "Identifier")) {
20530
- parameterIdentifier = parameter.argument;
20531
- parameterValue = {
20532
- isDefinitelyUndefined: false,
20533
- truthiness: "truthy"
20534
- };
20535
- }
20536
- if (!parameterIdentifier) continue;
20537
- const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
20538
- if (!parameterSymbol) continue;
20539
- const isWrittenBeforeLeak = parameterSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] < leakNode.range[0]);
20540
- parameterValues.set(parameterSymbol.id, isWrittenBeforeLeak ? {
20541
- isDefinitelyUndefined: false,
20542
- truthiness: "unknown"
20543
- } : parameterValue);
20544
- }
20545
- return parameterValues;
20546
- };
20547
- const isLeakPathDisabledForInvocation = (retainedFunction, leakNode, invocation, context) => {
20548
- if (!invocation.isDirect) return false;
20549
- const parameterValues = getInvocationParameterValues(retainedFunction, invocation, leakNode, context);
20550
- let child = leakNode;
20551
- let ancestor = leakNode.parent ?? null;
20552
- while (ancestor && ancestor !== retainedFunction) {
20553
- if (isNodeOfType(ancestor, "BlockStatement")) {
20554
- const childIndex = ancestor.body.findIndex((statement) => statement === child);
20555
- for (const precedingStatement of ancestor.body.slice(0, childIndex)) {
20556
- if (!isNodeOfType(precedingStatement, "IfStatement") || precedingStatement.alternate || !isEarlyExitStatement$1(precedingStatement.consequent)) continue;
20557
- if (readInvocationConditionTruthiness(precedingStatement.test, parameterValues, context) === "truthy") return true;
20558
- }
20559
- }
20560
- let requiredTruthiness = null;
20561
- let condition = null;
20562
- if (isNodeOfType(ancestor, "IfStatement")) {
20563
- condition = ancestor.test;
20564
- requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
20565
- } else if (isNodeOfType(ancestor, "ConditionalExpression")) {
20566
- condition = ancestor.test;
20567
- requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
20568
- } else if (isNodeOfType(ancestor, "LogicalExpression") && ancestor.right === child && ancestor.operator !== "??") {
20569
- condition = ancestor.left;
20570
- requiredTruthiness = ancestor.operator === "&&" ? "truthy" : "falsy";
20571
- } else if ((isNodeOfType(ancestor, "WhileStatement") || isNodeOfType(ancestor, "DoWhileStatement")) && ancestor.body === child) {
20572
- condition = ancestor.test;
20573
- requiredTruthiness = "truthy";
20574
- } else if (isNodeOfType(ancestor, "ForStatement") && ancestor.body === child && ancestor.test) {
20575
- condition = ancestor.test;
20576
- requiredTruthiness = "truthy";
20577
- }
20578
- if (condition && requiredTruthiness) {
20579
- const conditionTruthiness = readInvocationConditionTruthiness(condition, parameterValues, context);
20580
- if (conditionTruthiness !== "unknown" && conditionTruthiness !== requiredTruthiness) return true;
20581
- }
20582
- child = ancestor;
20583
- ancestor = ancestor.parent ?? null;
20584
- }
20585
- return false;
20586
- };
20587
- const getEffectRetainedInvocations = (retainedFunction, context) => {
20588
- if (!isFunctionLike$1(retainedFunction)) return [];
20589
- const componentFunction = findEnclosingFunction$1(retainedFunction);
20590
- if (!componentFunction || !isFunctionLike$1(componentFunction)) return [];
20591
- const invocations = [];
20592
- walkAst(componentFunction.body, (child) => {
20593
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactHookCall(child, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return;
20594
- const effectCallback = getEffectCallback(child);
20595
- if (!effectCallback || !isFunctionLike$1(effectCallback)) return;
20596
- walkAst(effectCallback.body, (effectChild) => {
20597
- if (effectChild !== effectCallback.body && isFunctionLike$1(effectChild)) return false;
20598
- if (!isNodeOfType(effectChild, "CallExpression") || !isNodeReachableWithinFunction(effectChild, context)) return;
20599
- const isDirectInvocation = resolveRefOwnedCleanupFunction(effectChild.callee, context) === retainedFunction;
20600
- const isSynchronousIteratorInvocation = effectChild.arguments.some((argument) => isAstNode(argument) && resolveRefOwnedCleanupFunction(argument, context) === retainedFunction && isSynchronousIteratorCallbackCall(effectChild, argument));
20601
- if (isDirectInvocation) invocations.push({
20602
- call: effectChild,
20603
- isDirect: true
20604
- });
20605
- if (isSynchronousIteratorInvocation) invocations.push({
20606
- call: effectChild,
20607
- isDirect: false
20608
- });
20609
- });
20610
- });
20611
- return invocations;
20612
- };
20613
20256
  const effectNeedsCleanup = defineRule({
20614
20257
  id: "effect-needs-cleanup",
20615
20258
  title: "Effect subscription or timer never cleaned up",
@@ -20620,19 +20263,13 @@ const effectNeedsCleanup = defineRule({
20620
20263
  const reportRetainedLeak = (retainedFunction) => {
20621
20264
  const refEffectUsage = getReactRefEffectUsage(retainedFunction, context);
20622
20265
  if (!refEffectUsage && !isPotentiallyReachableFunction(retainedFunction, context)) return;
20623
- const effectInvocations = getEffectRetainedInvocations(retainedFunction, context);
20624
- const isEffectInvoked = effectInvocations.length > 0;
20625
20266
  const leak = findRetainedFunctionLeak(retainedFunction, context, refEffectUsage ? {
20626
20267
  allowReturnedResourceEscape: refEffectUsage.doesEffectOwnEveryResult,
20627
20268
  allowReturnedTimerEscape: false,
20628
20269
  includeOneShotTimers: true,
20629
20270
  requireCallableReturnedResource: true
20630
- } : isEffectInvoked ? {
20631
- allowReturnedTimerEscape: false,
20632
- includeOneShotTimers: true
20633
20271
  } : void 0);
20634
20272
  if (!leak) return;
20635
- if (isEffectInvoked && leak.resourceName === "setTimeout" && (!isNodeReachableWithinFunction(leak.node, context) || isFunctionLike$1(retainedFunction) && retainedFunction.params.length > 0 && !context.cfg.isUnconditionalFromEntry(leak.node) && effectInvocations.every((invocation) => isLeakPathDisabledForInvocation(retainedFunction, leak.node, invocation, context)))) return;
20636
20273
  const resourceNoun = RESOURCE_NOUN_BY_KIND[leak.kind];
20637
20274
  context.report({
20638
20275
  node: leak.node,
@@ -25052,14 +24689,14 @@ const getFirstLegendChild = (children, targetNode) => {
25052
24689
  if (isNodeOfType(child, "JSXExpressionContainer")) {
25053
24690
  const potentialLegends = [];
25054
24691
  collectPotentialLegends(child.expression, potentialLegends);
25055
- const containingLegend = potentialLegends.find((legend) => isDescendantOf$1(targetNode, legend));
24692
+ const containingLegend = potentialLegends.find((legend) => isDescendantOf(targetNode, legend));
25056
24693
  if (containingLegend) return containingLegend;
25057
24694
  if (potentialLegends[0]) return potentialLegends[0];
25058
24695
  }
25059
24696
  }
25060
24697
  return null;
25061
24698
  };
25062
- const isDescendantOf$1 = (node, ancestor) => {
24699
+ const isDescendantOf = (node, ancestor) => {
25063
24700
  let current = node.parent;
25064
24701
  while (current) {
25065
24702
  if (current === ancestor) return true;
@@ -25084,7 +24721,7 @@ const isDisabledByFieldsetAncestor = (node, context) => {
25084
24721
  while (ancestor) {
25085
24722
  if (isNodeOfType(ancestor, "JSXElement") && resolveJsxElementType(ancestor.openingElement) === "fieldset" && openingElementMayBeDisabled(ancestor.openingElement, context)) {
25086
24723
  const firstLegend = getFirstLegendChild(ancestor.children, node);
25087
- if (!firstLegend || !isDescendantOf$1(node, firstLegend)) return true;
24724
+ if (!firstLegend || !isDescendantOf(node, firstLegend)) return true;
25088
24725
  }
25089
24726
  ancestor = ancestor.parent;
25090
24727
  }
@@ -45603,6 +45240,14 @@ const noAriaInvalidWithoutDescription = defineRule({
45603
45240
  } })
45604
45241
  });
45605
45242
  //#endregion
45243
+ //#region src/plugin/utils/is-early-exit-statement.ts
45244
+ const isEarlyExitStatement$1 = (statement) => {
45245
+ if (!statement) return false;
45246
+ if (statementAlwaysExits$1(statement)) return true;
45247
+ if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
45248
+ return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
45249
+ };
45250
+ //#endregion
45606
45251
  //#region src/plugin/utils/unwrap-negative-guard-form.ts
45607
45252
  const unwrapNegativeGuardForm = (test) => {
45608
45253
  const expression = stripParenExpression(test);
@@ -64498,106 +64143,6 @@ const isGatedByFalsyInitialState = (node, scopes) => {
64498
64143
  };
64499
64144
  //#endregion
64500
64145
  //#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
64501
- const findGuardingIfStatements = (node, functionBoundary) => {
64502
- const guardingIfStatements = [];
64503
- let currentNode = node.parent;
64504
- while (currentNode && currentNode !== functionBoundary) {
64505
- if (isNodeOfType(currentNode, "IfStatement")) guardingIfStatements.push(currentNode);
64506
- currentNode = currentNode.parent;
64507
- }
64508
- return guardingIfStatements;
64509
- };
64510
- const doesNodeReadSymbol = (node, symbol) => {
64511
- let doesReadSymbol = false;
64512
- walkAst(node, (childNode) => {
64513
- if (isNodeOfType(childNode, "Identifier") && symbol.references.some((reference) => reference.identifier === childNode && reference.flag !== "write")) {
64514
- doesReadSymbol = true;
64515
- return false;
64516
- }
64517
- });
64518
- return doesReadSymbol;
64519
- };
64520
- const collectWrittenSymbols = (node, scopes) => {
64521
- const writtenSymbols = /* @__PURE__ */ new Set();
64522
- walkAst(node, (childNode) => {
64523
- if (childNode !== node && isFunctionLike$1(childNode)) return false;
64524
- if (!isNodeOfType(childNode, "Identifier")) return;
64525
- const reference = scopes.referenceFor(childNode);
64526
- if (!reference || reference.flag === "read" || !reference.resolvedSymbol) return;
64527
- writtenSymbols.add(reference.resolvedSymbol);
64528
- });
64529
- return writtenSymbols;
64530
- };
64531
- const isDescendantOf = (node, ancestorNode) => {
64532
- let currentNode = node.parent;
64533
- while (currentNode) {
64534
- if (currentNode === ancestorNode) return true;
64535
- currentNode = currentNode.parent;
64536
- }
64537
- return false;
64538
- };
64539
- const getAssignedValue = (identifier) => {
64540
- const assignmentExpression = identifier.parent;
64541
- return isNodeOfType(assignmentExpression, "AssignmentExpression") && assignmentExpression.operator === "=" && assignmentExpression.left === identifier ? assignmentExpression.right : null;
64542
- };
64543
- const doesGuardPreserveInitialSymbolValue = (symbol, guardingIfStatement, scopes) => {
64544
- const initialValue = symbol.initializer;
64545
- if (!initialValue) return false;
64546
- const guardedWrites = symbol.references.filter((reference) => reference.flag !== "read" && isDescendantOf(reference.identifier, guardingIfStatement));
64547
- return guardedWrites.length > 0 && guardedWrites.every((reference) => {
64548
- const assignedValue = getAssignedValue(reference.identifier);
64549
- return Boolean(assignedValue && areExpressionsStructurallyEqual(initialValue, assignedValue) && doEquivalentExpressionBindingsMatch(initialValue, assignedValue, scopes));
64550
- });
64551
- };
64552
- const isWriteOverwrittenBefore = (symbol, writeIdentifier, guardingIfStatement, readIdentifier, context) => symbol.references.some((reference) => reference.flag !== "read" && reference.identifier !== writeIdentifier && !isDescendantOf(reference.identifier, guardingIfStatement) && isNodeReachableWithinFunction(reference.identifier, context) && isUnconditionalOrStaticallySelected(reference.identifier, context) && getNodeStartIndex(reference.identifier) > getNodeStartIndex(writeIdentifier) && getNodeStartIndex(reference.identifier) < getNodeStartIndex(readIdentifier));
64553
- const isUnconditionalOrStaticallySelected = (node, context) => {
64554
- if (context.cfg.isUnconditionalFromEntry(node)) return true;
64555
- let currentNode = node;
64556
- let outermostStaticIfStatement = null;
64557
- let parentNode = currentNode.parent;
64558
- while (parentNode) {
64559
- if (isFunctionLike$1(parentNode)) break;
64560
- if (isNodeOfType(parentNode, "IfStatement")) {
64561
- const staticResult = readInitialStateBoolean(parentNode.test, context.scopes);
64562
- let selectedBranch = null;
64563
- if (staticResult === true) selectedBranch = parentNode.consequent;
64564
- if (staticResult === false) selectedBranch = parentNode.alternate;
64565
- if (!selectedBranch || currentNode !== selectedBranch && !isDescendantOf(currentNode, selectedBranch)) return false;
64566
- outermostStaticIfStatement = parentNode;
64567
- }
64568
- currentNode = parentNode;
64569
- parentNode = currentNode.parent;
64570
- }
64571
- return Boolean(outermostStaticIfStatement && context.cfg.isUnconditionalFromEntry(outermostStaticIfStatement));
64572
- };
64573
- const containsExplicitReactRuntimeReference = (node, scopes) => {
64574
- let hasRuntimeReference = false;
64575
- walkAst(node, (childNode) => {
64576
- if (isNodeOfType(childNode, "ImportDeclaration") && typeof childNode.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(childNode.source.value)) {
64577
- hasRuntimeReference = true;
64578
- return false;
64579
- }
64580
- if (!isNodeOfType(childNode, "CallExpression")) return;
64581
- const sourceArgument = (childNode.arguments ?? [])[0];
64582
- if (!isNodeOfType(childNode.callee, "Identifier") || childNode.callee.name !== "require" || !scopes.isGlobalReference(childNode.callee) || !isNodeOfType(sourceArgument, "Literal") || typeof sourceArgument.value !== "string" || !REACT_RUNTIME_MODULE_SOURCES.has(sourceArgument.value)) return;
64583
- hasRuntimeReference = true;
64584
- return false;
64585
- });
64586
- return hasRuntimeReference;
64587
- };
64588
- const findComponentRenderingLocalFunctionResult = (functionNode, scopes) => {
64589
- const bindingIdentifier = getDirectFunctionBindingIdentifier(functionNode);
64590
- if (!isNodeOfType(bindingIdentifier, "Identifier")) return null;
64591
- const functionSymbol = scopes.symbolFor(bindingIdentifier);
64592
- if (!functionSymbol) return null;
64593
- for (const reference of functionSymbol.references) {
64594
- const callExpression = reference.identifier.parent;
64595
- if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== reference.identifier) continue;
64596
- const componentOrHookNode = findRenderPhaseComponentOrHook(callExpression, scopes);
64597
- if (componentOrHookNode && isInRenderedOutput(callExpression, componentOrHookNode, scopes)) return componentOrHookNode;
64598
- }
64599
- return null;
64600
- };
64601
64146
  const evaluateEquality$1 = (operator, left, right) => {
64602
64147
  if (operator === "===" || operator === "==") return left === right;
64603
64148
  if (operator === "!==" || operator === "!=") return left !== right;
@@ -64648,107 +64193,6 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
64648
64193
  if (leftResult === false && rightResult === false) return false;
64649
64194
  return null;
64650
64195
  };
64651
- const areLooselyEqualPrimitiveResults = (left, right) => {
64652
- if (left.kind === right.kind) return left.value === right.value;
64653
- if (left.kind === "null" && right.kind === "undefined" || left.kind === "undefined" && right.kind === "null") return true;
64654
- if (left.kind === "boolean") return areLooselyEqualPrimitiveResults({
64655
- kind: "number",
64656
- value: left.value ? 1 : 0
64657
- }, right);
64658
- if (right.kind === "boolean") return areLooselyEqualPrimitiveResults(left, {
64659
- kind: "number",
64660
- value: right.value ? 1 : 0
64661
- });
64662
- if (left.kind === "number" && right.kind === "string") return left.value === Number(right.value);
64663
- if (left.kind === "string" && right.kind === "number") return Number(left.value) === right.value;
64664
- return false;
64665
- };
64666
- const readHydrationPrimitiveResult = (expression, context, runtime, state) => {
64667
- const unwrappedExpression = stripParenExpression(expression);
64668
- const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
64669
- if (predicateMatch) return {
64670
- kind: "boolean",
64671
- value: predicateMatch[`${runtime}Result`]
64672
- };
64673
- if (isNodeOfType(unwrappedExpression, "Literal")) {
64674
- const value = unwrappedExpression.value;
64675
- if (value === null) return {
64676
- kind: "null",
64677
- value
64678
- };
64679
- if (typeof value === "boolean") return {
64680
- kind: "boolean",
64681
- value
64682
- };
64683
- if (typeof value === "number") return {
64684
- kind: "number",
64685
- value
64686
- };
64687
- if (typeof value === "string") return {
64688
- kind: "string",
64689
- value
64690
- };
64691
- return null;
64692
- }
64693
- if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return {
64694
- kind: "undefined",
64695
- value: void 0
64696
- };
64697
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
64698
- const symbol = context.scopes.symbolFor(unwrappedExpression);
64699
- const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
64700
- if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
64701
- state.visitedSymbolIds.add(symbol.id);
64702
- const result = readHydrationPrimitiveResult(parameterValue, context, runtime, state);
64703
- state.visitedSymbolIds.delete(symbol.id);
64704
- return result;
64705
- }
64706
- if (symbol && symbol.kind === "const" && symbol.initializer && symbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(symbol.id)) {
64707
- state.visitedSymbolIds.add(symbol.id);
64708
- const result = readHydrationPrimitiveResult(symbol.initializer, context, runtime, state);
64709
- state.visitedSymbolIds.delete(symbol.id);
64710
- return result;
64711
- }
64712
- }
64713
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64714
- const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64715
- return argumentResult === null ? null : {
64716
- kind: "boolean",
64717
- value: !argumentResult
64718
- };
64719
- }
64720
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64721
- const leftResult = readHydrationPrimitiveResult(unwrappedExpression.left, context, runtime, state);
64722
- const rightResult = readHydrationPrimitiveResult(unwrappedExpression.right, context, runtime, state);
64723
- if (!leftResult || !rightResult) return null;
64724
- if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
64725
- const areEqual = leftResult.kind === rightResult.kind && leftResult.value === rightResult.value;
64726
- return {
64727
- kind: "boolean",
64728
- value: unwrappedExpression.operator === "===" ? areEqual : !areEqual
64729
- };
64730
- }
64731
- if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
64732
- const areEqual = areLooselyEqualPrimitiveResults(leftResult, rightResult);
64733
- return {
64734
- kind: "boolean",
64735
- value: unwrappedExpression.operator === "==" ? areEqual : !areEqual
64736
- };
64737
- }
64738
- }
64739
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
64740
- const callArguments = unwrappedExpression.arguments ?? [];
64741
- const callee = stripParenExpression(unwrappedExpression.callee);
64742
- if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) {
64743
- const argumentResult = readHydrationConditionResult(callArguments[0], context, runtime, state);
64744
- return argumentResult === null ? null : {
64745
- kind: "boolean",
64746
- value: argumentResult
64747
- };
64748
- }
64749
- }
64750
- return null;
64751
- };
64752
64196
  const readHydrationConditionResult = (expression, context, runtime, state) => {
64753
64197
  const unwrappedExpression = stripParenExpression(expression);
64754
64198
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64797,10 +64241,6 @@ const readHydrationConditionResult = (expression, context, runtime, state) => {
64797
64241
  parameterValuesBySymbolId
64798
64242
  });
64799
64243
  }
64800
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64801
- const result = readHydrationPrimitiveResult(unwrappedExpression, context, runtime, state);
64802
- return result?.kind === "boolean" && typeof result.value === "boolean" ? result.value : null;
64803
- }
64804
64244
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64805
64245
  const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64806
64246
  return argumentResult === null ? null : !argumentResult;
@@ -64861,19 +64301,13 @@ const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, sc
64861
64301
  const rightSymbol = scopes.symbolFor(right);
64862
64302
  return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
64863
64303
  }
64864
- const rightEntries = new Map(Object.entries(right));
64865
- for (const [key, leftValue] of Object.entries(left)) {
64866
- if (key === "parent") continue;
64867
- const rightValue = rightEntries.get(key);
64868
- if (isAstNode(leftValue)) {
64869
- if (!isAstNode(rightValue) || !doEquivalentExpressionBindingsMatch(leftValue, rightValue, scopes)) return false;
64870
- continue;
64871
- }
64872
- if (!Array.isArray(leftValue)) continue;
64873
- if (!Array.isArray(rightValue)) return false;
64874
- const leftNodes = leftValue.filter(isAstNode);
64875
- const rightNodes = rightValue.filter(isAstNode);
64876
- if (leftNodes.length !== rightNodes.length || leftNodes.some((leftNode, index) => !rightNodes[index] || !doEquivalentExpressionBindingsMatch(leftNode, rightNodes[index], scopes))) return false;
64304
+ if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
64305
+ if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
64306
+ const rightArguments = right.arguments ?? [];
64307
+ return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
64308
+ const rightArgument = rightArguments[index];
64309
+ return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
64310
+ });
64877
64311
  }
64878
64312
  return true;
64879
64313
  };
@@ -64887,57 +64321,6 @@ const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
64887
64321
  const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
64888
64322
  return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
64889
64323
  };
64890
- const isExpressionProvablyReflexive = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
64891
- const unwrappedExpression = stripParenExpression(expression);
64892
- if (matchBrowserPredicate(unwrappedExpression, context)) return true;
64893
- if (isNodeOfType(unwrappedExpression, "Literal")) return typeof unwrappedExpression.value !== "number" || !Number.isNaN(unwrappedExpression.value);
64894
- if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return true;
64895
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
64896
- const symbol = context.scopes.symbolFor(unwrappedExpression);
64897
- if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer) return false;
64898
- visitedSymbolIds.add(symbol.id);
64899
- const assignedValues = symbol.references.filter((reference) => reference.flag !== "read").map((reference) => getAssignedValue(reference.identifier));
64900
- const isReflexive = isExpressionProvablyReflexive(symbol.initializer, context, visitedSymbolIds) && assignedValues.every((assignedValue) => Boolean(assignedValue && isExpressionProvablyReflexive(assignedValue, context, visitedSymbolIds)));
64901
- visitedSymbolIds.delete(symbol.id);
64902
- return isReflexive;
64903
- }
64904
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isExpressionProvablyReflexive(unwrappedExpression.consequent, context, visitedSymbolIds) && isExpressionProvablyReflexive(unwrappedExpression.alternate, context, visitedSymbolIds);
64905
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && (unwrappedExpression.operator === "!" || unwrappedExpression.operator === "typeof" || unwrappedExpression.operator === "void")) return true;
64906
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) return unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==" || unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=";
64907
- if (isNodeOfType(unwrappedExpression, "ArrayExpression") || isNodeOfType(unwrappedExpression, "ObjectExpression") || isNodeOfType(unwrappedExpression, "FunctionExpression") || isNodeOfType(unwrappedExpression, "ArrowFunctionExpression") || isNodeOfType(unwrappedExpression, "TemplateLiteral")) return true;
64908
- if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
64909
- const callee = stripParenExpression(unwrappedExpression.callee);
64910
- return isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee);
64911
- };
64912
- const getReturnedObjectPropertyValues = (node, propertyName, scopes) => {
64913
- if (isNodeOfType(node, "ReturnStatement")) return node.argument ? getReturnedObjectPropertyValues(node.argument, propertyName, scopes) : [];
64914
- if (isNodeOfType(node, "ObjectExpression")) return node.properties.flatMap((property) => isNodeOfType(property, "Property") && property.kind === "init" && getResolvedStaticPropertyName(property, scopes) === propertyName ? [property.value] : []);
64915
- if (isNodeOfType(node, "IfStatement")) return [...getReturnedObjectPropertyValues(node.consequent, propertyName, scopes), ...node.alternate ? getReturnedObjectPropertyValues(node.alternate, propertyName, scopes) : []];
64916
- if (isNodeOfType(node, "TryStatement")) return [
64917
- ...getReturnedObjectPropertyValues(node.block, propertyName, scopes),
64918
- ...node.handler ? getReturnedObjectPropertyValues(node.handler.body, propertyName, scopes) : [],
64919
- ...node.finalizer ? getReturnedObjectPropertyValues(node.finalizer, propertyName, scopes) : []
64920
- ];
64921
- if (!isNodeOfType(node, "BlockStatement")) return [];
64922
- const propertyValues = [];
64923
- for (const childStatement of node.body) {
64924
- propertyValues.push(...getReturnedObjectPropertyValues(childStatement, propertyName, scopes));
64925
- if (statementAlwaysExits$1(childStatement)) break;
64926
- }
64927
- return propertyValues;
64928
- };
64929
- const matchHydrationFunctionPropertyResult = (functionNode, propertyName, context, state) => {
64930
- if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
64931
- state.visitedFunctionNodes.add(functionNode);
64932
- const propertyValues = getReturnedObjectPropertyValues(functionNode.body, propertyName, context.scopes);
64933
- let match = null;
64934
- for (const propertyValue of propertyValues) {
64935
- match = matchHydrationConditionInternal(propertyValue, context, state);
64936
- if (match) break;
64937
- }
64938
- state.visitedFunctionNodes.delete(functionNode);
64939
- return match;
64940
- };
64941
64324
  const matchHydrationConditionInternal = (expression, context, state) => {
64942
64325
  const unwrappedExpression = stripParenExpression(expression);
64943
64326
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64954,90 +64337,14 @@ const matchHydrationConditionInternal = (expression, context, state) => {
64954
64337
  state.visitedSymbolIds.delete(symbol.id);
64955
64338
  return match;
64956
64339
  }
64957
- if (symbol && (symbol.kind === "let" || symbol.kind === "var") && !state.visitedSymbolIds.has(symbol.id)) {
64958
- state.visitedSymbolIds.add(symbol.id);
64959
- if (symbol.initializer && symbol.references.every((reference) => reference.flag === "read")) {
64960
- const match = matchHydrationConditionInternal(symbol.initializer, context, state);
64961
- state.visitedSymbolIds.delete(symbol.id);
64962
- return match;
64963
- }
64964
- for (const reference of symbol.references) {
64965
- if (reference.flag === "read") continue;
64966
- if (!isNodeReachableWithinFunction(reference.identifier, context)) continue;
64967
- const enclosingFunction = findEnclosingFunction$1(reference.identifier);
64968
- if (!enclosingFunction) continue;
64969
- for (const guardingIfStatement of findGuardingIfStatements(reference.identifier, enclosingFunction)) {
64970
- if (doesGuardPreserveInitialSymbolValue(symbol, guardingIfStatement, context.scopes) || isWriteOverwrittenBefore(symbol, reference.identifier, guardingIfStatement, unwrappedExpression, context)) continue;
64971
- const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
64972
- if (match) {
64973
- state.visitedSymbolIds.delete(symbol.id);
64974
- return match;
64975
- }
64976
- }
64977
- }
64978
- const readingFunction = findEnclosingFunction$1(unwrappedExpression);
64979
- for (const reference of symbol.references) {
64980
- if (reference.flag === "read") continue;
64981
- const writingFunction = findEnclosingFunction$1(reference.identifier);
64982
- if (!readingFunction || !isFunctionLike$1(writingFunction) || writingFunction === readingFunction || writingFunction.async || writingFunction.params.length > 0 || isNodeOfType(writingFunction, "FunctionDeclaration") && writingFunction.generator || isNodeOfType(writingFunction, "FunctionExpression") && writingFunction.generator) continue;
64983
- const assignedValue = getAssignedValue(reference.identifier);
64984
- if (symbol.initializer && assignedValue && areExpressionsStructurallyEqual(symbol.initializer, assignedValue) && doEquivalentExpressionBindingsMatch(symbol.initializer, assignedValue, context.scopes)) continue;
64985
- const functionBinding = getDirectFunctionBindingIdentifier(writingFunction);
64986
- if (!isNodeOfType(functionBinding, "Identifier")) continue;
64987
- const functionSymbol = context.scopes.symbolFor(functionBinding);
64988
- if (!functionSymbol) continue;
64989
- for (const functionReference of functionSymbol.references) {
64990
- const callExpression = functionReference.identifier.parent;
64991
- if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== functionReference.identifier || (callExpression.arguments ?? []).length > 0 || findEnclosingFunction$1(callExpression) !== readingFunction || !isNodeReachableWithinFunction(callExpression, context) || getNodeStartIndex(callExpression) >= getNodeStartIndex(unwrappedExpression)) continue;
64992
- for (const guardingIfStatement of findGuardingIfStatements(callExpression, readingFunction)) {
64993
- if (isWriteOverwrittenBefore(symbol, callExpression, guardingIfStatement, unwrappedExpression, context)) continue;
64994
- const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
64995
- if (match) {
64996
- state.visitedSymbolIds.delete(symbol.id);
64997
- return match;
64998
- }
64999
- }
65000
- }
65001
- }
65002
- state.visitedSymbolIds.delete(symbol.id);
65003
- }
65004
64340
  if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
65005
64341
  state.visitedSymbolIds.add(symbol.id);
65006
64342
  const match = matchHydrationConditionInternal(symbol.initializer, context, state);
65007
64343
  state.visitedSymbolIds.delete(symbol.id);
65008
64344
  return match;
65009
64345
  }
65010
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) {
65011
- const propertyName = getResolvedStaticPropertyName(unwrappedExpression, context.scopes, {
65012
- allowConstNumericLiteral: true,
65013
- stringifyNonStringLiterals: true
65014
- });
65015
- const object = stripParenExpression(unwrappedExpression.object);
65016
- if (propertyName === null || !isNodeOfType(object, "CallExpression")) return null;
65017
- const callArguments = object.arguments ?? [];
65018
- if (isReactApiCall(object, "useMemo", context.scopes, {
65019
- allowGlobalReactNamespace: true,
65020
- resolveNamedAliases: true
65021
- })) {
65022
- const callbackArgument = callArguments[0];
65023
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
65024
- const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
65025
- return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionPropertyResult(callbackFunction, propertyName, context, state) : null;
65026
- }
65027
- const helperFunction = resolveExactLocalFunction(object.callee, context.scopes);
65028
- return isFunctionLike$1(helperFunction) && helperFunction.params.length === 0 && callArguments.length === 0 ? matchHydrationFunctionPropertyResult(helperFunction, propertyName, context, state) : null;
65029
- }
65030
64346
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
65031
64347
  const callArguments = unwrappedExpression.arguments ?? [];
65032
- if (isReactApiCall(unwrappedExpression, "useState", context.scopes, {
65033
- allowGlobalReactNamespace: true,
65034
- resolveNamedAliases: true
65035
- })) {
65036
- const initialState = callArguments[0];
65037
- if (!initialState || isNodeOfType(initialState, "SpreadElement")) return null;
65038
- const lazyInitializer = resolveExactLocalFunction(initialState, context.scopes);
65039
- return isFunctionLike$1(lazyInitializer) && lazyInitializer.params.length === 0 ? matchHydrationFunctionResult(lazyInitializer, context, state) : matchHydrationConditionInternal(initialState, context, state);
65040
- }
65041
64348
  if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
65042
64349
  allowGlobalReactNamespace: true,
65043
64350
  resolveNamedAliases: true
@@ -65065,22 +64372,6 @@ const matchHydrationConditionInternal = (expression, context, state) => {
65065
64372
  });
65066
64373
  }
65067
64374
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
65068
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
65069
- const staticTestResult = readInitialStateBoolean(unwrappedExpression.test, context.scopes);
65070
- if (staticTestResult !== null) return matchHydrationConditionInternal(staticTestResult ? unwrappedExpression.consequent : unwrappedExpression.alternate, context, state);
65071
- return matchHydrationConditionInternal(unwrappedExpression.test, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.consequent, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.alternate, context, state);
65072
- }
65073
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
65074
- if (unwrappedExpression.operator !== "===" && unwrappedExpression.operator !== "!==" && unwrappedExpression.operator !== "==" && unwrappedExpression.operator !== "!=") return null;
65075
- const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
65076
- const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
65077
- const nestedMatch = leftMatch ?? rightMatch;
65078
- if (!nestedMatch) return null;
65079
- const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
65080
- const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
65081
- if (clientResult !== null && serverResult !== null) return clientResult !== serverResult ? nestedMatch : null;
65082
- return leftMatch && rightMatch && areExpressionsStructurallyEqual(unwrappedExpression.left, unwrappedExpression.right) && doEquivalentExpressionBindingsMatch(unwrappedExpression.left, unwrappedExpression.right, context.scopes) && isExpressionProvablyReflexive(unwrappedExpression.left, context) ? null : nestedMatch;
65083
- }
65084
64375
  if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
65085
64376
  const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
65086
64377
  const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
@@ -65097,13 +64388,8 @@ const matchHydrationReturningStatement = (statement, context, state) => {
65097
64388
  const consequentValues = getReturnedValues(statement.consequent);
65098
64389
  const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
65099
64390
  if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
65100
- if (conditionMatch) {
65101
- const followingReturnedValues = findFollowingReturnedValues(statement);
65102
- if ([...new Set([...collectWrittenSymbols(statement.consequent, context.scopes), ...statement.alternate ? collectWrittenSymbols(statement.alternate, context.scopes) : []])].some((symbol) => !doesGuardPreserveInitialSymbolValue(symbol, statement, context.scopes) && followingReturnedValues.some((value) => doesNodeReadSymbol(value, symbol)))) return conditionMatch;
65103
- }
65104
64391
  return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
65105
64392
  }
65106
- if (isNodeOfType(statement, "TryStatement")) return matchHydrationReturningStatement(statement.block, context, state) ?? (statement.handler ? matchHydrationReturningStatement(statement.handler.body, context, state) : null) ?? (statement.finalizer ? matchHydrationReturningStatement(statement.finalizer, context, state) : null);
65107
64393
  if (!isNodeOfType(statement, "BlockStatement")) return null;
65108
64394
  for (const childStatement of statement.body) {
65109
64395
  const match = matchHydrationReturningStatement(childStatement, context, state);
@@ -65124,62 +64410,47 @@ const matchHydrationCondition = (expression, context) => matchHydrationCondition
65124
64410
  visitedFunctionNodes: /* @__PURE__ */ new Set(),
65125
64411
  visitedSymbolIds: /* @__PURE__ */ new Set()
65126
64412
  });
65127
- const areNodeArraysEquivalent = (leftNodes, rightNodes, scopes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index], scopes));
65128
- const areRenderedBranchesEquivalent = (leftNode, rightNode, scopes) => {
64413
+ const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
64414
+ const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
65129
64415
  if (!leftNode || !rightNode) return leftNode === rightNode;
65130
64416
  const left = stripParenExpression(leftNode);
65131
64417
  const right = stripParenExpression(rightNode);
65132
- if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64418
+ if (areExpressionsStructurallyEqual(left, right)) return true;
65133
64419
  if (left.type !== right.type) return false;
65134
64420
  if (isNodeOfType(left, "JSXText") && isNodeOfType(right, "JSXText")) return left.value === right.value;
65135
64421
  if (isNodeOfType(left, "JSXExpressionContainer") && isNodeOfType(right, "JSXExpressionContainer")) {
65136
64422
  if (!isAstNode(left.expression) || !isAstNode(right.expression)) return left.expression.type === right.expression.type;
65137
- return areRenderedBranchesEquivalent(left.expression, right.expression, scopes);
64423
+ return areRenderedBranchesEquivalent(left.expression, right.expression);
65138
64424
  }
65139
64425
  if (isNodeOfType(left, "JSXElement") && isNodeOfType(right, "JSXElement")) {
65140
64426
  if (flattenJsxName$1(left.openingElement.name) !== flattenJsxName$1(right.openingElement.name)) return false;
65141
- if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes, scopes)) return false;
65142
- return areNodeArraysEquivalent(left.children, right.children, scopes);
64427
+ if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes)) return false;
64428
+ return areNodeArraysEquivalent(left.children, right.children);
65143
64429
  }
65144
- if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children, scopes);
64430
+ if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children);
65145
64431
  if (isNodeOfType(left, "JSXAttribute") && isNodeOfType(right, "JSXAttribute")) {
65146
64432
  if (flattenJsxName$1(left.name) !== flattenJsxName$1(right.name)) return false;
65147
- return areRenderedBranchesEquivalent(left.value, right.value, scopes);
64433
+ return areRenderedBranchesEquivalent(left.value, right.value);
65148
64434
  }
65149
- if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument, scopes);
64435
+ if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument);
65150
64436
  if (isNodeOfType(left, "TemplateLiteral") && isNodeOfType(right, "TemplateLiteral")) {
65151
64437
  if (left.quasis.length !== right.quasis.length) return false;
65152
64438
  if (!left.quasis.every((quasi, index) => quasi.value.cooked === right.quasis[index]?.value.cooked && quasi.value.raw === right.quasis[index]?.value.raw)) return false;
65153
- return areNodeArraysEquivalent(left.expressions, right.expressions, scopes);
64439
+ return areNodeArraysEquivalent(left.expressions, right.expressions);
65154
64440
  }
65155
64441
  return false;
65156
64442
  };
65157
- const isProvenReactCreateElementCall = (node, scopes) => {
65158
- if (isReactApiCall(node, "createElement", scopes, {
65159
- allowGlobalReactNamespace: true,
65160
- resolveNamedAliases: true
65161
- })) return true;
65162
- if (!isNodeOfType(node, "CallExpression")) return false;
65163
- const callee = stripParenExpression(node.callee);
65164
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createElement") return false;
65165
- const receiver = stripParenExpression(callee.object);
65166
- const namespaceIdentifier = isNodeOfType(receiver, "MemberExpression") && !receiver.computed && isNodeOfType(receiver.property, "Identifier") && receiver.property.name === "default" ? stripParenExpression(receiver.object) : receiver;
65167
- if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
65168
- const namespaceSymbol = scopes.symbolFor(namespaceIdentifier);
65169
- return Boolean(namespaceSymbol?.initializer && namespaceSymbol.references.every((reference) => reference.flag === "read") && containsExplicitReactRuntimeReference(namespaceSymbol.initializer, scopes));
65170
- };
65171
- const isRenderedValue = (node, scopes) => {
64443
+ const isRenderedValue = (node) => {
65172
64444
  const unwrappedNode = stripParenExpression(node);
65173
64445
  if (isNodeOfType(unwrappedNode, "Literal")) return unwrappedNode.value !== null && unwrappedNode.value !== true && unwrappedNode.value !== false && unwrappedNode.value !== "";
65174
64446
  if (isNodeOfType(unwrappedNode, "TemplateLiteral")) return unwrappedNode.expressions.length > 0 || unwrappedNode.quasis[0]?.value.cooked !== "";
65175
- if (isNodeOfType(unwrappedNode, "CallExpression")) return isProvenReactCreateElementCall(unwrappedNode, scopes);
65176
64447
  return isNodeOfType(unwrappedNode, "JSXElement") || isNodeOfType(unwrappedNode, "JSXFragment");
65177
64448
  };
65178
- const findRenderedValueInAndBranch = (node, scopes) => {
64449
+ const findRenderedValueInAndBranch = (node) => {
65179
64450
  const unwrappedNode = stripParenExpression(node);
65180
- if (isPotentiallyRenderedValue(unwrappedNode, scopes)) return unwrappedNode;
64451
+ if (isRenderedValue(unwrappedNode)) return unwrappedNode;
65181
64452
  if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "&&") return null;
65182
- return findRenderedValueInAndBranch(unwrappedNode.right, scopes);
64453
+ return findRenderedValueInAndBranch(unwrappedNode.right);
65183
64454
  };
65184
64455
  const findEnclosingJsxAttribute = (node) => {
65185
64456
  let currentNode = node.parent;
@@ -65212,11 +64483,6 @@ const getReturnedValues = (statement) => {
65212
64483
  if (!statement) return [];
65213
64484
  if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? [statement.argument] : [];
65214
64485
  if (isNodeOfType(statement, "IfStatement")) return [...getReturnedValues(statement.consequent), ...getReturnedValues(statement.alternate)];
65215
- if (isNodeOfType(statement, "TryStatement")) return [
65216
- ...getReturnedValues(statement.block),
65217
- ...getReturnedValues(statement.handler?.body),
65218
- ...getReturnedValues(statement.finalizer)
65219
- ];
65220
64486
  if (!isNodeOfType(statement, "BlockStatement")) return [];
65221
64487
  const returnedValues = [];
65222
64488
  for (const childStatement of statement.body) {
@@ -65225,127 +64491,6 @@ const getReturnedValues = (statement) => {
65225
64491
  }
65226
64492
  return returnedValues;
65227
64493
  };
65228
- const isPotentiallyRenderedValueInternal = (node, scopes, visitedFunctionNodes) => {
65229
- const unwrappedNode = stripParenExpression(node);
65230
- if (isRenderedValue(unwrappedNode, scopes)) return true;
65231
- if (isNodeOfType(unwrappedNode, "ConditionalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.consequent, scopes, visitedFunctionNodes) && isPotentiallyRenderedValueInternal(unwrappedNode.alternate, scopes, visitedFunctionNodes);
65232
- if (isNodeOfType(unwrappedNode, "LogicalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.right, scopes, visitedFunctionNodes);
65233
- if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
65234
- const calledFunction = resolveExactLocalFunction(unwrappedNode.callee, scopes);
65235
- if (!isFunctionLike$1(calledFunction) || visitedFunctionNodes.has(calledFunction)) return false;
65236
- visitedFunctionNodes.add(calledFunction);
65237
- const returnedValues = isNodeOfType(calledFunction.body, "BlockStatement") ? getReturnedValues(calledFunction.body) : [calledFunction.body];
65238
- const isPotentiallyRendered = returnedValues.length > 0 && returnedValues.every((returnedValue) => isPotentiallyRenderedValueInternal(returnedValue, scopes, visitedFunctionNodes));
65239
- visitedFunctionNodes.delete(calledFunction);
65240
- return isPotentiallyRendered;
65241
- };
65242
- const isPotentiallyRenderedValue = (node, scopes) => isPotentiallyRenderedValueInternal(node, scopes, /* @__PURE__ */ new Set());
65243
- const findUseStateBindingSymbol = (node, componentOrHookNode, scopes) => {
65244
- let currentNode = node.parent;
65245
- while (currentNode && currentNode !== componentOrHookNode) {
65246
- if (isNodeOfType(currentNode, "CallExpression") && isReactApiCall(currentNode, "useState", scopes, {
65247
- allowGlobalReactNamespace: true,
65248
- resolveNamedAliases: true
65249
- }) && isNodeOfType(currentNode.parent, "VariableDeclarator") && isNodeOfType(currentNode.parent.id, "ArrayPattern")) {
65250
- const stateBinding = currentNode.parent.id.elements?.[0];
65251
- return isNodeOfType(stateBinding, "Identifier") ? scopes.symbolFor(stateBinding) : null;
65252
- }
65253
- if (isFunctionLike$1(currentNode)) return null;
65254
- currentNode = currentNode.parent;
65255
- }
65256
- return null;
65257
- };
65258
- const doesReferenceControlStructuralRenderedValue = (referenceIdentifier) => {
65259
- let currentNode = referenceIdentifier;
65260
- let parentNode = currentNode.parent;
65261
- while (parentNode) {
65262
- if (isNodeOfType(parentNode, "ConditionalExpression") && parentNode.test === currentNode && (isStructuralRenderedValue(parentNode.consequent) || isStructuralRenderedValue(parentNode.alternate))) return true;
65263
- if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.left === currentNode && isStructuralRenderedValue(parentNode.right)) return true;
65264
- if (isNodeOfType(parentNode, "JSXExpressionContainer") || isFunctionLike$1(parentNode)) return false;
65265
- currentNode = parentNode;
65266
- parentNode = currentNode.parent;
65267
- }
65268
- return false;
65269
- };
65270
- const isRenderedHydrationConsumer = (node, producerHookNode, scopes) => {
65271
- const renderingComponent = findRenderPhaseComponentOrHook(node, scopes);
65272
- return Boolean(renderingComponent && renderingComponent !== producerHookNode && isInRenderedOutput(node, renderingComponent, scopes) && !isGatedByFalsyInitialState(node, scopes) && !isAfterClientOnlyEarlyReturn(node, renderingComponent, scopes) && (!hasSuppressHydrationWarningAttribute(findEnclosingJsxOpeningElement(node)) || doesReferenceControlStructuralRenderedValue(node)));
65273
- };
65274
- const doesConsumerExpressionReachRenderedOutput = (node, producerHookNode, scopes, visitedSymbolIds) => {
65275
- if (isRenderedHydrationConsumer(node, producerHookNode, scopes)) return true;
65276
- const parentNode = node.parent;
65277
- if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== node || !isNodeOfType(parentNode.id, "Identifier")) return false;
65278
- const aliasSymbol = scopes.symbolFor(parentNode.id);
65279
- if (!aliasSymbol || visitedSymbolIds.has(aliasSymbol.id)) return false;
65280
- visitedSymbolIds.add(aliasSymbol.id);
65281
- const doesReachRenderedOutput = aliasSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, visitedSymbolIds));
65282
- visitedSymbolIds.delete(aliasSymbol.id);
65283
- return doesReachRenderedOutput;
65284
- };
65285
- const doesConsumerBindingReachRenderedOutput = (bindingIdentifier, producerHookNode, scopes) => {
65286
- if (!isNodeOfType(bindingIdentifier, "Identifier")) return false;
65287
- const consumerSymbol = scopes.symbolFor(bindingIdentifier);
65288
- if (!consumerSymbol) return false;
65289
- return consumerSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, new Set([consumerSymbol.id])));
65290
- };
65291
- const getReturnedStatePaths = (returnedValue, stateSymbol, scopes) => {
65292
- const unwrappedValue = stripParenExpression(returnedValue);
65293
- if (isNodeOfType(unwrappedValue, "ObjectExpression")) return unwrappedValue.properties.flatMap((property) => {
65294
- if (!isNodeOfType(property, "Property") || property.kind !== "init" || !doesNodeReadSymbol(property.value, stateSymbol)) return [];
65295
- const propertyName = getResolvedStaticPropertyName(property, scopes);
65296
- return propertyName === null ? [] : [{
65297
- kind: "property",
65298
- key: propertyName
65299
- }];
65300
- });
65301
- if (isNodeOfType(unwrappedValue, "ArrayExpression")) return (unwrappedValue.elements ?? []).flatMap((element, index) => element && isAstNode(element) && doesNodeReadSymbol(element, stateSymbol) ? [{
65302
- kind: "index",
65303
- key: String(index)
65304
- }] : []);
65305
- return doesNodeReadSymbol(unwrappedValue, stateSymbol) ? [{
65306
- kind: "direct",
65307
- key: null
65308
- }] : [];
65309
- };
65310
- const doesCallResultPathReachRenderedOutput = (callExpression, returnedStatePath, producerHookNode, scopes) => {
65311
- const callParent = callExpression.parent;
65312
- if (returnedStatePath.kind === "direct") return doesConsumerExpressionReachRenderedOutput(callExpression, producerHookNode, scopes, /* @__PURE__ */ new Set());
65313
- if (isNodeOfType(callParent, "MemberExpression") && callParent.object === callExpression && getResolvedStaticPropertyName(callParent, scopes, {
65314
- allowConstNumericLiteral: true,
65315
- stringifyNonStringLiterals: true
65316
- }) === returnedStatePath.key) return doesConsumerExpressionReachRenderedOutput(callParent, producerHookNode, scopes, /* @__PURE__ */ new Set());
65317
- if (!isNodeOfType(callParent, "VariableDeclarator") || callParent.init !== callExpression) return false;
65318
- if (returnedStatePath.kind === "property" && isNodeOfType(callParent.id, "ObjectPattern")) return callParent.id.properties.some((property) => isNodeOfType(property, "Property") && getResolvedStaticPropertyName(property, scopes) === returnedStatePath.key && doesConsumerBindingReachRenderedOutput(property.value, producerHookNode, scopes));
65319
- if (returnedStatePath.kind === "index" && isNodeOfType(callParent.id, "ArrayPattern")) {
65320
- const element = callParent.id.elements?.[Number(returnedStatePath.key)];
65321
- return Boolean(element && doesConsumerBindingReachRenderedOutput(element, producerHookNode, scopes));
65322
- }
65323
- if (!isNodeOfType(callParent.id, "Identifier")) return false;
65324
- const resultSymbol = scopes.symbolFor(callParent.id);
65325
- if (!resultSymbol) return false;
65326
- return resultSymbol.references.some((reference) => {
65327
- const memberExpression = reference.identifier.parent;
65328
- return Boolean(isNodeOfType(memberExpression, "MemberExpression") && memberExpression.object === reference.identifier && getResolvedStaticPropertyName(memberExpression, scopes, {
65329
- allowConstNumericLiteral: true,
65330
- stringifyNonStringLiterals: true
65331
- }) === returnedStatePath.key && doesConsumerExpressionReachRenderedOutput(memberExpression, producerHookNode, scopes, new Set([resultSymbol.id])));
65332
- });
65333
- };
65334
- const isReturnedUseStateInitializerRendered = (node, componentOrHookNode, scopes) => {
65335
- if (!isFunctionLike$1(componentOrHookNode)) return false;
65336
- const stateSymbol = findUseStateBindingSymbol(node, componentOrHookNode, scopes);
65337
- if (!stateSymbol) return false;
65338
- const returnedStatePaths = (isNodeOfType(componentOrHookNode.body, "BlockStatement") ? getReturnedValues(componentOrHookNode.body) : [componentOrHookNode.body]).flatMap((returnedValue) => getReturnedStatePaths(returnedValue, stateSymbol, scopes));
65339
- if (returnedStatePaths.length === 0) return false;
65340
- const functionBinding = getDirectFunctionBindingIdentifier(componentOrHookNode);
65341
- if (!isNodeOfType(functionBinding, "Identifier")) return false;
65342
- const functionSymbol = scopes.symbolFor(functionBinding);
65343
- if (!functionSymbol) return false;
65344
- return functionSymbol.references.some((functionReference) => {
65345
- const callExpression = functionReference.identifier.parent;
65346
- return Boolean(isNodeOfType(callExpression, "CallExpression") && callExpression.callee === functionReference.identifier && returnedStatePaths.some((returnedStatePath) => doesCallResultPathReachRenderedOutput(callExpression, returnedStatePath, componentOrHookNode, scopes)));
65347
- });
65348
- };
65349
64494
  const findFollowingReturnedValues = (ifStatement) => {
65350
64495
  const parentNode = ifStatement.parent;
65351
64496
  if (!isNodeOfType(parentNode, "BlockStatement")) return [];
@@ -65358,24 +64503,24 @@ const findFollowingReturnedValues = (ifStatement) => {
65358
64503
  }
65359
64504
  return returnedValues;
65360
64505
  };
65361
- const areConditionExpressionsEquivalent = (leftExpression, rightExpression, scopes) => {
64506
+ const areConditionExpressionsEquivalent = (leftExpression, rightExpression) => {
65362
64507
  const left = stripParenExpression(leftExpression);
65363
64508
  const right = stripParenExpression(rightExpression);
65364
- if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64509
+ if (areExpressionsStructurallyEqual(left, right)) return true;
65365
64510
  if (left.type !== right.type) return false;
65366
- if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument, scopes);
65367
- if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
65368
- if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
64511
+ if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument);
64512
+ if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
64513
+ if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
65369
64514
  return false;
65370
64515
  };
65371
- const areReturnTreesEquivalent = (leftStatement, rightStatement, scopes) => {
64516
+ const areReturnTreesEquivalent = (leftStatement, rightStatement) => {
65372
64517
  if (!leftStatement || !rightStatement) return leftStatement === rightStatement;
65373
- if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument, scopes);
65374
- if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test, scopes) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent, scopes) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate, scopes);
64518
+ if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument);
64519
+ if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate);
65375
64520
  if (!isNodeOfType(leftStatement, "BlockStatement") || !isNodeOfType(rightStatement, "BlockStatement")) return false;
65376
64521
  const leftReturningStatements = leftStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
65377
64522
  const rightReturningStatements = rightStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
65378
- return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index], scopes));
64523
+ return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]));
65379
64524
  };
65380
64525
  const isStructuralRenderedValue = (node) => {
65381
64526
  if (!node) return false;
@@ -65399,22 +64544,19 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
65399
64544
  if (isTestlikeFilename(context.filename)) return {};
65400
64545
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
65401
64546
  let fileHasUseClientDirective = false;
65402
- let fileHasExplicitReactRuntimeReference = false;
65403
64547
  let fileIsEmailTemplate = false;
65404
64548
  const reportedNodes = /* @__PURE__ */ new Set();
65405
- const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext, hasProvenRenderedConsumer = false) => {
64549
+ const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext) => {
65406
64550
  const conditionMatch = matchHydrationCondition(conditionNode, context);
65407
64551
  if (!conditionMatch) return;
65408
64552
  const { predicateMatch, predicateNode } = conditionMatch;
65409
64553
  if (reportedNodes.has(predicateNode)) return;
65410
- if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch, context.scopes)) return;
65411
- const enclosingFunction = findEnclosingFunction$1(conditionNode);
65412
- const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
64554
+ if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
64555
+ const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
65413
64556
  if (!componentOrHookNode) return;
65414
- const hasRenderedLocalFunctionConsumer = Boolean(enclosingFunction && enclosingFunction !== componentOrHookNode && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) === componentOrHookNode);
65415
- if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective) && !fileHasExplicitReactRuntimeReference) return;
65416
- if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes) && !hasRenderedLocalFunctionConsumer) return;
65417
- if (!hasProvenRenderedConsumer && !(requiresRenderedContext ? isPotentiallyRenderedValue(leftBranch, context.scopes) : isRenderedValue(leftBranch, context.scopes)) && (!rightBranch || !(requiresRenderedContext ? isPotentiallyRenderedValue(rightBranch, context.scopes) : isRenderedValue(rightBranch, context.scopes)))) {
64557
+ if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
64558
+ if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
64559
+ if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
65418
64560
  const attribute = findEnclosingJsxAttribute(conditionNode);
65419
64561
  if (!attribute || isEventHandlerAttribute(attribute)) return;
65420
64562
  }
@@ -65433,31 +64575,28 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
65433
64575
  return {
65434
64576
  Program(node) {
65435
64577
  fileHasUseClientDirective = hasDirective(node, "use client");
65436
- fileHasExplicitReactRuntimeReference = containsExplicitReactRuntimeReference(node, context.scopes);
65437
64578
  fileIsEmailTemplate = hasEmailTemplateImport(node);
65438
64579
  },
65439
64580
  ConditionalExpression(node) {
65440
64581
  reportHydrationBranch(node.test, node.consequent, node.alternate, true);
65441
- const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
65442
- if (componentOrHookNode && isReturnedUseStateInitializerRendered(node, componentOrHookNode, context.scopes)) reportHydrationBranch(node.test, node.consequent, node.alternate, false, true);
65443
64582
  },
65444
64583
  LogicalExpression(node) {
65445
64584
  if (node.operator !== "&&" && node.operator !== "||") return;
65446
- const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right, context.scopes) : isPotentiallyRenderedValue(node.right, context.scopes) ? node.right : null;
64585
+ const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right) : isRenderedValue(node.right) ? node.right : null;
65447
64586
  if (!renderedValue) return;
65448
64587
  reportHydrationBranch(node, renderedValue, null, true);
65449
64588
  },
65450
64589
  IfStatement(node) {
65451
- if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate, context.scopes)) return;
64590
+ if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate)) return;
65452
64591
  const consequentValues = getReturnedValues(node.consequent);
65453
64592
  const alternateValues = node.alternate ? getReturnedValues(node.alternate) : findFollowingReturnedValues(node);
65454
64593
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
65455
- const enclosingFunction = findEnclosingFunction$1(node);
65456
- const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
64594
+ const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
65457
64595
  if (!componentOrHookNode) return;
65458
- if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes) && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) !== componentOrHookNode)) return;
64596
+ const enclosingFunction = findEnclosingFunction$1(node);
64597
+ if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
65459
64598
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
65460
- if (!isRenderedValue(consequentValue, context.scopes) && !isRenderedValue(alternateValue, context.scopes)) continue;
64599
+ if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
65461
64600
  reportHydrationBranch(node.test, consequentValue, alternateValue, false);
65462
64601
  }
65463
64602
  }
@@ -68623,77 +67762,6 @@ const isCancellationGuardTest = (test) => {
68623
67762
  });
68624
67763
  return matches;
68625
67764
  };
68626
- const getReactRefCurrent = (expression, context) => {
68627
- const stripped = stripParenExpression(expression);
68628
- if (!isNodeOfType(stripped, "MemberExpression") || getStaticPropertyName(stripped) !== "current") return null;
68629
- const receiver = stripParenExpression(stripped.object);
68630
- if (!isNodeOfType(receiver, "Identifier")) return null;
68631
- const binding = findVariableInitializer(receiver, receiver.name);
68632
- const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
68633
- return initializer && isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, USE_REF_HOOK_NAMES$1, context.scopes, {
68634
- allowGlobalReactNamespace: true,
68635
- allowUnboundBareCalls: true
68636
- }) ? stripped : null;
68637
- };
68638
- const getStableOwnershipToken = (expression, context) => {
68639
- const stripped = stripParenExpression(expression);
68640
- if (!isNodeOfType(stripped, "Identifier")) return null;
68641
- const symbol = context.scopes.symbolFor(stripped);
68642
- const initializer = symbol?.initializer ? stripParenExpression(symbol.initializer) : null;
68643
- const isStableAsyncIdentity = Boolean(initializer && isNodeOfType(initializer, "ObjectExpression")) || Boolean(initializer && getReactRefCurrent(initializer, context)) || Boolean(initializer && isNodeOfType(initializer, "UpdateExpression") && initializer.operator === "++" && getReactRefCurrent(initializer.argument, context));
68644
- return symbol && symbol.kind === "const" && symbol.references.every((reference) => reference.flag === "read") && isStableAsyncIdentity ? stripped : null;
68645
- };
68646
- const getAsyncOwnershipComparison = (test, context) => {
68647
- const stripped = stripParenExpression(test);
68648
- if (!isNodeOfType(stripped, "BinaryExpression")) return null;
68649
- const leftRef = getReactRefCurrent(stripped.left, context);
68650
- const rightRef = getReactRefCurrent(stripped.right, context);
68651
- const leftToken = getStableOwnershipToken(stripped.left, context);
68652
- const rightToken = getStableOwnershipToken(stripped.right, context);
68653
- if (stripped.operator === "===" || stripped.operator === "==") {
68654
- if (leftRef && rightToken) return {
68655
- refCurrent: leftRef,
68656
- token: rightToken,
68657
- mode: "owns",
68658
- isOrdered: false
68659
- };
68660
- if (rightRef && leftToken) return {
68661
- refCurrent: rightRef,
68662
- token: leftToken,
68663
- mode: "owns",
68664
- isOrdered: false
68665
- };
68666
- return null;
68667
- }
68668
- if (stripped.operator === "!==" || stripped.operator === "!=") {
68669
- if (leftRef && rightToken) return {
68670
- refCurrent: leftRef,
68671
- token: rightToken,
68672
- mode: "lost",
68673
- isOrdered: false
68674
- };
68675
- if (rightRef && leftToken) return {
68676
- refCurrent: rightRef,
68677
- token: leftToken,
68678
- mode: "lost",
68679
- isOrdered: false
68680
- };
68681
- return null;
68682
- }
68683
- if (stripped.operator === "<=" && leftRef && rightToken) return {
68684
- refCurrent: leftRef,
68685
- token: rightToken,
68686
- mode: "owns",
68687
- isOrdered: true
68688
- };
68689
- if (stripped.operator === ">=" && rightRef && leftToken) return {
68690
- refCurrent: rightRef,
68691
- token: leftToken,
68692
- mode: "owns",
68693
- isOrdered: true
68694
- };
68695
- return null;
68696
- };
68697
67765
  const dedupeCatchPathStates = (states) => {
68698
67766
  const statesByKey = /* @__PURE__ */ new Map();
68699
67767
  for (const state of states) statesByKey.set(`${Number(state.isCleared)}:${Number(state.isCancellationPath)}`, state);
@@ -69026,221 +68094,7 @@ const isInsideTryFinalizer = (node, tryStatement) => {
69026
68094
  }
69027
68095
  return false;
69028
68096
  };
69029
- const getDirectBlockEntry = (node, functionNode) => {
69030
- let entry = node;
69031
- let cursor = node.parent;
69032
- while (cursor && cursor !== functionNode) {
69033
- if (isNodeOfType(cursor, "BlockStatement")) return {
69034
- block: cursor,
69035
- entry
69036
- };
69037
- entry = cursor;
69038
- cursor = cursor.parent ?? null;
69039
- }
69040
- return null;
69041
- };
69042
- const claimPrecedesTruthySet = (claimNode, truthySet, firstRiskyAwait, functionNode, context) => {
69043
- const claimStart = getNodeStart$1(claimNode);
69044
- if (claimStart === null || claimStart >= firstRiskyAwait.start || truthySet.start >= firstRiskyAwait.start) return false;
69045
- const claimEntry = getDirectBlockEntry(claimNode, functionNode);
69046
- const truthyEntry = getDirectBlockEntry(truthySet.node, functionNode);
69047
- if (!claimEntry || !truthyEntry || claimEntry.block !== truthyEntry.block) return false;
69048
- let claimCursor = claimNode.parent;
69049
- while (claimCursor && claimCursor !== claimEntry.block) {
69050
- if (isNodeOfType(claimCursor, "IfStatement") || isNodeOfType(claimCursor, "SwitchCase") || isNodeOfType(claimCursor, "ConditionalExpression") || isNodeOfType(claimCursor, "LogicalExpression") || isNodeOfType(claimCursor, "ForStatement") || isNodeOfType(claimCursor, "ForInStatement") || isNodeOfType(claimCursor, "ForOfStatement") || isNodeOfType(claimCursor, "WhileStatement") || isNodeOfType(claimCursor, "DoWhileStatement")) return false;
69051
- claimCursor = claimCursor.parent ?? null;
69052
- }
69053
- const claimIndex = claimEntry.block.body.findIndex((statement) => statement === claimEntry.entry);
69054
- const truthyIndex = claimEntry.block.body.findIndex((statement) => statement === truthyEntry.entry);
69055
- if (claimIndex === -1 || truthyIndex === -1 || claimIndex >= truthyIndex) return false;
69056
- return claimEntry.block.body.slice(claimIndex + 1, truthyIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context));
69057
- };
69058
- const getOwningFunction = (functionNode) => {
69059
- let ownerFunction = functionNode;
69060
- let cursor = functionNode.parent;
69061
- while (cursor) {
69062
- if (isFunctionLike$1(cursor)) ownerFunction = cursor;
69063
- cursor = cursor.parent ?? null;
69064
- }
69065
- return ownerFunction;
69066
- };
69067
- const isEffectInvalidationPairedWithReset = (writeNode, truthySets, context) => {
69068
- const truthyCall = truthySets[0]?.node;
69069
- if (!truthyCall || !isNodeOfType(truthyCall, "CallExpression")) return false;
69070
- const setter = getSetterBooleanValue(truthyCall, context);
69071
- if (!setter) return false;
69072
- let effectCallback = writeNode.parent;
69073
- while (effectCallback && !isFunctionLike$1(effectCallback)) effectCallback = effectCallback.parent ?? null;
69074
- if (!effectCallback || !isEffectCallback(effectCallback, context)) return false;
69075
- if (!isUnconditionallyExecutedWithinFunction(writeNode, effectCallback, context)) return false;
69076
- const writeEntry = getDirectBlockEntry(writeNode, effectCallback);
69077
- if (!writeEntry) return false;
69078
- let isPaired = false;
69079
- walkOwnFunctionScope(effectCallback, (candidate) => {
69080
- if (isPaired || !isNodeOfType(candidate, "CallExpression")) return;
69081
- const candidateSetter = getSetterBooleanValue(candidate, context);
69082
- if (candidateSetter?.setterKey !== setter.setterKey || candidateSetter.value || !isUnconditionallyExecutedWithinFunction(candidate, effectCallback, context)) return;
69083
- const resetEntry = getDirectBlockEntry(candidate, effectCallback);
69084
- if (!resetEntry || resetEntry.block !== writeEntry.block) return;
69085
- const writeIndex = writeEntry.block.body.findIndex((statement) => statement === writeEntry.entry);
69086
- const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
69087
- if (writeIndex === -1 || resetIndex === -1) return;
69088
- if (resetIndex <= writeIndex) {
69089
- isPaired = true;
69090
- return false;
69091
- }
69092
- isPaired = writeEntry.block.body.slice(writeIndex + 1, resetIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, effectCallback, context));
69093
- return isPaired ? false : void 0;
69094
- });
69095
- return isPaired;
69096
- };
69097
- const isUnconditionalReturnBranch = (statement) => {
69098
- if (isNodeOfType(statement, "ReturnStatement")) return true;
69099
- return Boolean(isNodeOfType(statement, "BlockStatement") && statement.body.length === 1 && isNodeOfType(statement.body[0], "ReturnStatement"));
69100
- };
69101
- const findSingleFlightSnapshotClaim = (tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69102
- const snapshotEntry = getDirectBlockEntry(tokenInitializer, functionNode);
69103
- const resetEntry = getDirectBlockEntry(resetNode, functionNode);
69104
- if (!snapshotEntry || !resetEntry) return null;
69105
- const claimCandidates = [];
69106
- const releaseCandidates = [];
69107
- walkOwnFunctionScope(functionNode, (candidate) => {
69108
- if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=" || !getReactRefCurrent(candidate.left, context)) return;
69109
- const assignedValue = stripParenExpression(candidate.right);
69110
- if (!isNodeOfType(assignedValue, "Literal") || typeof assignedValue.value !== "boolean") return;
69111
- const candidateKey = serializeReferenceKey({
69112
- node: candidate.left,
69113
- scopes: context.scopes
69114
- });
69115
- if (!candidateKey) return;
69116
- if (!assignedValue.value) {
69117
- if (getDirectBlockEntry(candidate, functionNode)?.block === resetEntry.block) releaseCandidates.push(candidate);
69118
- return;
69119
- }
69120
- if (!truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context))) return;
69121
- const candidateEntry = getDirectBlockEntry(candidate, functionNode);
69122
- if (!candidateEntry || candidateEntry.block !== snapshotEntry.block) return;
69123
- const candidateIndex = candidateEntry.block.body.findIndex((statement) => statement === candidateEntry.entry);
69124
- const snapshotIndex = candidateEntry.block.body.findIndex((statement) => statement === snapshotEntry.entry);
69125
- if (candidateIndex === -1 || snapshotIndex === -1 || candidateIndex >= snapshotIndex) return;
69126
- const guardIndex = candidateEntry.block.body.findLastIndex((statement, statementIndex) => {
69127
- if (statementIndex >= candidateIndex || !isNodeOfType(statement, "IfStatement") || statement.alternate !== null || !isUnconditionalReturnBranch(statement.consequent)) return false;
69128
- return serializeReferenceKey({
69129
- node: stripParenExpression(statement.test),
69130
- scopes: context.scopes
69131
- }) === candidateKey;
69132
- });
69133
- if (guardIndex === -1 || !candidateEntry.block.body.slice(guardIndex + 1, candidateIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context))) return;
69134
- claimCandidates.push(candidate);
69135
- });
69136
- const claim = claimCandidates.find((claimCandidate) => {
69137
- const candidateKey = serializeReferenceKey({
69138
- node: claimCandidate.left,
69139
- scopes: context.scopes
69140
- });
69141
- return releaseCandidates.some((releaseCandidate) => serializeReferenceKey({
69142
- node: releaseCandidate.left,
69143
- scopes: context.scopes
69144
- }) === candidateKey);
69145
- });
69146
- if (!claim) return null;
69147
- const claimKey = serializeReferenceKey({
69148
- node: claim.left,
69149
- scopes: context.scopes
69150
- });
69151
- const release = releaseCandidates.find((releaseCandidate) => serializeReferenceKey({
69152
- node: releaseCandidate.left,
69153
- scopes: context.scopes
69154
- }) === claimKey);
69155
- if (!claimKey || !release) return null;
69156
- const releaseEntry = getDirectBlockEntry(release, functionNode);
69157
- if (!releaseEntry || releaseEntry.block !== resetEntry.block) return null;
69158
- const releaseIndex = resetEntry.block.body.findIndex((statement) => statement === releaseEntry.entry);
69159
- const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
69160
- if (releaseIndex === -1 || resetIndex === -1 || !resetEntry.block.body.slice(Math.min(releaseIndex, resetIndex) + 1, Math.max(releaseIndex, resetIndex)).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context))) return null;
69161
- let didFindUnsafeWrite = false;
69162
- walkAst(getOwningFunction(functionNode), (candidate) => {
69163
- if (didFindUnsafeWrite || candidate === claim || candidate === release) return;
69164
- const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69165
- if (writeTarget && serializeReferenceKey({
69166
- node: writeTarget,
69167
- scopes: context.scopes
69168
- }) === claimKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindUnsafeWrite = true;
69169
- });
69170
- return didFindUnsafeWrite ? null : claim;
69171
- };
69172
- const findOwnershipClaim = (comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69173
- const refKey = serializeReferenceKey({
69174
- node: comparison.refCurrent,
69175
- scopes: context.scopes
69176
- });
69177
- const tokenKey = serializeReferenceKey({
69178
- node: comparison.token,
69179
- scopes: context.scopes
69180
- });
69181
- if (!refKey || !tokenKey) return null;
69182
- const candidates = [];
69183
- const tokenSymbol = context.scopes.symbolFor(comparison.token);
69184
- const tokenInitializer = tokenSymbol?.initializer ? stripParenExpression(tokenSymbol.initializer) : null;
69185
- if (comparison.isOrdered && !isNodeOfType(tokenInitializer, "UpdateExpression")) return null;
69186
- if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression") && tokenInitializer.operator === "++" && serializeReferenceKey({
69187
- node: tokenInitializer.argument,
69188
- scopes: context.scopes
69189
- }) === refKey) candidates.push(tokenInitializer);
69190
- if (tokenInitializer && getReactRefCurrent(tokenInitializer, context) && serializeReferenceKey({
69191
- node: tokenInitializer,
69192
- scopes: context.scopes
69193
- }) === refKey) {
69194
- const singleFlightClaim = findSingleFlightSnapshotClaim(tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context);
69195
- if (singleFlightClaim) candidates.push(singleFlightClaim);
69196
- }
69197
- if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression")) {
69198
- const generationKey = serializeReferenceKey({
69199
- node: tokenInitializer.argument,
69200
- scopes: context.scopes
69201
- });
69202
- if (generationKey && generationKey === refKey) {
69203
- const ownerFunction = getOwningFunction(functionNode);
69204
- let didFindOtherGenerationWrite = false;
69205
- walkAst(ownerFunction, (candidate) => {
69206
- if (didFindOtherGenerationWrite || candidate === tokenInitializer) return;
69207
- const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69208
- if (writeTarget && serializeReferenceKey({
69209
- node: writeTarget,
69210
- scopes: context.scopes
69211
- }) === generationKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherGenerationWrite = true;
69212
- });
69213
- if (didFindOtherGenerationWrite) return null;
69214
- }
69215
- }
69216
- walkOwnFunctionScope(functionNode, (candidate) => {
69217
- if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=") return;
69218
- if (serializeReferenceKey({
69219
- node: candidate.left,
69220
- scopes: context.scopes
69221
- }) === refKey && serializeReferenceKey({
69222
- node: candidate.right,
69223
- scopes: context.scopes
69224
- }) === tokenKey) candidates.push(candidate);
69225
- });
69226
- const claim = candidates.find((candidate) => truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context)));
69227
- if (!claim) return null;
69228
- let didFindOtherWrite = false;
69229
- walkAst(getOwningFunction(functionNode), (candidate) => {
69230
- if (didFindOtherWrite || candidate === claim) return;
69231
- const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69232
- if (writeTarget && serializeReferenceKey({
69233
- node: writeTarget,
69234
- scopes: context.scopes
69235
- }) === refKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherWrite = true;
69236
- });
69237
- return didFindOtherWrite ? null : claim;
69238
- };
69239
- const isClaimedOwnershipComparison = (test, expectedMode, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69240
- const comparison = getAsyncOwnershipComparison(test, context);
69241
- return Boolean(comparison && comparison.mode === expectedMode && findOwnershipClaim(comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context));
69242
- };
69243
- const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedAssignments, context) => {
68097
+ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedCleanupAssignments, context) => {
69244
68098
  let didFindOtherWrite = false;
69245
68099
  walkAst(effectCallback, (candidate) => {
69246
68100
  if (didFindOtherWrite) return false;
@@ -69248,7 +68102,7 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
69248
68102
  if (serializeReferenceKey({
69249
68103
  node: candidate.left,
69250
68104
  scopes: context.scopes
69251
- }) === guardKey && !acceptedAssignments.has(candidate)) {
68105
+ }) === guardKey && !acceptedCleanupAssignments.has(candidate)) {
69252
68106
  didFindOtherWrite = true;
69253
68107
  return false;
69254
68108
  }
@@ -69264,103 +68118,48 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
69264
68118
  });
69265
68119
  return didFindOtherWrite;
69266
68120
  };
69267
- const collectCleanupBackedLifecycleAssignments = (effectCallback, guardKey, context) => {
69268
- const acceptedAssignments = /* @__PURE__ */ new Set();
69269
- for (const cleanupFunction of collectReturnedCleanupFunctions(effectCallback, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
69270
- const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
69271
- if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
69272
- node: cleanupNode.left,
69273
- scopes: context.scopes
69274
- }) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
69275
- acceptedAssignments.add(cleanupNode);
69276
- });
69277
- if (acceptedAssignments.size === 0) return null;
69278
- walkOwnFunctionScope(effectCallback, (effectNode) => {
69279
- const assignedValue = isNodeOfType(effectNode, "AssignmentExpression") ? stripParenExpression(effectNode.right) : null;
69280
- if (isNodeOfType(effectNode, "AssignmentExpression") && effectNode.operator === "=" && isNodeOfType(assignedValue, "Literal") && assignedValue.value === true && serializeReferenceKey({
69281
- node: effectNode.left,
69282
- scopes: context.scopes
69283
- }) === guardKey && isUnconditionallyExecutedWithinFunction(effectNode, effectCallback, context)) acceptedAssignments.add(effectNode);
69284
- });
69285
- return acceptedAssignments;
69286
- };
69287
- const isEffectCallback = (node, context) => {
69288
- const callbackRoot = findTransparentExpressionRoot(node);
69289
- const callbackCall = callbackRoot.parent;
69290
- return Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
69291
- allowGlobalReactNamespace: true,
69292
- allowUnboundBareCalls: true
69293
- }));
69294
- };
69295
- const isCleanupBackedLifecycleGuard = (guardExpression, functionNode, context) => {
69296
- const guardKey = serializeReferenceKey({
69297
- node: guardExpression,
69298
- scopes: context.scopes
69299
- });
69300
- if (!guardKey || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
69301
- let ownerFunction = functionNode.parent;
69302
- while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
69303
- if (!ownerFunction) return false;
69304
- const effectCallbacks = [];
69305
- if (isEffectCallback(ownerFunction, context)) effectCallbacks.push(ownerFunction);
69306
- walkOwnFunctionScope(ownerFunction, (candidate) => {
69307
- if (!isNodeOfType(candidate, "CallExpression")) return;
69308
- if (!isReactApiCall(candidate, EFFECT_HOOK_NAMES$6, context.scopes, {
69309
- allowGlobalReactNamespace: true,
69310
- allowUnboundBareCalls: true
69311
- })) return;
69312
- const effectCallback = candidate.arguments[0];
69313
- if (effectCallback && isFunctionLike$1(effectCallback)) effectCallbacks.push(effectCallback);
69314
- });
69315
- const acceptedAssignments = /* @__PURE__ */ new Set();
69316
- for (const effectCallback of effectCallbacks) {
69317
- const effectAssignments = collectCleanupBackedLifecycleAssignments(effectCallback, guardKey, context);
69318
- if (!effectAssignments) continue;
69319
- for (const assignment of effectAssignments) acceptedAssignments.add(assignment);
69320
- }
69321
- return Boolean(acceptedAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(ownerFunction, guardKey, acceptedAssignments, context));
69322
- };
69323
- const collectLogicalOperands = (expression, operator) => {
69324
- const stripped = stripParenExpression(expression);
69325
- if (isNodeOfType(stripped, "LogicalExpression") && stripped.operator === operator) return [...collectLogicalOperands(stripped.left, operator), ...collectLogicalOperands(stripped.right, operator)];
69326
- return [stripped];
69327
- };
69328
- const collectFinalizerGuardExpressions = (resetNode, protectingTry) => {
69329
- const positive = [];
69330
- const negative = [];
68121
+ const isResetGuardedByCleanupBackedLifecycle = (resetNode, functionNode, context) => {
69331
68122
  let child = resetNode;
69332
68123
  let cursor = resetNode.parent;
69333
- while (cursor && cursor !== protectingTry) {
69334
- if (isNodeOfType(cursor, "IfStatement")) {
69335
- if (cursor.consequent !== child || cursor.alternate !== null) return null;
69336
- positive.push(...collectLogicalOperands(cursor.test, "&&"));
69337
- } else if (isNodeOfType(cursor, "LogicalExpression")) {
69338
- if (cursor.operator !== "&&" || cursor.right !== child) return null;
69339
- positive.push(...collectLogicalOperands(cursor.left, "&&"));
69340
- } else if (isNodeOfType(cursor, "BlockStatement")) {
69341
- const childIndex = cursor.body.findIndex((statement) => statement === child);
69342
- if (childIndex !== -1) for (const statement of cursor.body.slice(0, childIndex)) {
69343
- if (!isNodeOfType(statement, "IfStatement") || statement.alternate !== null || !isUnconditionalReturnBranch(statement.consequent)) continue;
69344
- negative.push(...collectLogicalOperands(statement.test, "||"));
69345
- }
69346
- } else if (isNodeOfType(cursor, "SwitchCase") || isNodeOfType(cursor, "ConditionalExpression") || isNodeOfType(cursor, "ForStatement") || isNodeOfType(cursor, "ForInStatement") || isNodeOfType(cursor, "ForOfStatement") || isNodeOfType(cursor, "WhileStatement") || isNodeOfType(cursor, "DoWhileStatement")) return null;
68124
+ let guardKey = null;
68125
+ let guardExpression = null;
68126
+ while (cursor && cursor !== functionNode) {
68127
+ if (isNodeOfType(cursor, "IfStatement") && cursor.consequent === child && cursor.alternate === null) {
68128
+ guardExpression = cursor.test;
68129
+ guardKey = serializeReferenceKey({
68130
+ node: cursor.test,
68131
+ scopes: context.scopes
68132
+ });
68133
+ break;
68134
+ }
69347
68135
  child = cursor;
69348
68136
  cursor = cursor.parent ?? null;
69349
68137
  }
69350
- return cursor === protectingTry && positive.length + negative.length > 0 ? {
69351
- positive,
69352
- negative
69353
- } : null;
69354
- };
69355
- const isPositiveFinalizerGuard = (expression, resetNode, functionNode, truthySets, firstRiskyAwait, context) => isCleanupBackedLifecycleGuard(expression, functionNode, context) || isClaimedOwnershipComparison(expression, "owns", functionNode, truthySets, firstRiskyAwait, resetNode, context);
69356
- const isNegativeFinalizerGuard = (expression, resetNode, functionNode, truthySets, firstRiskyAwait, context) => {
69357
- const stripped = stripParenExpression(expression);
69358
- if (isNodeOfType(stripped, "UnaryExpression") && stripped.operator === "!") return isPositiveFinalizerGuard(stripped.argument, resetNode, functionNode, truthySets, firstRiskyAwait, context);
69359
- return isClaimedOwnershipComparison(stripped, "lost", functionNode, truthySets, firstRiskyAwait, resetNode, context);
69360
- };
69361
- const isFinalizerResetProvablyGuarded = (resetNode, protectingTry, functionNode, truthySets, firstRiskyAwait, context) => {
69362
- const guards = collectFinalizerGuardExpressions(resetNode, protectingTry);
69363
- return Boolean(guards && guards.positive.every((guard) => isPositiveFinalizerGuard(guard, resetNode, functionNode, truthySets, firstRiskyAwait, context)) && guards.negative.every((guard) => isNegativeFinalizerGuard(guard, resetNode, functionNode, truthySets, firstRiskyAwait, context)));
68138
+ if (!guardKey || !guardExpression || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
68139
+ cursor = functionNode.parent;
68140
+ while (cursor) {
68141
+ if (isFunctionLike$1(cursor)) {
68142
+ const callbackRoot = findTransparentExpressionRoot(cursor);
68143
+ const callbackCall = callbackRoot.parent;
68144
+ if (Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
68145
+ allowGlobalReactNamespace: true,
68146
+ allowUnboundBareCalls: true
68147
+ }))) {
68148
+ const acceptedCleanupAssignments = /* @__PURE__ */ new Set();
68149
+ for (const cleanupFunction of collectReturnedCleanupFunctions(cursor, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
68150
+ const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
68151
+ if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
68152
+ node: cleanupNode.left,
68153
+ scopes: context.scopes
68154
+ }) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
68155
+ acceptedCleanupAssignments.add(cleanupNode);
68156
+ });
68157
+ if (acceptedCleanupAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(cursor, guardKey, acceptedCleanupAssignments, context)) return true;
68158
+ }
68159
+ }
68160
+ cursor = cursor.parent ?? null;
68161
+ }
68162
+ return false;
69364
68163
  };
69365
68164
  const isAwaitInsideProtectedTry = (awaitNode, tryStatement) => {
69366
68165
  let child = awaitNode;
@@ -69466,13 +68265,7 @@ const analyzeFunction = (functionNode, context) => {
69466
68265
  const exceptionallyProtectedAwaits = collectExceptionallyProtectedAwaits(awaitSites, calls);
69467
68266
  const riskyAwaitsWithTruthySet = awaitSites.filter((awaitSite) => rejectingAwaitNodes.has(awaitSite.node) && !exceptionallyProtectedAwaits.has(awaitSite.node) && truthySets.some((truthySet) => truthySet.start < awaitSite.start && !areOnExclusiveBranches(truthySet.node, awaitSite.node, functionNode)));
69468
68267
  if (riskyAwaitsWithTruthySet.length === 0) continue;
69469
- const conditionalExceptionalResets = calls.filter((call) => {
69470
- if (call.value || call.context === "plain" || call.isUnconditional || call.protectingTry === null) return false;
69471
- const protectingTry = call.protectingTry;
69472
- if (!isInsideTryFinalizer(call.node, protectingTry)) return true;
69473
- const firstRiskyAwait = riskyAwaitsWithTruthySet.find((awaitSite) => isAwaitInsideProtectedTry(awaitSite.node, protectingTry));
69474
- return !(firstRiskyAwait && isFinalizerResetProvablyGuarded(call.node, protectingTry, functionNode, truthySets, firstRiskyAwait, context));
69475
- });
68268
+ const conditionalExceptionalResets = calls.filter((call) => !call.value && call.context !== "plain" && !call.isUnconditional && call.protectingTry !== null && !(isInsideTryFinalizer(call.node, call.protectingTry) && isResetGuardedByCleanupBackedLifecycle(call.node, functionNode, context)));
69476
68269
  for (const reset of conditionalExceptionalResets) {
69477
68270
  const catchHandler = reset.protectingTry?.handler;
69478
68271
  if (catchHandler && !catchHandlerCanBypassReset(catchHandler, functionNode, setterKey, context, false)) continue;
@@ -75344,29 +74137,6 @@ const doesPredicateTruthRequireMatch = (matchCall, predicateFunction) => {
75344
74137
  }
75345
74138
  return !isNegated && predicateFunction.body === child;
75346
74139
  };
75347
- const doesPredicateReturnNormalizedMatch = (matchCall, predicateFunction) => {
75348
- if (!isFunctionLike$1(predicateFunction) || !isNodeOfType(predicateFunction.body, "BlockStatement") || predicateFunction.body.body.length !== 2 || !isNodeOfType(predicateFunction.body.body[0], "VariableDeclaration")) return false;
75349
- const returnStatement = predicateFunction.body.body[1];
75350
- if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
75351
- let negationCount = 0;
75352
- let expression = matchCall;
75353
- let parent = expression.parent ?? null;
75354
- while (parent && parent !== returnStatement) {
75355
- if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") {
75356
- negationCount += 1;
75357
- expression = parent;
75358
- parent = parent.parent ?? null;
75359
- continue;
75360
- }
75361
- if (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) || isNodeOfType(parent, "ChainExpression")) {
75362
- expression = parent;
75363
- parent = parent.parent ?? null;
75364
- continue;
75365
- }
75366
- return false;
75367
- }
75368
- return parent === returnStatement && returnStatement.argument === expression && negationCount % 2 === 0;
75369
- };
75370
74140
  const isStringTypeofGuardForPath = (test, expectedPath) => {
75371
74141
  const target = stripParenExpression(test);
75372
74142
  if (!isNodeOfType(target, "BinaryExpression") || target.operator !== "===") return false;
@@ -75407,26 +74177,6 @@ const pathUsesOptionalAccess = (node) => {
75407
74177
  current = current.object;
75408
74178
  }
75409
74179
  };
75410
- const getNormalizedClassNameRoot = (expression) => {
75411
- const conditional = stripParenExpression(expression);
75412
- if (!isNodeOfType(conditional, "ConditionalExpression")) return null;
75413
- const consequent = stripParenExpression(conditional.consequent);
75414
- const rootIdentifier = getRootIdentifier(consequent);
75415
- if (!rootIdentifier || receiverPathKey(consequent) !== `${rootIdentifier.name}.className`) return null;
75416
- const test = stripParenExpression(conditional.test);
75417
- if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "===") return null;
75418
- const testOperands = [test.left, test.right].map((operand) => stripParenExpression(operand));
75419
- const typeofOperand = testOperands.find((operand) => isNodeOfType(operand, "UnaryExpression"));
75420
- const stringOperand = testOperands.find((operand) => isNodeOfType(operand, "Literal"));
75421
- if (!typeofOperand || !isNodeOfType(typeofOperand, "UnaryExpression") || typeofOperand.operator !== "typeof" || receiverPathKey(typeofOperand.argument) !== `${rootIdentifier.name}.className` || !stringOperand || !isNodeOfType(stringOperand, "Literal") || stringOperand.value !== "string") return null;
75422
- const alternate = stripParenExpression(conditional.alternate);
75423
- if (!isNodeOfType(alternate, "LogicalExpression") || alternate.operator !== "??") return null;
75424
- const fallback = stripParenExpression(alternate.right);
75425
- const attributeCall = stripParenExpression(alternate.left);
75426
- if (!isNodeOfType(fallback, "Literal") || fallback.value !== "" || !isNodeOfType(attributeCall, "CallExpression") || !isNodeOfType(attributeCall.callee, "MemberExpression") || getStaticPropertyName(attributeCall.callee) !== "getAttribute" || receiverPathKey(attributeCall.callee.object) !== rootIdentifier.name) return null;
75427
- const attributeName = attributeCall.arguments[0] ? stripParenExpression(attributeCall.arguments[0]) : null;
75428
- return attributeName && isNodeOfType(attributeName, "Literal") && attributeName.value === "class" ? rootIdentifier : null;
75429
- };
75430
74180
  const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
75431
74181
  const resultIdentifier = getRootIdentifier(matchReceiver);
75432
74182
  const resultPath = receiverPathKey(matchReceiver);
@@ -75435,17 +74185,11 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
75435
74185
  if (!isDirectFinderMatchReturn(assertion) && !isOptionalResultPath) return false;
75436
74186
  const resultSymbol = context.scopes.symbolFor(resultIdentifier);
75437
74187
  const initializer = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
75438
- if (resultSymbol?.kind !== "const" || !initializer) return false;
75439
- const finderCall = isNodeOfType(initializer, "CallExpression") ? initializer : isNodeOfType(initializer, "ConditionalExpression") ? (() => {
75440
- const alternate = stripParenExpression(initializer.alternate);
75441
- const consequent = stripParenExpression(initializer.consequent);
75442
- return (isNodeOfType(alternate, "Literal") && alternate.value === null || isNodeOfType(alternate, "Identifier") && alternate.name === "undefined" && context.scopes.isGlobalReference(alternate)) && isNodeOfType(consequent, "CallExpression") ? consequent : null;
75443
- })() : null;
75444
- if (!finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
74188
+ if (resultSymbol?.kind !== "const" || !initializer || !isNodeOfType(initializer, "CallExpression")) return false;
75445
74189
  if (!isOptionalResultPath && !isImmediatelyGuardedFinderResult(assertion, resultSymbol, resultPath, context)) return false;
75446
- const finderCallee = stripParenExpression(finderCall.callee);
74190
+ const finderCallee = stripParenExpression(initializer.callee);
75447
74191
  if (!isNodeOfType(finderCallee, "Identifier") || !(context.scopes.symbolFor(finderCallee)?.kind === "import" && getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) === "findUpUntil" || finderCallee.name === "findUpUntil" && context.scopes.isGlobalReference(finderCallee))) return false;
75448
- const predicateArgument = finderCall.arguments[1];
74192
+ const predicateArgument = initializer.arguments[1];
75449
74193
  if (!predicateArgument) return false;
75450
74194
  const predicateFunction = resolveExactLocalFunction(predicateArgument, context.scopes);
75451
74195
  if (!predicateFunction || !isFunctionLike$1(predicateFunction)) return false;
@@ -75469,43 +74213,6 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
75469
74213
  });
75470
74214
  return didProveMatch;
75471
74215
  };
75472
- const isMatchProvenByNormalizedFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
75473
- const normalizedReceiver = stripParenExpression(matchReceiver);
75474
- if (!isNodeOfType(normalizedReceiver, "Identifier")) return false;
75475
- const normalizedReceiverSymbol = context.scopes.symbolFor(normalizedReceiver);
75476
- const normalizedReceiverInitializer = normalizedReceiverSymbol?.initializer ? stripParenExpression(normalizedReceiverSymbol.initializer) : null;
75477
- const resultIdentifier = normalizedReceiverInitializer ? getNormalizedClassNameRoot(normalizedReceiverInitializer) : null;
75478
- if (normalizedReceiverSymbol?.kind !== "const" || normalizedReceiverSymbol.references.some((reference) => reference.flag !== "read") || !resultIdentifier) return false;
75479
- const resultSymbol = context.scopes.symbolFor(resultIdentifier);
75480
- const finderCall = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
75481
- if (resultSymbol?.kind !== "const" || resultSymbol.references.some((reference) => reference.flag !== "read") || !finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
75482
- const finderCallee = stripParenExpression(finderCall.callee);
75483
- if (!isNodeOfType(finderCallee, "Identifier") || context.scopes.symbolFor(finderCallee)?.kind !== "import" || getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) !== "findUpUntil") return false;
75484
- if (!isPresenceProvenBeforeNode(assertion, (test) => {
75485
- const expression = stripParenExpression(test);
75486
- return isNodeOfType(expression, "Identifier") && context.scopes.symbolFor(expression)?.id === resultSymbol.id;
75487
- })) return false;
75488
- const predicateArgument = finderCall.arguments[1];
75489
- const predicateFunction = predicateArgument ? resolveExactLocalFunction(predicateArgument, context.scopes) : null;
75490
- if (!predicateFunction || !isFunctionLike$1(predicateFunction) || predicateFunction.async || predicateFunction.generator) return false;
75491
- const predicateParameter = predicateFunction.params[0];
75492
- if (!isNodeOfType(predicateParameter, "Identifier")) return false;
75493
- let didProveNormalizedMatch = false;
75494
- walkAst(predicateFunction.body, (child) => {
75495
- if (didProveNormalizedMatch || isFunctionLike$1(child)) return false;
75496
- if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || getStaticPropertyName(child.callee) !== "match" || !child.arguments[0] || !areRegexPatternsEquivalent(child.arguments[0], assertedPattern, context) || !doesPredicateTruthRequireMatch(child, predicateFunction) && !doesPredicateReturnNormalizedMatch(child, predicateFunction)) return;
75497
- const predicateReceiver = stripParenExpression(child.callee.object);
75498
- if (!isNodeOfType(predicateReceiver, "Identifier")) return;
75499
- const predicateReceiverSymbol = context.scopes.symbolFor(predicateReceiver);
75500
- const predicateReceiverInitializer = predicateReceiverSymbol?.initializer ? stripParenExpression(predicateReceiverSymbol.initializer) : null;
75501
- const predicateRoot = predicateReceiverInitializer ? getNormalizedClassNameRoot(predicateReceiverInitializer) : null;
75502
- if (predicateReceiverSymbol?.kind === "const" && predicateReceiverSymbol.references.every((reference) => reference.flag === "read") && predicateRoot?.name === predicateParameter.name) {
75503
- didProveNormalizedMatch = true;
75504
- return false;
75505
- }
75506
- });
75507
- return didProveNormalizedMatch;
75508
- };
75509
74216
  const scopeProvesFindMatch = (assertion, findReceiver, findPredicate, context) => {
75510
74217
  if (!isStablePredicate(findPredicate, context)) return false;
75511
74218
  return isPresenceProvenBeforeNode(assertion, (test) => testPositivelyContainsCall(test, (call) => {
@@ -75606,123 +74313,6 @@ const isEnsureThenFind = (assertion, findReceiver, findPredicate) => {
75606
74313
  }
75607
74314
  return false;
75608
74315
  };
75609
- const getReceiverRootIdentifier = (node) => {
75610
- let target = stripParenExpression(node);
75611
- while (isNodeOfType(target, "MemberExpression")) target = stripParenExpression(target.object);
75612
- return isNodeOfType(target, "Identifier") ? target : null;
75613
- };
75614
- const getReceiverStatePath = (node) => {
75615
- const target = stripParenExpression(node);
75616
- if (isNodeOfType(target, "Identifier")) return target.name;
75617
- if (!isNodeOfType(target, "MemberExpression")) return null;
75618
- const objectPath = getReceiverStatePath(target.object);
75619
- if (!objectPath) return null;
75620
- return `${objectPath}.${getStaticPropertyName(target) ?? "*"}`;
75621
- };
75622
- const doesReceiverStateChangeBeforeAssertion = (ownerFunction, receiver, startOffset, assertion, context) => {
75623
- const receiverRoot = getReceiverRootIdentifier(receiver);
75624
- const receiverSymbol = receiverRoot ? context.scopes.symbolFor(receiverRoot) : null;
75625
- const receiverPath = getReceiverStatePath(receiver);
75626
- if (!receiverRoot || !receiverSymbol || !receiverPath) return true;
75627
- const receiverAliasPaths = new Map([[receiverSymbol.id, receiverRoot.name]]);
75628
- let didAddAlias = true;
75629
- while (didAddAlias) {
75630
- didAddAlias = false;
75631
- walkAst(ownerFunction, (child) => {
75632
- if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75633
- if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier") || !child.init) return;
75634
- const initializer = stripParenExpression(child.init);
75635
- if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return;
75636
- const initializerRoot = getReceiverRootIdentifier(initializer);
75637
- const initializerSymbol = initializerRoot ? context.scopes.symbolFor(initializerRoot) : null;
75638
- const initializerBasePath = initializerSymbol ? receiverAliasPaths.get(initializerSymbol.id) : null;
75639
- const initializerPath = getReceiverStatePath(initializer);
75640
- if (!initializerRoot || !initializerBasePath || !initializerPath) return;
75641
- const aliasSymbol = context.scopes.symbolFor(child.id);
75642
- if (aliasSymbol && !receiverAliasPaths.has(aliasSymbol.id)) {
75643
- const initializerSuffix = initializerPath.slice(initializerRoot.name.length);
75644
- receiverAliasPaths.set(aliasSymbol.id, `${initializerBasePath}${initializerSuffix}`);
75645
- didAddAlias = true;
75646
- }
75647
- });
75648
- }
75649
- let didChangeReceiverState = false;
75650
- walkAst(ownerFunction, (child) => {
75651
- if (didChangeReceiverState) return false;
75652
- if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75653
- if (child.range[0] <= startOffset || child.range[0] >= assertion.range[0]) return;
75654
- if (isNodeOfType(child, "CallExpression")) {
75655
- didChangeReceiverState = true;
75656
- return false;
75657
- }
75658
- const mutationTarget = isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete" ? stripParenExpression(isNodeOfType(child, "AssignmentExpression") ? child.left : child.argument) : null;
75659
- const mutationRoot = mutationTarget ? getReceiverRootIdentifier(mutationTarget) : null;
75660
- const mutationSymbol = mutationRoot ? context.scopes.symbolFor(mutationRoot) : null;
75661
- const mutationBasePath = mutationSymbol ? receiverAliasPaths.get(mutationSymbol.id) : null;
75662
- const mutationPath = mutationTarget ? getReceiverStatePath(mutationTarget) : null;
75663
- if (mutationRoot && mutationBasePath && mutationPath) {
75664
- const canonicalMutationPath = `${mutationBasePath}${mutationPath.slice(mutationRoot.name.length)}`;
75665
- if (canonicalMutationPath !== receiverPath && !canonicalMutationPath.startsWith(`${receiverPath}.`) && !receiverPath.startsWith(`${canonicalMutationPath}.`)) return;
75666
- didChangeReceiverState = true;
75667
- return false;
75668
- }
75669
- });
75670
- return didChangeReceiverState;
75671
- };
75672
- const isFindProvenByGuardedMaximum = (assertion, findReceiver, findPredicate, context) => {
75673
- const findLookup = findEqualityLookupParts(findPredicate);
75674
- const maximumIdentifier = findLookup ? stripParenExpression(findLookup.comparedValue) : null;
75675
- if (!findLookup || !maximumIdentifier || !isNodeOfType(maximumIdentifier, "Identifier")) return false;
75676
- const maximumSymbol = context.scopes.symbolFor(maximumIdentifier);
75677
- const maximumInitializer = maximumSymbol?.initializer ? stripParenExpression(maximumSymbol.initializer) : null;
75678
- if (maximumSymbol?.kind !== "const" || maximumSymbol.references.some((reference) => reference.flag !== "read") || !maximumInitializer || !isNodeOfType(maximumInitializer, "CallExpression") || !isNodeOfType(maximumInitializer.callee, "MemberExpression") || getStaticPropertyName(maximumInitializer.callee) !== "reduce") return false;
75679
- const filterCall = stripParenExpression(maximumInitializer.callee.object);
75680
- if (!isNodeOfType(filterCall, "CallExpression") || !isNodeOfType(filterCall.callee, "MemberExpression") || getStaticPropertyName(filterCall.callee) !== "filter" || !areNodesLooselyEqual(filterCall.callee.object, findReceiver)) return false;
75681
- const filterPredicate = filterCall.arguments[0] ? stripParenExpression(filterCall.arguments[0]) : null;
75682
- if (!filterPredicate || !isStablePredicate(filterPredicate, context)) return false;
75683
- const reducerArgument = maximumInitializer.arguments[0] ? stripParenExpression(maximumInitializer.arguments[0]) : null;
75684
- const reducerFunction = reducerArgument ? resolveExactLocalFunction(reducerArgument, context.scopes) : null;
75685
- const initialValue = maximumInitializer.arguments[1] ? stripParenExpression(maximumInitializer.arguments[1]) : null;
75686
- if (!reducerFunction || !isFunctionLike$1(reducerFunction) || reducerFunction.async || reducerFunction.generator || !initialValue || !isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return false;
75687
- const accumulatorParameter = reducerFunction.params[0];
75688
- const itemParameter = reducerFunction.params[1];
75689
- const reducerBody = singleExpressionPredicateBody(reducerFunction);
75690
- if (!isNodeOfType(accumulatorParameter, "Identifier") || !isNodeOfType(itemParameter, "Identifier") || !reducerBody || !isNodeOfType(reducerBody, "CallExpression") || !isNodeOfType(reducerBody.callee, "MemberExpression") || getStaticPropertyName(reducerBody.callee) !== "max") return false;
75691
- const mathReceiver = stripParenExpression(reducerBody.callee.object);
75692
- if (!isNodeOfType(mathReceiver, "Identifier") || mathReceiver.name !== "Math" || !context.scopes.isGlobalReference(mathReceiver) || reducerBody.arguments.length !== 2) return false;
75693
- const accumulatorArgument = reducerBody.arguments.find((argument) => {
75694
- const expression = stripParenExpression(argument);
75695
- return isNodeOfType(expression, "Identifier") && expression.name === accumulatorParameter.name;
75696
- });
75697
- const itemMemberArgument = reducerBody.arguments.find((argument) => {
75698
- const expression = stripParenExpression(argument);
75699
- const rootIdentifier = getRootIdentifier(expression);
75700
- return isNodeOfType(expression, "MemberExpression") && rootIdentifier?.name === itemParameter.name && receiverPathKey(expression)?.slice(itemParameter.name.length + 1) === findLookup.propertyName;
75701
- });
75702
- if (!accumulatorArgument || !itemMemberArgument) return false;
75703
- if (!receiverPathKey(findReceiver)) return false;
75704
- let ownerFunction = assertion.parent ?? null;
75705
- while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
75706
- if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
75707
- const maximumEnd = maximumInitializer.range[1];
75708
- if (doesReceiverStateChangeBeforeAssertion(ownerFunction.body, findReceiver, maximumEnd, assertion, context)) return false;
75709
- return isPresenceProvenBeforeNode(assertion, (test) => {
75710
- const comparison = stripParenExpression(test);
75711
- if (!isNodeOfType(comparison, "BinaryExpression")) return false;
75712
- return [[
75713
- comparison.left,
75714
- comparison.right,
75715
- comparison.operator
75716
- ], [
75717
- comparison.right,
75718
- comparison.left,
75719
- comparison.operator === "<" ? ">" : comparison.operator === ">" ? "<" : comparison.operator
75720
- ]].some(([candidateMaximum, candidateInitial, operator]) => {
75721
- const candidateMaximumIdentifier = stripParenExpression(candidateMaximum);
75722
- return operator === ">" && isNodeOfType(candidateMaximumIdentifier, "Identifier") && context.scopes.symbolFor(candidateMaximumIdentifier)?.id === maximumSymbol.id && areNodesLooselyEqual(stripParenExpression(candidateInitial), initialValue);
75723
- });
75724
- });
75725
- };
75726
74316
  const isDefinitelyNonNullishMapValue = (value) => {
75727
74317
  if (!value) return false;
75728
74318
  const expression = stripParenExpression(value);
@@ -75746,15 +74336,15 @@ const unwrapFalseBooleanGuard = (test) => {
75746
74336
  };
75747
74337
  const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
75748
74338
  const stableLookupKey = stripParenExpression(lookupKey);
75749
- const lookupKeyRoot = isNodeOfType(stableLookupKey, "MemberExpression") ? getRootIdentifier(stableLookupKey) : null;
75750
- const lookupKeySymbol = isNodeOfType(stableLookupKey, "Identifier") ? context.scopes.symbolFor(stableLookupKey) : lookupKeyRoot ? context.scopes.symbolFor(lookupKeyRoot) : null;
75751
- if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal") && (!isNodeOfType(stableLookupKey, "MemberExpression") || !lookupKeyRoot || lookupKeySymbol?.kind !== "const")) return false;
74339
+ if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal")) return false;
75752
74340
  const receiverSymbol = context.scopes.symbolFor(receiver);
75753
74341
  if (!receiverSymbol) return false;
75754
74342
  const receiverMatches = (candidate) => {
75755
74343
  const target = stripParenExpression(candidate);
75756
74344
  return isNodeOfType(target, "Identifier") && context.scopes.symbolFor(target)?.id === receiverSymbol.id;
75757
74345
  };
74346
+ const lookupKeyExpression = stripParenExpression(lookupKey);
74347
+ const lookupKeySymbol = isNodeOfType(lookupKeyExpression, "Identifier") ? context.scopes.symbolFor(lookupKeyExpression) : null;
75758
74348
  let child = assertion;
75759
74349
  let ancestor = assertion.parent ?? null;
75760
74350
  while (ancestor && !isFunctionLike$1(ancestor)) {
@@ -75774,7 +74364,7 @@ const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
75774
74364
  const populationCall = populationCalls[0];
75775
74365
  if (!populationCall) continue;
75776
74366
  const populationCallStart = populationCall.range[0];
75777
- if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) || Boolean(lookupKeySymbol && isNodeOfType(stableLookupKey, "MemberExpression") && subtreeWritesSymbol(ancestor, new Set([lookupKeySymbol.id]), context, void 0, assertion))) continue;
74367
+ if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))) continue;
75778
74368
  if (receiverSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) continue;
75779
74369
  if (!indexedRelevantCalls(ancestor).some((laterCall) => {
75780
74370
  if (laterCall.range[0] <= populationCallStart || laterCall.range[0] >= assertion.range[0] || !isNodeOfType(laterCall.callee, "MemberExpression") || !receiverMatches(laterCall.callee.object)) return false;
@@ -75884,7 +74474,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
75884
74474
  const findReceiver = callee.object;
75885
74475
  if (predicate && isExhaustiveLiteralTupleMapping(findReceiver, predicate, context)) return;
75886
74476
  if (predicate && scopeProvesFindMatch(node, findReceiver, predicate, context)) return;
75887
- if (predicate && isFindProvenByGuardedMaximum(node, findReceiver, predicate, context)) return;
75888
74477
  if (predicate && isEnsureThenFind(node, findReceiver, predicate)) return;
75889
74478
  }
75890
74479
  if (methodName === "match") {
@@ -75894,7 +74483,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
75894
74483
  const regexKey = pattern ? regexComparableKey(pattern, context) : null;
75895
74484
  if (pattern && isGuardedAnchoredCharacterMatch(node, matchReceiver, pattern)) return;
75896
74485
  if (pattern && regexKey && isMatchProvenByFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
75897
- if (pattern && regexKey && isMatchProvenByNormalizedFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
75898
74486
  if (regexKey && scopeProvesMatchTested(node, regexKey, matchReceiver, context)) return;
75899
74487
  }
75900
74488
  if (methodName === "get") {
@@ -110236,6 +108824,9 @@ const rerenderFunctionalSetstate = defineRule({
110236
108824
  } })
110237
108825
  });
110238
108826
  //#endregion
108827
+ //#region src/plugin/utils/is-trivial-built-in-construction.ts
108828
+ const isTrivialBuiltInConstruction = (expression) => isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && TRIVIAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && (expression.arguments ?? []).length === 0;
108829
+ //#endregion
110239
108830
  //#region src/plugin/rules/state-and-effects/rerender-lazy-ref-init.ts
110240
108831
  const rerenderLazyRefInit = defineRule({
110241
108832
  id: "rerender-lazy-ref-init",
@@ -110254,6 +108845,7 @@ const rerenderLazyRefInit = defineRule({
110254
108845
  const memberPropertyName = isNodeOfType(callee, "MemberExpression") && (isNodeOfType(callee.property, "Identifier") || isNodeOfType(callee.property, "PrivateIdentifier")) ? callee.property.name : null;
110255
108846
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : memberPropertyName ?? "fn";
110256
108847
  if (TRIVIAL_INITIALIZER_NAMES.has(calleeName)) return;
108848
+ if (isTrivialBuiltInConstruction(initializer)) return;
110257
108849
  if (isPlainCall && isReactHookName(calleeName)) return;
110258
108850
  const callShape = isNewCall ? `new ${calleeName}()` : `${calleeName}()`;
110259
108851
  context.report({