oxlint-plugin-react-doctor 0.9.2-dev.1098b9c → 0.9.2-dev.1d01dd3

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 +1966 -209
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -388,7 +388,7 @@ const SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?)$/i;
388
388
  const SCRIPT_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py|php)$/i;
389
389
  const DATABASE_SOURCE_FILE_PATTERN = /\.(?:[cm]?[jt]sx?|py)$/i;
390
390
  const SERVER_CONTEXT_PATTERN = /(?:^|\/)(?:api|backend|server|servers|middleware|route|routes|functions|lambdas|workers)(?:\/|$)|(?:^|\/)[^/]+\.server\.[cm]?[jt]sx?$/i;
391
- const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)|\.(?:test|spec|e2e|e2e-spec|integration-test|fixture|fixtures|stories|story)\.[cm]?[jt]sx?$|(?:^|\/)(?:playwright|cypress|vitest|jest|karma)[^/]*\.conf(?:ig)?\.[cm]?[jt]s$|(?:^|\/)(?:test_[^/]+|[^/]+_test|conftest)\.py$|\.env\.[^/]*(?:test|e2e)[^/]*$/i;
391
+ const TEST_CONTEXT_PATTERN = /(?:^|\/)(?:__fixtures__|__mocks__|__tests__|__integration__|fixtures|mocks|test|tests|testdata|test-data|e2e|playwright|cypress|specs?)(?:\/|$)|^(?:test|spec)-[^/]+\.[cm]?[jt]sx?$|\.(?:test|spec|e2e|e2e-spec|integration-test|fixture|fixtures|stories|story)\.[cm]?[jt]sx?$|(?:^|\/)(?:playwright|cypress|vitest|jest|karma)[^/]*\.conf(?:ig)?\.[cm]?[jt]s$|(?:^|\/)(?:test_[^/]+|[^/]+_test|conftest)\.py$|\.env\.[^/]*(?:test|e2e)[^/]*$/i;
392
392
  const BUILD_CONFIG_FILE_PATTERN = /(?:^|\/)(?:vite|next|nuxt|astro|remix|webpack|rollup|rspack|rsbuild|esbuild|tsup|metro|expo|babel|tailwind|postcss|svelte|farm|parcel|snowpack)[^/]*\.config\.[cm]?[jt]sx?$/i;
393
393
  const BUILD_SCRIPT_CONTEXT_PATTERN = /(?:^|\/)scripts(?:\/|$)/i;
394
394
  const DEMO_CONTEXT_PATTERN = /(?:^|\/)(?:examples?|tutorials?|demos?|samples?|playgrounds?)(?:\/|$)/i;
@@ -665,19 +665,6 @@ 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
- ]);
681
668
  const SETTER_PATTERN = /^set[A-Z]/;
682
669
  const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
683
670
  const UPPERCASE_PATTERN = /^[A-Z]/;
@@ -5887,6 +5874,7 @@ const isInlineFunctionExpression = (node) => Boolean(node && (isNodeOfType(node,
5887
5874
  //#endregion
5888
5875
  //#region src/plugin/rules/js-performance/async-await-in-loop.ts
5889
5876
  const LOOP_STATEMENT_TYPES$1 = new Set(LOOP_TYPES);
5877
+ const ORDERED_OUTPUT_INSERTION_METHOD_NAMES = new Set(["push", "unshift"]);
5890
5878
  const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) => {
5891
5879
  let firstAwait = null;
5892
5880
  walkAst(block, (child) => {
@@ -5953,7 +5941,151 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
5953
5941
  });
5954
5942
  return isWaitLike;
5955
5943
  };
5956
- const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
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);
5957
6089
  const collectPatternIdentifiers = (pattern, target) => {
5958
6090
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
5959
6091
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -6099,11 +6231,6 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
6099
6231
  });
6100
6232
  return hasAwaitDependentExit;
6101
6233
  };
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
- };
6107
6234
  const MUTATING_ARRAY_METHOD_NAMES$2 = new Set([
6108
6235
  ...ARRAY_MUTATION_METHOD_NAMES,
6109
6236
  "pop",
@@ -17872,6 +17999,14 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
17872
17999
  //#region src/plugin/utils/is-event-handler-attribute.ts
17873
18000
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
17874
18001
  //#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
17875
18010
  //#region src/plugin/utils/is-ast-descendant.ts
17876
18011
  /**
17877
18012
  * True when `inner` is `outer` itself or any descendant in the AST
@@ -18090,13 +18225,15 @@ const isSynchronousIteratorCall = (callNode, callbackArgument, scopes) => {
18090
18225
  }
18091
18226
  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)));
18092
18227
  };
18093
- const isSynchronousIteratorCallback = (functionNode) => {
18094
- const callNode = functionNode.parent;
18095
- if (!isNodeOfType(callNode, "CallExpression")) return false;
18228
+ const isSynchronousIteratorCallbackCall = (callNode, callbackArgument) => {
18096
18229
  const callee = stripParenExpression(callNode.callee);
18097
18230
  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;
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
+ const isSynchronousIteratorCallback = (functionNode) => {
18235
+ const callNode = functionNode.parent;
18236
+ return Boolean(isNodeOfType(callNode, "CallExpression") && isSynchronousIteratorCallbackCall(callNode, functionNode));
18100
18237
  };
18101
18238
  //#endregion
18102
18239
  //#region src/plugin/utils/is-within-assignment-target.ts
@@ -18216,11 +18353,35 @@ const resolveEventListenerCaptureValueIdentityKey = (expression, context) => {
18216
18353
  const rightIdentityKey = resolveEventListenerCaptureValueIdentityKey(unwrappedExpression.right, context);
18217
18354
  return leftIdentityKey && rightIdentityKey ? `${unwrappedExpression.type}:${unwrappedExpression.operator}:${leftIdentityKey}:${rightIdentityKey}` : null;
18218
18355
  };
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
+ };
18219
18378
  const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
18220
- const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
18379
+ const stableOptionsNode = optionsNode ? resolveReadOnlyEventListenerOptions(optionsNode, context) : null;
18380
+ if (optionsNode && !stableOptionsNode) return null;
18381
+ const capture = resolveEventListenerCapture(stableOptionsNode, { allowIndeterminateEntries: true });
18221
18382
  if (capture !== null) return `capture:${String(capture)}`;
18222
- if (!optionsNode) return null;
18223
- const unwrappedOptions = stripParenExpression(optionsNode);
18383
+ if (!stableOptionsNode) return null;
18384
+ const unwrappedOptions = stripParenExpression(stableOptionsNode);
18224
18385
  if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
18225
18386
  const optionsKey = allowOpaqueOptionsIdentity ? resolveEventListenerCaptureValueIdentityKey(unwrappedOptions, context) : null;
18226
18387
  return optionsKey ? `options:${optionsKey}` : null;
@@ -18267,12 +18428,8 @@ const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, conte
18267
18428
  return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
18268
18429
  };
18269
18430
  const findAssignedResourceKey = (resourceNode, context) => {
18270
- let currentNode = resourceNode;
18271
- let parentNode = currentNode.parent;
18272
- while (isNodeOfType(parentNode, "ChainExpression")) {
18273
- currentNode = parentNode;
18274
- parentNode = currentNode.parent;
18275
- }
18431
+ const currentNode = findTransparentExpressionRoot(resourceNode);
18432
+ const parentNode = currentNode.parent;
18276
18433
  if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode) return resolveExpressionKey(parentNode.id, context);
18277
18434
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
18278
18435
  return null;
@@ -18543,6 +18700,18 @@ const resolveIteratorCollectionKey = (expression, context) => {
18543
18700
  }
18544
18701
  return null;
18545
18702
  };
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
+ };
18546
18715
  const isStableLoopReceiver = (expression, context) => {
18547
18716
  if (!expression) return false;
18548
18717
  const unwrappedExpression = stripParenExpression(expression);
@@ -19313,6 +19482,28 @@ const isFunctionReturnedFromReactHook = (functionNode, context, requireRefProper
19313
19482
  });
19314
19483
  };
19315
19484
  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
+ };
19316
19507
  const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19317
19508
  if (!isNodeOfType(usage.node, "CallExpression")) return false;
19318
19509
  const usageFunction = findEnclosingFunction$1(usage.node);
@@ -19333,7 +19524,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19333
19524
  if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
19334
19525
  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);
19335
19526
  });
19336
- const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
19527
+ const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? findCallbackRefReplacementReleaseGuard(releaseCall, usageFunction, releaseReceiverKey, registrationReceiverKey, context) ?? releaseCall;
19337
19528
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
19338
19529
  return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
19339
19530
  };
@@ -19483,14 +19674,21 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19483
19674
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
19484
19675
  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;
19485
19676
  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;
19487
19677
  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
+ }
19488
19683
  if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
19489
19684
  if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
19490
19685
  if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
19491
19686
  }
19492
19687
  if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
19493
- if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) 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;
19494
19692
  if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
19495
19693
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
19496
19694
  if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
@@ -19527,7 +19725,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19527
19725
  const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
19528
19726
  const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
19529
19727
  if (!releaseHandler) return releaseVerbName === "off";
19530
- const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
19728
+ const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.handlerKey ?? usage.eventKey : usage.handlerKey;
19531
19729
  const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
19532
19730
  return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
19533
19731
  }
@@ -20001,6 +20199,7 @@ const findRetainedFunctionLeak = (retainedFunction, context, options) => {
20001
20199
  walkAst(body, (child) => {
20002
20200
  if (leak !== null) return false;
20003
20201
  if (isFunctionLike$1(child)) return false;
20202
+ if (!isNodeReachableWithinFunction(child, context)) return false;
20004
20203
  if (isSocketConstruction(child) && !doesResourceResultEscape(child, allowReturnedSocketEscape, false, context)) {
20005
20204
  const socketUsage = {
20006
20205
  kind: "socket",
@@ -20253,6 +20452,164 @@ const isInlineRetainedHandlerFunction = (functionNode, context) => {
20253
20452
  const objectParent = objectExpression.parent;
20254
20453
  return (isNodeOfType(objectParent, "CallExpression") && objectParent.arguments.some((argument) => argument === objectExpression) || isNodeOfType(objectParent, "JSXExpressionContainer")) && findRenderPhaseComponentOrHook(parentNode, context.scopes) !== null;
20255
20454
  };
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
+ };
20256
20613
  const effectNeedsCleanup = defineRule({
20257
20614
  id: "effect-needs-cleanup",
20258
20615
  title: "Effect subscription or timer never cleaned up",
@@ -20263,13 +20620,19 @@ const effectNeedsCleanup = defineRule({
20263
20620
  const reportRetainedLeak = (retainedFunction) => {
20264
20621
  const refEffectUsage = getReactRefEffectUsage(retainedFunction, context);
20265
20622
  if (!refEffectUsage && !isPotentiallyReachableFunction(retainedFunction, context)) return;
20623
+ const effectInvocations = getEffectRetainedInvocations(retainedFunction, context);
20624
+ const isEffectInvoked = effectInvocations.length > 0;
20266
20625
  const leak = findRetainedFunctionLeak(retainedFunction, context, refEffectUsage ? {
20267
20626
  allowReturnedResourceEscape: refEffectUsage.doesEffectOwnEveryResult,
20268
20627
  allowReturnedTimerEscape: false,
20269
20628
  includeOneShotTimers: true,
20270
20629
  requireCallableReturnedResource: true
20630
+ } : isEffectInvoked ? {
20631
+ allowReturnedTimerEscape: false,
20632
+ includeOneShotTimers: true
20271
20633
  } : void 0);
20272
20634
  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;
20273
20636
  const resourceNoun = RESOURCE_NOUN_BY_KIND[leak.kind];
20274
20637
  context.report({
20275
20638
  node: leak.node,
@@ -24689,14 +25052,14 @@ const getFirstLegendChild = (children, targetNode) => {
24689
25052
  if (isNodeOfType(child, "JSXExpressionContainer")) {
24690
25053
  const potentialLegends = [];
24691
25054
  collectPotentialLegends(child.expression, potentialLegends);
24692
- const containingLegend = potentialLegends.find((legend) => isDescendantOf(targetNode, legend));
25055
+ const containingLegend = potentialLegends.find((legend) => isDescendantOf$1(targetNode, legend));
24693
25056
  if (containingLegend) return containingLegend;
24694
25057
  if (potentialLegends[0]) return potentialLegends[0];
24695
25058
  }
24696
25059
  }
24697
25060
  return null;
24698
25061
  };
24699
- const isDescendantOf = (node, ancestor) => {
25062
+ const isDescendantOf$1 = (node, ancestor) => {
24700
25063
  let current = node.parent;
24701
25064
  while (current) {
24702
25065
  if (current === ancestor) return true;
@@ -24721,7 +25084,7 @@ const isDisabledByFieldsetAncestor = (node, context) => {
24721
25084
  while (ancestor) {
24722
25085
  if (isNodeOfType(ancestor, "JSXElement") && resolveJsxElementType(ancestor.openingElement) === "fieldset" && openingElementMayBeDisabled(ancestor.openingElement, context)) {
24723
25086
  const firstLegend = getFirstLegendChild(ancestor.children, node);
24724
- if (!firstLegend || !isDescendantOf(node, firstLegend)) return true;
25087
+ if (!firstLegend || !isDescendantOf$1(node, firstLegend)) return true;
24725
25088
  }
24726
25089
  ancestor = ancestor.parent;
24727
25090
  }
@@ -29098,6 +29461,11 @@ const jsCacheStorage = defineRule({
29098
29461
  });
29099
29462
  //#endregion
29100
29463
  //#region src/plugin/rules/js-performance/js-combine-iterations.ts
29464
+ const SMALL_ARRAY_NON_MUTATING_METHODS = new Set([
29465
+ ...CHAINABLE_ITERATION_METHODS,
29466
+ "find",
29467
+ "some"
29468
+ ]);
29101
29469
  const isIteratorProducingCall = (callExpression, generatorNamesInFile) => {
29102
29470
  const callee = callExpression.callee;
29103
29471
  if (isNodeOfType(callee, "MemberExpression")) {
@@ -29209,21 +29577,34 @@ const isStringSplitRootedChain = (receiverNode) => {
29209
29577
  return false;
29210
29578
  };
29211
29579
  const isSmallLiteralArray = (node) => {
29212
- if (!isNodeOfType(node, "ArrayExpression")) return false;
29213
- const elements = node.elements ?? [];
29214
- if (elements.length === 0 || elements.length > 8) return false;
29580
+ const arrayNode = stripParenExpression(node);
29581
+ if (!isNodeOfType(arrayNode, "ArrayExpression")) return false;
29582
+ const elements = arrayNode.elements ?? [];
29583
+ if (elements.length === 0 || elements.length > 9) return false;
29215
29584
  for (const element of elements) {
29216
29585
  if (!element) continue;
29217
29586
  if (isNodeOfType(element, "SpreadElement")) return false;
29218
29587
  }
29219
29588
  return true;
29220
29589
  };
29221
- const isSmallLiteralArrayRootedChain = (receiverNode, smallConstArrayNames) => {
29590
+ const isNonMutatingSmallArrayMethodReference = (identifier) => {
29591
+ const identifierRoot = findTransparentExpressionRoot(identifier);
29592
+ const memberExpression = identifierRoot.parent;
29593
+ if (!isNodeOfType(memberExpression, "MemberExpression") || memberExpression.object !== identifierRoot || !isNodeOfType(memberExpression.property, "Identifier") || !SMALL_ARRAY_NON_MUTATING_METHODS.has(memberExpression.property.name)) return false;
29594
+ const callExpression = memberExpression.parent;
29595
+ return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === memberExpression;
29596
+ };
29597
+ const isSmallLiteralArrayRootedChain = (receiverNode, scopes) => {
29222
29598
  let cursor = receiverNode;
29223
29599
  while (cursor) {
29224
29600
  cursor = stripParenExpression(cursor);
29225
29601
  if (isNodeOfType(cursor, "ArrayExpression")) return isSmallLiteralArray(cursor);
29226
- if (isNodeOfType(cursor, "Identifier")) return smallConstArrayNames.has(cursor.name);
29602
+ if (isNodeOfType(cursor, "Identifier")) {
29603
+ const symbol = scopes.symbolFor(cursor);
29604
+ if (!symbol?.initializer || !isSmallLiteralArray(symbol.initializer)) return false;
29605
+ if (!isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "Identifier")) return false;
29606
+ return (symbol.kind === "const" || symbol.kind === "let" || symbol.kind === "var") && symbol.references.every((reference) => reference.flag === "read" && isNonMutatingSmallArrayMethodReference(reference.identifier));
29607
+ }
29227
29608
  if (!isNodeOfType(cursor, "CallExpression")) return false;
29228
29609
  if (!isChainPassThroughCall(cursor)) return false;
29229
29610
  const nextCallee = cursor.callee;
@@ -29232,22 +29613,6 @@ const isSmallLiteralArrayRootedChain = (receiverNode, smallConstArrayNames) => {
29232
29613
  }
29233
29614
  return false;
29234
29615
  };
29235
- const collectSmallConstArrayNames = (programNode) => {
29236
- const names = /* @__PURE__ */ new Set();
29237
- const statements = programNode.body ?? [];
29238
- for (const statement of statements) {
29239
- const declaration = isNodeOfType(statement, "ExportNamedDeclaration") ? statement.declaration : statement;
29240
- if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) continue;
29241
- if (declaration.kind !== "const") continue;
29242
- for (const declarator of declaration.declarations ?? []) {
29243
- if (!isNodeOfType(declarator, "VariableDeclarator")) continue;
29244
- if (!isNodeOfType(declarator.id, "Identifier")) continue;
29245
- if (!declarator.init || !isSmallLiteralArray(declarator.init)) continue;
29246
- names.add(declarator.id.name);
29247
- }
29248
- }
29249
- return names;
29250
- };
29251
29616
  const collectGeneratorNames = (programNode) => {
29252
29617
  const generatorNames = /* @__PURE__ */ new Set();
29253
29618
  walkAst(programNode, (child) => {
@@ -29268,16 +29633,11 @@ const jsCombineIterations = defineRule({
29268
29633
  create: (context) => {
29269
29634
  let programNode = null;
29270
29635
  let generatorNamesInFile = null;
29271
- let smallConstArrayNames = null;
29272
29636
  const coveredChainCalls = /* @__PURE__ */ new WeakSet();
29273
29637
  const getGeneratorNamesInFile = () => {
29274
29638
  generatorNamesInFile ??= programNode ? collectGeneratorNames(programNode) : /* @__PURE__ */ new Set();
29275
29639
  return generatorNamesInFile;
29276
29640
  };
29277
- const getSmallConstArrayNames = () => {
29278
- smallConstArrayNames ??= programNode ? collectSmallConstArrayNames(programNode) : /* @__PURE__ */ new Set();
29279
- return smallConstArrayNames;
29280
- };
29281
29641
  return {
29282
29642
  Program(node) {
29283
29643
  programNode = node;
@@ -29307,7 +29667,7 @@ const jsCombineIterations = defineRule({
29307
29667
  if (isTypePredicateArrow(filterArgument)) return;
29308
29668
  }
29309
29669
  if (isReceiverChainIteratorRooted(innerCall.callee.object, getGeneratorNamesInFile())) return;
29310
- if (isSmallLiteralArrayRootedChain(innerCall.callee.object, getSmallConstArrayNames())) return;
29670
+ if (isSmallLiteralArrayRootedChain(innerCall.callee.object, context.scopes)) return;
29311
29671
  if (isStringSplitRootedChain(innerCall.callee.object)) return;
29312
29672
  coveredChainCalls.add(innerCall);
29313
29673
  context.report({
@@ -30534,7 +30894,8 @@ const STRING_TYPED_PROPERTY_NAMES = new Set([
30534
30894
  "code",
30535
30895
  "label",
30536
30896
  "slug",
30537
- "prefix"
30897
+ "prefix",
30898
+ "__html"
30538
30899
  ]);
30539
30900
  const STRING_TYPED_IDENTIFIER_SUFFIXES = [
30540
30901
  "Text",
@@ -30652,13 +31013,25 @@ const STRING_TYPED_IDENTIFIER_NAMES = new Set([
30652
31013
  "title"
30653
31014
  ]);
30654
31015
  const STRING_RETURNING_CALLEE_PREFIX_PATTERN = /^(?:normalize|format|stringify|serialize)/;
31016
+ const FRESH_ARRAY_METHOD_NAMES$2 = new Set([
31017
+ "concat",
31018
+ "filter",
31019
+ "flat",
31020
+ "flatMap",
31021
+ "map",
31022
+ "slice",
31023
+ "split"
31024
+ ]);
30655
31025
  const isLikelyStringReceiver = (receiver) => {
30656
31026
  if (!receiver) return false;
31027
+ const unwrappedReceiver = stripParenExpression(receiver);
31028
+ if (unwrappedReceiver !== receiver) return isLikelyStringReceiver(unwrappedReceiver);
30657
31029
  if (isNodeOfType(receiver, "Literal") && typeof receiver.value === "string") return true;
30658
31030
  if (isNodeOfType(receiver, "TemplateLiteral")) return true;
30659
31031
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && receiver.callee.name === "String") return true;
30660
31032
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && STRING_RETURNING_METHODS.has(receiver.callee.property.name)) return true;
30661
31033
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && STRING_RETURNING_CALLEE_PREFIX_PATTERN.test(receiver.callee.name)) return true;
31034
+ if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && (receiver.callee.property.name === "concat" || receiver.callee.property.name === "slice") && isLikelyStringReceiver(receiver.callee.object)) return true;
30662
31035
  if (isNodeOfType(receiver, "MemberExpression") && isNodeOfType(receiver.property, "Identifier")) {
30663
31036
  if (STRING_TYPED_PROPERTY_NAMES.has(receiver.property.name)) return true;
30664
31037
  }
@@ -30676,8 +31049,46 @@ const isLikelyStringReceiver = (receiver) => {
30676
31049
  }
30677
31050
  if (isNodeOfType(receiver, "BinaryExpression") && receiver.operator === "+") return isLikelyStringReceiver(receiver.left) || isLikelyStringReceiver(receiver.right);
30678
31051
  if (isNodeOfType(receiver, "ConditionalExpression")) return isLikelyStringReceiver(receiver.consequent) && isLikelyStringReceiver(receiver.alternate);
31052
+ if (isNodeOfType(receiver, "LogicalExpression")) return isLikelyStringReceiver(receiver.left) && isLikelyStringReceiver(receiver.right);
30679
31053
  return false;
30680
31054
  };
31055
+ const isFreshArrayReceiver = (receiver) => {
31056
+ const unwrappedReceiver = stripParenExpression(receiver);
31057
+ if (unwrappedReceiver !== receiver) return isFreshArrayReceiver(unwrappedReceiver);
31058
+ if (!isNodeOfType(receiver, "CallExpression") || !isNodeOfType(receiver.callee, "MemberExpression") || !isNodeOfType(receiver.callee.property, "Identifier")) return false;
31059
+ if (!FRESH_ARRAY_METHOD_NAMES$2.has(receiver.callee.property.name)) return false;
31060
+ if (receiver.callee.property.name === "split") return isLikelyStringReceiver(receiver.callee.object);
31061
+ const sourceReceiver = stripParenExpression(receiver.callee.object);
31062
+ return isKnownNativeArrayReceiver(sourceReceiver) || isFreshArrayReceiver(sourceReceiver);
31063
+ };
31064
+ const isSmallRestHelperOmissionList = (node) => {
31065
+ if (!node) return false;
31066
+ const candidate = stripParenExpression(node);
31067
+ if (!isNodeOfType(candidate, "ArrayExpression")) return false;
31068
+ const elements = candidate.elements ?? [];
31069
+ return elements.length <= 8 && elements.every((element) => element === null || !isNodeOfType(element, "SpreadElement"));
31070
+ };
31071
+ const isTypeScriptRestHelperLookup = (lookupCall, receiver, scopes) => {
31072
+ if (!isNodeOfType(receiver, "Identifier")) return false;
31073
+ const enclosingFunction = findEnclosingFunction$1(lookupCall);
31074
+ if (!isNodeOfType(enclosingFunction, "FunctionExpression") || !isNodeOfType(enclosingFunction.params?.[1], "Identifier") || enclosingFunction.params[1].name !== receiver.name) return false;
31075
+ let bindingIdentifier = null;
31076
+ let ancestor = enclosingFunction.parent;
31077
+ while (ancestor && !isFunctionLike$1(ancestor)) {
31078
+ if (isNodeOfType(ancestor, "VariableDeclarator") && isNodeOfType(ancestor.id, "Identifier") && ancestor.id.name === "__rest") {
31079
+ bindingIdentifier = ancestor.id;
31080
+ break;
31081
+ }
31082
+ ancestor = ancestor.parent;
31083
+ }
31084
+ if (!bindingIdentifier) return false;
31085
+ const helperSymbol = scopes.symbolFor(bindingIdentifier);
31086
+ if (!helperSymbol || helperSymbol.references.length === 0) return false;
31087
+ return helperSymbol.references.every((reference) => {
31088
+ const callExpression = reference.identifier.parent;
31089
+ return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === reference.identifier && isSmallRestHelperOmissionList(callExpression.arguments?.[1]);
31090
+ });
31091
+ };
30681
31092
  const INDEX_LIKE_IDENTIFIER_NAMES = new Set([
30682
31093
  "i",
30683
31094
  "j",
@@ -31274,6 +31685,8 @@ const jsSetMapLookups = defineRule({
31274
31685
  const query = node.arguments[0];
31275
31686
  if (methodName === "indexOf" && !isKnownSafeIndexOfQuery(query) && (isKnownUnsafeIndexOfQuery(query, receiver) || isKnownUnsafeIndexOfReceiver(receiver))) return;
31276
31687
  if (isLikelyStringReceiver(receiver)) return;
31688
+ if (isFreshArrayReceiver(receiver)) return;
31689
+ if (isTypeScriptRestHelperLookup(node, receiver, context.scopes)) return;
31277
31690
  if (isSmallInlineLiteralArray(receiver)) return;
31278
31691
  if (isScreamingSnakeCaseConstantReceiver(receiver)) return;
31279
31692
  if (isSmallFixedListMember(receiver)) return;
@@ -45187,14 +45600,6 @@ const noAriaInvalidWithoutDescription = defineRule({
45187
45600
  } })
45188
45601
  });
45189
45602
  //#endregion
45190
- //#region src/plugin/utils/is-early-exit-statement.ts
45191
- const isEarlyExitStatement$1 = (statement) => {
45192
- if (!statement) return false;
45193
- if (statementAlwaysExits$1(statement)) return true;
45194
- if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
45195
- return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
45196
- };
45197
- //#endregion
45198
45603
  //#region src/plugin/utils/unwrap-negative-guard-form.ts
45199
45604
  const unwrapNegativeGuardForm = (test) => {
45200
45605
  const expression = stripParenExpression(test);
@@ -55682,9 +56087,13 @@ const isInitialOnlyPropName = (propName) => {
55682
56087
  return /^initial[A-Z]/.test(propName) || /^default[A-Z]/.test(propName) || /^seed[A-Z]/.test(propName) || /^starting[A-Z]/.test(propName) || /^baseline[A-Z]/.test(propName) || /^preset[A-Z]/.test(propName);
55683
56088
  };
55684
56089
  //#endregion
56090
+ //#region src/plugin/utils/nextjs-page-data-export-names.ts
56091
+ const NEXTJS_PAGE_DATA_EXPORT_NAMES = new Set(["getServerSideProps", "getStaticProps"]);
56092
+ //#endregion
55685
56093
  //#region src/plugin/rules/state-and-effects/no-derived-use-state.ts
55686
56094
  const isInitialOnlySeedName = (propName) => isInitialOnlyPropName(propName) || propName === "initial" || propName === "autoFocus" || propName === "autoPlay" || propName === "startOpen" || /^initially[A-Z]/.test(propName) || /Initial([A-Z]|$)/.test(propName);
55687
56095
  const SNAPSHOT_STATE_NAME_PATTERN = /^(initial|previous|prev|preserved|saved|original|cached|snapshot|prior|debounced|deferred)([A-Z_]|$)/;
56096
+ const INTERNAL_STATE_NAME_PATTERN = /^(internal|uncontrolled)([A-Z_]|$)/;
55688
56097
  const getStateSetterName = (useStateCall) => {
55689
56098
  const declarator = useStateCall.parent;
55690
56099
  if (!isNodeOfType(declarator, "VariableDeclarator")) return null;
@@ -55838,7 +56247,6 @@ const isDraftCommittedToParent = (componentFunction, stateValueName, isPropName)
55838
56247
  });
55839
56248
  return isCommitted;
55840
56249
  };
55841
- const NEXTJS_PAGE_DATA_EXPORT_NAMES = new Set(["getServerSideProps", "getStaticProps"]);
55842
56250
  const isNextjsDataFetchingPage = (node) => {
55843
56251
  const program = findProgramRoot(node);
55844
56252
  if (!program) return false;
@@ -55882,6 +56290,48 @@ const isInRenderScope = (node, componentFunction) => {
55882
56290
  }
55883
56291
  return true;
55884
56292
  };
56293
+ const isReferenceToBinding = (reference, bindingIdentifier, context) => {
56294
+ if (!isNodeOfType(reference, "Identifier")) return false;
56295
+ const bindingSymbol = context.scopes.symbolFor(bindingIdentifier);
56296
+ if (!bindingSymbol) return false;
56297
+ return (context.scopes.referenceFor(reference)?.resolvedSymbol)?.id === bindingSymbol.id;
56298
+ };
56299
+ const isControlledPropFallbackExpression = (expression, stateBinding, isPropName, context) => {
56300
+ if (isNodeOfType(expression, "ConditionalExpression")) {
56301
+ const consequent = unwrapInitializerSeed(expression.consequent);
56302
+ const alternate = unwrapInitializerSeed(expression.alternate);
56303
+ return isReferenceToBinding(consequent, stateBinding, context) && isPropDerivedArgument(alternate, isPropName) || isPropDerivedArgument(consequent, isPropName) && isReferenceToBinding(alternate, stateBinding, context);
56304
+ }
56305
+ return isNodeOfType(expression, "LogicalExpression") && expression.operator === "??" && isPropDerivedArgument(unwrapInitializerSeed(expression.left), isPropName) && isReferenceToBinding(unwrapInitializerSeed(expression.right), stateBinding, context);
56306
+ };
56307
+ const isUserEditableControlledFallback = (useStateCall, isPropName, context) => {
56308
+ const declarator = useStateCall.parent;
56309
+ if (!isNodeOfType(declarator, "VariableDeclarator")) return false;
56310
+ if (!isNodeOfType(declarator.id, "ArrayPattern")) return false;
56311
+ const stateBinding = declarator.id.elements?.[0];
56312
+ const setterBinding = declarator.id.elements?.[1];
56313
+ if (!isNodeOfType(stateBinding, "Identifier") || !isNodeOfType(setterBinding, "Identifier")) return false;
56314
+ if (!INTERNAL_STATE_NAME_PATTERN.test(stateBinding.name)) return false;
56315
+ const componentFunction = findEnclosingFunction$1(useStateCall);
56316
+ if (!componentFunction) return false;
56317
+ let hasControlledFallback = false;
56318
+ let hasUserEdit = false;
56319
+ walkAst(componentFunction, (child) => {
56320
+ if (child !== componentFunction && isFunctionLike$1(child)) return false;
56321
+ if (!hasControlledFallback && (isNodeOfType(child, "ConditionalExpression") || isNodeOfType(child, "LogicalExpression")) && isControlledPropFallbackExpression(child, stateBinding, isPropName, context)) hasControlledFallback = true;
56322
+ });
56323
+ walkAst(componentFunction, (child) => {
56324
+ if (hasUserEdit) return false;
56325
+ if (!isNodeOfType(child, "CallExpression")) return;
56326
+ if (!isReferenceToBinding(child.callee, setterBinding, context)) return;
56327
+ if (!isHandlerShapedReseed(child, componentFunction)) return;
56328
+ const setterArgument = child.arguments?.[0];
56329
+ if (!setterArgument || isPropDerivedArgument(unwrapInitializerSeed(setterArgument), isPropName)) return;
56330
+ hasUserEdit = true;
56331
+ return false;
56332
+ });
56333
+ return hasControlledFallback && hasUserEdit;
56334
+ };
55885
56335
  const getStateValueName = (useStateCall) => {
55886
56336
  const declarator = useStateCall.parent;
55887
56337
  if (!isNodeOfType(declarator, "VariableDeclarator")) return null;
@@ -55946,6 +56396,7 @@ const noDerivedUseState = defineRule({
55946
56396
  const reportStalePropCopy = (propName) => {
55947
56397
  if (isIntentionalSnapshotState(node)) return;
55948
56398
  if (hasSessionDismissProp(propStackTracker.getCurrentPropNames())) return;
56399
+ if (isUserEditableControlledFallback(node, propStackTracker.isPropName, context)) return;
55949
56400
  if (isDraftReseedOrRenderAdjusted(node, propStackTracker.isPropName)) return;
55950
56401
  if (isEffectDrivenResync(node)) return;
55951
56402
  if (isNextjsDataFetchingPage(node)) return;
@@ -64090,6 +64541,106 @@ const isGatedByFalsyInitialState = (node, scopes) => {
64090
64541
  };
64091
64542
  //#endregion
64092
64543
  //#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
64544
+ const findGuardingIfStatements = (node, functionBoundary) => {
64545
+ const guardingIfStatements = [];
64546
+ let currentNode = node.parent;
64547
+ while (currentNode && currentNode !== functionBoundary) {
64548
+ if (isNodeOfType(currentNode, "IfStatement")) guardingIfStatements.push(currentNode);
64549
+ currentNode = currentNode.parent;
64550
+ }
64551
+ return guardingIfStatements;
64552
+ };
64553
+ const doesNodeReadSymbol = (node, symbol) => {
64554
+ let doesReadSymbol = false;
64555
+ walkAst(node, (childNode) => {
64556
+ if (isNodeOfType(childNode, "Identifier") && symbol.references.some((reference) => reference.identifier === childNode && reference.flag !== "write")) {
64557
+ doesReadSymbol = true;
64558
+ return false;
64559
+ }
64560
+ });
64561
+ return doesReadSymbol;
64562
+ };
64563
+ const collectWrittenSymbols = (node, scopes) => {
64564
+ const writtenSymbols = /* @__PURE__ */ new Set();
64565
+ walkAst(node, (childNode) => {
64566
+ if (childNode !== node && isFunctionLike$1(childNode)) return false;
64567
+ if (!isNodeOfType(childNode, "Identifier")) return;
64568
+ const reference = scopes.referenceFor(childNode);
64569
+ if (!reference || reference.flag === "read" || !reference.resolvedSymbol) return;
64570
+ writtenSymbols.add(reference.resolvedSymbol);
64571
+ });
64572
+ return writtenSymbols;
64573
+ };
64574
+ const isDescendantOf = (node, ancestorNode) => {
64575
+ let currentNode = node.parent;
64576
+ while (currentNode) {
64577
+ if (currentNode === ancestorNode) return true;
64578
+ currentNode = currentNode.parent;
64579
+ }
64580
+ return false;
64581
+ };
64582
+ const getAssignedValue = (identifier) => {
64583
+ const assignmentExpression = identifier.parent;
64584
+ return isNodeOfType(assignmentExpression, "AssignmentExpression") && assignmentExpression.operator === "=" && assignmentExpression.left === identifier ? assignmentExpression.right : null;
64585
+ };
64586
+ const doesGuardPreserveInitialSymbolValue = (symbol, guardingIfStatement, scopes) => {
64587
+ const initialValue = symbol.initializer;
64588
+ if (!initialValue) return false;
64589
+ const guardedWrites = symbol.references.filter((reference) => reference.flag !== "read" && isDescendantOf(reference.identifier, guardingIfStatement));
64590
+ return guardedWrites.length > 0 && guardedWrites.every((reference) => {
64591
+ const assignedValue = getAssignedValue(reference.identifier);
64592
+ return Boolean(assignedValue && areExpressionsStructurallyEqual(initialValue, assignedValue) && doEquivalentExpressionBindingsMatch(initialValue, assignedValue, scopes));
64593
+ });
64594
+ };
64595
+ 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));
64596
+ const isUnconditionalOrStaticallySelected = (node, context) => {
64597
+ if (context.cfg.isUnconditionalFromEntry(node)) return true;
64598
+ let currentNode = node;
64599
+ let outermostStaticIfStatement = null;
64600
+ let parentNode = currentNode.parent;
64601
+ while (parentNode) {
64602
+ if (isFunctionLike$1(parentNode)) break;
64603
+ if (isNodeOfType(parentNode, "IfStatement")) {
64604
+ const staticResult = readInitialStateBoolean(parentNode.test, context.scopes);
64605
+ let selectedBranch = null;
64606
+ if (staticResult === true) selectedBranch = parentNode.consequent;
64607
+ if (staticResult === false) selectedBranch = parentNode.alternate;
64608
+ if (!selectedBranch || currentNode !== selectedBranch && !isDescendantOf(currentNode, selectedBranch)) return false;
64609
+ outermostStaticIfStatement = parentNode;
64610
+ }
64611
+ currentNode = parentNode;
64612
+ parentNode = currentNode.parent;
64613
+ }
64614
+ return Boolean(outermostStaticIfStatement && context.cfg.isUnconditionalFromEntry(outermostStaticIfStatement));
64615
+ };
64616
+ const containsExplicitReactRuntimeReference = (node, scopes) => {
64617
+ let hasRuntimeReference = false;
64618
+ walkAst(node, (childNode) => {
64619
+ if (isNodeOfType(childNode, "ImportDeclaration") && typeof childNode.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(childNode.source.value)) {
64620
+ hasRuntimeReference = true;
64621
+ return false;
64622
+ }
64623
+ if (!isNodeOfType(childNode, "CallExpression")) return;
64624
+ const sourceArgument = (childNode.arguments ?? [])[0];
64625
+ 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;
64626
+ hasRuntimeReference = true;
64627
+ return false;
64628
+ });
64629
+ return hasRuntimeReference;
64630
+ };
64631
+ const findComponentRenderingLocalFunctionResult = (functionNode, scopes) => {
64632
+ const bindingIdentifier = getDirectFunctionBindingIdentifier(functionNode);
64633
+ if (!isNodeOfType(bindingIdentifier, "Identifier")) return null;
64634
+ const functionSymbol = scopes.symbolFor(bindingIdentifier);
64635
+ if (!functionSymbol) return null;
64636
+ for (const reference of functionSymbol.references) {
64637
+ const callExpression = reference.identifier.parent;
64638
+ if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== reference.identifier) continue;
64639
+ const componentOrHookNode = findRenderPhaseComponentOrHook(callExpression, scopes);
64640
+ if (componentOrHookNode && isInRenderedOutput(callExpression, componentOrHookNode, scopes)) return componentOrHookNode;
64641
+ }
64642
+ return null;
64643
+ };
64093
64644
  const evaluateEquality$1 = (operator, left, right) => {
64094
64645
  if (operator === "===" || operator === "==") return left === right;
64095
64646
  if (operator === "!==" || operator === "!=") return left !== right;
@@ -64140,6 +64691,107 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
64140
64691
  if (leftResult === false && rightResult === false) return false;
64141
64692
  return null;
64142
64693
  };
64694
+ const areLooselyEqualPrimitiveResults = (left, right) => {
64695
+ if (left.kind === right.kind) return left.value === right.value;
64696
+ if (left.kind === "null" && right.kind === "undefined" || left.kind === "undefined" && right.kind === "null") return true;
64697
+ if (left.kind === "boolean") return areLooselyEqualPrimitiveResults({
64698
+ kind: "number",
64699
+ value: left.value ? 1 : 0
64700
+ }, right);
64701
+ if (right.kind === "boolean") return areLooselyEqualPrimitiveResults(left, {
64702
+ kind: "number",
64703
+ value: right.value ? 1 : 0
64704
+ });
64705
+ if (left.kind === "number" && right.kind === "string") return left.value === Number(right.value);
64706
+ if (left.kind === "string" && right.kind === "number") return Number(left.value) === right.value;
64707
+ return false;
64708
+ };
64709
+ const readHydrationPrimitiveResult = (expression, context, runtime, state) => {
64710
+ const unwrappedExpression = stripParenExpression(expression);
64711
+ const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
64712
+ if (predicateMatch) return {
64713
+ kind: "boolean",
64714
+ value: predicateMatch[`${runtime}Result`]
64715
+ };
64716
+ if (isNodeOfType(unwrappedExpression, "Literal")) {
64717
+ const value = unwrappedExpression.value;
64718
+ if (value === null) return {
64719
+ kind: "null",
64720
+ value
64721
+ };
64722
+ if (typeof value === "boolean") return {
64723
+ kind: "boolean",
64724
+ value
64725
+ };
64726
+ if (typeof value === "number") return {
64727
+ kind: "number",
64728
+ value
64729
+ };
64730
+ if (typeof value === "string") return {
64731
+ kind: "string",
64732
+ value
64733
+ };
64734
+ return null;
64735
+ }
64736
+ if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return {
64737
+ kind: "undefined",
64738
+ value: void 0
64739
+ };
64740
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
64741
+ const symbol = context.scopes.symbolFor(unwrappedExpression);
64742
+ const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
64743
+ if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
64744
+ state.visitedSymbolIds.add(symbol.id);
64745
+ const result = readHydrationPrimitiveResult(parameterValue, context, runtime, state);
64746
+ state.visitedSymbolIds.delete(symbol.id);
64747
+ return result;
64748
+ }
64749
+ if (symbol && symbol.kind === "const" && symbol.initializer && symbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(symbol.id)) {
64750
+ state.visitedSymbolIds.add(symbol.id);
64751
+ const result = readHydrationPrimitiveResult(symbol.initializer, context, runtime, state);
64752
+ state.visitedSymbolIds.delete(symbol.id);
64753
+ return result;
64754
+ }
64755
+ }
64756
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64757
+ const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64758
+ return argumentResult === null ? null : {
64759
+ kind: "boolean",
64760
+ value: !argumentResult
64761
+ };
64762
+ }
64763
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64764
+ const leftResult = readHydrationPrimitiveResult(unwrappedExpression.left, context, runtime, state);
64765
+ const rightResult = readHydrationPrimitiveResult(unwrappedExpression.right, context, runtime, state);
64766
+ if (!leftResult || !rightResult) return null;
64767
+ if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
64768
+ const areEqual = leftResult.kind === rightResult.kind && leftResult.value === rightResult.value;
64769
+ return {
64770
+ kind: "boolean",
64771
+ value: unwrappedExpression.operator === "===" ? areEqual : !areEqual
64772
+ };
64773
+ }
64774
+ if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
64775
+ const areEqual = areLooselyEqualPrimitiveResults(leftResult, rightResult);
64776
+ return {
64777
+ kind: "boolean",
64778
+ value: unwrappedExpression.operator === "==" ? areEqual : !areEqual
64779
+ };
64780
+ }
64781
+ }
64782
+ if (isNodeOfType(unwrappedExpression, "CallExpression")) {
64783
+ const callArguments = unwrappedExpression.arguments ?? [];
64784
+ const callee = stripParenExpression(unwrappedExpression.callee);
64785
+ if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) {
64786
+ const argumentResult = readHydrationConditionResult(callArguments[0], context, runtime, state);
64787
+ return argumentResult === null ? null : {
64788
+ kind: "boolean",
64789
+ value: argumentResult
64790
+ };
64791
+ }
64792
+ }
64793
+ return null;
64794
+ };
64143
64795
  const readHydrationConditionResult = (expression, context, runtime, state) => {
64144
64796
  const unwrappedExpression = stripParenExpression(expression);
64145
64797
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64188,6 +64840,10 @@ const readHydrationConditionResult = (expression, context, runtime, state) => {
64188
64840
  parameterValuesBySymbolId
64189
64841
  });
64190
64842
  }
64843
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64844
+ const result = readHydrationPrimitiveResult(unwrappedExpression, context, runtime, state);
64845
+ return result?.kind === "boolean" && typeof result.value === "boolean" ? result.value : null;
64846
+ }
64191
64847
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64192
64848
  const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64193
64849
  return argumentResult === null ? null : !argumentResult;
@@ -64248,13 +64904,19 @@ const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, sc
64248
64904
  const rightSymbol = scopes.symbolFor(right);
64249
64905
  return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
64250
64906
  }
64251
- if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
64252
- if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
64253
- const rightArguments = right.arguments ?? [];
64254
- return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
64255
- const rightArgument = rightArguments[index];
64256
- return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
64257
- });
64907
+ const rightEntries = new Map(Object.entries(right));
64908
+ for (const [key, leftValue] of Object.entries(left)) {
64909
+ if (key === "parent") continue;
64910
+ const rightValue = rightEntries.get(key);
64911
+ if (isAstNode(leftValue)) {
64912
+ if (!isAstNode(rightValue) || !doEquivalentExpressionBindingsMatch(leftValue, rightValue, scopes)) return false;
64913
+ continue;
64914
+ }
64915
+ if (!Array.isArray(leftValue)) continue;
64916
+ if (!Array.isArray(rightValue)) return false;
64917
+ const leftNodes = leftValue.filter(isAstNode);
64918
+ const rightNodes = rightValue.filter(isAstNode);
64919
+ if (leftNodes.length !== rightNodes.length || leftNodes.some((leftNode, index) => !rightNodes[index] || !doEquivalentExpressionBindingsMatch(leftNode, rightNodes[index], scopes))) return false;
64258
64920
  }
64259
64921
  return true;
64260
64922
  };
@@ -64268,6 +64930,57 @@ const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
64268
64930
  const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
64269
64931
  return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
64270
64932
  };
64933
+ const isExpressionProvablyReflexive = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
64934
+ const unwrappedExpression = stripParenExpression(expression);
64935
+ if (matchBrowserPredicate(unwrappedExpression, context)) return true;
64936
+ if (isNodeOfType(unwrappedExpression, "Literal")) return typeof unwrappedExpression.value !== "number" || !Number.isNaN(unwrappedExpression.value);
64937
+ if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return true;
64938
+ if (isNodeOfType(unwrappedExpression, "Identifier")) {
64939
+ const symbol = context.scopes.symbolFor(unwrappedExpression);
64940
+ if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer) return false;
64941
+ visitedSymbolIds.add(symbol.id);
64942
+ const assignedValues = symbol.references.filter((reference) => reference.flag !== "read").map((reference) => getAssignedValue(reference.identifier));
64943
+ const isReflexive = isExpressionProvablyReflexive(symbol.initializer, context, visitedSymbolIds) && assignedValues.every((assignedValue) => Boolean(assignedValue && isExpressionProvablyReflexive(assignedValue, context, visitedSymbolIds)));
64944
+ visitedSymbolIds.delete(symbol.id);
64945
+ return isReflexive;
64946
+ }
64947
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isExpressionProvablyReflexive(unwrappedExpression.consequent, context, visitedSymbolIds) && isExpressionProvablyReflexive(unwrappedExpression.alternate, context, visitedSymbolIds);
64948
+ if (isNodeOfType(unwrappedExpression, "UnaryExpression") && (unwrappedExpression.operator === "!" || unwrappedExpression.operator === "typeof" || unwrappedExpression.operator === "void")) return true;
64949
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) return unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==" || unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=";
64950
+ if (isNodeOfType(unwrappedExpression, "ArrayExpression") || isNodeOfType(unwrappedExpression, "ObjectExpression") || isNodeOfType(unwrappedExpression, "FunctionExpression") || isNodeOfType(unwrappedExpression, "ArrowFunctionExpression") || isNodeOfType(unwrappedExpression, "TemplateLiteral")) return true;
64951
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
64952
+ const callee = stripParenExpression(unwrappedExpression.callee);
64953
+ return isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee);
64954
+ };
64955
+ const getReturnedObjectPropertyValues = (node, propertyName, scopes) => {
64956
+ if (isNodeOfType(node, "ReturnStatement")) return node.argument ? getReturnedObjectPropertyValues(node.argument, propertyName, scopes) : [];
64957
+ if (isNodeOfType(node, "ObjectExpression")) return node.properties.flatMap((property) => isNodeOfType(property, "Property") && property.kind === "init" && getResolvedStaticPropertyName(property, scopes) === propertyName ? [property.value] : []);
64958
+ if (isNodeOfType(node, "IfStatement")) return [...getReturnedObjectPropertyValues(node.consequent, propertyName, scopes), ...node.alternate ? getReturnedObjectPropertyValues(node.alternate, propertyName, scopes) : []];
64959
+ if (isNodeOfType(node, "TryStatement")) return [
64960
+ ...getReturnedObjectPropertyValues(node.block, propertyName, scopes),
64961
+ ...node.handler ? getReturnedObjectPropertyValues(node.handler.body, propertyName, scopes) : [],
64962
+ ...node.finalizer ? getReturnedObjectPropertyValues(node.finalizer, propertyName, scopes) : []
64963
+ ];
64964
+ if (!isNodeOfType(node, "BlockStatement")) return [];
64965
+ const propertyValues = [];
64966
+ for (const childStatement of node.body) {
64967
+ propertyValues.push(...getReturnedObjectPropertyValues(childStatement, propertyName, scopes));
64968
+ if (statementAlwaysExits$1(childStatement)) break;
64969
+ }
64970
+ return propertyValues;
64971
+ };
64972
+ const matchHydrationFunctionPropertyResult = (functionNode, propertyName, context, state) => {
64973
+ if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
64974
+ state.visitedFunctionNodes.add(functionNode);
64975
+ const propertyValues = getReturnedObjectPropertyValues(functionNode.body, propertyName, context.scopes);
64976
+ let match = null;
64977
+ for (const propertyValue of propertyValues) {
64978
+ match = matchHydrationConditionInternal(propertyValue, context, state);
64979
+ if (match) break;
64980
+ }
64981
+ state.visitedFunctionNodes.delete(functionNode);
64982
+ return match;
64983
+ };
64271
64984
  const matchHydrationConditionInternal = (expression, context, state) => {
64272
64985
  const unwrappedExpression = stripParenExpression(expression);
64273
64986
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64284,14 +64997,90 @@ const matchHydrationConditionInternal = (expression, context, state) => {
64284
64997
  state.visitedSymbolIds.delete(symbol.id);
64285
64998
  return match;
64286
64999
  }
65000
+ if (symbol && (symbol.kind === "let" || symbol.kind === "var") && !state.visitedSymbolIds.has(symbol.id)) {
65001
+ state.visitedSymbolIds.add(symbol.id);
65002
+ if (symbol.initializer && symbol.references.every((reference) => reference.flag === "read")) {
65003
+ const match = matchHydrationConditionInternal(symbol.initializer, context, state);
65004
+ state.visitedSymbolIds.delete(symbol.id);
65005
+ return match;
65006
+ }
65007
+ for (const reference of symbol.references) {
65008
+ if (reference.flag === "read") continue;
65009
+ if (!isNodeReachableWithinFunction(reference.identifier, context)) continue;
65010
+ const enclosingFunction = findEnclosingFunction$1(reference.identifier);
65011
+ if (!enclosingFunction) continue;
65012
+ for (const guardingIfStatement of findGuardingIfStatements(reference.identifier, enclosingFunction)) {
65013
+ if (doesGuardPreserveInitialSymbolValue(symbol, guardingIfStatement, context.scopes) || isWriteOverwrittenBefore(symbol, reference.identifier, guardingIfStatement, unwrappedExpression, context)) continue;
65014
+ const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
65015
+ if (match) {
65016
+ state.visitedSymbolIds.delete(symbol.id);
65017
+ return match;
65018
+ }
65019
+ }
65020
+ }
65021
+ const readingFunction = findEnclosingFunction$1(unwrappedExpression);
65022
+ for (const reference of symbol.references) {
65023
+ if (reference.flag === "read") continue;
65024
+ const writingFunction = findEnclosingFunction$1(reference.identifier);
65025
+ if (!readingFunction || !isFunctionLike$1(writingFunction) || writingFunction === readingFunction || writingFunction.async || writingFunction.params.length > 0 || isNodeOfType(writingFunction, "FunctionDeclaration") && writingFunction.generator || isNodeOfType(writingFunction, "FunctionExpression") && writingFunction.generator) continue;
65026
+ const assignedValue = getAssignedValue(reference.identifier);
65027
+ if (symbol.initializer && assignedValue && areExpressionsStructurallyEqual(symbol.initializer, assignedValue) && doEquivalentExpressionBindingsMatch(symbol.initializer, assignedValue, context.scopes)) continue;
65028
+ const functionBinding = getDirectFunctionBindingIdentifier(writingFunction);
65029
+ if (!isNodeOfType(functionBinding, "Identifier")) continue;
65030
+ const functionSymbol = context.scopes.symbolFor(functionBinding);
65031
+ if (!functionSymbol) continue;
65032
+ for (const functionReference of functionSymbol.references) {
65033
+ const callExpression = functionReference.identifier.parent;
65034
+ if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== functionReference.identifier || (callExpression.arguments ?? []).length > 0 || findEnclosingFunction$1(callExpression) !== readingFunction || !isNodeReachableWithinFunction(callExpression, context) || getNodeStartIndex(callExpression) >= getNodeStartIndex(unwrappedExpression)) continue;
65035
+ for (const guardingIfStatement of findGuardingIfStatements(callExpression, readingFunction)) {
65036
+ if (isWriteOverwrittenBefore(symbol, callExpression, guardingIfStatement, unwrappedExpression, context)) continue;
65037
+ const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
65038
+ if (match) {
65039
+ state.visitedSymbolIds.delete(symbol.id);
65040
+ return match;
65041
+ }
65042
+ }
65043
+ }
65044
+ }
65045
+ state.visitedSymbolIds.delete(symbol.id);
65046
+ }
64287
65047
  if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
64288
65048
  state.visitedSymbolIds.add(symbol.id);
64289
65049
  const match = matchHydrationConditionInternal(symbol.initializer, context, state);
64290
65050
  state.visitedSymbolIds.delete(symbol.id);
64291
65051
  return match;
64292
65052
  }
65053
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) {
65054
+ const propertyName = getResolvedStaticPropertyName(unwrappedExpression, context.scopes, {
65055
+ allowConstNumericLiteral: true,
65056
+ stringifyNonStringLiterals: true
65057
+ });
65058
+ const object = stripParenExpression(unwrappedExpression.object);
65059
+ if (propertyName === null || !isNodeOfType(object, "CallExpression")) return null;
65060
+ const callArguments = object.arguments ?? [];
65061
+ if (isReactApiCall(object, "useMemo", context.scopes, {
65062
+ allowGlobalReactNamespace: true,
65063
+ resolveNamedAliases: true
65064
+ })) {
65065
+ const callbackArgument = callArguments[0];
65066
+ if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
65067
+ const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
65068
+ return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionPropertyResult(callbackFunction, propertyName, context, state) : null;
65069
+ }
65070
+ const helperFunction = resolveExactLocalFunction(object.callee, context.scopes);
65071
+ return isFunctionLike$1(helperFunction) && helperFunction.params.length === 0 && callArguments.length === 0 ? matchHydrationFunctionPropertyResult(helperFunction, propertyName, context, state) : null;
65072
+ }
64293
65073
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
64294
65074
  const callArguments = unwrappedExpression.arguments ?? [];
65075
+ if (isReactApiCall(unwrappedExpression, "useState", context.scopes, {
65076
+ allowGlobalReactNamespace: true,
65077
+ resolveNamedAliases: true
65078
+ })) {
65079
+ const initialState = callArguments[0];
65080
+ if (!initialState || isNodeOfType(initialState, "SpreadElement")) return null;
65081
+ const lazyInitializer = resolveExactLocalFunction(initialState, context.scopes);
65082
+ return isFunctionLike$1(lazyInitializer) && lazyInitializer.params.length === 0 ? matchHydrationFunctionResult(lazyInitializer, context, state) : matchHydrationConditionInternal(initialState, context, state);
65083
+ }
64295
65084
  if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
64296
65085
  allowGlobalReactNamespace: true,
64297
65086
  resolveNamedAliases: true
@@ -64319,6 +65108,22 @@ const matchHydrationConditionInternal = (expression, context, state) => {
64319
65108
  });
64320
65109
  }
64321
65110
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
65111
+ if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
65112
+ const staticTestResult = readInitialStateBoolean(unwrappedExpression.test, context.scopes);
65113
+ if (staticTestResult !== null) return matchHydrationConditionInternal(staticTestResult ? unwrappedExpression.consequent : unwrappedExpression.alternate, context, state);
65114
+ return matchHydrationConditionInternal(unwrappedExpression.test, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.consequent, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.alternate, context, state);
65115
+ }
65116
+ if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
65117
+ if (unwrappedExpression.operator !== "===" && unwrappedExpression.operator !== "!==" && unwrappedExpression.operator !== "==" && unwrappedExpression.operator !== "!=") return null;
65118
+ const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
65119
+ const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
65120
+ const nestedMatch = leftMatch ?? rightMatch;
65121
+ if (!nestedMatch) return null;
65122
+ const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
65123
+ const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
65124
+ if (clientResult !== null && serverResult !== null) return clientResult !== serverResult ? nestedMatch : null;
65125
+ return leftMatch && rightMatch && areExpressionsStructurallyEqual(unwrappedExpression.left, unwrappedExpression.right) && doEquivalentExpressionBindingsMatch(unwrappedExpression.left, unwrappedExpression.right, context.scopes) && isExpressionProvablyReflexive(unwrappedExpression.left, context) ? null : nestedMatch;
65126
+ }
64322
65127
  if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
64323
65128
  const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
64324
65129
  const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
@@ -64335,8 +65140,13 @@ const matchHydrationReturningStatement = (statement, context, state) => {
64335
65140
  const consequentValues = getReturnedValues(statement.consequent);
64336
65141
  const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
64337
65142
  if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
65143
+ if (conditionMatch) {
65144
+ const followingReturnedValues = findFollowingReturnedValues(statement);
65145
+ 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;
65146
+ }
64338
65147
  return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
64339
65148
  }
65149
+ 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);
64340
65150
  if (!isNodeOfType(statement, "BlockStatement")) return null;
64341
65151
  for (const childStatement of statement.body) {
64342
65152
  const match = matchHydrationReturningStatement(childStatement, context, state);
@@ -64357,47 +65167,62 @@ const matchHydrationCondition = (expression, context) => matchHydrationCondition
64357
65167
  visitedFunctionNodes: /* @__PURE__ */ new Set(),
64358
65168
  visitedSymbolIds: /* @__PURE__ */ new Set()
64359
65169
  });
64360
- const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
64361
- const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
65170
+ const areNodeArraysEquivalent = (leftNodes, rightNodes, scopes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index], scopes));
65171
+ const areRenderedBranchesEquivalent = (leftNode, rightNode, scopes) => {
64362
65172
  if (!leftNode || !rightNode) return leftNode === rightNode;
64363
65173
  const left = stripParenExpression(leftNode);
64364
65174
  const right = stripParenExpression(rightNode);
64365
- if (areExpressionsStructurallyEqual(left, right)) return true;
65175
+ if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64366
65176
  if (left.type !== right.type) return false;
64367
65177
  if (isNodeOfType(left, "JSXText") && isNodeOfType(right, "JSXText")) return left.value === right.value;
64368
65178
  if (isNodeOfType(left, "JSXExpressionContainer") && isNodeOfType(right, "JSXExpressionContainer")) {
64369
65179
  if (!isAstNode(left.expression) || !isAstNode(right.expression)) return left.expression.type === right.expression.type;
64370
- return areRenderedBranchesEquivalent(left.expression, right.expression);
65180
+ return areRenderedBranchesEquivalent(left.expression, right.expression, scopes);
64371
65181
  }
64372
65182
  if (isNodeOfType(left, "JSXElement") && isNodeOfType(right, "JSXElement")) {
64373
65183
  if (flattenJsxName$1(left.openingElement.name) !== flattenJsxName$1(right.openingElement.name)) return false;
64374
- if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes)) return false;
64375
- return areNodeArraysEquivalent(left.children, right.children);
65184
+ if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes, scopes)) return false;
65185
+ return areNodeArraysEquivalent(left.children, right.children, scopes);
64376
65186
  }
64377
- if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children);
65187
+ if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children, scopes);
64378
65188
  if (isNodeOfType(left, "JSXAttribute") && isNodeOfType(right, "JSXAttribute")) {
64379
65189
  if (flattenJsxName$1(left.name) !== flattenJsxName$1(right.name)) return false;
64380
- return areRenderedBranchesEquivalent(left.value, right.value);
65190
+ return areRenderedBranchesEquivalent(left.value, right.value, scopes);
64381
65191
  }
64382
- if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument);
65192
+ if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument, scopes);
64383
65193
  if (isNodeOfType(left, "TemplateLiteral") && isNodeOfType(right, "TemplateLiteral")) {
64384
65194
  if (left.quasis.length !== right.quasis.length) return false;
64385
65195
  if (!left.quasis.every((quasi, index) => quasi.value.cooked === right.quasis[index]?.value.cooked && quasi.value.raw === right.quasis[index]?.value.raw)) return false;
64386
- return areNodeArraysEquivalent(left.expressions, right.expressions);
65196
+ return areNodeArraysEquivalent(left.expressions, right.expressions, scopes);
64387
65197
  }
64388
65198
  return false;
64389
65199
  };
64390
- const isRenderedValue = (node) => {
65200
+ const isProvenReactCreateElementCall = (node, scopes) => {
65201
+ if (isReactApiCall(node, "createElement", scopes, {
65202
+ allowGlobalReactNamespace: true,
65203
+ resolveNamedAliases: true
65204
+ })) return true;
65205
+ if (!isNodeOfType(node, "CallExpression")) return false;
65206
+ const callee = stripParenExpression(node.callee);
65207
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createElement") return false;
65208
+ const receiver = stripParenExpression(callee.object);
65209
+ const namespaceIdentifier = isNodeOfType(receiver, "MemberExpression") && !receiver.computed && isNodeOfType(receiver.property, "Identifier") && receiver.property.name === "default" ? stripParenExpression(receiver.object) : receiver;
65210
+ if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
65211
+ const namespaceSymbol = scopes.symbolFor(namespaceIdentifier);
65212
+ return Boolean(namespaceSymbol?.initializer && namespaceSymbol.references.every((reference) => reference.flag === "read") && containsExplicitReactRuntimeReference(namespaceSymbol.initializer, scopes));
65213
+ };
65214
+ const isRenderedValue = (node, scopes) => {
64391
65215
  const unwrappedNode = stripParenExpression(node);
64392
65216
  if (isNodeOfType(unwrappedNode, "Literal")) return unwrappedNode.value !== null && unwrappedNode.value !== true && unwrappedNode.value !== false && unwrappedNode.value !== "";
64393
65217
  if (isNodeOfType(unwrappedNode, "TemplateLiteral")) return unwrappedNode.expressions.length > 0 || unwrappedNode.quasis[0]?.value.cooked !== "";
65218
+ if (isNodeOfType(unwrappedNode, "CallExpression")) return isProvenReactCreateElementCall(unwrappedNode, scopes);
64394
65219
  return isNodeOfType(unwrappedNode, "JSXElement") || isNodeOfType(unwrappedNode, "JSXFragment");
64395
65220
  };
64396
- const findRenderedValueInAndBranch = (node) => {
65221
+ const findRenderedValueInAndBranch = (node, scopes) => {
64397
65222
  const unwrappedNode = stripParenExpression(node);
64398
- if (isRenderedValue(unwrappedNode)) return unwrappedNode;
65223
+ if (isPotentiallyRenderedValue(unwrappedNode, scopes)) return unwrappedNode;
64399
65224
  if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "&&") return null;
64400
- return findRenderedValueInAndBranch(unwrappedNode.right);
65225
+ return findRenderedValueInAndBranch(unwrappedNode.right, scopes);
64401
65226
  };
64402
65227
  const findEnclosingJsxAttribute = (node) => {
64403
65228
  let currentNode = node.parent;
@@ -64430,6 +65255,11 @@ const getReturnedValues = (statement) => {
64430
65255
  if (!statement) return [];
64431
65256
  if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? [statement.argument] : [];
64432
65257
  if (isNodeOfType(statement, "IfStatement")) return [...getReturnedValues(statement.consequent), ...getReturnedValues(statement.alternate)];
65258
+ if (isNodeOfType(statement, "TryStatement")) return [
65259
+ ...getReturnedValues(statement.block),
65260
+ ...getReturnedValues(statement.handler?.body),
65261
+ ...getReturnedValues(statement.finalizer)
65262
+ ];
64433
65263
  if (!isNodeOfType(statement, "BlockStatement")) return [];
64434
65264
  const returnedValues = [];
64435
65265
  for (const childStatement of statement.body) {
@@ -64438,6 +65268,127 @@ const getReturnedValues = (statement) => {
64438
65268
  }
64439
65269
  return returnedValues;
64440
65270
  };
65271
+ const isPotentiallyRenderedValueInternal = (node, scopes, visitedFunctionNodes) => {
65272
+ const unwrappedNode = stripParenExpression(node);
65273
+ if (isRenderedValue(unwrappedNode, scopes)) return true;
65274
+ if (isNodeOfType(unwrappedNode, "ConditionalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.consequent, scopes, visitedFunctionNodes) && isPotentiallyRenderedValueInternal(unwrappedNode.alternate, scopes, visitedFunctionNodes);
65275
+ if (isNodeOfType(unwrappedNode, "LogicalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.right, scopes, visitedFunctionNodes);
65276
+ if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
65277
+ const calledFunction = resolveExactLocalFunction(unwrappedNode.callee, scopes);
65278
+ if (!isFunctionLike$1(calledFunction) || visitedFunctionNodes.has(calledFunction)) return false;
65279
+ visitedFunctionNodes.add(calledFunction);
65280
+ const returnedValues = isNodeOfType(calledFunction.body, "BlockStatement") ? getReturnedValues(calledFunction.body) : [calledFunction.body];
65281
+ const isPotentiallyRendered = returnedValues.length > 0 && returnedValues.every((returnedValue) => isPotentiallyRenderedValueInternal(returnedValue, scopes, visitedFunctionNodes));
65282
+ visitedFunctionNodes.delete(calledFunction);
65283
+ return isPotentiallyRendered;
65284
+ };
65285
+ const isPotentiallyRenderedValue = (node, scopes) => isPotentiallyRenderedValueInternal(node, scopes, /* @__PURE__ */ new Set());
65286
+ const findUseStateBindingSymbol = (node, componentOrHookNode, scopes) => {
65287
+ let currentNode = node.parent;
65288
+ while (currentNode && currentNode !== componentOrHookNode) {
65289
+ if (isNodeOfType(currentNode, "CallExpression") && isReactApiCall(currentNode, "useState", scopes, {
65290
+ allowGlobalReactNamespace: true,
65291
+ resolveNamedAliases: true
65292
+ }) && isNodeOfType(currentNode.parent, "VariableDeclarator") && isNodeOfType(currentNode.parent.id, "ArrayPattern")) {
65293
+ const stateBinding = currentNode.parent.id.elements?.[0];
65294
+ return isNodeOfType(stateBinding, "Identifier") ? scopes.symbolFor(stateBinding) : null;
65295
+ }
65296
+ if (isFunctionLike$1(currentNode)) return null;
65297
+ currentNode = currentNode.parent;
65298
+ }
65299
+ return null;
65300
+ };
65301
+ const doesReferenceControlStructuralRenderedValue = (referenceIdentifier) => {
65302
+ let currentNode = referenceIdentifier;
65303
+ let parentNode = currentNode.parent;
65304
+ while (parentNode) {
65305
+ if (isNodeOfType(parentNode, "ConditionalExpression") && parentNode.test === currentNode && (isStructuralRenderedValue(parentNode.consequent) || isStructuralRenderedValue(parentNode.alternate))) return true;
65306
+ if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.left === currentNode && isStructuralRenderedValue(parentNode.right)) return true;
65307
+ if (isNodeOfType(parentNode, "JSXExpressionContainer") || isFunctionLike$1(parentNode)) return false;
65308
+ currentNode = parentNode;
65309
+ parentNode = currentNode.parent;
65310
+ }
65311
+ return false;
65312
+ };
65313
+ const isRenderedHydrationConsumer = (node, producerHookNode, scopes) => {
65314
+ const renderingComponent = findRenderPhaseComponentOrHook(node, scopes);
65315
+ return Boolean(renderingComponent && renderingComponent !== producerHookNode && isInRenderedOutput(node, renderingComponent, scopes) && !isGatedByFalsyInitialState(node, scopes) && !isAfterClientOnlyEarlyReturn(node, renderingComponent, scopes) && (!hasSuppressHydrationWarningAttribute(findEnclosingJsxOpeningElement(node)) || doesReferenceControlStructuralRenderedValue(node)));
65316
+ };
65317
+ const doesConsumerExpressionReachRenderedOutput = (node, producerHookNode, scopes, visitedSymbolIds) => {
65318
+ if (isRenderedHydrationConsumer(node, producerHookNode, scopes)) return true;
65319
+ const parentNode = node.parent;
65320
+ if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== node || !isNodeOfType(parentNode.id, "Identifier")) return false;
65321
+ const aliasSymbol = scopes.symbolFor(parentNode.id);
65322
+ if (!aliasSymbol || visitedSymbolIds.has(aliasSymbol.id)) return false;
65323
+ visitedSymbolIds.add(aliasSymbol.id);
65324
+ const doesReachRenderedOutput = aliasSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, visitedSymbolIds));
65325
+ visitedSymbolIds.delete(aliasSymbol.id);
65326
+ return doesReachRenderedOutput;
65327
+ };
65328
+ const doesConsumerBindingReachRenderedOutput = (bindingIdentifier, producerHookNode, scopes) => {
65329
+ if (!isNodeOfType(bindingIdentifier, "Identifier")) return false;
65330
+ const consumerSymbol = scopes.symbolFor(bindingIdentifier);
65331
+ if (!consumerSymbol) return false;
65332
+ return consumerSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, new Set([consumerSymbol.id])));
65333
+ };
65334
+ const getReturnedStatePaths = (returnedValue, stateSymbol, scopes) => {
65335
+ const unwrappedValue = stripParenExpression(returnedValue);
65336
+ if (isNodeOfType(unwrappedValue, "ObjectExpression")) return unwrappedValue.properties.flatMap((property) => {
65337
+ if (!isNodeOfType(property, "Property") || property.kind !== "init" || !doesNodeReadSymbol(property.value, stateSymbol)) return [];
65338
+ const propertyName = getResolvedStaticPropertyName(property, scopes);
65339
+ return propertyName === null ? [] : [{
65340
+ kind: "property",
65341
+ key: propertyName
65342
+ }];
65343
+ });
65344
+ if (isNodeOfType(unwrappedValue, "ArrayExpression")) return (unwrappedValue.elements ?? []).flatMap((element, index) => element && isAstNode(element) && doesNodeReadSymbol(element, stateSymbol) ? [{
65345
+ kind: "index",
65346
+ key: String(index)
65347
+ }] : []);
65348
+ return doesNodeReadSymbol(unwrappedValue, stateSymbol) ? [{
65349
+ kind: "direct",
65350
+ key: null
65351
+ }] : [];
65352
+ };
65353
+ const doesCallResultPathReachRenderedOutput = (callExpression, returnedStatePath, producerHookNode, scopes) => {
65354
+ const callParent = callExpression.parent;
65355
+ if (returnedStatePath.kind === "direct") return doesConsumerExpressionReachRenderedOutput(callExpression, producerHookNode, scopes, /* @__PURE__ */ new Set());
65356
+ if (isNodeOfType(callParent, "MemberExpression") && callParent.object === callExpression && getResolvedStaticPropertyName(callParent, scopes, {
65357
+ allowConstNumericLiteral: true,
65358
+ stringifyNonStringLiterals: true
65359
+ }) === returnedStatePath.key) return doesConsumerExpressionReachRenderedOutput(callParent, producerHookNode, scopes, /* @__PURE__ */ new Set());
65360
+ if (!isNodeOfType(callParent, "VariableDeclarator") || callParent.init !== callExpression) return false;
65361
+ 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));
65362
+ if (returnedStatePath.kind === "index" && isNodeOfType(callParent.id, "ArrayPattern")) {
65363
+ const element = callParent.id.elements?.[Number(returnedStatePath.key)];
65364
+ return Boolean(element && doesConsumerBindingReachRenderedOutput(element, producerHookNode, scopes));
65365
+ }
65366
+ if (!isNodeOfType(callParent.id, "Identifier")) return false;
65367
+ const resultSymbol = scopes.symbolFor(callParent.id);
65368
+ if (!resultSymbol) return false;
65369
+ return resultSymbol.references.some((reference) => {
65370
+ const memberExpression = reference.identifier.parent;
65371
+ return Boolean(isNodeOfType(memberExpression, "MemberExpression") && memberExpression.object === reference.identifier && getResolvedStaticPropertyName(memberExpression, scopes, {
65372
+ allowConstNumericLiteral: true,
65373
+ stringifyNonStringLiterals: true
65374
+ }) === returnedStatePath.key && doesConsumerExpressionReachRenderedOutput(memberExpression, producerHookNode, scopes, new Set([resultSymbol.id])));
65375
+ });
65376
+ };
65377
+ const isReturnedUseStateInitializerRendered = (node, componentOrHookNode, scopes) => {
65378
+ if (!isFunctionLike$1(componentOrHookNode)) return false;
65379
+ const stateSymbol = findUseStateBindingSymbol(node, componentOrHookNode, scopes);
65380
+ if (!stateSymbol) return false;
65381
+ const returnedStatePaths = (isNodeOfType(componentOrHookNode.body, "BlockStatement") ? getReturnedValues(componentOrHookNode.body) : [componentOrHookNode.body]).flatMap((returnedValue) => getReturnedStatePaths(returnedValue, stateSymbol, scopes));
65382
+ if (returnedStatePaths.length === 0) return false;
65383
+ const functionBinding = getDirectFunctionBindingIdentifier(componentOrHookNode);
65384
+ if (!isNodeOfType(functionBinding, "Identifier")) return false;
65385
+ const functionSymbol = scopes.symbolFor(functionBinding);
65386
+ if (!functionSymbol) return false;
65387
+ return functionSymbol.references.some((functionReference) => {
65388
+ const callExpression = functionReference.identifier.parent;
65389
+ return Boolean(isNodeOfType(callExpression, "CallExpression") && callExpression.callee === functionReference.identifier && returnedStatePaths.some((returnedStatePath) => doesCallResultPathReachRenderedOutput(callExpression, returnedStatePath, componentOrHookNode, scopes)));
65390
+ });
65391
+ };
64441
65392
  const findFollowingReturnedValues = (ifStatement) => {
64442
65393
  const parentNode = ifStatement.parent;
64443
65394
  if (!isNodeOfType(parentNode, "BlockStatement")) return [];
@@ -64450,24 +65401,24 @@ const findFollowingReturnedValues = (ifStatement) => {
64450
65401
  }
64451
65402
  return returnedValues;
64452
65403
  };
64453
- const areConditionExpressionsEquivalent = (leftExpression, rightExpression) => {
65404
+ const areConditionExpressionsEquivalent = (leftExpression, rightExpression, scopes) => {
64454
65405
  const left = stripParenExpression(leftExpression);
64455
65406
  const right = stripParenExpression(rightExpression);
64456
- if (areExpressionsStructurallyEqual(left, right)) return true;
65407
+ if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64457
65408
  if (left.type !== right.type) return false;
64458
- if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument);
64459
- if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
64460
- if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
65409
+ if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument, scopes);
65410
+ if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
65411
+ if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
64461
65412
  return false;
64462
65413
  };
64463
- const areReturnTreesEquivalent = (leftStatement, rightStatement) => {
65414
+ const areReturnTreesEquivalent = (leftStatement, rightStatement, scopes) => {
64464
65415
  if (!leftStatement || !rightStatement) return leftStatement === rightStatement;
64465
- if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument);
64466
- if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate);
65416
+ if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument, scopes);
65417
+ 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);
64467
65418
  if (!isNodeOfType(leftStatement, "BlockStatement") || !isNodeOfType(rightStatement, "BlockStatement")) return false;
64468
65419
  const leftReturningStatements = leftStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
64469
65420
  const rightReturningStatements = rightStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
64470
- return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]));
65421
+ return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index], scopes));
64471
65422
  };
64472
65423
  const isStructuralRenderedValue = (node) => {
64473
65424
  if (!node) return false;
@@ -64491,19 +65442,22 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
64491
65442
  if (isTestlikeFilename(context.filename)) return {};
64492
65443
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
64493
65444
  let fileHasUseClientDirective = false;
65445
+ let fileHasExplicitReactRuntimeReference = false;
64494
65446
  let fileIsEmailTemplate = false;
64495
65447
  const reportedNodes = /* @__PURE__ */ new Set();
64496
- const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext) => {
65448
+ const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext, hasProvenRenderedConsumer = false) => {
64497
65449
  const conditionMatch = matchHydrationCondition(conditionNode, context);
64498
65450
  if (!conditionMatch) return;
64499
65451
  const { predicateMatch, predicateNode } = conditionMatch;
64500
65452
  if (reportedNodes.has(predicateNode)) return;
64501
- if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
64502
- const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
65453
+ if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch, context.scopes)) return;
65454
+ const enclosingFunction = findEnclosingFunction$1(conditionNode);
65455
+ const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
64503
65456
  if (!componentOrHookNode) return;
64504
- if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
64505
- if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
64506
- if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
65457
+ const hasRenderedLocalFunctionConsumer = Boolean(enclosingFunction && enclosingFunction !== componentOrHookNode && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) === componentOrHookNode);
65458
+ if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective) && !fileHasExplicitReactRuntimeReference) return;
65459
+ if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes) && !hasRenderedLocalFunctionConsumer) return;
65460
+ if (!hasProvenRenderedConsumer && !(requiresRenderedContext ? isPotentiallyRenderedValue(leftBranch, context.scopes) : isRenderedValue(leftBranch, context.scopes)) && (!rightBranch || !(requiresRenderedContext ? isPotentiallyRenderedValue(rightBranch, context.scopes) : isRenderedValue(rightBranch, context.scopes)))) {
64507
65461
  const attribute = findEnclosingJsxAttribute(conditionNode);
64508
65462
  if (!attribute || isEventHandlerAttribute(attribute)) return;
64509
65463
  }
@@ -64522,28 +65476,31 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
64522
65476
  return {
64523
65477
  Program(node) {
64524
65478
  fileHasUseClientDirective = hasDirective(node, "use client");
65479
+ fileHasExplicitReactRuntimeReference = containsExplicitReactRuntimeReference(node, context.scopes);
64525
65480
  fileIsEmailTemplate = hasEmailTemplateImport(node);
64526
65481
  },
64527
65482
  ConditionalExpression(node) {
64528
65483
  reportHydrationBranch(node.test, node.consequent, node.alternate, true);
65484
+ const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
65485
+ if (componentOrHookNode && isReturnedUseStateInitializerRendered(node, componentOrHookNode, context.scopes)) reportHydrationBranch(node.test, node.consequent, node.alternate, false, true);
64529
65486
  },
64530
65487
  LogicalExpression(node) {
64531
65488
  if (node.operator !== "&&" && node.operator !== "||") return;
64532
- const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right) : isRenderedValue(node.right) ? node.right : null;
65489
+ const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right, context.scopes) : isPotentiallyRenderedValue(node.right, context.scopes) ? node.right : null;
64533
65490
  if (!renderedValue) return;
64534
65491
  reportHydrationBranch(node, renderedValue, null, true);
64535
65492
  },
64536
65493
  IfStatement(node) {
64537
- if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate)) return;
65494
+ if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate, context.scopes)) return;
64538
65495
  const consequentValues = getReturnedValues(node.consequent);
64539
65496
  const alternateValues = node.alternate ? getReturnedValues(node.alternate) : findFollowingReturnedValues(node);
64540
65497
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
64541
- const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
64542
- if (!componentOrHookNode) return;
64543
65498
  const enclosingFunction = findEnclosingFunction$1(node);
64544
- if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
65499
+ const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
65500
+ if (!componentOrHookNode) return;
65501
+ if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes) && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) !== componentOrHookNode)) return;
64545
65502
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
64546
- if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
65503
+ if (!isRenderedValue(consequentValue, context.scopes) && !isRenderedValue(alternateValue, context.scopes)) continue;
64547
65504
  reportHydrationBranch(node.test, consequentValue, alternateValue, false);
64548
65505
  }
64549
65506
  }
@@ -66277,6 +67234,124 @@ const isInsideSnapshotHelper = (node) => {
66277
67234
  }
66278
67235
  return false;
66279
67236
  };
67237
+ const findEnclosingNextjsPageDataFunction = (node) => {
67238
+ let outermostFunction = null;
67239
+ let cursor = node.parent;
67240
+ while (cursor) {
67241
+ if (isFunctionLike$1(cursor)) outermostFunction = cursor;
67242
+ if (isNodeOfType(cursor, "Program")) {
67243
+ if (!outermostFunction) return null;
67244
+ for (const exportName of NEXTJS_PAGE_DATA_EXPORT_NAMES) {
67245
+ const exportedValue = findExportedValue(cursor, exportName);
67246
+ if (exportedValue && isAstDescendant(outermostFunction, exportedValue)) return outermostFunction;
67247
+ }
67248
+ return null;
67249
+ }
67250
+ cursor = cursor.parent ?? null;
67251
+ }
67252
+ return null;
67253
+ };
67254
+ const findConditionalReturnExpressionRoot = (node) => {
67255
+ let expressionRoot = findTransparentExpressionRoot(node);
67256
+ while (expressionRoot.parent && isNodeOfType(expressionRoot.parent, "ConditionalExpression") && (expressionRoot.parent.consequent === expressionRoot || expressionRoot.parent.alternate === expressionRoot)) expressionRoot = findTransparentExpressionRoot(expressionRoot.parent);
67257
+ return expressionRoot;
67258
+ };
67259
+ const isReturnedPageDataResultBinding = (returnExpression, pageDataFunction, context) => {
67260
+ const declarator = returnExpression.parent;
67261
+ if (!isNodeOfType(declarator, "VariableDeclarator") || declarator.init !== returnExpression || !isNodeOfType(declarator.id, "Identifier") || findEnclosingFunction$1(declarator) !== pageDataFunction) return false;
67262
+ const bindingSymbol = context.scopes.symbolFor(declarator.id);
67263
+ if (!bindingSymbol || bindingSymbol.references.length !== 1) return false;
67264
+ const referenceRoot = findTransparentExpressionRoot(bindingSymbol.references[0].identifier);
67265
+ const returnStatement = referenceRoot.parent;
67266
+ return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === referenceRoot && findEnclosingFunction$1(returnStatement) === pageDataFunction;
67267
+ };
67268
+ const isSameShorthandPropertyValue = (node, property) => property.shorthand && (node === property.key || node === property.value);
67269
+ const isValueForwardedThroughLiteralStructure = (node, structure) => {
67270
+ const strippedNode = stripParenExpression(node);
67271
+ const strippedStructure = stripParenExpression(structure);
67272
+ if (strippedNode === strippedStructure) return true;
67273
+ if (isNodeOfType(strippedStructure, "ConditionalExpression")) return isValueForwardedThroughLiteralStructure(strippedNode, strippedStructure.consequent) || isValueForwardedThroughLiteralStructure(strippedNode, strippedStructure.alternate);
67274
+ if (isNodeOfType(strippedStructure, "ArrayExpression")) return strippedStructure.elements.some((element) => element && !isNodeOfType(element, "SpreadElement") && isValueForwardedThroughLiteralStructure(strippedNode, element));
67275
+ if (!isNodeOfType(strippedStructure, "ObjectExpression")) return false;
67276
+ return strippedStructure.properties.some((property) => {
67277
+ if (isNodeOfType(property, "SpreadElement")) return isValueForwardedThroughLiteralStructure(strippedNode, property.argument);
67278
+ if (!isNodeOfType(property, "Property")) return false;
67279
+ if (isValueForwardedThroughLiteralStructure(strippedNode, property.value)) return true;
67280
+ return isSameShorthandPropertyValue(strippedNode, property);
67281
+ });
67282
+ };
67283
+ const isValueForwardedToPropertyValue = (node, property) => {
67284
+ const directValue = findConditionalReturnExpressionRoot(node);
67285
+ if (isValueForwardedThroughLiteralStructure(directValue, property.value)) return true;
67286
+ return isSameShorthandPropertyValue(directValue, property);
67287
+ };
67288
+ const isInsideReturnedNextjsProps = (node, pageDataFunction, context) => {
67289
+ let cursor = node.parent;
67290
+ while (cursor && cursor !== pageDataFunction) {
67291
+ if (isNodeOfType(cursor, "Property") && getStaticPropertyKeyName(cursor, { allowComputedString: true }) === "props" && isValueForwardedToPropertyValue(node, cursor)) {
67292
+ const propertyContainer = cursor.parent;
67293
+ if (!propertyContainer) return false;
67294
+ const returnExpression = findConditionalReturnExpressionRoot(propertyContainer);
67295
+ const returnStatement = returnExpression.parent;
67296
+ if (isNodeOfType(returnStatement, "ReturnStatement") && findEnclosingFunction$1(returnStatement) === pageDataFunction) return true;
67297
+ if (isNodeOfType(pageDataFunction, "ArrowFunctionExpression") && !isNodeOfType(pageDataFunction.body, "BlockStatement") && stripParenExpression(pageDataFunction.body) === stripParenExpression(returnExpression)) return true;
67298
+ if (isReturnedPageDataResultBinding(returnExpression, pageDataFunction, context)) return true;
67299
+ }
67300
+ cursor = cursor.parent ?? null;
67301
+ }
67302
+ return false;
67303
+ };
67304
+ const isExpressionReturnedByFunction = (node, functionNode) => {
67305
+ const returnExpression = findConditionalReturnExpressionRoot(node);
67306
+ if (isNodeOfType(functionNode, "ArrowFunctionExpression") && !isNodeOfType(functionNode.body, "BlockStatement")) return stripParenExpression(functionNode.body) === stripParenExpression(returnExpression);
67307
+ const returnStatement = returnExpression.parent;
67308
+ return isNodeOfType(returnStatement, "ReturnStatement") && returnStatement.argument === returnExpression && findEnclosingFunction$1(returnStatement) === functionNode;
67309
+ };
67310
+ const isValueForwardedToBindingInitializer = (node, bindingInitializer) => {
67311
+ if (isValueForwardedThroughLiteralStructure(findConditionalReturnExpressionRoot(node), bindingInitializer)) return true;
67312
+ const initializer = stripParenExpression(bindingInitializer);
67313
+ if (!isNodeOfType(initializer, "CallExpression")) return false;
67314
+ const callee = stripParenExpression(initializer.callee);
67315
+ return isFunctionLike$1(callee) && isExpressionReturnedByFunction(node, callee);
67316
+ };
67317
+ const findPageDataResultBinding = (node) => {
67318
+ let cursor = node.parent;
67319
+ while (cursor) {
67320
+ if (isNodeOfType(cursor, "VariableDeclarator")) {
67321
+ if (cursor.init && isNodeOfType(cursor.id, "Identifier") && isValueForwardedToBindingInitializer(node, cursor.init)) return cursor.id;
67322
+ return null;
67323
+ }
67324
+ cursor = cursor.parent ?? null;
67325
+ }
67326
+ return null;
67327
+ };
67328
+ const isUsedToSerializeNextjsPageProps = (node, context) => {
67329
+ if (!isInProjectDirectory(context, "pages") || isInProjectDirectory(context, "pages/api")) return false;
67330
+ const pageDataFunction = findEnclosingNextjsPageDataFunction(node);
67331
+ if (!pageDataFunction) return false;
67332
+ if (isInsideReturnedNextjsProps(node, pageDataFunction, context)) return true;
67333
+ const bindingIdentifier = findPageDataResultBinding(node);
67334
+ const bindingSymbol = bindingIdentifier ? context.scopes.symbolFor(bindingIdentifier) : null;
67335
+ if (!bindingSymbol) return false;
67336
+ const aliasSymbols = collectConstAliasSymbols(bindingSymbol, context.scopes);
67337
+ const aliasSymbolIds = new Set(aliasSymbols.map((aliasSymbol) => aliasSymbol.id));
67338
+ let hasPagePropsReference = false;
67339
+ for (const aliasSymbol of aliasSymbols) for (const reference of aliasSymbol.references) {
67340
+ if (findEnclosingFunction$1(reference.identifier) !== pageDataFunction) return false;
67341
+ if (isInsideReturnedNextjsProps(reference.identifier, pageDataFunction, context)) {
67342
+ hasPagePropsReference = true;
67343
+ continue;
67344
+ }
67345
+ const referenceRoot = findTransparentExpressionRoot(reference.identifier);
67346
+ const declarator = referenceRoot.parent;
67347
+ if (isNodeOfType(declarator, "VariableDeclarator") && declarator.init === referenceRoot && isNodeOfType(declarator.id, "Identifier")) {
67348
+ const aliasSymbolForReference = context.scopes.symbolFor(declarator.id);
67349
+ if (aliasSymbolForReference && aliasSymbolIds.has(aliasSymbolForReference.id)) continue;
67350
+ }
67351
+ return false;
67352
+ }
67353
+ return hasPagePropsReference;
67354
+ };
66280
67355
  const noJsonParseStringifyClone = defineRule({
66281
67356
  id: "no-json-parse-stringify-clone",
66282
67357
  title: "JSON parse/stringify deep clone",
@@ -66294,6 +67369,7 @@ const noJsonParseStringifyClone = defineRule({
66294
67369
  if (isInsideSnapshotHelper(node)) return;
66295
67370
  if (isAssignedToNormalizationBinding(node)) return;
66296
67371
  if (isCatchParameterRoundTrip(firstArgument)) return;
67372
+ if (isUsedToSerializeNextjsPageProps(node, context)) return;
66297
67373
  context.report({
66298
67374
  node,
66299
67375
  message: MESSAGE$35
@@ -67709,6 +68785,77 @@ const isCancellationGuardTest = (test) => {
67709
68785
  });
67710
68786
  return matches;
67711
68787
  };
68788
+ const getReactRefCurrent = (expression, context) => {
68789
+ const stripped = stripParenExpression(expression);
68790
+ if (!isNodeOfType(stripped, "MemberExpression") || getStaticPropertyName(stripped) !== "current") return null;
68791
+ const receiver = stripParenExpression(stripped.object);
68792
+ if (!isNodeOfType(receiver, "Identifier")) return null;
68793
+ const binding = findVariableInitializer(receiver, receiver.name);
68794
+ const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
68795
+ return initializer && isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, USE_REF_HOOK_NAMES$1, context.scopes, {
68796
+ allowGlobalReactNamespace: true,
68797
+ allowUnboundBareCalls: true
68798
+ }) ? stripped : null;
68799
+ };
68800
+ const getStableOwnershipToken = (expression, context) => {
68801
+ const stripped = stripParenExpression(expression);
68802
+ if (!isNodeOfType(stripped, "Identifier")) return null;
68803
+ const symbol = context.scopes.symbolFor(stripped);
68804
+ const initializer = symbol?.initializer ? stripParenExpression(symbol.initializer) : null;
68805
+ const isStableAsyncIdentity = Boolean(initializer && isNodeOfType(initializer, "ObjectExpression")) || Boolean(initializer && getReactRefCurrent(initializer, context)) || Boolean(initializer && isNodeOfType(initializer, "UpdateExpression") && initializer.operator === "++" && getReactRefCurrent(initializer.argument, context));
68806
+ return symbol && symbol.kind === "const" && symbol.references.every((reference) => reference.flag === "read") && isStableAsyncIdentity ? stripped : null;
68807
+ };
68808
+ const getAsyncOwnershipComparison = (test, context) => {
68809
+ const stripped = stripParenExpression(test);
68810
+ if (!isNodeOfType(stripped, "BinaryExpression")) return null;
68811
+ const leftRef = getReactRefCurrent(stripped.left, context);
68812
+ const rightRef = getReactRefCurrent(stripped.right, context);
68813
+ const leftToken = getStableOwnershipToken(stripped.left, context);
68814
+ const rightToken = getStableOwnershipToken(stripped.right, context);
68815
+ if (stripped.operator === "===" || stripped.operator === "==") {
68816
+ if (leftRef && rightToken) return {
68817
+ refCurrent: leftRef,
68818
+ token: rightToken,
68819
+ mode: "owns",
68820
+ isOrdered: false
68821
+ };
68822
+ if (rightRef && leftToken) return {
68823
+ refCurrent: rightRef,
68824
+ token: leftToken,
68825
+ mode: "owns",
68826
+ isOrdered: false
68827
+ };
68828
+ return null;
68829
+ }
68830
+ if (stripped.operator === "!==" || stripped.operator === "!=") {
68831
+ if (leftRef && rightToken) return {
68832
+ refCurrent: leftRef,
68833
+ token: rightToken,
68834
+ mode: "lost",
68835
+ isOrdered: false
68836
+ };
68837
+ if (rightRef && leftToken) return {
68838
+ refCurrent: rightRef,
68839
+ token: leftToken,
68840
+ mode: "lost",
68841
+ isOrdered: false
68842
+ };
68843
+ return null;
68844
+ }
68845
+ if (stripped.operator === "<=" && leftRef && rightToken) return {
68846
+ refCurrent: leftRef,
68847
+ token: rightToken,
68848
+ mode: "owns",
68849
+ isOrdered: true
68850
+ };
68851
+ if (stripped.operator === ">=" && rightRef && leftToken) return {
68852
+ refCurrent: rightRef,
68853
+ token: leftToken,
68854
+ mode: "owns",
68855
+ isOrdered: true
68856
+ };
68857
+ return null;
68858
+ };
67712
68859
  const dedupeCatchPathStates = (states) => {
67713
68860
  const statesByKey = /* @__PURE__ */ new Map();
67714
68861
  for (const state of states) statesByKey.set(`${Number(state.isCleared)}:${Number(state.isCancellationPath)}`, state);
@@ -68041,7 +69188,221 @@ const isInsideTryFinalizer = (node, tryStatement) => {
68041
69188
  }
68042
69189
  return false;
68043
69190
  };
68044
- const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedCleanupAssignments, context) => {
69191
+ const getDirectBlockEntry = (node, functionNode) => {
69192
+ let entry = node;
69193
+ let cursor = node.parent;
69194
+ while (cursor && cursor !== functionNode) {
69195
+ if (isNodeOfType(cursor, "BlockStatement")) return {
69196
+ block: cursor,
69197
+ entry
69198
+ };
69199
+ entry = cursor;
69200
+ cursor = cursor.parent ?? null;
69201
+ }
69202
+ return null;
69203
+ };
69204
+ const claimPrecedesTruthySet = (claimNode, truthySet, firstRiskyAwait, functionNode, context) => {
69205
+ const claimStart = getNodeStart$1(claimNode);
69206
+ if (claimStart === null || claimStart >= firstRiskyAwait.start || truthySet.start >= firstRiskyAwait.start) return false;
69207
+ const claimEntry = getDirectBlockEntry(claimNode, functionNode);
69208
+ const truthyEntry = getDirectBlockEntry(truthySet.node, functionNode);
69209
+ if (!claimEntry || !truthyEntry || claimEntry.block !== truthyEntry.block) return false;
69210
+ let claimCursor = claimNode.parent;
69211
+ while (claimCursor && claimCursor !== claimEntry.block) {
69212
+ 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;
69213
+ claimCursor = claimCursor.parent ?? null;
69214
+ }
69215
+ const claimIndex = claimEntry.block.body.findIndex((statement) => statement === claimEntry.entry);
69216
+ const truthyIndex = claimEntry.block.body.findIndex((statement) => statement === truthyEntry.entry);
69217
+ if (claimIndex === -1 || truthyIndex === -1 || claimIndex >= truthyIndex) return false;
69218
+ return claimEntry.block.body.slice(claimIndex + 1, truthyIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context));
69219
+ };
69220
+ const getOwningFunction = (functionNode) => {
69221
+ let ownerFunction = functionNode;
69222
+ let cursor = functionNode.parent;
69223
+ while (cursor) {
69224
+ if (isFunctionLike$1(cursor)) ownerFunction = cursor;
69225
+ cursor = cursor.parent ?? null;
69226
+ }
69227
+ return ownerFunction;
69228
+ };
69229
+ const isEffectInvalidationPairedWithReset = (writeNode, truthySets, context) => {
69230
+ const truthyCall = truthySets[0]?.node;
69231
+ if (!truthyCall || !isNodeOfType(truthyCall, "CallExpression")) return false;
69232
+ const setter = getSetterBooleanValue(truthyCall, context);
69233
+ if (!setter) return false;
69234
+ let effectCallback = writeNode.parent;
69235
+ while (effectCallback && !isFunctionLike$1(effectCallback)) effectCallback = effectCallback.parent ?? null;
69236
+ if (!effectCallback || !isEffectCallback(effectCallback, context)) return false;
69237
+ if (!isUnconditionallyExecutedWithinFunction(writeNode, effectCallback, context)) return false;
69238
+ const writeEntry = getDirectBlockEntry(writeNode, effectCallback);
69239
+ if (!writeEntry) return false;
69240
+ let isPaired = false;
69241
+ walkOwnFunctionScope(effectCallback, (candidate) => {
69242
+ if (isPaired || !isNodeOfType(candidate, "CallExpression")) return;
69243
+ const candidateSetter = getSetterBooleanValue(candidate, context);
69244
+ if (candidateSetter?.setterKey !== setter.setterKey || candidateSetter.value || !isUnconditionallyExecutedWithinFunction(candidate, effectCallback, context)) return;
69245
+ const resetEntry = getDirectBlockEntry(candidate, effectCallback);
69246
+ if (!resetEntry || resetEntry.block !== writeEntry.block) return;
69247
+ const writeIndex = writeEntry.block.body.findIndex((statement) => statement === writeEntry.entry);
69248
+ const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
69249
+ if (writeIndex === -1 || resetIndex === -1) return;
69250
+ if (resetIndex <= writeIndex) {
69251
+ isPaired = true;
69252
+ return false;
69253
+ }
69254
+ isPaired = writeEntry.block.body.slice(writeIndex + 1, resetIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, effectCallback, context));
69255
+ return isPaired ? false : void 0;
69256
+ });
69257
+ return isPaired;
69258
+ };
69259
+ const isUnconditionalReturnBranch = (statement) => {
69260
+ if (isNodeOfType(statement, "ReturnStatement")) return true;
69261
+ return Boolean(isNodeOfType(statement, "BlockStatement") && statement.body.length === 1 && isNodeOfType(statement.body[0], "ReturnStatement"));
69262
+ };
69263
+ const findSingleFlightSnapshotClaim = (tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69264
+ const snapshotEntry = getDirectBlockEntry(tokenInitializer, functionNode);
69265
+ const resetEntry = getDirectBlockEntry(resetNode, functionNode);
69266
+ if (!snapshotEntry || !resetEntry) return null;
69267
+ const claimCandidates = [];
69268
+ const releaseCandidates = [];
69269
+ walkOwnFunctionScope(functionNode, (candidate) => {
69270
+ if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=" || !getReactRefCurrent(candidate.left, context)) return;
69271
+ const assignedValue = stripParenExpression(candidate.right);
69272
+ if (!isNodeOfType(assignedValue, "Literal") || typeof assignedValue.value !== "boolean") return;
69273
+ const candidateKey = serializeReferenceKey({
69274
+ node: candidate.left,
69275
+ scopes: context.scopes
69276
+ });
69277
+ if (!candidateKey) return;
69278
+ if (!assignedValue.value) {
69279
+ if (getDirectBlockEntry(candidate, functionNode)?.block === resetEntry.block) releaseCandidates.push(candidate);
69280
+ return;
69281
+ }
69282
+ if (!truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context))) return;
69283
+ const candidateEntry = getDirectBlockEntry(candidate, functionNode);
69284
+ if (!candidateEntry || candidateEntry.block !== snapshotEntry.block) return;
69285
+ const candidateIndex = candidateEntry.block.body.findIndex((statement) => statement === candidateEntry.entry);
69286
+ const snapshotIndex = candidateEntry.block.body.findIndex((statement) => statement === snapshotEntry.entry);
69287
+ if (candidateIndex === -1 || snapshotIndex === -1 || candidateIndex >= snapshotIndex) return;
69288
+ const guardIndex = candidateEntry.block.body.findLastIndex((statement, statementIndex) => {
69289
+ if (statementIndex >= candidateIndex || !isNodeOfType(statement, "IfStatement") || statement.alternate !== null || !isUnconditionalReturnBranch(statement.consequent)) return false;
69290
+ return serializeReferenceKey({
69291
+ node: stripParenExpression(statement.test),
69292
+ scopes: context.scopes
69293
+ }) === candidateKey;
69294
+ });
69295
+ if (guardIndex === -1 || !candidateEntry.block.body.slice(guardIndex + 1, candidateIndex).every((statement) => !subtreeHasAbruptSynchronousOperation(statement, functionNode, context))) return;
69296
+ claimCandidates.push(candidate);
69297
+ });
69298
+ const claim = claimCandidates.find((claimCandidate) => {
69299
+ const candidateKey = serializeReferenceKey({
69300
+ node: claimCandidate.left,
69301
+ scopes: context.scopes
69302
+ });
69303
+ return releaseCandidates.some((releaseCandidate) => serializeReferenceKey({
69304
+ node: releaseCandidate.left,
69305
+ scopes: context.scopes
69306
+ }) === candidateKey);
69307
+ });
69308
+ if (!claim) return null;
69309
+ const claimKey = serializeReferenceKey({
69310
+ node: claim.left,
69311
+ scopes: context.scopes
69312
+ });
69313
+ const release = releaseCandidates.find((releaseCandidate) => serializeReferenceKey({
69314
+ node: releaseCandidate.left,
69315
+ scopes: context.scopes
69316
+ }) === claimKey);
69317
+ if (!claimKey || !release) return null;
69318
+ const releaseEntry = getDirectBlockEntry(release, functionNode);
69319
+ if (!releaseEntry || releaseEntry.block !== resetEntry.block) return null;
69320
+ const releaseIndex = resetEntry.block.body.findIndex((statement) => statement === releaseEntry.entry);
69321
+ const resetIndex = resetEntry.block.body.findIndex((statement) => statement === resetEntry.entry);
69322
+ 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;
69323
+ let didFindUnsafeWrite = false;
69324
+ walkAst(getOwningFunction(functionNode), (candidate) => {
69325
+ if (didFindUnsafeWrite || candidate === claim || candidate === release) return;
69326
+ const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69327
+ if (writeTarget && serializeReferenceKey({
69328
+ node: writeTarget,
69329
+ scopes: context.scopes
69330
+ }) === claimKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindUnsafeWrite = true;
69331
+ });
69332
+ return didFindUnsafeWrite ? null : claim;
69333
+ };
69334
+ const findOwnershipClaim = (comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69335
+ const refKey = serializeReferenceKey({
69336
+ node: comparison.refCurrent,
69337
+ scopes: context.scopes
69338
+ });
69339
+ const tokenKey = serializeReferenceKey({
69340
+ node: comparison.token,
69341
+ scopes: context.scopes
69342
+ });
69343
+ if (!refKey || !tokenKey) return null;
69344
+ const candidates = [];
69345
+ const tokenSymbol = context.scopes.symbolFor(comparison.token);
69346
+ const tokenInitializer = tokenSymbol?.initializer ? stripParenExpression(tokenSymbol.initializer) : null;
69347
+ if (comparison.isOrdered && !isNodeOfType(tokenInitializer, "UpdateExpression")) return null;
69348
+ if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression") && tokenInitializer.operator === "++" && serializeReferenceKey({
69349
+ node: tokenInitializer.argument,
69350
+ scopes: context.scopes
69351
+ }) === refKey) candidates.push(tokenInitializer);
69352
+ if (tokenInitializer && getReactRefCurrent(tokenInitializer, context) && serializeReferenceKey({
69353
+ node: tokenInitializer,
69354
+ scopes: context.scopes
69355
+ }) === refKey) {
69356
+ const singleFlightClaim = findSingleFlightSnapshotClaim(tokenInitializer, functionNode, truthySets, firstRiskyAwait, resetNode, context);
69357
+ if (singleFlightClaim) candidates.push(singleFlightClaim);
69358
+ }
69359
+ if (tokenInitializer && isNodeOfType(tokenInitializer, "UpdateExpression")) {
69360
+ const generationKey = serializeReferenceKey({
69361
+ node: tokenInitializer.argument,
69362
+ scopes: context.scopes
69363
+ });
69364
+ if (generationKey && generationKey === refKey) {
69365
+ const ownerFunction = getOwningFunction(functionNode);
69366
+ let didFindOtherGenerationWrite = false;
69367
+ walkAst(ownerFunction, (candidate) => {
69368
+ if (didFindOtherGenerationWrite || candidate === tokenInitializer) return;
69369
+ const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69370
+ if (writeTarget && serializeReferenceKey({
69371
+ node: writeTarget,
69372
+ scopes: context.scopes
69373
+ }) === generationKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherGenerationWrite = true;
69374
+ });
69375
+ if (didFindOtherGenerationWrite) return null;
69376
+ }
69377
+ }
69378
+ walkOwnFunctionScope(functionNode, (candidate) => {
69379
+ if (!isNodeOfType(candidate, "AssignmentExpression") || candidate.operator !== "=") return;
69380
+ if (serializeReferenceKey({
69381
+ node: candidate.left,
69382
+ scopes: context.scopes
69383
+ }) === refKey && serializeReferenceKey({
69384
+ node: candidate.right,
69385
+ scopes: context.scopes
69386
+ }) === tokenKey) candidates.push(candidate);
69387
+ });
69388
+ const claim = candidates.find((candidate) => truthySets.some((truthySet) => claimPrecedesTruthySet(candidate, truthySet, firstRiskyAwait, functionNode, context)));
69389
+ if (!claim) return null;
69390
+ let didFindOtherWrite = false;
69391
+ walkAst(getOwningFunction(functionNode), (candidate) => {
69392
+ if (didFindOtherWrite || candidate === claim) return;
69393
+ const writeTarget = isNodeOfType(candidate, "AssignmentExpression") ? candidate.left : isNodeOfType(candidate, "UpdateExpression") || isNodeOfType(candidate, "UnaryExpression") && candidate.operator === "delete" ? candidate.argument : null;
69394
+ if (writeTarget && serializeReferenceKey({
69395
+ node: writeTarget,
69396
+ scopes: context.scopes
69397
+ }) === refKey && !isEffectInvalidationPairedWithReset(candidate, truthySets, context)) didFindOtherWrite = true;
69398
+ });
69399
+ return didFindOtherWrite ? null : claim;
69400
+ };
69401
+ const isClaimedOwnershipComparison = (test, expectedMode, functionNode, truthySets, firstRiskyAwait, resetNode, context) => {
69402
+ const comparison = getAsyncOwnershipComparison(test, context);
69403
+ return Boolean(comparison && comparison.mode === expectedMode && findOwnershipClaim(comparison, functionNode, truthySets, firstRiskyAwait, resetNode, context));
69404
+ };
69405
+ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, acceptedAssignments, context) => {
68045
69406
  let didFindOtherWrite = false;
68046
69407
  walkAst(effectCallback, (candidate) => {
68047
69408
  if (didFindOtherWrite) return false;
@@ -68049,7 +69410,7 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
68049
69410
  if (serializeReferenceKey({
68050
69411
  node: candidate.left,
68051
69412
  scopes: context.scopes
68052
- }) === guardKey && !acceptedCleanupAssignments.has(candidate)) {
69413
+ }) === guardKey && !acceptedAssignments.has(candidate)) {
68053
69414
  didFindOtherWrite = true;
68054
69415
  return false;
68055
69416
  }
@@ -68065,48 +69426,103 @@ const hasLifecycleGuardWriteOutsideCleanup = (effectCallback, guardKey, accepted
68065
69426
  });
68066
69427
  return didFindOtherWrite;
68067
69428
  };
68068
- const isResetGuardedByCleanupBackedLifecycle = (resetNode, functionNode, context) => {
69429
+ const collectCleanupBackedLifecycleAssignments = (effectCallback, guardKey, context) => {
69430
+ const acceptedAssignments = /* @__PURE__ */ new Set();
69431
+ for (const cleanupFunction of collectReturnedCleanupFunctions(effectCallback, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
69432
+ const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
69433
+ if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
69434
+ node: cleanupNode.left,
69435
+ scopes: context.scopes
69436
+ }) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
69437
+ acceptedAssignments.add(cleanupNode);
69438
+ });
69439
+ if (acceptedAssignments.size === 0) return null;
69440
+ walkOwnFunctionScope(effectCallback, (effectNode) => {
69441
+ const assignedValue = isNodeOfType(effectNode, "AssignmentExpression") ? stripParenExpression(effectNode.right) : null;
69442
+ if (isNodeOfType(effectNode, "AssignmentExpression") && effectNode.operator === "=" && isNodeOfType(assignedValue, "Literal") && assignedValue.value === true && serializeReferenceKey({
69443
+ node: effectNode.left,
69444
+ scopes: context.scopes
69445
+ }) === guardKey && isUnconditionallyExecutedWithinFunction(effectNode, effectCallback, context)) acceptedAssignments.add(effectNode);
69446
+ });
69447
+ return acceptedAssignments;
69448
+ };
69449
+ const isEffectCallback = (node, context) => {
69450
+ const callbackRoot = findTransparentExpressionRoot(node);
69451
+ const callbackCall = callbackRoot.parent;
69452
+ return Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
69453
+ allowGlobalReactNamespace: true,
69454
+ allowUnboundBareCalls: true
69455
+ }));
69456
+ };
69457
+ const isCleanupBackedLifecycleGuard = (guardExpression, functionNode, context) => {
69458
+ const guardKey = serializeReferenceKey({
69459
+ node: guardExpression,
69460
+ scopes: context.scopes
69461
+ });
69462
+ if (!guardKey || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
69463
+ let ownerFunction = functionNode.parent;
69464
+ while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
69465
+ if (!ownerFunction) return false;
69466
+ const effectCallbacks = [];
69467
+ if (isEffectCallback(ownerFunction, context)) effectCallbacks.push(ownerFunction);
69468
+ walkOwnFunctionScope(ownerFunction, (candidate) => {
69469
+ if (!isNodeOfType(candidate, "CallExpression")) return;
69470
+ if (!isReactApiCall(candidate, EFFECT_HOOK_NAMES$6, context.scopes, {
69471
+ allowGlobalReactNamespace: true,
69472
+ allowUnboundBareCalls: true
69473
+ })) return;
69474
+ const effectCallback = candidate.arguments[0];
69475
+ if (effectCallback && isFunctionLike$1(effectCallback)) effectCallbacks.push(effectCallback);
69476
+ });
69477
+ const acceptedAssignments = /* @__PURE__ */ new Set();
69478
+ for (const effectCallback of effectCallbacks) {
69479
+ const effectAssignments = collectCleanupBackedLifecycleAssignments(effectCallback, guardKey, context);
69480
+ if (!effectAssignments) continue;
69481
+ for (const assignment of effectAssignments) acceptedAssignments.add(assignment);
69482
+ }
69483
+ return Boolean(acceptedAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(ownerFunction, guardKey, acceptedAssignments, context));
69484
+ };
69485
+ const collectLogicalOperands = (expression, operator) => {
69486
+ const stripped = stripParenExpression(expression);
69487
+ if (isNodeOfType(stripped, "LogicalExpression") && stripped.operator === operator) return [...collectLogicalOperands(stripped.left, operator), ...collectLogicalOperands(stripped.right, operator)];
69488
+ return [stripped];
69489
+ };
69490
+ const collectFinalizerGuardExpressions = (resetNode, protectingTry) => {
69491
+ const positive = [];
69492
+ const negative = [];
68069
69493
  let child = resetNode;
68070
69494
  let cursor = resetNode.parent;
68071
- let guardKey = null;
68072
- let guardExpression = null;
68073
- while (cursor && cursor !== functionNode) {
68074
- if (isNodeOfType(cursor, "IfStatement") && cursor.consequent === child && cursor.alternate === null) {
68075
- guardExpression = cursor.test;
68076
- guardKey = serializeReferenceKey({
68077
- node: cursor.test,
68078
- scopes: context.scopes
68079
- });
68080
- break;
68081
- }
69495
+ while (cursor && cursor !== protectingTry) {
69496
+ if (isNodeOfType(cursor, "IfStatement")) {
69497
+ if (cursor.consequent !== child || cursor.alternate !== null) return null;
69498
+ positive.push(...collectLogicalOperands(cursor.test, "&&"));
69499
+ } else if (isNodeOfType(cursor, "LogicalExpression")) {
69500
+ if (cursor.operator !== "&&" || cursor.right !== child) return null;
69501
+ positive.push(...collectLogicalOperands(cursor.left, "&&"));
69502
+ } else if (isNodeOfType(cursor, "BlockStatement")) {
69503
+ const childIndex = cursor.body.findIndex((statement) => statement === child);
69504
+ if (childIndex !== -1) for (const statement of cursor.body.slice(0, childIndex)) {
69505
+ if (!isNodeOfType(statement, "IfStatement") || statement.alternate !== null || !isUnconditionalReturnBranch(statement.consequent)) continue;
69506
+ negative.push(...collectLogicalOperands(statement.test, "||"));
69507
+ }
69508
+ } 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;
68082
69509
  child = cursor;
68083
69510
  cursor = cursor.parent ?? null;
68084
69511
  }
68085
- if (!guardKey || !guardExpression || !isInitiallyActiveLifecycleGuard(guardExpression, context)) return false;
68086
- cursor = functionNode.parent;
68087
- while (cursor) {
68088
- if (isFunctionLike$1(cursor)) {
68089
- const callbackRoot = findTransparentExpressionRoot(cursor);
68090
- const callbackCall = callbackRoot.parent;
68091
- if (Boolean(callbackCall && isNodeOfType(callbackCall, "CallExpression") && callbackCall.arguments[0] === callbackRoot && isReactApiCall(callbackCall, EFFECT_HOOK_NAMES$6, context.scopes, {
68092
- allowGlobalReactNamespace: true,
68093
- allowUnboundBareCalls: true
68094
- }))) {
68095
- const acceptedCleanupAssignments = /* @__PURE__ */ new Set();
68096
- for (const cleanupFunction of collectReturnedCleanupFunctions(cursor, context.scopes)) walkOwnFunctionScope(cleanupFunction, (cleanupNode) => {
68097
- const assignedValue = isNodeOfType(cleanupNode, "AssignmentExpression") ? stripParenExpression(cleanupNode.right) : null;
68098
- if (!isNodeOfType(cleanupNode, "AssignmentExpression") || cleanupNode.operator !== "=" || !isNodeOfType(assignedValue, "Literal") || assignedValue.value !== false || serializeReferenceKey({
68099
- node: cleanupNode.left,
68100
- scopes: context.scopes
68101
- }) !== guardKey || !isUnconditionallyExecutedWithinFunction(cleanupNode, cleanupFunction, context)) return;
68102
- acceptedCleanupAssignments.add(cleanupNode);
68103
- });
68104
- if (acceptedCleanupAssignments.size > 0 && !hasLifecycleGuardWriteOutsideCleanup(cursor, guardKey, acceptedCleanupAssignments, context)) return true;
68105
- }
68106
- }
68107
- cursor = cursor.parent ?? null;
68108
- }
68109
- return false;
69512
+ return cursor === protectingTry && positive.length + negative.length > 0 ? {
69513
+ positive,
69514
+ negative
69515
+ } : null;
69516
+ };
69517
+ const isPositiveFinalizerGuard = (expression, resetNode, functionNode, truthySets, firstRiskyAwait, context) => isCleanupBackedLifecycleGuard(expression, functionNode, context) || isClaimedOwnershipComparison(expression, "owns", functionNode, truthySets, firstRiskyAwait, resetNode, context);
69518
+ const isNegativeFinalizerGuard = (expression, resetNode, functionNode, truthySets, firstRiskyAwait, context) => {
69519
+ const stripped = stripParenExpression(expression);
69520
+ if (isNodeOfType(stripped, "UnaryExpression") && stripped.operator === "!") return isPositiveFinalizerGuard(stripped.argument, resetNode, functionNode, truthySets, firstRiskyAwait, context);
69521
+ return isClaimedOwnershipComparison(stripped, "lost", functionNode, truthySets, firstRiskyAwait, resetNode, context);
69522
+ };
69523
+ const isFinalizerResetProvablyGuarded = (resetNode, protectingTry, functionNode, truthySets, firstRiskyAwait, context) => {
69524
+ const guards = collectFinalizerGuardExpressions(resetNode, protectingTry);
69525
+ 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)));
68110
69526
  };
68111
69527
  const isAwaitInsideProtectedTry = (awaitNode, tryStatement) => {
68112
69528
  let child = awaitNode;
@@ -68212,7 +69628,13 @@ const analyzeFunction = (functionNode, context) => {
68212
69628
  const exceptionallyProtectedAwaits = collectExceptionallyProtectedAwaits(awaitSites, calls);
68213
69629
  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)));
68214
69630
  if (riskyAwaitsWithTruthySet.length === 0) continue;
68215
- 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)));
69631
+ const conditionalExceptionalResets = calls.filter((call) => {
69632
+ if (call.value || call.context === "plain" || call.isUnconditional || call.protectingTry === null) return false;
69633
+ const protectingTry = call.protectingTry;
69634
+ if (!isInsideTryFinalizer(call.node, protectingTry)) return true;
69635
+ const firstRiskyAwait = riskyAwaitsWithTruthySet.find((awaitSite) => isAwaitInsideProtectedTry(awaitSite.node, protectingTry));
69636
+ return !(firstRiskyAwait && isFinalizerResetProvablyGuarded(call.node, protectingTry, functionNode, truthySets, firstRiskyAwait, context));
69637
+ });
68216
69638
  for (const reset of conditionalExceptionalResets) {
68217
69639
  const catchHandler = reset.protectingTry?.handler;
68218
69640
  if (catchHandler && !catchHandlerCanBypassReset(catchHandler, functionNode, setterKey, context, false)) continue;
@@ -74084,6 +75506,29 @@ const doesPredicateTruthRequireMatch = (matchCall, predicateFunction) => {
74084
75506
  }
74085
75507
  return !isNegated && predicateFunction.body === child;
74086
75508
  };
75509
+ const doesPredicateReturnNormalizedMatch = (matchCall, predicateFunction) => {
75510
+ if (!isFunctionLike$1(predicateFunction) || !isNodeOfType(predicateFunction.body, "BlockStatement") || predicateFunction.body.body.length !== 2 || !isNodeOfType(predicateFunction.body.body[0], "VariableDeclaration")) return false;
75511
+ const returnStatement = predicateFunction.body.body[1];
75512
+ if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
75513
+ let negationCount = 0;
75514
+ let expression = matchCall;
75515
+ let parent = expression.parent ?? null;
75516
+ while (parent && parent !== returnStatement) {
75517
+ if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") {
75518
+ negationCount += 1;
75519
+ expression = parent;
75520
+ parent = parent.parent ?? null;
75521
+ continue;
75522
+ }
75523
+ if (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) || isNodeOfType(parent, "ChainExpression")) {
75524
+ expression = parent;
75525
+ parent = parent.parent ?? null;
75526
+ continue;
75527
+ }
75528
+ return false;
75529
+ }
75530
+ return parent === returnStatement && returnStatement.argument === expression && negationCount % 2 === 0;
75531
+ };
74087
75532
  const isStringTypeofGuardForPath = (test, expectedPath) => {
74088
75533
  const target = stripParenExpression(test);
74089
75534
  if (!isNodeOfType(target, "BinaryExpression") || target.operator !== "===") return false;
@@ -74124,6 +75569,26 @@ const pathUsesOptionalAccess = (node) => {
74124
75569
  current = current.object;
74125
75570
  }
74126
75571
  };
75572
+ const getNormalizedClassNameRoot = (expression) => {
75573
+ const conditional = stripParenExpression(expression);
75574
+ if (!isNodeOfType(conditional, "ConditionalExpression")) return null;
75575
+ const consequent = stripParenExpression(conditional.consequent);
75576
+ const rootIdentifier = getRootIdentifier(consequent);
75577
+ if (!rootIdentifier || receiverPathKey(consequent) !== `${rootIdentifier.name}.className`) return null;
75578
+ const test = stripParenExpression(conditional.test);
75579
+ if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "===") return null;
75580
+ const testOperands = [test.left, test.right].map((operand) => stripParenExpression(operand));
75581
+ const typeofOperand = testOperands.find((operand) => isNodeOfType(operand, "UnaryExpression"));
75582
+ const stringOperand = testOperands.find((operand) => isNodeOfType(operand, "Literal"));
75583
+ if (!typeofOperand || !isNodeOfType(typeofOperand, "UnaryExpression") || typeofOperand.operator !== "typeof" || receiverPathKey(typeofOperand.argument) !== `${rootIdentifier.name}.className` || !stringOperand || !isNodeOfType(stringOperand, "Literal") || stringOperand.value !== "string") return null;
75584
+ const alternate = stripParenExpression(conditional.alternate);
75585
+ if (!isNodeOfType(alternate, "LogicalExpression") || alternate.operator !== "??") return null;
75586
+ const fallback = stripParenExpression(alternate.right);
75587
+ const attributeCall = stripParenExpression(alternate.left);
75588
+ 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;
75589
+ const attributeName = attributeCall.arguments[0] ? stripParenExpression(attributeCall.arguments[0]) : null;
75590
+ return attributeName && isNodeOfType(attributeName, "Literal") && attributeName.value === "class" ? rootIdentifier : null;
75591
+ };
74127
75592
  const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
74128
75593
  const resultIdentifier = getRootIdentifier(matchReceiver);
74129
75594
  const resultPath = receiverPathKey(matchReceiver);
@@ -74132,11 +75597,17 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
74132
75597
  if (!isDirectFinderMatchReturn(assertion) && !isOptionalResultPath) return false;
74133
75598
  const resultSymbol = context.scopes.symbolFor(resultIdentifier);
74134
75599
  const initializer = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
74135
- if (resultSymbol?.kind !== "const" || !initializer || !isNodeOfType(initializer, "CallExpression")) return false;
75600
+ if (resultSymbol?.kind !== "const" || !initializer) return false;
75601
+ const finderCall = isNodeOfType(initializer, "CallExpression") ? initializer : isNodeOfType(initializer, "ConditionalExpression") ? (() => {
75602
+ const alternate = stripParenExpression(initializer.alternate);
75603
+ const consequent = stripParenExpression(initializer.consequent);
75604
+ return (isNodeOfType(alternate, "Literal") && alternate.value === null || isNodeOfType(alternate, "Identifier") && alternate.name === "undefined" && context.scopes.isGlobalReference(alternate)) && isNodeOfType(consequent, "CallExpression") ? consequent : null;
75605
+ })() : null;
75606
+ if (!finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
74136
75607
  if (!isOptionalResultPath && !isImmediatelyGuardedFinderResult(assertion, resultSymbol, resultPath, context)) return false;
74137
- const finderCallee = stripParenExpression(initializer.callee);
75608
+ const finderCallee = stripParenExpression(finderCall.callee);
74138
75609
  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;
74139
- const predicateArgument = initializer.arguments[1];
75610
+ const predicateArgument = finderCall.arguments[1];
74140
75611
  if (!predicateArgument) return false;
74141
75612
  const predicateFunction = resolveExactLocalFunction(predicateArgument, context.scopes);
74142
75613
  if (!predicateFunction || !isFunctionLike$1(predicateFunction)) return false;
@@ -74160,6 +75631,43 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
74160
75631
  });
74161
75632
  return didProveMatch;
74162
75633
  };
75634
+ const isMatchProvenByNormalizedFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
75635
+ const normalizedReceiver = stripParenExpression(matchReceiver);
75636
+ if (!isNodeOfType(normalizedReceiver, "Identifier")) return false;
75637
+ const normalizedReceiverSymbol = context.scopes.symbolFor(normalizedReceiver);
75638
+ const normalizedReceiverInitializer = normalizedReceiverSymbol?.initializer ? stripParenExpression(normalizedReceiverSymbol.initializer) : null;
75639
+ const resultIdentifier = normalizedReceiverInitializer ? getNormalizedClassNameRoot(normalizedReceiverInitializer) : null;
75640
+ if (normalizedReceiverSymbol?.kind !== "const" || normalizedReceiverSymbol.references.some((reference) => reference.flag !== "read") || !resultIdentifier) return false;
75641
+ const resultSymbol = context.scopes.symbolFor(resultIdentifier);
75642
+ const finderCall = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
75643
+ if (resultSymbol?.kind !== "const" || resultSymbol.references.some((reference) => reference.flag !== "read") || !finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
75644
+ const finderCallee = stripParenExpression(finderCall.callee);
75645
+ if (!isNodeOfType(finderCallee, "Identifier") || context.scopes.symbolFor(finderCallee)?.kind !== "import" || getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) !== "findUpUntil") return false;
75646
+ if (!isPresenceProvenBeforeNode(assertion, (test) => {
75647
+ const expression = stripParenExpression(test);
75648
+ return isNodeOfType(expression, "Identifier") && context.scopes.symbolFor(expression)?.id === resultSymbol.id;
75649
+ })) return false;
75650
+ const predicateArgument = finderCall.arguments[1];
75651
+ const predicateFunction = predicateArgument ? resolveExactLocalFunction(predicateArgument, context.scopes) : null;
75652
+ if (!predicateFunction || !isFunctionLike$1(predicateFunction) || predicateFunction.async || predicateFunction.generator) return false;
75653
+ const predicateParameter = predicateFunction.params[0];
75654
+ if (!isNodeOfType(predicateParameter, "Identifier")) return false;
75655
+ let didProveNormalizedMatch = false;
75656
+ walkAst(predicateFunction.body, (child) => {
75657
+ if (didProveNormalizedMatch || isFunctionLike$1(child)) return false;
75658
+ 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;
75659
+ const predicateReceiver = stripParenExpression(child.callee.object);
75660
+ if (!isNodeOfType(predicateReceiver, "Identifier")) return;
75661
+ const predicateReceiverSymbol = context.scopes.symbolFor(predicateReceiver);
75662
+ const predicateReceiverInitializer = predicateReceiverSymbol?.initializer ? stripParenExpression(predicateReceiverSymbol.initializer) : null;
75663
+ const predicateRoot = predicateReceiverInitializer ? getNormalizedClassNameRoot(predicateReceiverInitializer) : null;
75664
+ if (predicateReceiverSymbol?.kind === "const" && predicateReceiverSymbol.references.every((reference) => reference.flag === "read") && predicateRoot?.name === predicateParameter.name) {
75665
+ didProveNormalizedMatch = true;
75666
+ return false;
75667
+ }
75668
+ });
75669
+ return didProveNormalizedMatch;
75670
+ };
74163
75671
  const scopeProvesFindMatch = (assertion, findReceiver, findPredicate, context) => {
74164
75672
  if (!isStablePredicate(findPredicate, context)) return false;
74165
75673
  return isPresenceProvenBeforeNode(assertion, (test) => testPositivelyContainsCall(test, (call) => {
@@ -74260,6 +75768,123 @@ const isEnsureThenFind = (assertion, findReceiver, findPredicate) => {
74260
75768
  }
74261
75769
  return false;
74262
75770
  };
75771
+ const getReceiverRootIdentifier = (node) => {
75772
+ let target = stripParenExpression(node);
75773
+ while (isNodeOfType(target, "MemberExpression")) target = stripParenExpression(target.object);
75774
+ return isNodeOfType(target, "Identifier") ? target : null;
75775
+ };
75776
+ const getReceiverStatePath = (node) => {
75777
+ const target = stripParenExpression(node);
75778
+ if (isNodeOfType(target, "Identifier")) return target.name;
75779
+ if (!isNodeOfType(target, "MemberExpression")) return null;
75780
+ const objectPath = getReceiverStatePath(target.object);
75781
+ if (!objectPath) return null;
75782
+ return `${objectPath}.${getStaticPropertyName(target) ?? "*"}`;
75783
+ };
75784
+ const doesReceiverStateChangeBeforeAssertion = (ownerFunction, receiver, startOffset, assertion, context) => {
75785
+ const receiverRoot = getReceiverRootIdentifier(receiver);
75786
+ const receiverSymbol = receiverRoot ? context.scopes.symbolFor(receiverRoot) : null;
75787
+ const receiverPath = getReceiverStatePath(receiver);
75788
+ if (!receiverRoot || !receiverSymbol || !receiverPath) return true;
75789
+ const receiverAliasPaths = new Map([[receiverSymbol.id, receiverRoot.name]]);
75790
+ let didAddAlias = true;
75791
+ while (didAddAlias) {
75792
+ didAddAlias = false;
75793
+ walkAst(ownerFunction, (child) => {
75794
+ if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75795
+ if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier") || !child.init) return;
75796
+ const initializer = stripParenExpression(child.init);
75797
+ if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return;
75798
+ const initializerRoot = getReceiverRootIdentifier(initializer);
75799
+ const initializerSymbol = initializerRoot ? context.scopes.symbolFor(initializerRoot) : null;
75800
+ const initializerBasePath = initializerSymbol ? receiverAliasPaths.get(initializerSymbol.id) : null;
75801
+ const initializerPath = getReceiverStatePath(initializer);
75802
+ if (!initializerRoot || !initializerBasePath || !initializerPath) return;
75803
+ const aliasSymbol = context.scopes.symbolFor(child.id);
75804
+ if (aliasSymbol && !receiverAliasPaths.has(aliasSymbol.id)) {
75805
+ const initializerSuffix = initializerPath.slice(initializerRoot.name.length);
75806
+ receiverAliasPaths.set(aliasSymbol.id, `${initializerBasePath}${initializerSuffix}`);
75807
+ didAddAlias = true;
75808
+ }
75809
+ });
75810
+ }
75811
+ let didChangeReceiverState = false;
75812
+ walkAst(ownerFunction, (child) => {
75813
+ if (didChangeReceiverState) return false;
75814
+ if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75815
+ if (child.range[0] <= startOffset || child.range[0] >= assertion.range[0]) return;
75816
+ if (isNodeOfType(child, "CallExpression")) {
75817
+ didChangeReceiverState = true;
75818
+ return false;
75819
+ }
75820
+ const mutationTarget = isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete" ? stripParenExpression(isNodeOfType(child, "AssignmentExpression") ? child.left : child.argument) : null;
75821
+ const mutationRoot = mutationTarget ? getReceiverRootIdentifier(mutationTarget) : null;
75822
+ const mutationSymbol = mutationRoot ? context.scopes.symbolFor(mutationRoot) : null;
75823
+ const mutationBasePath = mutationSymbol ? receiverAliasPaths.get(mutationSymbol.id) : null;
75824
+ const mutationPath = mutationTarget ? getReceiverStatePath(mutationTarget) : null;
75825
+ if (mutationRoot && mutationBasePath && mutationPath) {
75826
+ const canonicalMutationPath = `${mutationBasePath}${mutationPath.slice(mutationRoot.name.length)}`;
75827
+ if (canonicalMutationPath !== receiverPath && !canonicalMutationPath.startsWith(`${receiverPath}.`) && !receiverPath.startsWith(`${canonicalMutationPath}.`)) return;
75828
+ didChangeReceiverState = true;
75829
+ return false;
75830
+ }
75831
+ });
75832
+ return didChangeReceiverState;
75833
+ };
75834
+ const isFindProvenByGuardedMaximum = (assertion, findReceiver, findPredicate, context) => {
75835
+ const findLookup = findEqualityLookupParts(findPredicate);
75836
+ const maximumIdentifier = findLookup ? stripParenExpression(findLookup.comparedValue) : null;
75837
+ if (!findLookup || !maximumIdentifier || !isNodeOfType(maximumIdentifier, "Identifier")) return false;
75838
+ const maximumSymbol = context.scopes.symbolFor(maximumIdentifier);
75839
+ const maximumInitializer = maximumSymbol?.initializer ? stripParenExpression(maximumSymbol.initializer) : null;
75840
+ 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;
75841
+ const filterCall = stripParenExpression(maximumInitializer.callee.object);
75842
+ if (!isNodeOfType(filterCall, "CallExpression") || !isNodeOfType(filterCall.callee, "MemberExpression") || getStaticPropertyName(filterCall.callee) !== "filter" || !areNodesLooselyEqual(filterCall.callee.object, findReceiver)) return false;
75843
+ const filterPredicate = filterCall.arguments[0] ? stripParenExpression(filterCall.arguments[0]) : null;
75844
+ if (!filterPredicate || !isStablePredicate(filterPredicate, context)) return false;
75845
+ const reducerArgument = maximumInitializer.arguments[0] ? stripParenExpression(maximumInitializer.arguments[0]) : null;
75846
+ const reducerFunction = reducerArgument ? resolveExactLocalFunction(reducerArgument, context.scopes) : null;
75847
+ const initialValue = maximumInitializer.arguments[1] ? stripParenExpression(maximumInitializer.arguments[1]) : null;
75848
+ if (!reducerFunction || !isFunctionLike$1(reducerFunction) || reducerFunction.async || reducerFunction.generator || !initialValue || !isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return false;
75849
+ const accumulatorParameter = reducerFunction.params[0];
75850
+ const itemParameter = reducerFunction.params[1];
75851
+ const reducerBody = singleExpressionPredicateBody(reducerFunction);
75852
+ if (!isNodeOfType(accumulatorParameter, "Identifier") || !isNodeOfType(itemParameter, "Identifier") || !reducerBody || !isNodeOfType(reducerBody, "CallExpression") || !isNodeOfType(reducerBody.callee, "MemberExpression") || getStaticPropertyName(reducerBody.callee) !== "max") return false;
75853
+ const mathReceiver = stripParenExpression(reducerBody.callee.object);
75854
+ if (!isNodeOfType(mathReceiver, "Identifier") || mathReceiver.name !== "Math" || !context.scopes.isGlobalReference(mathReceiver) || reducerBody.arguments.length !== 2) return false;
75855
+ const accumulatorArgument = reducerBody.arguments.find((argument) => {
75856
+ const expression = stripParenExpression(argument);
75857
+ return isNodeOfType(expression, "Identifier") && expression.name === accumulatorParameter.name;
75858
+ });
75859
+ const itemMemberArgument = reducerBody.arguments.find((argument) => {
75860
+ const expression = stripParenExpression(argument);
75861
+ const rootIdentifier = getRootIdentifier(expression);
75862
+ return isNodeOfType(expression, "MemberExpression") && rootIdentifier?.name === itemParameter.name && receiverPathKey(expression)?.slice(itemParameter.name.length + 1) === findLookup.propertyName;
75863
+ });
75864
+ if (!accumulatorArgument || !itemMemberArgument) return false;
75865
+ if (!receiverPathKey(findReceiver)) return false;
75866
+ let ownerFunction = assertion.parent ?? null;
75867
+ while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
75868
+ if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
75869
+ const maximumEnd = maximumInitializer.range[1];
75870
+ if (doesReceiverStateChangeBeforeAssertion(ownerFunction.body, findReceiver, maximumEnd, assertion, context)) return false;
75871
+ return isPresenceProvenBeforeNode(assertion, (test) => {
75872
+ const comparison = stripParenExpression(test);
75873
+ if (!isNodeOfType(comparison, "BinaryExpression")) return false;
75874
+ return [[
75875
+ comparison.left,
75876
+ comparison.right,
75877
+ comparison.operator
75878
+ ], [
75879
+ comparison.right,
75880
+ comparison.left,
75881
+ comparison.operator === "<" ? ">" : comparison.operator === ">" ? "<" : comparison.operator
75882
+ ]].some(([candidateMaximum, candidateInitial, operator]) => {
75883
+ const candidateMaximumIdentifier = stripParenExpression(candidateMaximum);
75884
+ return operator === ">" && isNodeOfType(candidateMaximumIdentifier, "Identifier") && context.scopes.symbolFor(candidateMaximumIdentifier)?.id === maximumSymbol.id && areNodesLooselyEqual(stripParenExpression(candidateInitial), initialValue);
75885
+ });
75886
+ });
75887
+ };
74263
75888
  const isDefinitelyNonNullishMapValue = (value) => {
74264
75889
  if (!value) return false;
74265
75890
  const expression = stripParenExpression(value);
@@ -74283,15 +75908,15 @@ const unwrapFalseBooleanGuard = (test) => {
74283
75908
  };
74284
75909
  const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
74285
75910
  const stableLookupKey = stripParenExpression(lookupKey);
74286
- if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal")) return false;
75911
+ const lookupKeyRoot = isNodeOfType(stableLookupKey, "MemberExpression") ? getRootIdentifier(stableLookupKey) : null;
75912
+ const lookupKeySymbol = isNodeOfType(stableLookupKey, "Identifier") ? context.scopes.symbolFor(stableLookupKey) : lookupKeyRoot ? context.scopes.symbolFor(lookupKeyRoot) : null;
75913
+ if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal") && (!isNodeOfType(stableLookupKey, "MemberExpression") || !lookupKeyRoot || lookupKeySymbol?.kind !== "const")) return false;
74287
75914
  const receiverSymbol = context.scopes.symbolFor(receiver);
74288
75915
  if (!receiverSymbol) return false;
74289
75916
  const receiverMatches = (candidate) => {
74290
75917
  const target = stripParenExpression(candidate);
74291
75918
  return isNodeOfType(target, "Identifier") && context.scopes.symbolFor(target)?.id === receiverSymbol.id;
74292
75919
  };
74293
- const lookupKeyExpression = stripParenExpression(lookupKey);
74294
- const lookupKeySymbol = isNodeOfType(lookupKeyExpression, "Identifier") ? context.scopes.symbolFor(lookupKeyExpression) : null;
74295
75920
  let child = assertion;
74296
75921
  let ancestor = assertion.parent ?? null;
74297
75922
  while (ancestor && !isFunctionLike$1(ancestor)) {
@@ -74311,7 +75936,7 @@ const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
74311
75936
  const populationCall = populationCalls[0];
74312
75937
  if (!populationCall) continue;
74313
75938
  const populationCallStart = populationCall.range[0];
74314
- if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))) continue;
75939
+ 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;
74315
75940
  if (receiverSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) continue;
74316
75941
  if (!indexedRelevantCalls(ancestor).some((laterCall) => {
74317
75942
  if (laterCall.range[0] <= populationCallStart || laterCall.range[0] >= assertion.range[0] || !isNodeOfType(laterCall.callee, "MemberExpression") || !receiverMatches(laterCall.callee.object)) return false;
@@ -74421,6 +76046,7 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
74421
76046
  const findReceiver = callee.object;
74422
76047
  if (predicate && isExhaustiveLiteralTupleMapping(findReceiver, predicate, context)) return;
74423
76048
  if (predicate && scopeProvesFindMatch(node, findReceiver, predicate, context)) return;
76049
+ if (predicate && isFindProvenByGuardedMaximum(node, findReceiver, predicate, context)) return;
74424
76050
  if (predicate && isEnsureThenFind(node, findReceiver, predicate)) return;
74425
76051
  }
74426
76052
  if (methodName === "match") {
@@ -74430,6 +76056,7 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
74430
76056
  const regexKey = pattern ? regexComparableKey(pattern, context) : null;
74431
76057
  if (pattern && isGuardedAnchoredCharacterMatch(node, matchReceiver, pattern)) return;
74432
76058
  if (pattern && regexKey && isMatchProvenByFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
76059
+ if (pattern && regexKey && isMatchProvenByNormalizedFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
74433
76060
  if (regexKey && scopeProvesMatchTested(node, regexKey, matchReceiver, context)) return;
74434
76061
  }
74435
76062
  if (methodName === "get") {
@@ -78368,16 +79995,23 @@ const MAX_INITIATOR_RESOLUTION_DEPTH = 3;
78368
79995
  const STATE_DISPATCHER_HOOK_NAMES = new Set(["useState", "useReducer"]);
78369
79996
  const REF_HOOK_NAMES = new Set(["useRef"]);
78370
79997
  const MESSAGE$26 = "This promise chain runs in an effect, ends in a `.then` that sets state or mutates a ref, and has no `.catch` or enclosing try/catch, so a rejection leaves the state unset and surfaces as an unhandled rejection. Add a `.catch` handler on the chain (`.finally` does not count).";
78371
- const isKnownNonThenableHandlerReturn = (expression, context, visitedBindingIdentifiers = /* @__PURE__ */ new Set()) => {
79998
+ const isKnownNonRejectingHandlerReturn = (expression, context, visitedBindingIdentifiers = /* @__PURE__ */ new Set()) => {
78372
79999
  const strippedExpression = stripParenExpression(expression);
78373
80000
  if (isDefinitelyNonThenableValue(strippedExpression)) return true;
80001
+ if (isNodeOfType(strippedExpression, "CallExpression") && isNodeOfType(strippedExpression.callee, "MemberExpression")) {
80002
+ const receiver = stripParenExpression(strippedExpression.callee.object);
80003
+ if (isNodeOfType(receiver, "Identifier") && receiver.name === "Promise" && context.scopes.isGlobalReference(receiver) && getStaticPropertyName(strippedExpression.callee) === "resolve") {
80004
+ const resolvedValue = strippedExpression.arguments[0];
80005
+ return !resolvedValue || !isNodeOfType(resolvedValue, "SpreadElement") && isKnownNonRejectingHandlerReturn(resolvedValue, context, visitedBindingIdentifiers);
80006
+ }
80007
+ }
78374
80008
  if (!isNodeOfType(strippedExpression, "Identifier")) return false;
78375
80009
  if (strippedExpression.name === "undefined" && context.scopes.isGlobalReference(strippedExpression)) return true;
78376
80010
  const symbol = context.scopes.symbolFor(strippedExpression);
78377
80011
  if (!symbol || visitedBindingIdentifiers.has(symbol.bindingIdentifier)) return false;
78378
80012
  visitedBindingIdentifiers.add(symbol.bindingIdentifier);
78379
80013
  const initializer = getDirectUnreassignedInitializer(symbol);
78380
- return Boolean(initializer && isKnownNonThenableHandlerReturn(initializer, context, visitedBindingIdentifiers));
80014
+ return Boolean(initializer && isKnownNonRejectingHandlerReturn(initializer, context, visitedBindingIdentifiers));
78381
80015
  };
78382
80016
  const isKnownNonRejectingHandler = (argument, context) => {
78383
80017
  if (!argument) return false;
@@ -78392,7 +80026,7 @@ const isKnownNonRejectingHandler = (argument, context) => {
78392
80026
  canReject = true;
78393
80027
  return false;
78394
80028
  }
78395
- if (isNodeOfType(child, "ReturnStatement") && child.argument && !isKnownNonThenableHandlerReturn(child.argument, context)) {
80029
+ if (isNodeOfType(child, "ReturnStatement") && child.argument && !isKnownNonRejectingHandlerReturn(child.argument, context)) {
78396
80030
  if (!isNodeOfType(stripParenExpression(child.argument), "CallExpression")) {
78397
80031
  canReject = true;
78398
80032
  return false;
@@ -78441,6 +80075,28 @@ const handlerHasPotentiallyThrowingMemberRead = (argument, context) => {
78441
80075
  });
78442
80076
  return hasPotentiallyThrowingMemberRead;
78443
80077
  };
80078
+ const hasRejectionHandler = (chain, argument, context, allowTerminalCatchBlock) => {
80079
+ if (!argument) return false;
80080
+ if (!handlerHasPotentiallyThrowingMemberRead(argument, context) && (chainCarriesRejectionHandler(chain, context.scopes) || isKnownNonRejectingHandler(argument, context))) return true;
80081
+ if (!allowTerminalCatchBlock) return false;
80082
+ const candidate = stripParenExpression(argument);
80083
+ const handler = isNodeOfType(candidate, "Identifier") ? resolveExactLocalFunction(candidate, context.scopes) : candidate;
80084
+ if (!handler || !isFunctionLike$1(handler)) return isNodeOfType(candidate, "MemberExpression") || isNodeOfType(candidate, "Identifier") && candidate.name !== "undefined";
80085
+ if (!isNodeOfType(handler.body, "BlockStatement")) return false;
80086
+ let doesExplicitlyReject = false;
80087
+ walkOwnFunctionScope(handler, (child) => {
80088
+ if (doesExplicitlyReject) return false;
80089
+ if (isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "AwaitExpression")) {
80090
+ doesExplicitlyReject = true;
80091
+ return false;
80092
+ }
80093
+ if (isNodeOfType(child, "ReturnStatement") && child.argument && !isKnownNonRejectingHandlerReturn(child.argument, context)) {
80094
+ doesExplicitlyReject = true;
80095
+ return false;
80096
+ }
80097
+ });
80098
+ return !doesExplicitlyReject;
80099
+ };
78444
80100
  const walkPromiseChain = (chainExpression, context) => {
78445
80101
  let cursor = stripParenExpression(chainExpression);
78446
80102
  let hasCatch = false;
@@ -78452,10 +80108,9 @@ const walkPromiseChain = (chainExpression, context) => {
78452
80108
  while (isNodeOfType(cursor, "CallExpression") && isNodeOfType(cursor.callee, "MemberExpression") && PROMISE_METHOD_NAMES.has(getStaticPropertyName(cursor.callee) ?? "")) {
78453
80109
  const methodName = getStaticPropertyName(cursor.callee);
78454
80110
  const rejectionHandlerArgument = methodName === "catch" ? cursor.arguments[0] : cursor.arguments[1];
78455
- const hasAbsorbingRejectionHandler = !handlerHasPotentiallyThrowingMemberRead(rejectionHandlerArgument, context) && (chainCarriesRejectionHandler(cursor, context.scopes) || isKnownNonRejectingHandler(rejectionHandlerArgument, context));
78456
- if (!didReachTerminalThen && methodName === "catch" && hasAbsorbingRejectionHandler) hasCatch = true;
80111
+ if (!didReachTerminalThen && methodName === "catch" && hasRejectionHandler(cursor, rejectionHandlerArgument, context, true)) hasCatch = true;
78457
80112
  if (methodName === "then") {
78458
- if (!didReachTerminalThen && hasAbsorbingRejectionHandler) hasRejectionHandlerArgument = true;
80113
+ if (!didReachTerminalThen && hasRejectionHandler(cursor, rejectionHandlerArgument, context, false)) hasRejectionHandlerArgument = true;
78459
80114
  didReachTerminalThen = true;
78460
80115
  sawThen = true;
78461
80116
  const callbackArgument = cursor.arguments[0];
@@ -80782,6 +82437,45 @@ const noRefCallbackCleanupBeforeReact19 = defineRule({
80782
82437
  } })
80783
82438
  });
80784
82439
  //#endregion
82440
+ //#region src/plugin/utils/contains-non-deterministic-source.ts
82441
+ const NON_DETERMINISTIC_MEMBER_CALLS = new Set([
82442
+ "Math.random",
82443
+ "Date.now",
82444
+ "performance.now",
82445
+ "crypto.randomUUID",
82446
+ "crypto.getRandomValues"
82447
+ ]);
82448
+ const NON_DETERMINISTIC_ID_GENERATOR_NAMES = new Set([
82449
+ "nanoid",
82450
+ "uuid",
82451
+ "cuid",
82452
+ "ulid",
82453
+ "createId"
82454
+ ]);
82455
+ const isZeroArgDateConstruction = (node) => isNodeOfType(node, "NewExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Date" && (node.arguments?.length ?? 0) === 0;
82456
+ const containsNonDeterministicSource = (root) => {
82457
+ let found = false;
82458
+ walkAst(root, (child) => {
82459
+ if (found) return false;
82460
+ if (isFunctionLike$1(child)) return false;
82461
+ if (isZeroArgDateConstruction(child)) {
82462
+ found = true;
82463
+ return false;
82464
+ }
82465
+ if (!isNodeOfType(child, "CallExpression")) return;
82466
+ const callee = child.callee;
82467
+ if (isNodeOfType(callee, "Identifier") && NON_DETERMINISTIC_ID_GENERATOR_NAMES.has(callee.name)) {
82468
+ found = true;
82469
+ return false;
82470
+ }
82471
+ if (isNodeOfType(callee, "MemberExpression") && isNodeOfType(callee.object, "Identifier") && isNodeOfType(callee.property, "Identifier") && NON_DETERMINISTIC_MEMBER_CALLS.has(`${callee.object.name}.${callee.property.name}`)) {
82472
+ found = true;
82473
+ return false;
82474
+ }
82475
+ });
82476
+ return found;
82477
+ };
82478
+ //#endregion
80785
82479
  //#region src/plugin/rules/state-and-effects/no-ref-current-in-render.ts
80786
82480
  const REPEATED_ANCESTOR_TYPES = new Set([
80787
82481
  "DoWhileStatement",
@@ -80812,45 +82506,75 @@ const resolveImmutableInitializationValue = (node, scopes, visitedSymbolIds = /*
80812
82506
  };
80813
82507
  const isProvablyTruthyInitializationValue = (node, scopes) => {
80814
82508
  const expression = resolveImmutableInitializationValue(node, scopes);
80815
- return Boolean(expression && (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression")));
82509
+ if (!expression) return false;
82510
+ if (isNodeOfType(expression, "CallExpression")) {
82511
+ const callee = stripParenExpression(expression.callee);
82512
+ return isNodeOfType(callee, "Identifier") && callee.name.startsWith("create");
82513
+ }
82514
+ return isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression") || isNodeOfType(expression, "ClassExpression");
80816
82515
  };
80817
- const getInitializationConstructorName = (node, scopes) => {
82516
+ const getInitializationValueName = (node, scopes) => {
80818
82517
  const expression = resolveImmutableInitializationValue(node, scopes);
80819
82518
  if (!expression) return null;
80820
- if (isNodeOfType(expression, "NewExpression")) {
82519
+ if (isNodeOfType(expression, "NewExpression") || isNodeOfType(expression, "CallExpression")) {
80821
82520
  const callee = stripParenExpression(expression.callee);
80822
- return isNodeOfType(callee, "Identifier") ? callee.name : null;
82521
+ if (!isNodeOfType(callee, "Identifier")) return null;
82522
+ return callee.name.startsWith("create") && callee.name.length > 6 ? callee.name.slice(6) : callee.name;
80823
82523
  }
80824
82524
  return null;
80825
82525
  };
82526
+ const isMatchingReturnType = (typeNode, initializationValue, scopes) => {
82527
+ if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
82528
+ const typeName = typeNode.typeName;
82529
+ if (!isNodeOfType(typeName, "Identifier") || typeName.name !== "ReturnType") return false;
82530
+ const [returnTypeArgument] = typeNode.typeArguments?.params ?? [];
82531
+ if (!returnTypeArgument || !isNodeOfType(returnTypeArgument, "TSTypeQuery")) return false;
82532
+ const queriedName = returnTypeArgument.exprName;
82533
+ const expression = stripParenExpression(initializationValue);
82534
+ if (!isNodeOfType(queriedName, "Identifier") || !isNodeOfType(expression, "CallExpression")) return false;
82535
+ const callee = stripParenExpression(expression.callee);
82536
+ if (!isNodeOfType(callee, "Identifier")) return false;
82537
+ const queriedSymbol = scopes.symbolFor(queriedName);
82538
+ const calleeSymbol = scopes.symbolFor(callee);
82539
+ return queriedSymbol && calleeSymbol ? queriedSymbol.id === calleeSymbol.id : queriedName.name === callee.name;
82540
+ };
80826
82541
  const isClosedTruthyTypeDomain = (typeNode, initializationValue, scopes) => {
80827
82542
  const initializationExpression = stripParenExpression(initializationValue);
80828
82543
  if (isNodeOfType(typeNode, "TSTypeLiteral")) return isNodeOfType(initializationExpression, "ObjectExpression");
80829
82544
  if (isNodeOfType(typeNode, "TSArrayType") || isNodeOfType(typeNode, "TSTupleType")) return isNodeOfType(initializationExpression, "ArrayExpression");
80830
82545
  if (isNodeOfType(typeNode, "TSFunctionType") || isNodeOfType(typeNode, "TSConstructorType")) return isNodeOfType(initializationExpression, "ArrowFunctionExpression") || isNodeOfType(initializationExpression, "FunctionExpression") || isNodeOfType(initializationExpression, "ClassExpression");
80831
82546
  if (isNodeOfType(typeNode, "TSObjectKeyword")) return true;
82547
+ if (isNodeOfType(typeNode, "TSIndexedAccessType")) return isNodeOfType(initializationExpression, "ObjectExpression");
80832
82548
  if (!isNodeOfType(typeNode, "TSTypeReference")) return false;
80833
82549
  const typeName = typeNode.typeName;
80834
- return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationConstructorName(initializationExpression, scopes);
82550
+ if (isNodeOfType(initializationExpression, "ObjectExpression") || isMatchingReturnType(typeNode, initializationExpression, scopes)) return true;
82551
+ return isNodeOfType(typeName, "Identifier") && typeName.name === getInitializationValueName(initializationExpression, scopes);
80835
82552
  };
80836
82553
  const refHasClosedFalsySentinelDomain = (refSymbol, initializationValue, scopes) => {
80837
82554
  const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
80838
82555
  if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
80839
82556
  const [initialValue] = initializer.arguments ?? [];
80840
- if (!initialValue || isNodeOfType(initialValue, "SpreadElement") || !isEmptySentinel(initialValue, scopes)) return false;
82557
+ if (initialValue && isNodeOfType(initialValue, "SpreadElement") || initialValue && !isEmptySentinel(initialValue, scopes)) return false;
80841
82558
  const [declaredType] = initializer.typeArguments?.params ?? [];
80842
- if (!declaredType || !isNodeOfType(declaredType, "TSUnionType")) return false;
80843
- let hasEmptySentinel = false;
82559
+ if (!declaredType) return false;
82560
+ const domainTypes = isNodeOfType(declaredType, "TSUnionType") ? declaredType.types : [declaredType];
80844
82561
  let hasTruthyDomain = false;
80845
- for (const memberType of declaredType.types ?? []) {
80846
- if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) {
80847
- hasEmptySentinel = true;
80848
- continue;
80849
- }
82562
+ for (const memberType of domainTypes) {
82563
+ if (isNodeOfType(memberType, "TSNullKeyword") || isNodeOfType(memberType, "TSUndefinedKeyword")) continue;
80850
82564
  if (!isClosedTruthyTypeDomain(memberType, initializationValue, scopes)) return false;
80851
82565
  hasTruthyDomain = true;
80852
82566
  }
80853
- return hasEmptySentinel && hasTruthyDomain;
82567
+ return hasTruthyDomain;
82568
+ };
82569
+ const refHasEmptySentinelInitializer = (refSymbol, scopes) => {
82570
+ const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
82571
+ if (!initializer || !isNodeOfType(initializer, "CallExpression")) return false;
82572
+ const [initialValue] = initializer.arguments ?? [];
82573
+ return Boolean(!initialValue || !isNodeOfType(initialValue, "SpreadElement") && isEmptySentinel(initialValue, scopes));
82574
+ };
82575
+ const refHasDeclaredType = (refSymbol) => {
82576
+ const initializer = refSymbol.initializer ? stripParenExpression(refSymbol.initializer) : null;
82577
+ return Boolean(initializer && isNodeOfType(initializer, "CallExpression") && (initializer.typeArguments?.params.length ?? 0) > 0);
80854
82578
  };
80855
82579
  const isSafeRefIdentifierUse = (identifier) => {
80856
82580
  const expressionRoot = findTransparentExpressionRoot(identifier);
@@ -80882,25 +82606,40 @@ const expressionContainsRefCurrent = (expression, refSymbol, scopes) => {
80882
82606
  });
80883
82607
  return didFindRefCurrent;
80884
82608
  };
80885
- const hasNoCompetingRefCurrentWrite = (branchRoot, assignmentExpression, refSymbol, scopes) => {
80886
- let writeCount = 0;
80887
- walkAst(branchRoot, (child) => {
80888
- if (writeCount > 1) return false;
80889
- if (isNodeOfType(child, "AssignmentExpression")) {
80890
- if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
80891
- return;
82609
+ const isEmptySentinel = (node, scopes) => {
82610
+ const expression = stripParenExpression(node);
82611
+ return isNodeOfType(expression, "Literal") && expression.value === null || isNodeOfType(expression, "Identifier") && expression.name === "undefined" && scopes.isGlobalReference(expression);
82612
+ };
82613
+ const isInitializationInputIndependent = (node, renderOwner, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
82614
+ let isInputIndependent = true;
82615
+ walkAst(node, (child) => {
82616
+ if (!isInputIndependent) return false;
82617
+ if (resolveReactRefSymbol(child, scopes)) return false;
82618
+ if (!isNodeOfType(child, "Identifier")) return;
82619
+ const symbol = scopes.symbolFor(child);
82620
+ if (!symbol) return;
82621
+ if (symbol.kind === "import") return false;
82622
+ if (symbol.kind === "let" || symbol.kind === "var" || symbol.kind === "using") {
82623
+ isInputIndependent = false;
82624
+ return false;
80892
82625
  }
80893
- if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
80894
- if (expressionContainsRefCurrent(child.argument, refSymbol, scopes)) writeCount++;
80895
- return;
82626
+ if (isOutsideAllFunctions(symbol)) return false;
82627
+ if (symbol.kind === "parameter") {
82628
+ if (symbol.scope.node === renderOwner) isInputIndependent = false;
82629
+ return false;
80896
82630
  }
80897
- if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) {
80898
- if (expressionContainsRefCurrent(child.left, refSymbol, scopes)) writeCount++;
82631
+ if (!symbol.initializer || symbol.references.some((reference) => reference.flag !== "read")) {
82632
+ isInputIndependent = false;
82633
+ return false;
80899
82634
  }
82635
+ if (visitedSymbolIds.has(symbol.id)) return false;
82636
+ visitedSymbolIds.add(symbol.id);
82637
+ if (!isInitializationInputIndependent(symbol.initializer, renderOwner, scopes, visitedSymbolIds)) isInputIndependent = false;
82638
+ return false;
80900
82639
  });
80901
- return writeCount === 1 && expressionContainsRefCurrent(assignmentExpression.left, refSymbol, scopes);
82640
+ return isInputIndependent;
80902
82641
  };
80903
- const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
82642
+ const isPredictableInitializationValue = (node, refSymbol, renderOwner, scopes, requiresClosedTruthyDomain) => isInitializationInputIndependent(node, renderOwner, scopes) && !containsNonDeterministicSource(node) && (isProvablyTruthyInitializationValue(node, scopes) && (!requiresClosedTruthyDomain || !refHasDeclaredType(refSymbol)) || refHasClosedFalsySentinelDomain(refSymbol, node, scopes));
80904
82643
  const hasRepeatedExecutionAncestor = (node, stop) => {
80905
82644
  let ancestor = node.parent;
80906
82645
  while (ancestor && ancestor !== stop) {
@@ -80930,6 +82669,27 @@ const canExecuteTogether = (firstConstraints, secondConstraints) => {
80930
82669
  }
80931
82670
  return true;
80932
82671
  };
82672
+ const hasNoCoExecutableCompetingWrite = (assignmentExpression, renderOwner, refSymbol, scopes) => {
82673
+ const assignmentConstraints = getBranchConstraints(assignmentExpression, renderOwner);
82674
+ const synchronouslyInvokedFunctions = collectSynchronouslyEffectInvokedFunctions(renderOwner, scopes);
82675
+ let hasCompetingWrite = false;
82676
+ walkAst(renderOwner, (child) => {
82677
+ if (hasCompetingWrite) return false;
82678
+ let writtenExpression = null;
82679
+ if (isNodeOfType(child, "AssignmentExpression")) {
82680
+ if (child === assignmentExpression) return;
82681
+ writtenExpression = child.left;
82682
+ } else if (isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") writtenExpression = child.argument;
82683
+ else if (isNodeOfType(child, "ForInStatement") || isNodeOfType(child, "ForOfStatement")) writtenExpression = child.left;
82684
+ const deferredExecutionBoundary = findDeferredExecutionBoundary(child);
82685
+ const deferredWriteValue = isNodeOfType(child, "AssignmentExpression") && child.operator === "=" ? resolveImmutableInitializationValue(child.right, scopes) : null;
82686
+ const isDeferredTruthyWrite = deferredExecutionBoundary !== null && deferredExecutionBoundary !== renderOwner && !synchronouslyInvokedFunctions.has(deferredExecutionBoundary) && !executesDuringRender(deferredExecutionBoundary, scopes) && deferredWriteValue !== null && !isNodeOfType(deferredWriteValue, "CallExpression") && isProvablyTruthyInitializationValue(deferredWriteValue, scopes);
82687
+ if (!writtenExpression || isDeferredTruthyWrite || !expressionContainsRefCurrent(writtenExpression, refSymbol, scopes) || !canExecuteTogether(assignmentConstraints, getBranchConstraints(child, renderOwner))) return;
82688
+ hasCompetingWrite = true;
82689
+ return false;
82690
+ });
82691
+ return !hasCompetingWrite;
82692
+ };
80933
82693
  const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol, scopes) => {
80934
82694
  const assignmentConstraints = getBranchConstraints(assignmentExpression, branchRoot);
80935
82695
  const assignmentStart = getRangeStart(assignmentExpression);
@@ -80945,16 +82705,17 @@ const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol
80945
82705
  });
80946
82706
  return !hasCoExecutableWrite;
80947
82707
  };
82708
+ const isPredictableGuardedInitialization = (assignmentExpression, guardedBranch, renderOwner, refSymbol, scopes, requiresClosedTruthyDomain) => refHasEmptySentinelInitializer(refSymbol, scopes) && isPredictableInitializationValue(assignmentExpression.right, refSymbol, renderOwner, scopes, requiresClosedTruthyDomain) && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && (guardedBranch === renderOwner || !hasRepeatedExecutionAncestor(guardedBranch, renderOwner)) && hasNoPriorCoExecutableWrite(assignmentExpression, renderOwner, refSymbol, scopes) && hasNoCoExecutableCompetingWrite(assignmentExpression, renderOwner, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes);
80948
82709
  const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
80949
- if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
80950
- if (assignmentExpression.operator !== "=") return false;
80951
82710
  const renderOwner = findRenderPhaseComponentOrHook(assignmentExpression, scopes);
80952
82711
  if (!renderOwner) return false;
82712
+ if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return isPredictableGuardedInitialization(assignmentExpression, renderOwner, renderOwner, refSymbol, scopes, assignmentExpression.operator === "||=");
82713
+ if (assignmentExpression.operator !== "=") return false;
80953
82714
  let descendant = assignmentExpression;
80954
82715
  let ancestor = descendant.parent;
80955
82716
  while (ancestor) {
80956
82717
  const test = isNodeOfType(ancestor, "IfStatement") ? stripParenExpression(ancestor.test) : null;
80957
- if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant && isProvablyTruthyInitializationValue(assignmentExpression.right, scopes) && refHasClosedFalsySentinelDomain(refSymbol, assignmentExpression.right, scopes) && !hasRepeatedExecutionAncestor(assignmentExpression, ancestor.consequent) && !hasRepeatedExecutionAncestor(ancestor, renderOwner) && hasNoPriorCoExecutableWrite(assignmentExpression, ancestor.consequent, refSymbol, scopes) && hasNoCompetingRefCurrentWrite(renderOwner, assignmentExpression, refSymbol, scopes) && refDoesNotEscape(renderOwner, refSymbol, scopes)) return true;
82718
+ if (isNodeOfType(ancestor, "IfStatement") && test && isNodeOfType(test, "UnaryExpression") && test.operator === "!" && isSameRefCurrentAlias(test.argument, refSymbol, scopes) && ancestor.consequent === descendant && isPredictableGuardedInitialization(assignmentExpression, ancestor.consequent, renderOwner, refSymbol, scopes, true)) return true;
80958
82719
  if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(test, "BinaryExpression") && [
80959
82720
  "===",
80960
82721
  "==",
@@ -80964,7 +82725,7 @@ const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes)
80964
82725
  const { left, right } = test;
80965
82726
  const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
80966
82727
  const guardedBranch = test.operator === "===" || test.operator === "==" ? ancestor.consequent : ancestor.alternate;
80967
- if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
82728
+ if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && isPredictableGuardedInitialization(assignmentExpression, guardedBranch, renderOwner, refSymbol, scopes, false)) return true;
80968
82729
  }
80969
82730
  descendant = ancestor;
80970
82731
  ancestor = descendant.parent;
@@ -108771,9 +110532,6 @@ const rerenderFunctionalSetstate = defineRule({
108771
110532
  } })
108772
110533
  });
108773
110534
  //#endregion
108774
- //#region src/plugin/utils/is-trivial-built-in-construction.ts
108775
- const isTrivialBuiltInConstruction = (expression) => isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && TRIVIAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && (expression.arguments ?? []).length === 0;
108776
- //#endregion
108777
110535
  //#region src/plugin/rules/state-and-effects/rerender-lazy-ref-init.ts
108778
110536
  const rerenderLazyRefInit = defineRule({
108779
110537
  id: "rerender-lazy-ref-init",
@@ -108792,7 +110550,6 @@ const rerenderLazyRefInit = defineRule({
108792
110550
  const memberPropertyName = isNodeOfType(callee, "MemberExpression") && (isNodeOfType(callee.property, "Identifier") || isNodeOfType(callee.property, "PrivateIdentifier")) ? callee.property.name : null;
108793
110551
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : memberPropertyName ?? "fn";
108794
110552
  if (TRIVIAL_INITIALIZER_NAMES.has(calleeName)) return;
108795
- if (isTrivialBuiltInConstruction(initializer)) return;
108796
110553
  if (isPlainCall && isReactHookName(calleeName)) return;
108797
110554
  const callShape = isNewCall ? `new ${calleeName}()` : `${calleeName}()`;
108798
110555
  context.report({