oxlint-plugin-react-doctor 0.9.2-dev.9512488 → 0.9.2-dev.b10cd4c

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 +135 -1512
  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)-[^/]+\.[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;
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;
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,6 +665,19 @@ const TRIVIAL_INITIALIZER_NAMES = new Set([
665
665
  "parseInt",
666
666
  "parseFloat"
667
667
  ]);
668
+ const TRIVIAL_CONSTRUCTOR_NAMES = new Set([
669
+ "Date",
670
+ "Map",
671
+ "Set",
672
+ "WeakMap",
673
+ "WeakSet",
674
+ "WeakRef",
675
+ "RegExp",
676
+ "Error",
677
+ "URL",
678
+ "URLSearchParams",
679
+ "AbortController"
680
+ ]);
668
681
  const SETTER_PATTERN = /^set[A-Z]/;
669
682
  const RENDER_FUNCTION_PATTERN = /^render[A-Z]/;
670
683
  const UPPERCASE_PATTERN = /^[A-Z]/;
@@ -5874,7 +5887,6 @@ const isInlineFunctionExpression = (node) => Boolean(node && (isNodeOfType(node,
5874
5887
  //#endregion
5875
5888
  //#region src/plugin/rules/js-performance/async-await-in-loop.ts
5876
5889
  const LOOP_STATEMENT_TYPES$1 = new Set(LOOP_TYPES);
5877
- const ORDERED_OUTPUT_INSERTION_METHOD_NAMES = new Set(["push", "unshift"]);
5878
5890
  const findFirstAwaitOutsideNestedFunctions = (block, skipNestedLoops = false) => {
5879
5891
  let firstAwait = null;
5880
5892
  walkAst(block, (child) => {
@@ -5941,151 +5953,7 @@ const isAwaitingManualPromiseWait = (awaitNode) => {
5941
5953
  });
5942
5954
  return isWaitLike;
5943
5955
  };
5944
- const getRootObjectIdentifierName = (node) => {
5945
- let current = node;
5946
- while (isNodeOfType(current, "MemberExpression")) current = current.object;
5947
- return isNodeOfType(current, "Identifier") ? current.name : null;
5948
- };
5949
- const isScopeWithinFunction = (candidateScope, functionScope) => {
5950
- let currentScope = candidateScope;
5951
- while (currentScope) {
5952
- if (currentScope === functionScope) return true;
5953
- currentScope = currentScope.parent;
5954
- }
5955
- return false;
5956
- };
5957
- const isSymbolDirectlyReturned = (symbol, callerFunction) => Boolean(callerFunction) && symbol.references.some((reference) => {
5958
- const expressionRoot = findTransparentExpressionRoot(reference.identifier);
5959
- const parent = expressionRoot.parent;
5960
- return isNodeOfType(parent, "ReturnStatement") && parent.argument === expressionRoot && findEnclosingFunction$1(parent) === callerFunction;
5961
- });
5962
- const collectPatternBindingSymbolIds = (pattern, scopes, target) => {
5963
- if (isNodeOfType(pattern, "Identifier")) {
5964
- const symbol = scopes.symbolFor(pattern);
5965
- if (symbol) target.add(symbol.id);
5966
- return;
5967
- }
5968
- if (isNodeOfType(pattern, "ObjectPattern")) {
5969
- for (const property of pattern.properties ?? []) if (isNodeOfType(property, "Property") && property.value) collectPatternBindingSymbolIds(property.value, scopes, target);
5970
- else if (isNodeOfType(property, "RestElement") && property.argument) collectPatternBindingSymbolIds(property.argument, scopes, target);
5971
- return;
5972
- }
5973
- if (isNodeOfType(pattern, "ArrayPattern")) {
5974
- for (const element of pattern.elements ?? []) if (element) collectPatternBindingSymbolIds(element, scopes, target);
5975
- return;
5976
- }
5977
- if (isNodeOfType(pattern, "AssignmentPattern") && pattern.left) collectPatternBindingSymbolIds(pattern.left, scopes, target);
5978
- };
5979
- const collectReferencedSymbolIds = (expression, scopes) => {
5980
- const referencedSymbolIds = /* @__PURE__ */ new Set();
5981
- walkAst(expression, (child) => {
5982
- if (child !== expression && isFunctionLike$1(child)) return false;
5983
- if (!isNodeOfType(child, "Identifier")) return;
5984
- const symbol = scopes.symbolFor(child);
5985
- if (symbol) referencedSymbolIds.add(symbol.id);
5986
- });
5987
- return referencedSymbolIds;
5988
- };
5989
- const collectAwaitDerivedSymbolIds = (block, scopes) => {
5990
- const awaitDerivedSymbolIds = /* @__PURE__ */ new Set();
5991
- const bindingDependencies = [];
5992
- walkAst(block, (child) => {
5993
- if (child !== block && isFunctionLike$1(child)) return false;
5994
- if (isNodeOfType(child, "VariableDeclarator") && child.id && child.init) {
5995
- const declaredSymbolIds = /* @__PURE__ */ new Set();
5996
- collectPatternBindingSymbolIds(child.id, scopes, declaredSymbolIds);
5997
- if (containsDirectAwait(child.init)) for (const symbolId of declaredSymbolIds) awaitDerivedSymbolIds.add(symbolId);
5998
- const referencedSymbolIds = collectReferencedSymbolIds(child.init, scopes);
5999
- for (const declaredSymbolId of declaredSymbolIds) bindingDependencies.push({
6000
- declaredSymbolId,
6001
- referencedSymbolIds
6002
- });
6003
- return;
6004
- }
6005
- if (isNodeOfType(child, "AssignmentExpression") && child.left) {
6006
- const assignedSymbolIds = /* @__PURE__ */ new Set();
6007
- collectPatternBindingSymbolIds(child.left, scopes, assignedSymbolIds);
6008
- if (containsDirectAwait(child.right)) for (const symbolId of assignedSymbolIds) awaitDerivedSymbolIds.add(symbolId);
6009
- const referencedSymbolIds = collectReferencedSymbolIds(child.right, scopes);
6010
- for (const assignedSymbolId of assignedSymbolIds) bindingDependencies.push({
6011
- declaredSymbolId: assignedSymbolId,
6012
- referencedSymbolIds
6013
- });
6014
- }
6015
- });
6016
- let didGrow = true;
6017
- while (didGrow) {
6018
- didGrow = false;
6019
- for (const { declaredSymbolId, referencedSymbolIds } of bindingDependencies) {
6020
- if (awaitDerivedSymbolIds.has(declaredSymbolId)) continue;
6021
- for (const referencedSymbolId of referencedSymbolIds) {
6022
- if (!awaitDerivedSymbolIds.has(referencedSymbolId)) continue;
6023
- awaitDerivedSymbolIds.add(declaredSymbolId);
6024
- didGrow = true;
6025
- break;
6026
- }
6027
- }
6028
- }
6029
- return awaitDerivedSymbolIds;
6030
- };
6031
- const getSimpleParameterIdentifier = (parameter) => {
6032
- if (isNodeOfType(parameter, "Identifier")) return parameter;
6033
- if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) return parameter.left;
6034
- return null;
6035
- };
6036
- const doesAwaitedLocalCallInsertAwaitDerivedOutput = (awaitNode, context) => {
6037
- if (!isNodeOfType(awaitNode, "AwaitExpression")) return false;
6038
- const callExpression = awaitNode.argument;
6039
- if (!isNodeOfType(callExpression, "CallExpression")) return false;
6040
- const localFunction = resolveStaticLocalCallFunction(callExpression, context.scopes);
6041
- if (!isFunctionLike$1(localFunction)) return false;
6042
- const callerFunction = findEnclosingFunction$1(callExpression);
6043
- const functionScope = context.scopes.ownScopeFor(localFunction);
6044
- if (!functionScope) return false;
6045
- const awaitDerivedSymbolIds = collectAwaitDerivedSymbolIds(localFunction.body, context.scopes);
6046
- const externallyReachableParameterSymbolIds = /* @__PURE__ */ new Set();
6047
- for (const [parameterIndex, parameter] of localFunction.params.entries()) {
6048
- const parameterIdentifier = getSimpleParameterIdentifier(parameter);
6049
- if (!parameterIdentifier) continue;
6050
- const argument = callExpression.arguments[parameterIndex];
6051
- if (!isNodeOfType(argument, "Identifier")) continue;
6052
- const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
6053
- const argumentSymbol = context.scopes.symbolFor(argument);
6054
- if (parameterSymbol && argumentSymbol && (isSymbolDirectlyReturned(argumentSymbol, callerFunction) || argumentSymbol.id === parameterSymbol.id)) externallyReachableParameterSymbolIds.add(parameterSymbol.id);
6055
- }
6056
- let doesInsertAwaitDerivedOutput = false;
6057
- walkAst(localFunction.body, (child) => {
6058
- if (doesInsertAwaitDerivedOutput) return false;
6059
- if (child !== localFunction.body && isFunctionLike$1(child)) return false;
6060
- if (!isNodeOfType(child, "CallExpression")) return;
6061
- const callee = child.callee;
6062
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !ORDERED_OUTPUT_INSERTION_METHOD_NAMES.has(callee.property.name)) return;
6063
- let doesMutationConsumeAwaitedValue = false;
6064
- for (const mutationArgument of child.arguments ?? []) {
6065
- if (containsDirectAwait(mutationArgument)) {
6066
- doesMutationConsumeAwaitedValue = true;
6067
- break;
6068
- }
6069
- const referencedSymbolIds = collectReferencedSymbolIds(mutationArgument, context.scopes);
6070
- for (const referencedSymbolId of referencedSymbolIds) if (awaitDerivedSymbolIds.has(referencedSymbolId)) {
6071
- doesMutationConsumeAwaitedValue = true;
6072
- break;
6073
- }
6074
- if (doesMutationConsumeAwaitedValue) break;
6075
- }
6076
- if (!doesMutationConsumeAwaitedValue) return;
6077
- let receiverIdentifier = callee.object;
6078
- while (isNodeOfType(receiverIdentifier, "MemberExpression")) receiverIdentifier = receiverIdentifier.object;
6079
- if (!isNodeOfType(receiverIdentifier, "Identifier")) return;
6080
- const receiverSymbol = context.scopes.symbolFor(receiverIdentifier);
6081
- if (receiverSymbol && (externallyReachableParameterSymbolIds.has(receiverSymbol.id) || !isScopeWithinFunction(receiverSymbol.scope, functionScope) && isSymbolDirectlyReturned(receiverSymbol, callerFunction))) {
6082
- doesInsertAwaitDerivedOutput = true;
6083
- return false;
6084
- }
6085
- });
6086
- return doesInsertAwaitDerivedOutput;
6087
- };
6088
- const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode) || doesAwaitedLocalCallInsertAwaitDerivedOutput(awaitNode, context);
5956
+ const isIntentionallySequentialAwait = (awaitNode, context) => isAwaitingPossiblyMutatedMemberCall(awaitNode, context) || isAwaitingSleepLikeCall(awaitNode, context) || isAwaitingPromiseConcurrencyCall(awaitNode) || isAwaitingManualPromiseWait(awaitNode);
6089
5957
  const collectPatternIdentifiers = (pattern, target) => {
6090
5958
  if (isNodeOfType(pattern, "Identifier")) target.add(pattern.name);
6091
5959
  else if (isNodeOfType(pattern, "ObjectPattern")) {
@@ -6231,6 +6099,11 @@ const loopBodyHasAwaitDependentEarlyExit = (block, loopLabelName) => {
6231
6099
  });
6232
6100
  return hasAwaitDependentExit;
6233
6101
  };
6102
+ const getRootObjectIdentifierName = (node) => {
6103
+ let current = node;
6104
+ while (isNodeOfType(current, "MemberExpression")) current = current.object;
6105
+ return isNodeOfType(current, "Identifier") ? current.name : null;
6106
+ };
6234
6107
  const MUTATING_ARRAY_METHOD_NAMES$2 = new Set([
6235
6108
  ...ARRAY_MUTATION_METHOD_NAMES,
6236
6109
  "pop",
@@ -17999,14 +17872,6 @@ const findRenderPhaseComponentOrHook = (node, scopes) => {
17999
17872
  //#region src/plugin/utils/is-event-handler-attribute.ts
18000
17873
  const isEventHandlerAttribute = (node) => isNodeOfType(node, "JSXAttribute") && isNodeOfType(node.name, "JSXIdentifier") && /^on[A-Z]/.test(node.name.name);
18001
17874
  //#endregion
18002
- //#region src/plugin/utils/is-early-exit-statement.ts
18003
- const isEarlyExitStatement$1 = (statement) => {
18004
- if (!statement) return false;
18005
- if (statementAlwaysExits$1(statement)) return true;
18006
- if (isNodeOfType(statement, "BlockStatement")) return isEarlyExitStatement$1(statement.body.at(-1));
18007
- return isNodeOfType(statement, "ContinueStatement") || isNodeOfType(statement, "BreakStatement");
18008
- };
18009
- //#endregion
18010
17875
  //#region src/plugin/utils/is-ast-descendant.ts
18011
17876
  /**
18012
17877
  * True when `inner` is `outer` itself or any descendant in the AST
@@ -18225,15 +18090,13 @@ const isSynchronousIteratorCall = (callNode, callbackArgument, scopes) => {
18225
18090
  }
18226
18091
  return Boolean(methodName && EAGER_ITERATOR_METHOD_NAMES.has(methodName) && callNode.arguments[0] === callbackArgument && !isProvablyEmptyEagerCollection(callee.object, scopes) && (methodName === "forEach" ? isProvablyEagerForEachCollection(callee.object, scopes) : isProvablyEagerCollection(callee.object, scopes)));
18227
18092
  };
18228
- const isSynchronousIteratorCallbackCall = (callNode, callbackArgument) => {
18229
- const callee = stripParenExpression(callNode.callee);
18230
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
18231
- if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === callbackArgument;
18232
- return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === callbackArgument;
18233
- };
18234
18093
  const isSynchronousIteratorCallback = (functionNode) => {
18235
18094
  const callNode = functionNode.parent;
18236
- return Boolean(isNodeOfType(callNode, "CallExpression") && isSynchronousIteratorCallbackCall(callNode, functionNode));
18095
+ if (!isNodeOfType(callNode, "CallExpression")) return false;
18096
+ const callee = stripParenExpression(callNode.callee);
18097
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
18098
+ if (isNodeOfType(callee.object, "Identifier") && callee.object.name === "Array" && callee.property.name === "from") return callNode.arguments[1] === functionNode;
18099
+ return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments[0] === functionNode;
18237
18100
  };
18238
18101
  //#endregion
18239
18102
  //#region src/plugin/utils/is-within-assignment-target.ts
@@ -18353,35 +18216,11 @@ const resolveEventListenerCaptureValueIdentityKey = (expression, context) => {
18353
18216
  const rightIdentityKey = resolveEventListenerCaptureValueIdentityKey(unwrappedExpression.right, context);
18354
18217
  return leftIdentityKey && rightIdentityKey ? `${unwrappedExpression.type}:${unwrappedExpression.operator}:${leftIdentityKey}:${rightIdentityKey}` : null;
18355
18218
  };
18356
- const resolveReadOnlyEventListenerOptions = (optionsNode, context) => {
18357
- const unwrappedOptions = stripParenExpression(optionsNode);
18358
- if (!isNodeOfType(unwrappedOptions, "Identifier")) return resolveStableValue(unwrappedOptions, context);
18359
- const optionsSymbol = context.scopes.symbolFor(unwrappedOptions);
18360
- const initializer = optionsSymbol?.initializer ? stripParenExpression(optionsSymbol.initializer) : null;
18361
- if (!optionsSymbol || !initializer) return resolveStableValue(unwrappedOptions, context);
18362
- if (!isNodeOfType(initializer, "ObjectExpression")) {
18363
- if (isNodeOfType(initializer, "Identifier") || isNodeOfType(initializer, "MemberExpression")) return null;
18364
- return resolveStableValue(unwrappedOptions, context);
18365
- }
18366
- if (optionsSymbol.kind !== "const") return null;
18367
- return optionsSymbol.references.every((reference) => {
18368
- if (reference.flag !== "read" || isWithinAssignmentTarget(reference.identifier)) return false;
18369
- const referenceRoot = findTransparentExpressionRoot(reference.identifier);
18370
- const callNode = referenceRoot.parent;
18371
- if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments[2] !== referenceRoot) return false;
18372
- const callee = stripParenExpression(callNode.callee);
18373
- if (!isNodeOfType(callee, "MemberExpression")) return false;
18374
- const methodName = getStaticPropertyKeyName(callee);
18375
- return methodName === "addEventListener" || methodName === "removeEventListener";
18376
- }) ? initializer : null;
18377
- };
18378
18219
  const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
18379
- const stableOptionsNode = optionsNode ? resolveReadOnlyEventListenerOptions(optionsNode, context) : null;
18380
- if (optionsNode && !stableOptionsNode) return null;
18381
- const capture = resolveEventListenerCapture(stableOptionsNode, { allowIndeterminateEntries: true });
18220
+ const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
18382
18221
  if (capture !== null) return `capture:${String(capture)}`;
18383
- if (!stableOptionsNode) return null;
18384
- const unwrappedOptions = stripParenExpression(stableOptionsNode);
18222
+ if (!optionsNode) return null;
18223
+ const unwrappedOptions = stripParenExpression(optionsNode);
18385
18224
  if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
18386
18225
  const optionsKey = allowOpaqueOptionsIdentity ? resolveEventListenerCaptureValueIdentityKey(unwrappedOptions, context) : null;
18387
18226
  return optionsKey ? `options:${optionsKey}` : null;
@@ -18428,8 +18267,12 @@ const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, conte
18428
18267
  return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
18429
18268
  };
18430
18269
  const findAssignedResourceKey = (resourceNode, context) => {
18431
- const currentNode = findTransparentExpressionRoot(resourceNode);
18432
- const parentNode = currentNode.parent;
18270
+ let currentNode = resourceNode;
18271
+ let parentNode = currentNode.parent;
18272
+ while (isNodeOfType(parentNode, "ChainExpression")) {
18273
+ currentNode = parentNode;
18274
+ parentNode = currentNode.parent;
18275
+ }
18433
18276
  if (isNodeOfType(parentNode, "VariableDeclarator") && parentNode.init === currentNode) return resolveExpressionKey(parentNode.id, context);
18434
18277
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
18435
18278
  return null;
@@ -18700,18 +18543,6 @@ const resolveIteratorCollectionKey = (expression, context) => {
18700
18543
  }
18701
18544
  return null;
18702
18545
  };
18703
- const resolveReceiverIteratorCollectionKey = (expression, context) => {
18704
- if (!expression) return null;
18705
- const unwrappedExpression = stripParenExpression(expression);
18706
- if (!isNodeOfType(unwrappedExpression, "Identifier")) return null;
18707
- const collectionExpression = findForOfStatementForIteratorExpression(unwrappedExpression, context)?.right;
18708
- if (!collectionExpression) return null;
18709
- const collectionIdentifier = stripParenExpression(collectionExpression);
18710
- if (!isNodeOfType(collectionIdentifier, "Identifier") || !isPrivatePlainConstIdentifier(collectionIdentifier, context)) return null;
18711
- const collectionSymbol = context.scopes.symbolFor(collectionIdentifier);
18712
- const initializer = collectionSymbol?.initializer ? stripParenExpression(collectionSymbol.initializer) : null;
18713
- return collectionSymbol && isNodeOfType(initializer, "ArrayExpression") && hasOnlyReplayableCollectionReferences(collectionIdentifier, context, /* @__PURE__ */ new Set()) ? `symbol:${collectionSymbol.id}` : null;
18714
- };
18715
18546
  const isStableLoopReceiver = (expression, context) => {
18716
18547
  if (!expression) return false;
18717
18548
  const unwrappedExpression = stripParenExpression(expression);
@@ -19482,28 +19313,6 @@ const isFunctionReturnedFromReactHook = (functionNode, context, requireRefProper
19482
19313
  });
19483
19314
  };
19484
19315
  const isFunctionUsedAsReactRef = (functionNode, context) => isFunctionForwardedToReactRef(functionNode, context) || isFunctionReturnedFromReactHook(functionNode, context, true);
19485
- const findCallbackRefReplacementReleaseGuard = (releaseCall, ownerFunction, releaseReceiverKey, registrationReceiverKey, context) => {
19486
- let descendant = releaseCall;
19487
- let ancestor = descendant.parent;
19488
- while (ancestor && ancestor !== ownerFunction) {
19489
- if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant && ancestor.alternate === null) {
19490
- const test = stripParenExpression(ancestor.test);
19491
- if (!isNodeOfType(test, "LogicalExpression") || test.operator !== "&&") return null;
19492
- const operands = [stripParenExpression(test.left), stripParenExpression(test.right)];
19493
- const hasLiveReceiverTest = operands.some((operand) => doesTestRequireLiveExpressionKey(operand, releaseReceiverKey, context));
19494
- const hasDifferentReceiverTest = operands.some((operand) => {
19495
- if (!isNodeOfType(operand, "BinaryExpression") || operand.operator !== "!==" && operand.operator !== "!=") return false;
19496
- const leftKey = resolveExpressionKey(operand.left, context);
19497
- const rightKey = resolveExpressionKey(operand.right, context);
19498
- return leftKey === releaseReceiverKey && rightKey === registrationReceiverKey || rightKey === releaseReceiverKey && leftKey === registrationReceiverKey;
19499
- });
19500
- return hasLiveReceiverTest && hasDifferentReceiverTest ? ancestor : null;
19501
- }
19502
- descendant = ancestor;
19503
- ancestor = descendant.parent;
19504
- }
19505
- return null;
19506
- };
19507
19316
  const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19508
19317
  if (!isNodeOfType(usage.node, "CallExpression")) return false;
19509
19318
  const usageFunction = findEnclosingFunction$1(usage.node);
@@ -19524,7 +19333,7 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
19524
19333
  if (child !== usageFunctionBody && isFunctionLike$1(child)) return false;
19525
19334
  if (isNodeOfType(child, "AssignmentExpression") && child.operator === "=" && resolveReactRefSymbol(stripParenExpression(child.left), context.scopes)?.id === releaseRefSymbol.id && resolveExpressionKey(child.right, context) === registrationReceiverKey && releaseStart !== null && (getRangeStart(child) ?? -1) > releaseStart) matchingOwnershipAssignments.push(child);
19526
19335
  });
19527
- const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? findCallbackRefReplacementReleaseGuard(releaseCall, usageFunction, releaseReceiverKey, registrationReceiverKey, context) ?? releaseCall;
19336
+ const releaseAnchor = findLiveExpressionGuardForRelease(releaseCall, usageFunction, releaseReceiverKey, context) ?? releaseCall;
19528
19337
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
19529
19338
  return doNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
19530
19339
  };
@@ -19674,21 +19483,14 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19674
19483
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
19675
19484
  if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
19676
19485
  if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
19486
+ if (usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) return isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && releaseVerbName === "removeListener" && isProvenLegacyMediaQueryListMethodCall(callNode, "removeListener", context) && usage.receiverKey !== null && resolveStableMediaQueryListenerIdentityKey(callee.object, context) === usage.receiverKey && usage.handlerKey !== null && resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context) === usage.handlerKey;
19677
19487
  if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
19678
- if (usage.registrationVerbName === "addListener" && releaseVerbName === "removeListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1) {
19679
- if (callNode.arguments?.length !== 1) return false;
19680
- const registrationHandler = resolveStableValue(usage.node.arguments[0], context);
19681
- if (!isProvenLegacyMediaQueryListMethodCall(usage.node, "addListener", context) && !isFunctionLike$1(registrationHandler)) return false;
19682
- }
19683
19488
  if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
19684
19489
  if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
19685
19490
  if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
19686
19491
  }
19687
19492
  if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
19688
- const registrationCallee = isNodeOfType(usage.node, "CallExpression") ? stripParenExpression(usage.node.callee) : null;
19689
- const registrationReceiverCollectionKey = isNodeOfType(registrationCallee, "MemberExpression") ? resolveReceiverIteratorCollectionKey(registrationCallee.object, context) : null;
19690
- const releaseReceiverCollectionKeyForPair = resolveReceiverIteratorCollectionKey(callee.object, context);
19691
- if (!(registrationReceiverCollectionKey !== null && registrationReceiverCollectionKey === releaseReceiverCollectionKeyForPair) && (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey)) return false;
19493
+ if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
19692
19494
  if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
19693
19495
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
19694
19496
  if (!pairedVerbNames || !matchesPairedReleaseVerb(releaseVerbName, pairedVerbNames)) return false;
@@ -19725,7 +19527,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
19725
19527
  const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
19726
19528
  const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
19727
19529
  if (!releaseHandler) return releaseVerbName === "off";
19728
- const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.handlerKey ?? usage.eventKey : usage.handlerKey;
19530
+ const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
19729
19531
  const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
19730
19532
  return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
19731
19533
  }
@@ -20199,7 +20001,6 @@ const findRetainedFunctionLeak = (retainedFunction, context, options) => {
20199
20001
  walkAst(body, (child) => {
20200
20002
  if (leak !== null) return false;
20201
20003
  if (isFunctionLike$1(child)) return false;
20202
- if (!isNodeReachableWithinFunction(child, context)) return false;
20203
20004
  if (isSocketConstruction(child) && !doesResourceResultEscape(child, allowReturnedSocketEscape, false, context)) {
20204
20005
  const socketUsage = {
20205
20006
  kind: "socket",
@@ -20452,164 +20253,6 @@ const isInlineRetainedHandlerFunction = (functionNode, context) => {
20452
20253
  const objectParent = objectExpression.parent;
20453
20254
  return (isNodeOfType(objectParent, "CallExpression") && objectParent.arguments.some((argument) => argument === objectExpression) || isNodeOfType(objectParent, "JSXExpressionContainer")) && findRenderPhaseComponentOrHook(parentNode, context.scopes) !== null;
20454
20255
  };
20455
- const readInvocationArgumentValue = (expression, context) => {
20456
- if (!expression) return {
20457
- isDefinitelyUndefined: true,
20458
- truthiness: "falsy"
20459
- };
20460
- const target = stripParenExpression(expression);
20461
- if (isNodeOfType(target, "Literal")) return {
20462
- isDefinitelyUndefined: false,
20463
- truthiness: target.value ? "truthy" : "falsy"
20464
- };
20465
- if (isNodeOfType(target, "Identifier") && target.name === "undefined" && context.scopes.isGlobalReference(target)) return {
20466
- isDefinitelyUndefined: true,
20467
- truthiness: "falsy"
20468
- };
20469
- if (isNodeOfType(target, "UnaryExpression") && target.operator === "void") return {
20470
- isDefinitelyUndefined: true,
20471
- truthiness: "falsy"
20472
- };
20473
- if (isNodeOfType(target, "ArrayExpression") || isNodeOfType(target, "ArrowFunctionExpression") || isNodeOfType(target, "ClassExpression") || isNodeOfType(target, "FunctionExpression") || isNodeOfType(target, "NewExpression") || isNodeOfType(target, "ObjectExpression")) return {
20474
- isDefinitelyUndefined: false,
20475
- truthiness: "truthy"
20476
- };
20477
- return {
20478
- isDefinitelyUndefined: false,
20479
- truthiness: "unknown"
20480
- };
20481
- };
20482
- const readInvocationConditionTruthiness = (expression, parameterValues, context) => {
20483
- const target = stripParenExpression(expression);
20484
- const atomicValue = readInvocationArgumentValue(target, context);
20485
- if (atomicValue.truthiness !== "unknown") return atomicValue.truthiness;
20486
- if (isNodeOfType(target, "Identifier")) {
20487
- const symbol = context.scopes.symbolFor(target);
20488
- return symbol ? parameterValues.get(symbol.id)?.truthiness ?? "unknown" : "unknown";
20489
- }
20490
- if (isNodeOfType(target, "UnaryExpression") && target.operator === "!") {
20491
- const argumentTruthiness = readInvocationConditionTruthiness(target.argument, parameterValues, context);
20492
- return argumentTruthiness === "truthy" ? "falsy" : argumentTruthiness === "falsy" ? "truthy" : "unknown";
20493
- }
20494
- if (isNodeOfType(target, "LogicalExpression")) {
20495
- const leftTruthiness = readInvocationConditionTruthiness(target.left, parameterValues, context);
20496
- const rightTruthiness = readInvocationConditionTruthiness(target.right, parameterValues, context);
20497
- if (target.operator === "&&") {
20498
- if (leftTruthiness === "falsy" || rightTruthiness === "falsy") return "falsy";
20499
- return leftTruthiness === "truthy" && rightTruthiness === "truthy" ? "truthy" : "unknown";
20500
- }
20501
- if (target.operator === "||") {
20502
- if (leftTruthiness === "truthy" || rightTruthiness === "truthy") return "truthy";
20503
- return leftTruthiness === "falsy" && rightTruthiness === "falsy" ? "falsy" : "unknown";
20504
- }
20505
- return "unknown";
20506
- }
20507
- if (isNodeOfType(target, "ConditionalExpression")) {
20508
- const testTruthiness = readInvocationConditionTruthiness(target.test, parameterValues, context);
20509
- if (testTruthiness === "truthy") return readInvocationConditionTruthiness(target.consequent, parameterValues, context);
20510
- if (testTruthiness === "falsy") return readInvocationConditionTruthiness(target.alternate, parameterValues, context);
20511
- const consequentTruthiness = readInvocationConditionTruthiness(target.consequent, parameterValues, context);
20512
- return consequentTruthiness === readInvocationConditionTruthiness(target.alternate, parameterValues, context) ? consequentTruthiness : "unknown";
20513
- }
20514
- if (isNodeOfType(target, "CallExpression") && isNodeOfType(target.callee, "Identifier") && target.callee.name === "Boolean" && context.scopes.isGlobalReference(target.callee) && target.arguments[0] && isAstNode(target.arguments[0])) return readInvocationConditionTruthiness(target.arguments[0], parameterValues, context);
20515
- return "unknown";
20516
- };
20517
- const getInvocationParameterValues = (retainedFunction, invocation, leakNode, context) => {
20518
- const parameterValues = /* @__PURE__ */ new Map();
20519
- if (!isFunctionLike$1(retainedFunction) || !invocation.isDirect) return parameterValues;
20520
- for (const [parameterIndex, parameter] of retainedFunction.params.entries()) {
20521
- const argument = invocation.call.arguments[parameterIndex];
20522
- const argumentExpression = argument && isAstNode(argument) ? argument : null;
20523
- let parameterIdentifier = null;
20524
- let parameterValue = readInvocationArgumentValue(argumentExpression, context);
20525
- if (isNodeOfType(parameter, "Identifier")) parameterIdentifier = parameter;
20526
- else if (isNodeOfType(parameter, "AssignmentPattern") && isNodeOfType(parameter.left, "Identifier")) {
20527
- parameterIdentifier = parameter.left;
20528
- if (parameterValue.isDefinitelyUndefined) parameterValue = readInvocationArgumentValue(parameter.right, context);
20529
- } else if (isNodeOfType(parameter, "RestElement") && isNodeOfType(parameter.argument, "Identifier")) {
20530
- parameterIdentifier = parameter.argument;
20531
- parameterValue = {
20532
- isDefinitelyUndefined: false,
20533
- truthiness: "truthy"
20534
- };
20535
- }
20536
- if (!parameterIdentifier) continue;
20537
- const parameterSymbol = context.scopes.symbolFor(parameterIdentifier);
20538
- if (!parameterSymbol) continue;
20539
- const isWrittenBeforeLeak = parameterSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] < leakNode.range[0]);
20540
- parameterValues.set(parameterSymbol.id, isWrittenBeforeLeak ? {
20541
- isDefinitelyUndefined: false,
20542
- truthiness: "unknown"
20543
- } : parameterValue);
20544
- }
20545
- return parameterValues;
20546
- };
20547
- const isLeakPathDisabledForInvocation = (retainedFunction, leakNode, invocation, context) => {
20548
- if (!invocation.isDirect) return false;
20549
- const parameterValues = getInvocationParameterValues(retainedFunction, invocation, leakNode, context);
20550
- let child = leakNode;
20551
- let ancestor = leakNode.parent ?? null;
20552
- while (ancestor && ancestor !== retainedFunction) {
20553
- if (isNodeOfType(ancestor, "BlockStatement")) {
20554
- const childIndex = ancestor.body.findIndex((statement) => statement === child);
20555
- for (const precedingStatement of ancestor.body.slice(0, childIndex)) {
20556
- if (!isNodeOfType(precedingStatement, "IfStatement") || precedingStatement.alternate || !isEarlyExitStatement$1(precedingStatement.consequent)) continue;
20557
- if (readInvocationConditionTruthiness(precedingStatement.test, parameterValues, context) === "truthy") return true;
20558
- }
20559
- }
20560
- let requiredTruthiness = null;
20561
- let condition = null;
20562
- if (isNodeOfType(ancestor, "IfStatement")) {
20563
- condition = ancestor.test;
20564
- requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
20565
- } else if (isNodeOfType(ancestor, "ConditionalExpression")) {
20566
- condition = ancestor.test;
20567
- requiredTruthiness = ancestor.consequent === child ? "truthy" : "falsy";
20568
- } else if (isNodeOfType(ancestor, "LogicalExpression") && ancestor.right === child && ancestor.operator !== "??") {
20569
- condition = ancestor.left;
20570
- requiredTruthiness = ancestor.operator === "&&" ? "truthy" : "falsy";
20571
- } else if ((isNodeOfType(ancestor, "WhileStatement") || isNodeOfType(ancestor, "DoWhileStatement")) && ancestor.body === child) {
20572
- condition = ancestor.test;
20573
- requiredTruthiness = "truthy";
20574
- } else if (isNodeOfType(ancestor, "ForStatement") && ancestor.body === child && ancestor.test) {
20575
- condition = ancestor.test;
20576
- requiredTruthiness = "truthy";
20577
- }
20578
- if (condition && requiredTruthiness) {
20579
- const conditionTruthiness = readInvocationConditionTruthiness(condition, parameterValues, context);
20580
- if (conditionTruthiness !== "unknown" && conditionTruthiness !== requiredTruthiness) return true;
20581
- }
20582
- child = ancestor;
20583
- ancestor = ancestor.parent ?? null;
20584
- }
20585
- return false;
20586
- };
20587
- const getEffectRetainedInvocations = (retainedFunction, context) => {
20588
- if (!isFunctionLike$1(retainedFunction)) return [];
20589
- const componentFunction = findEnclosingFunction$1(retainedFunction);
20590
- if (!componentFunction || !isFunctionLike$1(componentFunction)) return [];
20591
- const invocations = [];
20592
- walkAst(componentFunction.body, (child) => {
20593
- if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction$1(child) !== componentFunction || !isReactHookCall(child, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return;
20594
- const effectCallback = getEffectCallback(child);
20595
- if (!effectCallback || !isFunctionLike$1(effectCallback)) return;
20596
- walkAst(effectCallback.body, (effectChild) => {
20597
- if (effectChild !== effectCallback.body && isFunctionLike$1(effectChild)) return false;
20598
- if (!isNodeOfType(effectChild, "CallExpression") || !isNodeReachableWithinFunction(effectChild, context)) return;
20599
- const isDirectInvocation = resolveRefOwnedCleanupFunction(effectChild.callee, context) === retainedFunction;
20600
- const isSynchronousIteratorInvocation = effectChild.arguments.some((argument) => isAstNode(argument) && resolveRefOwnedCleanupFunction(argument, context) === retainedFunction && isSynchronousIteratorCallbackCall(effectChild, argument));
20601
- if (isDirectInvocation) invocations.push({
20602
- call: effectChild,
20603
- isDirect: true
20604
- });
20605
- if (isSynchronousIteratorInvocation) invocations.push({
20606
- call: effectChild,
20607
- isDirect: false
20608
- });
20609
- });
20610
- });
20611
- return invocations;
20612
- };
20613
20256
  const effectNeedsCleanup = defineRule({
20614
20257
  id: "effect-needs-cleanup",
20615
20258
  title: "Effect subscription or timer never cleaned up",
@@ -20620,19 +20263,13 @@ const effectNeedsCleanup = defineRule({
20620
20263
  const reportRetainedLeak = (retainedFunction) => {
20621
20264
  const refEffectUsage = getReactRefEffectUsage(retainedFunction, context);
20622
20265
  if (!refEffectUsage && !isPotentiallyReachableFunction(retainedFunction, context)) return;
20623
- const effectInvocations = getEffectRetainedInvocations(retainedFunction, context);
20624
- const isEffectInvoked = effectInvocations.length > 0;
20625
20266
  const leak = findRetainedFunctionLeak(retainedFunction, context, refEffectUsage ? {
20626
20267
  allowReturnedResourceEscape: refEffectUsage.doesEffectOwnEveryResult,
20627
20268
  allowReturnedTimerEscape: false,
20628
20269
  includeOneShotTimers: true,
20629
20270
  requireCallableReturnedResource: true
20630
- } : isEffectInvoked ? {
20631
- allowReturnedTimerEscape: false,
20632
- includeOneShotTimers: true
20633
20271
  } : void 0);
20634
20272
  if (!leak) return;
20635
- if (isEffectInvoked && leak.resourceName === "setTimeout" && (!isNodeReachableWithinFunction(leak.node, context) || isFunctionLike$1(retainedFunction) && retainedFunction.params.length > 0 && !context.cfg.isUnconditionalFromEntry(leak.node) && effectInvocations.every((invocation) => isLeakPathDisabledForInvocation(retainedFunction, leak.node, invocation, context)))) return;
20636
20273
  const resourceNoun = RESOURCE_NOUN_BY_KIND[leak.kind];
20637
20274
  context.report({
20638
20275
  node: leak.node,
@@ -25052,14 +24689,14 @@ const getFirstLegendChild = (children, targetNode) => {
25052
24689
  if (isNodeOfType(child, "JSXExpressionContainer")) {
25053
24690
  const potentialLegends = [];
25054
24691
  collectPotentialLegends(child.expression, potentialLegends);
25055
- const containingLegend = potentialLegends.find((legend) => isDescendantOf$1(targetNode, legend));
24692
+ const containingLegend = potentialLegends.find((legend) => isDescendantOf(targetNode, legend));
25056
24693
  if (containingLegend) return containingLegend;
25057
24694
  if (potentialLegends[0]) return potentialLegends[0];
25058
24695
  }
25059
24696
  }
25060
24697
  return null;
25061
24698
  };
25062
- const isDescendantOf$1 = (node, ancestor) => {
24699
+ const isDescendantOf = (node, ancestor) => {
25063
24700
  let current = node.parent;
25064
24701
  while (current) {
25065
24702
  if (current === ancestor) return true;
@@ -25084,7 +24721,7 @@ const isDisabledByFieldsetAncestor = (node, context) => {
25084
24721
  while (ancestor) {
25085
24722
  if (isNodeOfType(ancestor, "JSXElement") && resolveJsxElementType(ancestor.openingElement) === "fieldset" && openingElementMayBeDisabled(ancestor.openingElement, context)) {
25086
24723
  const firstLegend = getFirstLegendChild(ancestor.children, node);
25087
- if (!firstLegend || !isDescendantOf$1(node, firstLegend)) return true;
24724
+ if (!firstLegend || !isDescendantOf(node, firstLegend)) return true;
25088
24725
  }
25089
24726
  ancestor = ancestor.parent;
25090
24727
  }
@@ -30897,8 +30534,7 @@ const STRING_TYPED_PROPERTY_NAMES = new Set([
30897
30534
  "code",
30898
30535
  "label",
30899
30536
  "slug",
30900
- "prefix",
30901
- "__html"
30537
+ "prefix"
30902
30538
  ]);
30903
30539
  const STRING_TYPED_IDENTIFIER_SUFFIXES = [
30904
30540
  "Text",
@@ -31016,25 +30652,13 @@ const STRING_TYPED_IDENTIFIER_NAMES = new Set([
31016
30652
  "title"
31017
30653
  ]);
31018
30654
  const STRING_RETURNING_CALLEE_PREFIX_PATTERN = /^(?:normalize|format|stringify|serialize)/;
31019
- const FRESH_ARRAY_METHOD_NAMES$2 = new Set([
31020
- "concat",
31021
- "filter",
31022
- "flat",
31023
- "flatMap",
31024
- "map",
31025
- "slice",
31026
- "split"
31027
- ]);
31028
30655
  const isLikelyStringReceiver = (receiver) => {
31029
30656
  if (!receiver) return false;
31030
- const unwrappedReceiver = stripParenExpression(receiver);
31031
- if (unwrappedReceiver !== receiver) return isLikelyStringReceiver(unwrappedReceiver);
31032
30657
  if (isNodeOfType(receiver, "Literal") && typeof receiver.value === "string") return true;
31033
30658
  if (isNodeOfType(receiver, "TemplateLiteral")) return true;
31034
30659
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && receiver.callee.name === "String") return true;
31035
30660
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "MemberExpression") && isNodeOfType(receiver.callee.property, "Identifier") && STRING_RETURNING_METHODS.has(receiver.callee.property.name)) return true;
31036
30661
  if (isNodeOfType(receiver, "CallExpression") && isNodeOfType(receiver.callee, "Identifier") && STRING_RETURNING_CALLEE_PREFIX_PATTERN.test(receiver.callee.name)) return true;
31037
- 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;
31038
30662
  if (isNodeOfType(receiver, "MemberExpression") && isNodeOfType(receiver.property, "Identifier")) {
31039
30663
  if (STRING_TYPED_PROPERTY_NAMES.has(receiver.property.name)) return true;
31040
30664
  }
@@ -31052,46 +30676,8 @@ const isLikelyStringReceiver = (receiver) => {
31052
30676
  }
31053
30677
  if (isNodeOfType(receiver, "BinaryExpression") && receiver.operator === "+") return isLikelyStringReceiver(receiver.left) || isLikelyStringReceiver(receiver.right);
31054
30678
  if (isNodeOfType(receiver, "ConditionalExpression")) return isLikelyStringReceiver(receiver.consequent) && isLikelyStringReceiver(receiver.alternate);
31055
- if (isNodeOfType(receiver, "LogicalExpression")) return isLikelyStringReceiver(receiver.left) && isLikelyStringReceiver(receiver.right);
31056
30679
  return false;
31057
30680
  };
31058
- const isFreshArrayReceiver = (receiver) => {
31059
- const unwrappedReceiver = stripParenExpression(receiver);
31060
- if (unwrappedReceiver !== receiver) return isFreshArrayReceiver(unwrappedReceiver);
31061
- if (!isNodeOfType(receiver, "CallExpression") || !isNodeOfType(receiver.callee, "MemberExpression") || !isNodeOfType(receiver.callee.property, "Identifier")) return false;
31062
- if (!FRESH_ARRAY_METHOD_NAMES$2.has(receiver.callee.property.name)) return false;
31063
- if (receiver.callee.property.name === "split") return isLikelyStringReceiver(receiver.callee.object);
31064
- const sourceReceiver = stripParenExpression(receiver.callee.object);
31065
- return isKnownNativeArrayReceiver(sourceReceiver) || isFreshArrayReceiver(sourceReceiver);
31066
- };
31067
- const isSmallRestHelperOmissionList = (node) => {
31068
- if (!node) return false;
31069
- const candidate = stripParenExpression(node);
31070
- if (!isNodeOfType(candidate, "ArrayExpression")) return false;
31071
- const elements = candidate.elements ?? [];
31072
- return elements.length <= 8 && elements.every((element) => element === null || !isNodeOfType(element, "SpreadElement"));
31073
- };
31074
- const isTypeScriptRestHelperLookup = (lookupCall, receiver, scopes) => {
31075
- if (!isNodeOfType(receiver, "Identifier")) return false;
31076
- const enclosingFunction = findEnclosingFunction$1(lookupCall);
31077
- if (!isNodeOfType(enclosingFunction, "FunctionExpression") || !isNodeOfType(enclosingFunction.params?.[1], "Identifier") || enclosingFunction.params[1].name !== receiver.name) return false;
31078
- let bindingIdentifier = null;
31079
- let ancestor = enclosingFunction.parent;
31080
- while (ancestor && !isFunctionLike$1(ancestor)) {
31081
- if (isNodeOfType(ancestor, "VariableDeclarator") && isNodeOfType(ancestor.id, "Identifier") && ancestor.id.name === "__rest") {
31082
- bindingIdentifier = ancestor.id;
31083
- break;
31084
- }
31085
- ancestor = ancestor.parent;
31086
- }
31087
- if (!bindingIdentifier) return false;
31088
- const helperSymbol = scopes.symbolFor(bindingIdentifier);
31089
- if (!helperSymbol || helperSymbol.references.length === 0) return false;
31090
- return helperSymbol.references.every((reference) => {
31091
- const callExpression = reference.identifier.parent;
31092
- return isNodeOfType(callExpression, "CallExpression") && callExpression.callee === reference.identifier && isSmallRestHelperOmissionList(callExpression.arguments?.[1]);
31093
- });
31094
- };
31095
30681
  const INDEX_LIKE_IDENTIFIER_NAMES = new Set([
31096
30682
  "i",
31097
30683
  "j",
@@ -31688,8 +31274,6 @@ const jsSetMapLookups = defineRule({
31688
31274
  const query = node.arguments[0];
31689
31275
  if (methodName === "indexOf" && !isKnownSafeIndexOfQuery(query) && (isKnownUnsafeIndexOfQuery(query, receiver) || isKnownUnsafeIndexOfReceiver(receiver))) return;
31690
31276
  if (isLikelyStringReceiver(receiver)) return;
31691
- if (isFreshArrayReceiver(receiver)) return;
31692
- if (isTypeScriptRestHelperLookup(node, receiver, context.scopes)) return;
31693
31277
  if (isSmallInlineLiteralArray(receiver)) return;
31694
31278
  if (isScreamingSnakeCaseConstantReceiver(receiver)) return;
31695
31279
  if (isSmallFixedListMember(receiver)) return;
@@ -45603,6 +45187,14 @@ const noAriaInvalidWithoutDescription = defineRule({
45603
45187
  } })
45604
45188
  });
45605
45189
  //#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
45606
45198
  //#region src/plugin/utils/unwrap-negative-guard-form.ts
45607
45199
  const unwrapNegativeGuardForm = (test) => {
45608
45200
  const expression = stripParenExpression(test);
@@ -47123,101 +46715,6 @@ const isLoopCounterDeclarator = (declarator, referenceNode, indexName) => {
47123
46715
  }
47124
46716
  return isBindingReassignedOrMutated(referenceNode, indexName);
47125
46717
  };
47126
- const findEnclosingWhileLoop = (node) => {
47127
- let current = node.parent;
47128
- while (current) {
47129
- if (isNodeOfType(current, "WhileStatement") || isNodeOfType(current, "DoWhileStatement")) return current;
47130
- if (isFunctionLike$1(current) || isNodeOfType(current, "Program")) return null;
47131
- current = current.parent;
47132
- }
47133
- return null;
47134
- };
47135
- const isStaticMemberChain = (expression) => {
47136
- const candidate = stripParenExpression(expression);
47137
- if (isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "ThisExpression")) return true;
47138
- return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isNodeOfType(candidate.property, "Identifier") && isStaticMemberChain(candidate.object));
47139
- };
47140
- const findLengthBoundCollections = (expression) => {
47141
- const collections = [];
47142
- walkAst(expression, (child) => {
47143
- if (isNodeOfType(child, "MemberExpression") && !child.computed && isStaticMemberChain(child.object) && isNodeOfType(child.property, "Identifier") && child.property.name === "length") collections.push(child.object);
47144
- });
47145
- return collections;
47146
- };
47147
- const areSameBoundStaticMemberChains = (first, second) => isStaticMemberChain(first) && isStaticMemberChain(second) && areExpressionsStructurallyEqual(first, second, { areIdentifiersEqual: (firstIdentifier, secondIdentifier) => {
47148
- if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
47149
- const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
47150
- const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
47151
- return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
47152
- } });
47153
- const RELATIONAL_BOUND_OPERATORS = new Set([
47154
- "<",
47155
- "<=",
47156
- ">",
47157
- ">="
47158
- ]);
47159
- const loopTestBoundsCounterByLength = (expression, indexName, bindingIdentifier) => {
47160
- const readsCounter = (candidate) => {
47161
- let didReadCounter = false;
47162
- walkAst(candidate, (child) => {
47163
- if (didReadCounter) return false;
47164
- if (isNodeOfType(child, "Identifier") && child.name === indexName && findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
47165
- didReadCounter = true;
47166
- return false;
47167
- }
47168
- });
47169
- return didReadCounter;
47170
- };
47171
- const readsLength = (candidate) => {
47172
- let didReadLength = false;
47173
- walkAst(candidate, (child) => {
47174
- if (didReadLength) return false;
47175
- if (isNodeOfType(child, "MemberExpression") && !child.computed && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
47176
- didReadLength = true;
47177
- return false;
47178
- }
47179
- });
47180
- return didReadLength;
47181
- };
47182
- let didFindLengthBound = false;
47183
- walkAst(expression, (child) => {
47184
- if (didFindLengthBound) return false;
47185
- if (!isNodeOfType(child, "BinaryExpression") || !RELATIONAL_BOUND_OPERATORS.has(child.operator)) return;
47186
- if (readsCounter(child.left) && readsLength(child.right) || readsCounter(child.right) && readsLength(child.left)) {
47187
- didFindLengthBound = true;
47188
- return false;
47189
- }
47190
- });
47191
- return didFindLengthBound;
47192
- };
47193
- const isDataIndexedWhileLoopCounter = (referenceNode, bindingIdentifier, indexName) => {
47194
- if (!INDEX_PARAMETER_NAMES.has(indexName)) return false;
47195
- const loop = findEnclosingWhileLoop(referenceNode);
47196
- if (!loop) return false;
47197
- let doesTestReadCounter = false;
47198
- walkAst(loop.test, (child) => {
47199
- if (doesTestReadCounter) return false;
47200
- if (!isNodeOfType(child, "Identifier") || child.name !== indexName) return;
47201
- if (findVariableInitializer(child, indexName)?.bindingIdentifier === bindingIdentifier) {
47202
- doesTestReadCounter = true;
47203
- return false;
47204
- }
47205
- });
47206
- if (!doesTestReadCounter) return false;
47207
- const lengthBoundCollections = findLengthBoundCollections(loop.test);
47208
- if (lengthBoundCollections.length === 0) return false;
47209
- let didFindIndexedCollectionRead = false;
47210
- walkAst(loop.body, (child) => {
47211
- if (didFindIndexedCollectionRead) return false;
47212
- if (isFunctionLike$1(child)) return false;
47213
- if (!isNodeOfType(child, "MemberExpression") || !child.computed || !isNodeOfType(child.property, "Identifier") || child.property.name !== indexName) return;
47214
- if (findVariableInitializer(child.property, indexName)?.bindingIdentifier === bindingIdentifier && lengthBoundCollections.some((collection) => areSameBoundStaticMemberChains(collection, child.object))) {
47215
- didFindIndexedCollectionRead = true;
47216
- return false;
47217
- }
47218
- });
47219
- return didFindIndexedCollectionRead;
47220
- };
47221
46718
  /**
47222
46719
  * Resolves whether an identifier is PROVABLY the positional array
47223
46720
  * index, by classifying its binding. Per the official rule prompt,
@@ -47268,12 +46765,6 @@ const resolvePositionalIndexBinding = (identifierNode, depth) => {
47268
46765
  }
47269
46766
  const declarator = binding.bindingIdentifier.parent;
47270
46767
  if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declarator.id === binding.bindingIdentifier && declarator.init) {
47271
- if (isDataIndexedWhileLoopCounter(identifierNode, binding.bindingIdentifier, identifierNode.name)) return {
47272
- iteratorCall: null,
47273
- bindingFunction: null,
47274
- indexParameterPosition: null,
47275
- isDataIndexedLoopCounter: true
47276
- };
47277
46768
  const initializer = stripParenExpression(declarator.init);
47278
46769
  if (isNodeOfType(initializer, "Literal") && typeof initializer.value === "number") {
47279
46770
  if (!INDEX_PARAMETER_NAMES.has(identifierNode.name)) return null;
@@ -47308,101 +46799,6 @@ const iteratorCallExemptsIndexKey = (iteratorCall) => {
47308
46799
  const receiver = iteratorCall.callee.object;
47309
46800
  return isStaticPlaceholderReceiver(receiver) || isFixedMemoReceiver(receiver) || isStaticDefaultLiteralReceiver(receiver) || isStringDerivedReceiver(receiver);
47310
46801
  };
47311
- const isReactNamespaceIdentifier = (node) => {
47312
- if (!isNodeOfType(node, "Identifier")) return false;
47313
- const importBinding = getImportBindingForName(node, node.name);
47314
- const visibleBinding = findVariableInitializer(node, node.name);
47315
- if (!importBinding) return node.name === "React" && !visibleBinding;
47316
- return visibleBinding?.initializer?.type.startsWith("Import") === true && importBinding.source === "react" && (importBinding.isNamespace || importBinding.exportedName === "default");
47317
- };
47318
- const isReactChildrenObject = (node) => {
47319
- const candidate = stripParenExpression(node);
47320
- if (isNodeOfType(candidate, "Identifier")) {
47321
- const importBinding = getImportBindingForName(candidate, candidate.name);
47322
- const visibleBinding = findVariableInitializer(candidate, candidate.name);
47323
- return Boolean(visibleBinding?.initializer?.type === "ImportSpecifier" && importBinding?.source === "react" && importBinding.exportedName === "Children");
47324
- }
47325
- return Boolean(isNodeOfType(candidate, "MemberExpression") && !candidate.computed && isReactNamespaceIdentifier(candidate.object) && isNodeOfType(candidate.property, "Identifier") && candidate.property.name === "Children");
47326
- };
47327
- const isReactChildrenToArrayCall = (node) => {
47328
- const candidate = stripParenExpression(node);
47329
- if (!isNodeOfType(candidate, "CallExpression") || !isNodeOfType(candidate.callee, "MemberExpression") || candidate.callee.computed || !isReactChildrenObject(candidate.callee.object) || !isNodeOfType(candidate.callee.property, "Identifier") || candidate.callee.property.name !== "toArray") return false;
47330
- const normalizedValueNode = candidate.arguments?.[0];
47331
- const normalizedValue = normalizedValueNode ? stripParenExpression(normalizedValueNode) : null;
47332
- if (!normalizedValue || !isNodeOfType(normalizedValue, "Identifier") || normalizedValue.name !== "children") return false;
47333
- const normalizedBinding = findVariableInitializer(normalizedValue, normalizedValue.name);
47334
- return Boolean(normalizedBinding && findEnclosingParameter(normalizedBinding.bindingIdentifier));
47335
- };
47336
- const isSameIdentifier = (first, second) => {
47337
- const firstIdentifier = stripParenExpression(first);
47338
- const secondIdentifier = stripParenExpression(second);
47339
- if (!isNodeOfType(firstIdentifier, "Identifier") || !isNodeOfType(secondIdentifier, "Identifier") || firstIdentifier.name !== secondIdentifier.name) return false;
47340
- const firstBinding = findVariableInitializer(firstIdentifier, firstIdentifier.name);
47341
- const secondBinding = findVariableInitializer(secondIdentifier, secondIdentifier.name);
47342
- return firstBinding?.bindingIdentifier === secondBinding?.bindingIdentifier;
47343
- };
47344
- const isReactChildrenArrayNormalization = (node) => {
47345
- const candidate = stripParenExpression(node);
47346
- if (!isNodeOfType(candidate, "ConditionalExpression")) return false;
47347
- const test = stripParenExpression(candidate.test);
47348
- if (!isNodeOfType(test, "CallExpression") || !isNodeOfType(test.callee, "MemberExpression") || test.callee.computed || !isNodeOfType(test.callee.object, "Identifier") || test.callee.object.name !== "Array" || findVariableInitializer(test.callee.object, "Array") || !isNodeOfType(test.callee.property, "Identifier") || test.callee.property.name !== "isArray") return false;
47349
- const testedValueNode = test.arguments?.[0];
47350
- if (!testedValueNode) return false;
47351
- const testedValue = stripParenExpression(testedValueNode);
47352
- if (!isNodeOfType(testedValue, "Identifier")) return false;
47353
- const testedBinding = findVariableInitializer(testedValue, testedValue.name);
47354
- if (testedValue.name !== "children" || !testedBinding || !findEnclosingParameter(testedBinding.bindingIdentifier)) return false;
47355
- const branchWrapsTestedValue = (branch) => {
47356
- const unwrappedBranch = stripParenExpression(branch);
47357
- return isNodeOfType(unwrappedBranch, "ArrayExpression") && unwrappedBranch.elements?.length === 1 && Boolean(unwrappedBranch.elements[0] && isSameIdentifier(unwrappedBranch.elements[0], testedValue));
47358
- };
47359
- return isSameIdentifier(candidate.consequent, testedValue) && branchWrapsTestedValue(candidate.alternate) || isSameIdentifier(candidate.alternate, testedValue) && branchWrapsTestedValue(candidate.consequent);
47360
- };
47361
- const isMutatedEmptyArrayBinding = (identifierNode, depth) => {
47362
- const binding = findVariableInitializer(identifierNode, identifierNode.name);
47363
- const initializer = binding?.initializer ? stripParenExpression(binding.initializer) : null;
47364
- if (!binding || !initializer || !isNodeOfType(initializer, "ArrayExpression") || initializer.elements?.length !== 0) return false;
47365
- const program = findProgramRoot(identifierNode);
47366
- if (!program) return false;
47367
- let didFindReactChildPush = false;
47368
- walkAst(program, (child) => {
47369
- if (didFindReactChildPush) return false;
47370
- if (!isNodeOfType(child, "CallExpression") || !isNodeOfType(child.callee, "MemberExpression") || child.callee.computed || !isNodeOfType(child.callee.object, "Identifier") || child.callee.object.name !== identifierNode.name || !isNodeOfType(child.callee.property, "Identifier") || child.callee.property.name !== "push") return;
47371
- if (findVariableInitializer(child.callee.object, identifierNode.name)?.bindingIdentifier === binding.bindingIdentifier && (child.arguments ?? []).some((argument) => {
47372
- const candidate = stripParenExpression(argument);
47373
- if (!(isNodeOfType(candidate, "Identifier") || isNodeOfType(candidate, "JSXElement") || isNodeOfType(candidate, "JSXFragment"))) return false;
47374
- let doesCarryReactChild = false;
47375
- walkAst(candidate, (argumentChild) => {
47376
- if (doesCarryReactChild) return false;
47377
- if (!isNodeOfType(argumentChild, "Identifier")) return;
47378
- const declarator = findVariableInitializer(argumentChild, argumentChild.name)?.bindingIdentifier.parent;
47379
- const declaration = declarator?.parent;
47380
- const forOfStatement = declaration?.parent;
47381
- if (declarator && isNodeOfType(declarator, "VariableDeclarator") && declaration && isNodeOfType(declaration, "VariableDeclaration") && forOfStatement && isNodeOfType(forOfStatement, "ForOfStatement") && forOfStatement.left === declaration && isDynamicReactChildrenExpression(forOfStatement.right, depth + 1)) {
47382
- doesCarryReactChild = true;
47383
- return false;
47384
- }
47385
- });
47386
- return doesCarryReactChild;
47387
- })) {
47388
- didFindReactChildPush = true;
47389
- return false;
47390
- }
47391
- });
47392
- return didFindReactChildPush;
47393
- };
47394
- const isDynamicReactChildrenExpression = (expression, depth) => {
47395
- if (depth > TYPE_RESOLUTION_DEPTH_LIMIT$2) return false;
47396
- const candidate = stripParenExpression(expression);
47397
- if (isReactChildrenToArrayCall(candidate) || isReactChildrenArrayNormalization(candidate)) return true;
47398
- if (isNodeOfType(candidate, "Identifier")) {
47399
- if (isMutatedEmptyArrayBinding(candidate, depth)) return true;
47400
- const binding = findVariableInitializer(candidate, candidate.name);
47401
- return Boolean(binding?.initializer && isDynamicReactChildrenExpression(binding.initializer, depth + 1));
47402
- }
47403
- if (isNodeOfType(candidate, "CallExpression") && isNodeOfType(candidate.callee, "MemberExpression") && !candidate.callee.computed && isNodeOfType(candidate.callee.property, "Identifier") && candidate.callee.property.name === "filter") return isDynamicReactChildrenExpression(candidate.callee.object, depth + 1);
47404
- return false;
47405
- };
47406
46802
  const resolveKeyTemplateLiteral = (expression) => {
47407
46803
  const node = stripParenExpression(expression);
47408
46804
  if (isNodeOfType(node, "TemplateLiteral")) return node;
@@ -47455,19 +46851,28 @@ const findBareItemNamesReferencedByTemplate = (template, itemNames) => {
47455
46851
  }
47456
46852
  return referencedItemNames;
47457
46853
  };
47458
- const isNumericPlaceholderLoopCounter = (attributeNode, indexName) => {
46854
+ const forLoopTestReadsDataLength = (test) => {
46855
+ let didFindLengthRead = false;
46856
+ walkAst(test, (child) => {
46857
+ if (didFindLengthRead) return false;
46858
+ if (isNodeOfType(child, "MemberExpression") && isNodeOfType(child.property, "Identifier") && child.property.name === "length") {
46859
+ didFindLengthRead = true;
46860
+ return false;
46861
+ }
46862
+ });
46863
+ return didFindLengthRead;
46864
+ };
46865
+ const isNumericForLoopCounter = (attributeNode, indexName) => {
47459
46866
  const binding = findVariableInitializer(attributeNode, indexName);
47460
46867
  if (!binding) return false;
47461
46868
  const declarator = binding.bindingIdentifier.parent;
47462
46869
  if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return false;
47463
46870
  const declaration = declarator.parent;
47464
46871
  if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return false;
47465
- if (!declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
47466
46872
  const forStatement = declaration.parent;
47467
- if (forStatement && isNodeOfType(forStatement, "ForStatement") && forStatement.init === declaration) return !(forStatement.test && loopTestBoundsCounterByLength(forStatement.test, indexName, binding.bindingIdentifier));
47468
- const whileLoop = findEnclosingWhileLoop(attributeNode);
47469
- if (!whileLoop) return false;
47470
- return !loopTestBoundsCounterByLength(whileLoop.test, indexName, binding.bindingIdentifier);
46873
+ if (!forStatement || !isNodeOfType(forStatement, "ForStatement") || forStatement.init !== declaration || !declarator.init || !isNodeOfType(declarator.init, "Literal") || typeof declarator.init.value !== "number") return false;
46874
+ if (forStatement.test && forLoopTestReadsDataLength(forStatement.test)) return false;
46875
+ return true;
47471
46876
  };
47472
46877
  const EMPTY_NAME_SET$1 = /* @__PURE__ */ new Set();
47473
46878
  const findIteratorItemNamesOfBinding = (binding) => {
@@ -47501,11 +46906,11 @@ const collectDerivedRowContentNames = (bindingFunction, itemNames) => {
47501
46906
  * observable harm; anything stateful — form controls, media, custom
47502
46907
  * components, unknown calls — keeps the diagnostic.
47503
46908
  */
47504
- const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames, areBareItemsDynamicReactChildren) => {
46909
+ const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames) => {
47505
46910
  const jsxElement = openingElement.parent;
47506
46911
  if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
47507
46912
  const children = jsxElement.children ?? [];
47508
- const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames : areBareItemsDynamicReactChildren ? derivedNames : new Set([...derivedNames, ...itemNames]);
46913
+ const bareIdentifierNames = children.some((child) => isNodeOfType(child, "JSXElement")) ? derivedNames : new Set([...derivedNames, ...itemNames]);
47509
46914
  return children.some((child) => containsStatefulDescendant(child, {
47510
46915
  memberRootNames: itemNames,
47511
46916
  allowAnyMemberRead: true,
@@ -47513,15 +46918,6 @@ const fragmentHasStatefulChildren = (openingElement, itemNames, derivedNames, ar
47513
46918
  callCalleeRootNames: itemNames
47514
46919
  }));
47515
46920
  };
47516
- const elementHasDirectItemChild = (openingElement, itemNames) => {
47517
- const jsxElement = openingElement.parent;
47518
- if (!jsxElement || !isNodeOfType(jsxElement, "JSXElement")) return false;
47519
- return (jsxElement.children ?? []).some((child) => {
47520
- if (!isNodeOfType(child, "JSXExpressionContainer")) return false;
47521
- const expression = stripParenExpression(child.expression);
47522
- return isNodeOfType(expression, "Identifier") && itemNames.has(expression.name);
47523
- });
47524
- };
47525
46921
  const callbackFiltersRows = (bindingFunction) => {
47526
46922
  if (!bindingFunction) return false;
47527
46923
  let didFindNullReturn = false;
@@ -47573,21 +46969,19 @@ const noArrayIndexAsKey = defineRule({
47573
46969
  const indexUse = findPositionalIndexUse(node.value.expression, 0);
47574
46970
  if (!indexUse) return;
47575
46971
  const indexName = indexUse.identifier.name;
47576
- if (isNumericPlaceholderLoopCounter(node, indexName)) return;
46972
+ if (isNumericForLoopCounter(node, indexName)) return;
47577
46973
  if (indexUse.binding.iteratorCall && iteratorCallExemptsIndexKey(indexUse.binding.iteratorCall)) return;
47578
46974
  const keyTemplate = resolveKeyTemplateLiteral(node.value.expression);
47579
46975
  if (keyTemplate && templateHasOuterMemberIdentity(keyTemplate, indexUse.binding.bindingFunction)) return;
47580
- if (hasAriaHiddenAncestor(node) && !indexUse.binding.isDataIndexedLoopCounter) return;
46976
+ if (hasAriaHiddenAncestor(node)) return;
47581
46977
  const itemNames = findIteratorItemNamesOfBinding(indexUse.binding);
47582
46978
  const derivedNames = collectDerivedRowContentNames(indexUse.binding.bindingFunction, itemNames);
47583
- const iteratorCallee = indexUse.binding.iteratorCall?.callee;
47584
- const hasDynamicReactChildren = Boolean(iteratorCallee && isNodeOfType(iteratorCallee, "MemberExpression") && isDynamicReactChildrenExpression(iteratorCallee.object, 0));
47585
46979
  const openingElement = node.parent;
47586
46980
  if (openingElement && isNodeOfType(openingElement, "JSXOpeningElement")) {
47587
46981
  const elementName = openingElement.name;
47588
46982
  if (isNodeOfType(elementName, "JSXIdentifier")) {
47589
46983
  if (elementName.name === "Fragment") {
47590
- if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames, hasDynamicReactChildren)) return;
46984
+ if (!fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
47591
46985
  } else if (PURE_SVG_PRIMITIVE_TAGS.has(elementName.name)) {
47592
46986
  if (!callbackFiltersRows(indexUse.binding.bindingFunction)) return;
47593
46987
  } else if (STATELESS_HTML_LEAF_TAGS.has(elementName.name)) {
@@ -47595,14 +46989,14 @@ const noArrayIndexAsKey = defineRule({
47595
46989
  if (jsxElement && isNodeOfType(jsxElement, "JSXElement")) {
47596
46990
  const isInlineTextRun = INLINE_TEXT_LEAF_TAGS.has(elementName.name);
47597
46991
  const primitiveItemNames = keyTemplate ? findBareItemNamesReferencedByTemplate(keyTemplate, itemNames) : EMPTY_NAME_SET$1;
47598
- if (!(hasDynamicReactChildren && elementHasDirectItemChild(openingElement, itemNames) || containsStatefulDescendant(jsxElement, {
46992
+ if (!containsStatefulDescendant(jsxElement, {
47599
46993
  memberRootNames: isInlineTextRun ? itemNames : EMPTY_NAME_SET$1,
47600
46994
  bareIdentifierNames: primitiveItemNames.size > 0 ? new Set([...derivedNames, ...primitiveItemNames]) : derivedNames
47601
- }))) return;
46995
+ })) return;
47602
46996
  }
47603
46997
  }
47604
46998
  }
47605
- if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames, hasDynamicReactChildren)) return;
46999
+ if (isNodeOfType(elementName, "JSXMemberExpression") && isNodeOfType(elementName.object, "JSXIdentifier") && isNodeOfType(elementName.property, "JSXIdentifier") && elementName.object.name === "React" && elementName.property.name === "Fragment" && !fragmentHasStatefulChildren(openingElement, itemNames, derivedNames)) return;
47606
47000
  }
47607
47001
  context.report({
47608
47002
  node,
@@ -64498,106 +63892,6 @@ const isGatedByFalsyInitialState = (node, scopes) => {
64498
63892
  };
64499
63893
  //#endregion
64500
63894
  //#region src/plugin/rules/performance/no-hydration-branch-on-browser-global.ts
64501
- const findGuardingIfStatements = (node, functionBoundary) => {
64502
- const guardingIfStatements = [];
64503
- let currentNode = node.parent;
64504
- while (currentNode && currentNode !== functionBoundary) {
64505
- if (isNodeOfType(currentNode, "IfStatement")) guardingIfStatements.push(currentNode);
64506
- currentNode = currentNode.parent;
64507
- }
64508
- return guardingIfStatements;
64509
- };
64510
- const doesNodeReadSymbol = (node, symbol) => {
64511
- let doesReadSymbol = false;
64512
- walkAst(node, (childNode) => {
64513
- if (isNodeOfType(childNode, "Identifier") && symbol.references.some((reference) => reference.identifier === childNode && reference.flag !== "write")) {
64514
- doesReadSymbol = true;
64515
- return false;
64516
- }
64517
- });
64518
- return doesReadSymbol;
64519
- };
64520
- const collectWrittenSymbols = (node, scopes) => {
64521
- const writtenSymbols = /* @__PURE__ */ new Set();
64522
- walkAst(node, (childNode) => {
64523
- if (childNode !== node && isFunctionLike$1(childNode)) return false;
64524
- if (!isNodeOfType(childNode, "Identifier")) return;
64525
- const reference = scopes.referenceFor(childNode);
64526
- if (!reference || reference.flag === "read" || !reference.resolvedSymbol) return;
64527
- writtenSymbols.add(reference.resolvedSymbol);
64528
- });
64529
- return writtenSymbols;
64530
- };
64531
- const isDescendantOf = (node, ancestorNode) => {
64532
- let currentNode = node.parent;
64533
- while (currentNode) {
64534
- if (currentNode === ancestorNode) return true;
64535
- currentNode = currentNode.parent;
64536
- }
64537
- return false;
64538
- };
64539
- const getAssignedValue = (identifier) => {
64540
- const assignmentExpression = identifier.parent;
64541
- return isNodeOfType(assignmentExpression, "AssignmentExpression") && assignmentExpression.operator === "=" && assignmentExpression.left === identifier ? assignmentExpression.right : null;
64542
- };
64543
- const doesGuardPreserveInitialSymbolValue = (symbol, guardingIfStatement, scopes) => {
64544
- const initialValue = symbol.initializer;
64545
- if (!initialValue) return false;
64546
- const guardedWrites = symbol.references.filter((reference) => reference.flag !== "read" && isDescendantOf(reference.identifier, guardingIfStatement));
64547
- return guardedWrites.length > 0 && guardedWrites.every((reference) => {
64548
- const assignedValue = getAssignedValue(reference.identifier);
64549
- return Boolean(assignedValue && areExpressionsStructurallyEqual(initialValue, assignedValue) && doEquivalentExpressionBindingsMatch(initialValue, assignedValue, scopes));
64550
- });
64551
- };
64552
- const isWriteOverwrittenBefore = (symbol, writeIdentifier, guardingIfStatement, readIdentifier, context) => symbol.references.some((reference) => reference.flag !== "read" && reference.identifier !== writeIdentifier && !isDescendantOf(reference.identifier, guardingIfStatement) && isNodeReachableWithinFunction(reference.identifier, context) && isUnconditionalOrStaticallySelected(reference.identifier, context) && getNodeStartIndex(reference.identifier) > getNodeStartIndex(writeIdentifier) && getNodeStartIndex(reference.identifier) < getNodeStartIndex(readIdentifier));
64553
- const isUnconditionalOrStaticallySelected = (node, context) => {
64554
- if (context.cfg.isUnconditionalFromEntry(node)) return true;
64555
- let currentNode = node;
64556
- let outermostStaticIfStatement = null;
64557
- let parentNode = currentNode.parent;
64558
- while (parentNode) {
64559
- if (isFunctionLike$1(parentNode)) break;
64560
- if (isNodeOfType(parentNode, "IfStatement")) {
64561
- const staticResult = readInitialStateBoolean(parentNode.test, context.scopes);
64562
- let selectedBranch = null;
64563
- if (staticResult === true) selectedBranch = parentNode.consequent;
64564
- if (staticResult === false) selectedBranch = parentNode.alternate;
64565
- if (!selectedBranch || currentNode !== selectedBranch && !isDescendantOf(currentNode, selectedBranch)) return false;
64566
- outermostStaticIfStatement = parentNode;
64567
- }
64568
- currentNode = parentNode;
64569
- parentNode = currentNode.parent;
64570
- }
64571
- return Boolean(outermostStaticIfStatement && context.cfg.isUnconditionalFromEntry(outermostStaticIfStatement));
64572
- };
64573
- const containsExplicitReactRuntimeReference = (node, scopes) => {
64574
- let hasRuntimeReference = false;
64575
- walkAst(node, (childNode) => {
64576
- if (isNodeOfType(childNode, "ImportDeclaration") && typeof childNode.source.value === "string" && REACT_RUNTIME_MODULE_SOURCES.has(childNode.source.value)) {
64577
- hasRuntimeReference = true;
64578
- return false;
64579
- }
64580
- if (!isNodeOfType(childNode, "CallExpression")) return;
64581
- const sourceArgument = (childNode.arguments ?? [])[0];
64582
- if (!isNodeOfType(childNode.callee, "Identifier") || childNode.callee.name !== "require" || !scopes.isGlobalReference(childNode.callee) || !isNodeOfType(sourceArgument, "Literal") || typeof sourceArgument.value !== "string" || !REACT_RUNTIME_MODULE_SOURCES.has(sourceArgument.value)) return;
64583
- hasRuntimeReference = true;
64584
- return false;
64585
- });
64586
- return hasRuntimeReference;
64587
- };
64588
- const findComponentRenderingLocalFunctionResult = (functionNode, scopes) => {
64589
- const bindingIdentifier = getDirectFunctionBindingIdentifier(functionNode);
64590
- if (!isNodeOfType(bindingIdentifier, "Identifier")) return null;
64591
- const functionSymbol = scopes.symbolFor(bindingIdentifier);
64592
- if (!functionSymbol) return null;
64593
- for (const reference of functionSymbol.references) {
64594
- const callExpression = reference.identifier.parent;
64595
- if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== reference.identifier) continue;
64596
- const componentOrHookNode = findRenderPhaseComponentOrHook(callExpression, scopes);
64597
- if (componentOrHookNode && isInRenderedOutput(callExpression, componentOrHookNode, scopes)) return componentOrHookNode;
64598
- }
64599
- return null;
64600
- };
64601
63895
  const evaluateEquality$1 = (operator, left, right) => {
64602
63896
  if (operator === "===" || operator === "==") return left === right;
64603
63897
  if (operator === "!==" || operator === "!=") return left !== right;
@@ -64648,107 +63942,6 @@ const readLogicalConditionResult = (operator, leftResult, rightResult) => {
64648
63942
  if (leftResult === false && rightResult === false) return false;
64649
63943
  return null;
64650
63944
  };
64651
- const areLooselyEqualPrimitiveResults = (left, right) => {
64652
- if (left.kind === right.kind) return left.value === right.value;
64653
- if (left.kind === "null" && right.kind === "undefined" || left.kind === "undefined" && right.kind === "null") return true;
64654
- if (left.kind === "boolean") return areLooselyEqualPrimitiveResults({
64655
- kind: "number",
64656
- value: left.value ? 1 : 0
64657
- }, right);
64658
- if (right.kind === "boolean") return areLooselyEqualPrimitiveResults(left, {
64659
- kind: "number",
64660
- value: right.value ? 1 : 0
64661
- });
64662
- if (left.kind === "number" && right.kind === "string") return left.value === Number(right.value);
64663
- if (left.kind === "string" && right.kind === "number") return Number(left.value) === right.value;
64664
- return false;
64665
- };
64666
- const readHydrationPrimitiveResult = (expression, context, runtime, state) => {
64667
- const unwrappedExpression = stripParenExpression(expression);
64668
- const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
64669
- if (predicateMatch) return {
64670
- kind: "boolean",
64671
- value: predicateMatch[`${runtime}Result`]
64672
- };
64673
- if (isNodeOfType(unwrappedExpression, "Literal")) {
64674
- const value = unwrappedExpression.value;
64675
- if (value === null) return {
64676
- kind: "null",
64677
- value
64678
- };
64679
- if (typeof value === "boolean") return {
64680
- kind: "boolean",
64681
- value
64682
- };
64683
- if (typeof value === "number") return {
64684
- kind: "number",
64685
- value
64686
- };
64687
- if (typeof value === "string") return {
64688
- kind: "string",
64689
- value
64690
- };
64691
- return null;
64692
- }
64693
- if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return {
64694
- kind: "undefined",
64695
- value: void 0
64696
- };
64697
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
64698
- const symbol = context.scopes.symbolFor(unwrappedExpression);
64699
- const parameterValue = symbol ? state.parameterValuesBySymbolId.get(symbol.id) : null;
64700
- if (symbol && parameterValue && !state.visitedSymbolIds.has(symbol.id)) {
64701
- state.visitedSymbolIds.add(symbol.id);
64702
- const result = readHydrationPrimitiveResult(parameterValue, context, runtime, state);
64703
- state.visitedSymbolIds.delete(symbol.id);
64704
- return result;
64705
- }
64706
- if (symbol && symbol.kind === "const" && symbol.initializer && symbol.references.every((reference) => reference.flag === "read") && !state.visitedSymbolIds.has(symbol.id)) {
64707
- state.visitedSymbolIds.add(symbol.id);
64708
- const result = readHydrationPrimitiveResult(symbol.initializer, context, runtime, state);
64709
- state.visitedSymbolIds.delete(symbol.id);
64710
- return result;
64711
- }
64712
- }
64713
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64714
- const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64715
- return argumentResult === null ? null : {
64716
- kind: "boolean",
64717
- value: !argumentResult
64718
- };
64719
- }
64720
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64721
- const leftResult = readHydrationPrimitiveResult(unwrappedExpression.left, context, runtime, state);
64722
- const rightResult = readHydrationPrimitiveResult(unwrappedExpression.right, context, runtime, state);
64723
- if (!leftResult || !rightResult) return null;
64724
- if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
64725
- const areEqual = leftResult.kind === rightResult.kind && leftResult.value === rightResult.value;
64726
- return {
64727
- kind: "boolean",
64728
- value: unwrappedExpression.operator === "===" ? areEqual : !areEqual
64729
- };
64730
- }
64731
- if (unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=") {
64732
- const areEqual = areLooselyEqualPrimitiveResults(leftResult, rightResult);
64733
- return {
64734
- kind: "boolean",
64735
- value: unwrappedExpression.operator === "==" ? areEqual : !areEqual
64736
- };
64737
- }
64738
- }
64739
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
64740
- const callArguments = unwrappedExpression.arguments ?? [];
64741
- const callee = stripParenExpression(unwrappedExpression.callee);
64742
- if (isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee) && callArguments.length === 1 && !isNodeOfType(callArguments[0], "SpreadElement")) {
64743
- const argumentResult = readHydrationConditionResult(callArguments[0], context, runtime, state);
64744
- return argumentResult === null ? null : {
64745
- kind: "boolean",
64746
- value: argumentResult
64747
- };
64748
- }
64749
- }
64750
- return null;
64751
- };
64752
63945
  const readHydrationConditionResult = (expression, context, runtime, state) => {
64753
63946
  const unwrappedExpression = stripParenExpression(expression);
64754
63947
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64797,10 +63990,6 @@ const readHydrationConditionResult = (expression, context, runtime, state) => {
64797
63990
  parameterValuesBySymbolId
64798
63991
  });
64799
63992
  }
64800
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
64801
- const result = readHydrationPrimitiveResult(unwrappedExpression, context, runtime, state);
64802
- return result?.kind === "boolean" && typeof result.value === "boolean" ? result.value : null;
64803
- }
64804
63993
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") {
64805
63994
  const argumentResult = readHydrationConditionResult(unwrappedExpression.argument, context, runtime, state);
64806
63995
  return argumentResult === null ? null : !argumentResult;
@@ -64861,19 +64050,13 @@ const doEquivalentExpressionBindingsMatch = (leftExpression, rightExpression, sc
64861
64050
  const rightSymbol = scopes.symbolFor(right);
64862
64051
  return leftSymbol || rightSymbol ? leftSymbol?.id === rightSymbol?.id : true;
64863
64052
  }
64864
- const rightEntries = new Map(Object.entries(right));
64865
- for (const [key, leftValue] of Object.entries(left)) {
64866
- if (key === "parent") continue;
64867
- const rightValue = rightEntries.get(key);
64868
- if (isAstNode(leftValue)) {
64869
- if (!isAstNode(rightValue) || !doEquivalentExpressionBindingsMatch(leftValue, rightValue, scopes)) return false;
64870
- continue;
64871
- }
64872
- if (!Array.isArray(leftValue)) continue;
64873
- if (!Array.isArray(rightValue)) return false;
64874
- const leftNodes = leftValue.filter(isAstNode);
64875
- const rightNodes = rightValue.filter(isAstNode);
64876
- if (leftNodes.length !== rightNodes.length || leftNodes.some((leftNode, index) => !rightNodes[index] || !doEquivalentExpressionBindingsMatch(leftNode, rightNodes[index], scopes))) return false;
64053
+ if (isNodeOfType(left, "MemberExpression") && isNodeOfType(right, "MemberExpression")) return doEquivalentExpressionBindingsMatch(left.object, right.object, scopes) && (!left.computed || doEquivalentExpressionBindingsMatch(left.property, right.property, scopes));
64054
+ if (isNodeOfType(left, "CallExpression") && isNodeOfType(right, "CallExpression")) {
64055
+ const rightArguments = right.arguments ?? [];
64056
+ return doEquivalentExpressionBindingsMatch(left.callee, right.callee, scopes) && (left.arguments ?? []).every((argument, index) => {
64057
+ const rightArgument = rightArguments[index];
64058
+ return Boolean(rightArgument && doEquivalentExpressionBindingsMatch(argument, rightArgument, scopes));
64059
+ });
64877
64060
  }
64878
64061
  return true;
64879
64062
  };
@@ -64887,57 +64070,6 @@ const doHelperReturnValuesDiffer = (leftValues, rightValues, context) => {
64887
64070
  const everyValueHasEquivalent = (values, candidateValues) => values.every((value) => candidateValues.some((candidateValue) => areHelperReturnValuesEquivalent(value, candidateValue, context)));
64888
64071
  return !everyValueHasEquivalent(leftValues, rightValues) || !everyValueHasEquivalent(rightValues, leftValues);
64889
64072
  };
64890
- const isExpressionProvablyReflexive = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
64891
- const unwrappedExpression = stripParenExpression(expression);
64892
- if (matchBrowserPredicate(unwrappedExpression, context)) return true;
64893
- if (isNodeOfType(unwrappedExpression, "Literal")) return typeof unwrappedExpression.value !== "number" || !Number.isNaN(unwrappedExpression.value);
64894
- if (isNodeOfType(unwrappedExpression, "Identifier") && unwrappedExpression.name === "undefined" && context.scopes.isGlobalReference(unwrappedExpression)) return true;
64895
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
64896
- const symbol = context.scopes.symbolFor(unwrappedExpression);
64897
- if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.initializer) return false;
64898
- visitedSymbolIds.add(symbol.id);
64899
- const assignedValues = symbol.references.filter((reference) => reference.flag !== "read").map((reference) => getAssignedValue(reference.identifier));
64900
- const isReflexive = isExpressionProvablyReflexive(symbol.initializer, context, visitedSymbolIds) && assignedValues.every((assignedValue) => Boolean(assignedValue && isExpressionProvablyReflexive(assignedValue, context, visitedSymbolIds)));
64901
- visitedSymbolIds.delete(symbol.id);
64902
- return isReflexive;
64903
- }
64904
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return isExpressionProvablyReflexive(unwrappedExpression.consequent, context, visitedSymbolIds) && isExpressionProvablyReflexive(unwrappedExpression.alternate, context, visitedSymbolIds);
64905
- if (isNodeOfType(unwrappedExpression, "UnaryExpression") && (unwrappedExpression.operator === "!" || unwrappedExpression.operator === "typeof" || unwrappedExpression.operator === "void")) return true;
64906
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) return unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==" || unwrappedExpression.operator === "==" || unwrappedExpression.operator === "!=";
64907
- if (isNodeOfType(unwrappedExpression, "ArrayExpression") || isNodeOfType(unwrappedExpression, "ObjectExpression") || isNodeOfType(unwrappedExpression, "FunctionExpression") || isNodeOfType(unwrappedExpression, "ArrowFunctionExpression") || isNodeOfType(unwrappedExpression, "TemplateLiteral")) return true;
64908
- if (!isNodeOfType(unwrappedExpression, "CallExpression")) return false;
64909
- const callee = stripParenExpression(unwrappedExpression.callee);
64910
- return isNodeOfType(callee, "Identifier") && callee.name === "Boolean" && context.scopes.isGlobalReference(callee);
64911
- };
64912
- const getReturnedObjectPropertyValues = (node, propertyName, scopes) => {
64913
- if (isNodeOfType(node, "ReturnStatement")) return node.argument ? getReturnedObjectPropertyValues(node.argument, propertyName, scopes) : [];
64914
- if (isNodeOfType(node, "ObjectExpression")) return node.properties.flatMap((property) => isNodeOfType(property, "Property") && property.kind === "init" && getResolvedStaticPropertyName(property, scopes) === propertyName ? [property.value] : []);
64915
- if (isNodeOfType(node, "IfStatement")) return [...getReturnedObjectPropertyValues(node.consequent, propertyName, scopes), ...node.alternate ? getReturnedObjectPropertyValues(node.alternate, propertyName, scopes) : []];
64916
- if (isNodeOfType(node, "TryStatement")) return [
64917
- ...getReturnedObjectPropertyValues(node.block, propertyName, scopes),
64918
- ...node.handler ? getReturnedObjectPropertyValues(node.handler.body, propertyName, scopes) : [],
64919
- ...node.finalizer ? getReturnedObjectPropertyValues(node.finalizer, propertyName, scopes) : []
64920
- ];
64921
- if (!isNodeOfType(node, "BlockStatement")) return [];
64922
- const propertyValues = [];
64923
- for (const childStatement of node.body) {
64924
- propertyValues.push(...getReturnedObjectPropertyValues(childStatement, propertyName, scopes));
64925
- if (statementAlwaysExits$1(childStatement)) break;
64926
- }
64927
- return propertyValues;
64928
- };
64929
- const matchHydrationFunctionPropertyResult = (functionNode, propertyName, context, state) => {
64930
- if (!isFunctionLike$1(functionNode) || state.visitedFunctionNodes.has(functionNode)) return null;
64931
- state.visitedFunctionNodes.add(functionNode);
64932
- const propertyValues = getReturnedObjectPropertyValues(functionNode.body, propertyName, context.scopes);
64933
- let match = null;
64934
- for (const propertyValue of propertyValues) {
64935
- match = matchHydrationConditionInternal(propertyValue, context, state);
64936
- if (match) break;
64937
- }
64938
- state.visitedFunctionNodes.delete(functionNode);
64939
- return match;
64940
- };
64941
64073
  const matchHydrationConditionInternal = (expression, context, state) => {
64942
64074
  const unwrappedExpression = stripParenExpression(expression);
64943
64075
  const predicateMatch = matchBrowserPredicate(unwrappedExpression, context);
@@ -64954,90 +64086,14 @@ const matchHydrationConditionInternal = (expression, context, state) => {
64954
64086
  state.visitedSymbolIds.delete(symbol.id);
64955
64087
  return match;
64956
64088
  }
64957
- if (symbol && (symbol.kind === "let" || symbol.kind === "var") && !state.visitedSymbolIds.has(symbol.id)) {
64958
- state.visitedSymbolIds.add(symbol.id);
64959
- if (symbol.initializer && symbol.references.every((reference) => reference.flag === "read")) {
64960
- const match = matchHydrationConditionInternal(symbol.initializer, context, state);
64961
- state.visitedSymbolIds.delete(symbol.id);
64962
- return match;
64963
- }
64964
- for (const reference of symbol.references) {
64965
- if (reference.flag === "read") continue;
64966
- if (!isNodeReachableWithinFunction(reference.identifier, context)) continue;
64967
- const enclosingFunction = findEnclosingFunction$1(reference.identifier);
64968
- if (!enclosingFunction) continue;
64969
- for (const guardingIfStatement of findGuardingIfStatements(reference.identifier, enclosingFunction)) {
64970
- if (doesGuardPreserveInitialSymbolValue(symbol, guardingIfStatement, context.scopes) || isWriteOverwrittenBefore(symbol, reference.identifier, guardingIfStatement, unwrappedExpression, context)) continue;
64971
- const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
64972
- if (match) {
64973
- state.visitedSymbolIds.delete(symbol.id);
64974
- return match;
64975
- }
64976
- }
64977
- }
64978
- const readingFunction = findEnclosingFunction$1(unwrappedExpression);
64979
- for (const reference of symbol.references) {
64980
- if (reference.flag === "read") continue;
64981
- const writingFunction = findEnclosingFunction$1(reference.identifier);
64982
- if (!readingFunction || !isFunctionLike$1(writingFunction) || writingFunction === readingFunction || writingFunction.async || writingFunction.params.length > 0 || isNodeOfType(writingFunction, "FunctionDeclaration") && writingFunction.generator || isNodeOfType(writingFunction, "FunctionExpression") && writingFunction.generator) continue;
64983
- const assignedValue = getAssignedValue(reference.identifier);
64984
- if (symbol.initializer && assignedValue && areExpressionsStructurallyEqual(symbol.initializer, assignedValue) && doEquivalentExpressionBindingsMatch(symbol.initializer, assignedValue, context.scopes)) continue;
64985
- const functionBinding = getDirectFunctionBindingIdentifier(writingFunction);
64986
- if (!isNodeOfType(functionBinding, "Identifier")) continue;
64987
- const functionSymbol = context.scopes.symbolFor(functionBinding);
64988
- if (!functionSymbol) continue;
64989
- for (const functionReference of functionSymbol.references) {
64990
- const callExpression = functionReference.identifier.parent;
64991
- if (!isNodeOfType(callExpression, "CallExpression") || callExpression.callee !== functionReference.identifier || (callExpression.arguments ?? []).length > 0 || findEnclosingFunction$1(callExpression) !== readingFunction || !isNodeReachableWithinFunction(callExpression, context) || getNodeStartIndex(callExpression) >= getNodeStartIndex(unwrappedExpression)) continue;
64992
- for (const guardingIfStatement of findGuardingIfStatements(callExpression, readingFunction)) {
64993
- if (isWriteOverwrittenBefore(symbol, callExpression, guardingIfStatement, unwrappedExpression, context)) continue;
64994
- const match = matchHydrationConditionInternal(guardingIfStatement.test, context, state);
64995
- if (match) {
64996
- state.visitedSymbolIds.delete(symbol.id);
64997
- return match;
64998
- }
64999
- }
65000
- }
65001
- }
65002
- state.visitedSymbolIds.delete(symbol.id);
65003
- }
65004
64089
  if (!symbol || symbol.kind !== "const" || !symbol.initializer || symbol.references.some((reference) => reference.flag !== "read") || state.visitedSymbolIds.has(symbol.id)) return null;
65005
64090
  state.visitedSymbolIds.add(symbol.id);
65006
64091
  const match = matchHydrationConditionInternal(symbol.initializer, context, state);
65007
64092
  state.visitedSymbolIds.delete(symbol.id);
65008
64093
  return match;
65009
64094
  }
65010
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) {
65011
- const propertyName = getResolvedStaticPropertyName(unwrappedExpression, context.scopes, {
65012
- allowConstNumericLiteral: true,
65013
- stringifyNonStringLiterals: true
65014
- });
65015
- const object = stripParenExpression(unwrappedExpression.object);
65016
- if (propertyName === null || !isNodeOfType(object, "CallExpression")) return null;
65017
- const callArguments = object.arguments ?? [];
65018
- if (isReactApiCall(object, "useMemo", context.scopes, {
65019
- allowGlobalReactNamespace: true,
65020
- resolveNamedAliases: true
65021
- })) {
65022
- const callbackArgument = callArguments[0];
65023
- if (!callbackArgument || isNodeOfType(callbackArgument, "SpreadElement")) return null;
65024
- const callbackFunction = resolveExactLocalFunction(callbackArgument, context.scopes);
65025
- return isFunctionLike$1(callbackFunction) && callbackFunction.params.length === 0 ? matchHydrationFunctionPropertyResult(callbackFunction, propertyName, context, state) : null;
65026
- }
65027
- const helperFunction = resolveExactLocalFunction(object.callee, context.scopes);
65028
- return isFunctionLike$1(helperFunction) && helperFunction.params.length === 0 && callArguments.length === 0 ? matchHydrationFunctionPropertyResult(helperFunction, propertyName, context, state) : null;
65029
- }
65030
64095
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
65031
64096
  const callArguments = unwrappedExpression.arguments ?? [];
65032
- if (isReactApiCall(unwrappedExpression, "useState", context.scopes, {
65033
- allowGlobalReactNamespace: true,
65034
- resolveNamedAliases: true
65035
- })) {
65036
- const initialState = callArguments[0];
65037
- if (!initialState || isNodeOfType(initialState, "SpreadElement")) return null;
65038
- const lazyInitializer = resolveExactLocalFunction(initialState, context.scopes);
65039
- return isFunctionLike$1(lazyInitializer) && lazyInitializer.params.length === 0 ? matchHydrationFunctionResult(lazyInitializer, context, state) : matchHydrationConditionInternal(initialState, context, state);
65040
- }
65041
64097
  if (isReactApiCall(unwrappedExpression, "useMemo", context.scopes, {
65042
64098
  allowGlobalReactNamespace: true,
65043
64099
  resolveNamedAliases: true
@@ -65065,22 +64121,6 @@ const matchHydrationConditionInternal = (expression, context, state) => {
65065
64121
  });
65066
64122
  }
65067
64123
  if (isNodeOfType(unwrappedExpression, "UnaryExpression") && unwrappedExpression.operator === "!") return matchHydrationConditionInternal(unwrappedExpression.argument, context, state);
65068
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
65069
- const staticTestResult = readInitialStateBoolean(unwrappedExpression.test, context.scopes);
65070
- if (staticTestResult !== null) return matchHydrationConditionInternal(staticTestResult ? unwrappedExpression.consequent : unwrappedExpression.alternate, context, state);
65071
- return matchHydrationConditionInternal(unwrappedExpression.test, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.consequent, context, state) ?? matchHydrationConditionInternal(unwrappedExpression.alternate, context, state);
65072
- }
65073
- if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
65074
- if (unwrappedExpression.operator !== "===" && unwrappedExpression.operator !== "!==" && unwrappedExpression.operator !== "==" && unwrappedExpression.operator !== "!=") return null;
65075
- const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
65076
- const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
65077
- const nestedMatch = leftMatch ?? rightMatch;
65078
- if (!nestedMatch) return null;
65079
- const clientResult = readHydrationConditionResult(unwrappedExpression, context, "client", state);
65080
- const serverResult = readHydrationConditionResult(unwrappedExpression, context, "server", state);
65081
- if (clientResult !== null && serverResult !== null) return clientResult !== serverResult ? nestedMatch : null;
65082
- return leftMatch && rightMatch && areExpressionsStructurallyEqual(unwrappedExpression.left, unwrappedExpression.right) && doEquivalentExpressionBindingsMatch(unwrappedExpression.left, unwrappedExpression.right, context.scopes) && isExpressionProvablyReflexive(unwrappedExpression.left, context) ? null : nestedMatch;
65083
- }
65084
64124
  if (!isNodeOfType(unwrappedExpression, "LogicalExpression") || unwrappedExpression.operator !== "&&" && unwrappedExpression.operator !== "||") return null;
65085
64125
  const leftMatch = matchHydrationConditionInternal(unwrappedExpression.left, context, state);
65086
64126
  const rightMatch = matchHydrationConditionInternal(unwrappedExpression.right, context, state);
@@ -65097,13 +64137,8 @@ const matchHydrationReturningStatement = (statement, context, state) => {
65097
64137
  const consequentValues = getReturnedValues(statement.consequent);
65098
64138
  const alternateValues = statement.alternate ? getReturnedValues(statement.alternate) : findFollowingReturnedValues(statement);
65099
64139
  if (conditionMatch && consequentValues.length > 0 && alternateValues.length > 0 && doHelperReturnValuesDiffer(consequentValues, alternateValues, context)) return conditionMatch;
65100
- if (conditionMatch) {
65101
- const followingReturnedValues = findFollowingReturnedValues(statement);
65102
- if ([...new Set([...collectWrittenSymbols(statement.consequent, context.scopes), ...statement.alternate ? collectWrittenSymbols(statement.alternate, context.scopes) : []])].some((symbol) => !doesGuardPreserveInitialSymbolValue(symbol, statement, context.scopes) && followingReturnedValues.some((value) => doesNodeReadSymbol(value, symbol)))) return conditionMatch;
65103
- }
65104
64140
  return matchHydrationReturningStatement(statement.consequent, context, state) ?? (statement.alternate ? matchHydrationReturningStatement(statement.alternate, context, state) : null);
65105
64141
  }
65106
- if (isNodeOfType(statement, "TryStatement")) return matchHydrationReturningStatement(statement.block, context, state) ?? (statement.handler ? matchHydrationReturningStatement(statement.handler.body, context, state) : null) ?? (statement.finalizer ? matchHydrationReturningStatement(statement.finalizer, context, state) : null);
65107
64142
  if (!isNodeOfType(statement, "BlockStatement")) return null;
65108
64143
  for (const childStatement of statement.body) {
65109
64144
  const match = matchHydrationReturningStatement(childStatement, context, state);
@@ -65124,62 +64159,47 @@ const matchHydrationCondition = (expression, context) => matchHydrationCondition
65124
64159
  visitedFunctionNodes: /* @__PURE__ */ new Set(),
65125
64160
  visitedSymbolIds: /* @__PURE__ */ new Set()
65126
64161
  });
65127
- const areNodeArraysEquivalent = (leftNodes, rightNodes, scopes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index], scopes));
65128
- const areRenderedBranchesEquivalent = (leftNode, rightNode, scopes) => {
64162
+ const areNodeArraysEquivalent = (leftNodes, rightNodes) => leftNodes.length === rightNodes.length && leftNodes.every((leftNode, index) => areRenderedBranchesEquivalent(leftNode, rightNodes[index]));
64163
+ const areRenderedBranchesEquivalent = (leftNode, rightNode) => {
65129
64164
  if (!leftNode || !rightNode) return leftNode === rightNode;
65130
64165
  const left = stripParenExpression(leftNode);
65131
64166
  const right = stripParenExpression(rightNode);
65132
- if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64167
+ if (areExpressionsStructurallyEqual(left, right)) return true;
65133
64168
  if (left.type !== right.type) return false;
65134
64169
  if (isNodeOfType(left, "JSXText") && isNodeOfType(right, "JSXText")) return left.value === right.value;
65135
64170
  if (isNodeOfType(left, "JSXExpressionContainer") && isNodeOfType(right, "JSXExpressionContainer")) {
65136
64171
  if (!isAstNode(left.expression) || !isAstNode(right.expression)) return left.expression.type === right.expression.type;
65137
- return areRenderedBranchesEquivalent(left.expression, right.expression, scopes);
64172
+ return areRenderedBranchesEquivalent(left.expression, right.expression);
65138
64173
  }
65139
64174
  if (isNodeOfType(left, "JSXElement") && isNodeOfType(right, "JSXElement")) {
65140
64175
  if (flattenJsxName$1(left.openingElement.name) !== flattenJsxName$1(right.openingElement.name)) return false;
65141
- if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes, scopes)) return false;
65142
- return areNodeArraysEquivalent(left.children, right.children, scopes);
64176
+ if (!areNodeArraysEquivalent(left.openingElement.attributes, right.openingElement.attributes)) return false;
64177
+ return areNodeArraysEquivalent(left.children, right.children);
65143
64178
  }
65144
- if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children, scopes);
64179
+ if (isNodeOfType(left, "JSXFragment") && isNodeOfType(right, "JSXFragment")) return areNodeArraysEquivalent(left.children, right.children);
65145
64180
  if (isNodeOfType(left, "JSXAttribute") && isNodeOfType(right, "JSXAttribute")) {
65146
64181
  if (flattenJsxName$1(left.name) !== flattenJsxName$1(right.name)) return false;
65147
- return areRenderedBranchesEquivalent(left.value, right.value, scopes);
64182
+ return areRenderedBranchesEquivalent(left.value, right.value);
65148
64183
  }
65149
- if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument, scopes);
64184
+ if (isNodeOfType(left, "JSXSpreadAttribute") && isNodeOfType(right, "JSXSpreadAttribute")) return areRenderedBranchesEquivalent(left.argument, right.argument);
65150
64185
  if (isNodeOfType(left, "TemplateLiteral") && isNodeOfType(right, "TemplateLiteral")) {
65151
64186
  if (left.quasis.length !== right.quasis.length) return false;
65152
64187
  if (!left.quasis.every((quasi, index) => quasi.value.cooked === right.quasis[index]?.value.cooked && quasi.value.raw === right.quasis[index]?.value.raw)) return false;
65153
- return areNodeArraysEquivalent(left.expressions, right.expressions, scopes);
64188
+ return areNodeArraysEquivalent(left.expressions, right.expressions);
65154
64189
  }
65155
64190
  return false;
65156
64191
  };
65157
- const isProvenReactCreateElementCall = (node, scopes) => {
65158
- if (isReactApiCall(node, "createElement", scopes, {
65159
- allowGlobalReactNamespace: true,
65160
- resolveNamedAliases: true
65161
- })) return true;
65162
- if (!isNodeOfType(node, "CallExpression")) return false;
65163
- const callee = stripParenExpression(node.callee);
65164
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "createElement") return false;
65165
- const receiver = stripParenExpression(callee.object);
65166
- const namespaceIdentifier = isNodeOfType(receiver, "MemberExpression") && !receiver.computed && isNodeOfType(receiver.property, "Identifier") && receiver.property.name === "default" ? stripParenExpression(receiver.object) : receiver;
65167
- if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
65168
- const namespaceSymbol = scopes.symbolFor(namespaceIdentifier);
65169
- return Boolean(namespaceSymbol?.initializer && namespaceSymbol.references.every((reference) => reference.flag === "read") && containsExplicitReactRuntimeReference(namespaceSymbol.initializer, scopes));
65170
- };
65171
- const isRenderedValue = (node, scopes) => {
64192
+ const isRenderedValue = (node) => {
65172
64193
  const unwrappedNode = stripParenExpression(node);
65173
64194
  if (isNodeOfType(unwrappedNode, "Literal")) return unwrappedNode.value !== null && unwrappedNode.value !== true && unwrappedNode.value !== false && unwrappedNode.value !== "";
65174
64195
  if (isNodeOfType(unwrappedNode, "TemplateLiteral")) return unwrappedNode.expressions.length > 0 || unwrappedNode.quasis[0]?.value.cooked !== "";
65175
- if (isNodeOfType(unwrappedNode, "CallExpression")) return isProvenReactCreateElementCall(unwrappedNode, scopes);
65176
64196
  return isNodeOfType(unwrappedNode, "JSXElement") || isNodeOfType(unwrappedNode, "JSXFragment");
65177
64197
  };
65178
- const findRenderedValueInAndBranch = (node, scopes) => {
64198
+ const findRenderedValueInAndBranch = (node) => {
65179
64199
  const unwrappedNode = stripParenExpression(node);
65180
- if (isPotentiallyRenderedValue(unwrappedNode, scopes)) return unwrappedNode;
64200
+ if (isRenderedValue(unwrappedNode)) return unwrappedNode;
65181
64201
  if (!isNodeOfType(unwrappedNode, "LogicalExpression") || unwrappedNode.operator !== "&&") return null;
65182
- return findRenderedValueInAndBranch(unwrappedNode.right, scopes);
64202
+ return findRenderedValueInAndBranch(unwrappedNode.right);
65183
64203
  };
65184
64204
  const findEnclosingJsxAttribute = (node) => {
65185
64205
  let currentNode = node.parent;
@@ -65212,11 +64232,6 @@ const getReturnedValues = (statement) => {
65212
64232
  if (!statement) return [];
65213
64233
  if (isNodeOfType(statement, "ReturnStatement")) return statement.argument ? [statement.argument] : [];
65214
64234
  if (isNodeOfType(statement, "IfStatement")) return [...getReturnedValues(statement.consequent), ...getReturnedValues(statement.alternate)];
65215
- if (isNodeOfType(statement, "TryStatement")) return [
65216
- ...getReturnedValues(statement.block),
65217
- ...getReturnedValues(statement.handler?.body),
65218
- ...getReturnedValues(statement.finalizer)
65219
- ];
65220
64235
  if (!isNodeOfType(statement, "BlockStatement")) return [];
65221
64236
  const returnedValues = [];
65222
64237
  for (const childStatement of statement.body) {
@@ -65225,127 +64240,6 @@ const getReturnedValues = (statement) => {
65225
64240
  }
65226
64241
  return returnedValues;
65227
64242
  };
65228
- const isPotentiallyRenderedValueInternal = (node, scopes, visitedFunctionNodes) => {
65229
- const unwrappedNode = stripParenExpression(node);
65230
- if (isRenderedValue(unwrappedNode, scopes)) return true;
65231
- if (isNodeOfType(unwrappedNode, "ConditionalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.consequent, scopes, visitedFunctionNodes) && isPotentiallyRenderedValueInternal(unwrappedNode.alternate, scopes, visitedFunctionNodes);
65232
- if (isNodeOfType(unwrappedNode, "LogicalExpression")) return isPotentiallyRenderedValueInternal(unwrappedNode.right, scopes, visitedFunctionNodes);
65233
- if (!isNodeOfType(unwrappedNode, "CallExpression")) return false;
65234
- const calledFunction = resolveExactLocalFunction(unwrappedNode.callee, scopes);
65235
- if (!isFunctionLike$1(calledFunction) || visitedFunctionNodes.has(calledFunction)) return false;
65236
- visitedFunctionNodes.add(calledFunction);
65237
- const returnedValues = isNodeOfType(calledFunction.body, "BlockStatement") ? getReturnedValues(calledFunction.body) : [calledFunction.body];
65238
- const isPotentiallyRendered = returnedValues.length > 0 && returnedValues.every((returnedValue) => isPotentiallyRenderedValueInternal(returnedValue, scopes, visitedFunctionNodes));
65239
- visitedFunctionNodes.delete(calledFunction);
65240
- return isPotentiallyRendered;
65241
- };
65242
- const isPotentiallyRenderedValue = (node, scopes) => isPotentiallyRenderedValueInternal(node, scopes, /* @__PURE__ */ new Set());
65243
- const findUseStateBindingSymbol = (node, componentOrHookNode, scopes) => {
65244
- let currentNode = node.parent;
65245
- while (currentNode && currentNode !== componentOrHookNode) {
65246
- if (isNodeOfType(currentNode, "CallExpression") && isReactApiCall(currentNode, "useState", scopes, {
65247
- allowGlobalReactNamespace: true,
65248
- resolveNamedAliases: true
65249
- }) && isNodeOfType(currentNode.parent, "VariableDeclarator") && isNodeOfType(currentNode.parent.id, "ArrayPattern")) {
65250
- const stateBinding = currentNode.parent.id.elements?.[0];
65251
- return isNodeOfType(stateBinding, "Identifier") ? scopes.symbolFor(stateBinding) : null;
65252
- }
65253
- if (isFunctionLike$1(currentNode)) return null;
65254
- currentNode = currentNode.parent;
65255
- }
65256
- return null;
65257
- };
65258
- const doesReferenceControlStructuralRenderedValue = (referenceIdentifier) => {
65259
- let currentNode = referenceIdentifier;
65260
- let parentNode = currentNode.parent;
65261
- while (parentNode) {
65262
- if (isNodeOfType(parentNode, "ConditionalExpression") && parentNode.test === currentNode && (isStructuralRenderedValue(parentNode.consequent) || isStructuralRenderedValue(parentNode.alternate))) return true;
65263
- if (isNodeOfType(parentNode, "LogicalExpression") && parentNode.left === currentNode && isStructuralRenderedValue(parentNode.right)) return true;
65264
- if (isNodeOfType(parentNode, "JSXExpressionContainer") || isFunctionLike$1(parentNode)) return false;
65265
- currentNode = parentNode;
65266
- parentNode = currentNode.parent;
65267
- }
65268
- return false;
65269
- };
65270
- const isRenderedHydrationConsumer = (node, producerHookNode, scopes) => {
65271
- const renderingComponent = findRenderPhaseComponentOrHook(node, scopes);
65272
- return Boolean(renderingComponent && renderingComponent !== producerHookNode && isInRenderedOutput(node, renderingComponent, scopes) && !isGatedByFalsyInitialState(node, scopes) && !isAfterClientOnlyEarlyReturn(node, renderingComponent, scopes) && (!hasSuppressHydrationWarningAttribute(findEnclosingJsxOpeningElement(node)) || doesReferenceControlStructuralRenderedValue(node)));
65273
- };
65274
- const doesConsumerExpressionReachRenderedOutput = (node, producerHookNode, scopes, visitedSymbolIds) => {
65275
- if (isRenderedHydrationConsumer(node, producerHookNode, scopes)) return true;
65276
- const parentNode = node.parent;
65277
- if (!isNodeOfType(parentNode, "VariableDeclarator") || parentNode.init !== node || !isNodeOfType(parentNode.id, "Identifier")) return false;
65278
- const aliasSymbol = scopes.symbolFor(parentNode.id);
65279
- if (!aliasSymbol || visitedSymbolIds.has(aliasSymbol.id)) return false;
65280
- visitedSymbolIds.add(aliasSymbol.id);
65281
- const doesReachRenderedOutput = aliasSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, visitedSymbolIds));
65282
- visitedSymbolIds.delete(aliasSymbol.id);
65283
- return doesReachRenderedOutput;
65284
- };
65285
- const doesConsumerBindingReachRenderedOutput = (bindingIdentifier, producerHookNode, scopes) => {
65286
- if (!isNodeOfType(bindingIdentifier, "Identifier")) return false;
65287
- const consumerSymbol = scopes.symbolFor(bindingIdentifier);
65288
- if (!consumerSymbol) return false;
65289
- return consumerSymbol.references.some((reference) => doesConsumerExpressionReachRenderedOutput(reference.identifier, producerHookNode, scopes, new Set([consumerSymbol.id])));
65290
- };
65291
- const getReturnedStatePaths = (returnedValue, stateSymbol, scopes) => {
65292
- const unwrappedValue = stripParenExpression(returnedValue);
65293
- if (isNodeOfType(unwrappedValue, "ObjectExpression")) return unwrappedValue.properties.flatMap((property) => {
65294
- if (!isNodeOfType(property, "Property") || property.kind !== "init" || !doesNodeReadSymbol(property.value, stateSymbol)) return [];
65295
- const propertyName = getResolvedStaticPropertyName(property, scopes);
65296
- return propertyName === null ? [] : [{
65297
- kind: "property",
65298
- key: propertyName
65299
- }];
65300
- });
65301
- if (isNodeOfType(unwrappedValue, "ArrayExpression")) return (unwrappedValue.elements ?? []).flatMap((element, index) => element && isAstNode(element) && doesNodeReadSymbol(element, stateSymbol) ? [{
65302
- kind: "index",
65303
- key: String(index)
65304
- }] : []);
65305
- return doesNodeReadSymbol(unwrappedValue, stateSymbol) ? [{
65306
- kind: "direct",
65307
- key: null
65308
- }] : [];
65309
- };
65310
- const doesCallResultPathReachRenderedOutput = (callExpression, returnedStatePath, producerHookNode, scopes) => {
65311
- const callParent = callExpression.parent;
65312
- if (returnedStatePath.kind === "direct") return doesConsumerExpressionReachRenderedOutput(callExpression, producerHookNode, scopes, /* @__PURE__ */ new Set());
65313
- if (isNodeOfType(callParent, "MemberExpression") && callParent.object === callExpression && getResolvedStaticPropertyName(callParent, scopes, {
65314
- allowConstNumericLiteral: true,
65315
- stringifyNonStringLiterals: true
65316
- }) === returnedStatePath.key) return doesConsumerExpressionReachRenderedOutput(callParent, producerHookNode, scopes, /* @__PURE__ */ new Set());
65317
- if (!isNodeOfType(callParent, "VariableDeclarator") || callParent.init !== callExpression) return false;
65318
- if (returnedStatePath.kind === "property" && isNodeOfType(callParent.id, "ObjectPattern")) return callParent.id.properties.some((property) => isNodeOfType(property, "Property") && getResolvedStaticPropertyName(property, scopes) === returnedStatePath.key && doesConsumerBindingReachRenderedOutput(property.value, producerHookNode, scopes));
65319
- if (returnedStatePath.kind === "index" && isNodeOfType(callParent.id, "ArrayPattern")) {
65320
- const element = callParent.id.elements?.[Number(returnedStatePath.key)];
65321
- return Boolean(element && doesConsumerBindingReachRenderedOutput(element, producerHookNode, scopes));
65322
- }
65323
- if (!isNodeOfType(callParent.id, "Identifier")) return false;
65324
- const resultSymbol = scopes.symbolFor(callParent.id);
65325
- if (!resultSymbol) return false;
65326
- return resultSymbol.references.some((reference) => {
65327
- const memberExpression = reference.identifier.parent;
65328
- return Boolean(isNodeOfType(memberExpression, "MemberExpression") && memberExpression.object === reference.identifier && getResolvedStaticPropertyName(memberExpression, scopes, {
65329
- allowConstNumericLiteral: true,
65330
- stringifyNonStringLiterals: true
65331
- }) === returnedStatePath.key && doesConsumerExpressionReachRenderedOutput(memberExpression, producerHookNode, scopes, new Set([resultSymbol.id])));
65332
- });
65333
- };
65334
- const isReturnedUseStateInitializerRendered = (node, componentOrHookNode, scopes) => {
65335
- if (!isFunctionLike$1(componentOrHookNode)) return false;
65336
- const stateSymbol = findUseStateBindingSymbol(node, componentOrHookNode, scopes);
65337
- if (!stateSymbol) return false;
65338
- const returnedStatePaths = (isNodeOfType(componentOrHookNode.body, "BlockStatement") ? getReturnedValues(componentOrHookNode.body) : [componentOrHookNode.body]).flatMap((returnedValue) => getReturnedStatePaths(returnedValue, stateSymbol, scopes));
65339
- if (returnedStatePaths.length === 0) return false;
65340
- const functionBinding = getDirectFunctionBindingIdentifier(componentOrHookNode);
65341
- if (!isNodeOfType(functionBinding, "Identifier")) return false;
65342
- const functionSymbol = scopes.symbolFor(functionBinding);
65343
- if (!functionSymbol) return false;
65344
- return functionSymbol.references.some((functionReference) => {
65345
- const callExpression = functionReference.identifier.parent;
65346
- return Boolean(isNodeOfType(callExpression, "CallExpression") && callExpression.callee === functionReference.identifier && returnedStatePaths.some((returnedStatePath) => doesCallResultPathReachRenderedOutput(callExpression, returnedStatePath, componentOrHookNode, scopes)));
65347
- });
65348
- };
65349
64243
  const findFollowingReturnedValues = (ifStatement) => {
65350
64244
  const parentNode = ifStatement.parent;
65351
64245
  if (!isNodeOfType(parentNode, "BlockStatement")) return [];
@@ -65358,24 +64252,24 @@ const findFollowingReturnedValues = (ifStatement) => {
65358
64252
  }
65359
64253
  return returnedValues;
65360
64254
  };
65361
- const areConditionExpressionsEquivalent = (leftExpression, rightExpression, scopes) => {
64255
+ const areConditionExpressionsEquivalent = (leftExpression, rightExpression) => {
65362
64256
  const left = stripParenExpression(leftExpression);
65363
64257
  const right = stripParenExpression(rightExpression);
65364
- if (areExpressionsStructurallyEqual(left, right)) return doEquivalentExpressionBindingsMatch(left, right, scopes);
64258
+ if (areExpressionsStructurallyEqual(left, right)) return true;
65365
64259
  if (left.type !== right.type) return false;
65366
- if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument, scopes);
65367
- if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
65368
- if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left, scopes) && areConditionExpressionsEquivalent(left.right, right.right, scopes);
64260
+ if (isNodeOfType(left, "UnaryExpression") && isNodeOfType(right, "UnaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.argument, right.argument);
64261
+ if (isNodeOfType(left, "LogicalExpression") && isNodeOfType(right, "LogicalExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
64262
+ if (isNodeOfType(left, "BinaryExpression") && isNodeOfType(right, "BinaryExpression")) return left.operator === right.operator && areConditionExpressionsEquivalent(left.left, right.left) && areConditionExpressionsEquivalent(left.right, right.right);
65369
64263
  return false;
65370
64264
  };
65371
- const areReturnTreesEquivalent = (leftStatement, rightStatement, scopes) => {
64265
+ const areReturnTreesEquivalent = (leftStatement, rightStatement) => {
65372
64266
  if (!leftStatement || !rightStatement) return leftStatement === rightStatement;
65373
- if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument, scopes);
65374
- if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test, scopes) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent, scopes) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate, scopes);
64267
+ if (isNodeOfType(leftStatement, "ReturnStatement") && isNodeOfType(rightStatement, "ReturnStatement")) return areRenderedBranchesEquivalent(leftStatement.argument, rightStatement.argument);
64268
+ if (isNodeOfType(leftStatement, "IfStatement") && isNodeOfType(rightStatement, "IfStatement")) return areConditionExpressionsEquivalent(leftStatement.test, rightStatement.test) && areReturnTreesEquivalent(leftStatement.consequent, rightStatement.consequent) && areReturnTreesEquivalent(leftStatement.alternate, rightStatement.alternate);
65375
64269
  if (!isNodeOfType(leftStatement, "BlockStatement") || !isNodeOfType(rightStatement, "BlockStatement")) return false;
65376
64270
  const leftReturningStatements = leftStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
65377
64271
  const rightReturningStatements = rightStatement.body.filter((statement) => getReturnedValues(statement).length > 0);
65378
- return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index], scopes));
64272
+ return leftReturningStatements.length === rightReturningStatements.length && leftReturningStatements.every((statement, index) => areReturnTreesEquivalent(statement, rightReturningStatements[index]));
65379
64273
  };
65380
64274
  const isStructuralRenderedValue = (node) => {
65381
64275
  if (!node) return false;
@@ -65399,22 +64293,19 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
65399
64293
  if (isTestlikeFilename(context.filename)) return {};
65400
64294
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
65401
64295
  let fileHasUseClientDirective = false;
65402
- let fileHasExplicitReactRuntimeReference = false;
65403
64296
  let fileIsEmailTemplate = false;
65404
64297
  const reportedNodes = /* @__PURE__ */ new Set();
65405
- const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext, hasProvenRenderedConsumer = false) => {
64298
+ const reportHydrationBranch = (conditionNode, leftBranch, rightBranch, requiresRenderedContext) => {
65406
64299
  const conditionMatch = matchHydrationCondition(conditionNode, context);
65407
64300
  if (!conditionMatch) return;
65408
64301
  const { predicateMatch, predicateNode } = conditionMatch;
65409
64302
  if (reportedNodes.has(predicateNode)) return;
65410
- if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch, context.scopes)) return;
65411
- const enclosingFunction = findEnclosingFunction$1(conditionNode);
65412
- const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
64303
+ if (rightBranch && areRenderedBranchesEquivalent(leftBranch, rightBranch)) return;
64304
+ const componentOrHookNode = findRenderPhaseComponentOrHook(conditionNode, context.scopes);
65413
64305
  if (!componentOrHookNode) return;
65414
- const hasRenderedLocalFunctionConsumer = Boolean(enclosingFunction && enclosingFunction !== componentOrHookNode && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) === componentOrHookNode);
65415
- if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective) && !fileHasExplicitReactRuntimeReference) return;
65416
- if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes) && !hasRenderedLocalFunctionConsumer) return;
65417
- if (!hasProvenRenderedConsumer && !(requiresRenderedContext ? isPotentiallyRenderedValue(leftBranch, context.scopes) : isRenderedValue(leftBranch, context.scopes)) && (!rightBranch || !(requiresRenderedContext ? isPotentiallyRenderedValue(rightBranch, context.scopes) : isRenderedValue(rightBranch, context.scopes)))) {
64306
+ if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
64307
+ if (requiresRenderedContext && !isInRenderedOutput(conditionNode, componentOrHookNode, context.scopes)) return;
64308
+ if (!isRenderedValue(leftBranch) && (!rightBranch || !isRenderedValue(rightBranch))) {
65418
64309
  const attribute = findEnclosingJsxAttribute(conditionNode);
65419
64310
  if (!attribute || isEventHandlerAttribute(attribute)) return;
65420
64311
  }
@@ -65433,31 +64324,28 @@ const noHydrationBranchOnBrowserGlobal = defineRule({
65433
64324
  return {
65434
64325
  Program(node) {
65435
64326
  fileHasUseClientDirective = hasDirective(node, "use client");
65436
- fileHasExplicitReactRuntimeReference = containsExplicitReactRuntimeReference(node, context.scopes);
65437
64327
  fileIsEmailTemplate = hasEmailTemplateImport(node);
65438
64328
  },
65439
64329
  ConditionalExpression(node) {
65440
64330
  reportHydrationBranch(node.test, node.consequent, node.alternate, true);
65441
- const componentOrHookNode = findRenderPhaseComponentOrHook(node, context.scopes);
65442
- if (componentOrHookNode && isReturnedUseStateInitializerRendered(node, componentOrHookNode, context.scopes)) reportHydrationBranch(node.test, node.consequent, node.alternate, false, true);
65443
64331
  },
65444
64332
  LogicalExpression(node) {
65445
64333
  if (node.operator !== "&&" && node.operator !== "||") return;
65446
- const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right, context.scopes) : isPotentiallyRenderedValue(node.right, context.scopes) ? node.right : null;
64334
+ const renderedValue = node.operator === "&&" ? findRenderedValueInAndBranch(node.right) : isRenderedValue(node.right) ? node.right : null;
65447
64335
  if (!renderedValue) return;
65448
64336
  reportHydrationBranch(node, renderedValue, null, true);
65449
64337
  },
65450
64338
  IfStatement(node) {
65451
- if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate, context.scopes)) return;
64339
+ if (node.alternate && areReturnTreesEquivalent(node.consequent, node.alternate)) return;
65452
64340
  const consequentValues = getReturnedValues(node.consequent);
65453
64341
  const alternateValues = node.alternate ? getReturnedValues(node.alternate) : findFollowingReturnedValues(node);
65454
64342
  if (consequentValues.length === 0 || alternateValues.length === 0) return;
65455
- const enclosingFunction = findEnclosingFunction$1(node);
65456
- const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes) ?? (enclosingFunction ? findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) : null);
64343
+ const componentOrHookNode = findRenderPhaseComponentOrHook(node.test, context.scopes);
65457
64344
  if (!componentOrHookNode) return;
65458
- if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes) && findComponentRenderingLocalFunctionResult(enclosingFunction, context.scopes) !== componentOrHookNode)) return;
64345
+ const enclosingFunction = findEnclosingFunction$1(node);
64346
+ if (enclosingFunction !== componentOrHookNode && (!enclosingFunction || !isInRenderedOutput(enclosingFunction, componentOrHookNode, context.scopes))) return;
65459
64347
  for (const consequentValue of consequentValues) for (const alternateValue of alternateValues) {
65460
- if (!isRenderedValue(consequentValue, context.scopes) && !isRenderedValue(alternateValue, context.scopes)) continue;
64348
+ if (!isRenderedValue(consequentValue) && !isRenderedValue(alternateValue)) continue;
65461
64349
  reportHydrationBranch(node.test, consequentValue, alternateValue, false);
65462
64350
  }
65463
64351
  }
@@ -74998,29 +73886,6 @@ const doesPredicateTruthRequireMatch = (matchCall, predicateFunction) => {
74998
73886
  }
74999
73887
  return !isNegated && predicateFunction.body === child;
75000
73888
  };
75001
- const doesPredicateReturnNormalizedMatch = (matchCall, predicateFunction) => {
75002
- if (!isFunctionLike$1(predicateFunction) || !isNodeOfType(predicateFunction.body, "BlockStatement") || predicateFunction.body.body.length !== 2 || !isNodeOfType(predicateFunction.body.body[0], "VariableDeclaration")) return false;
75003
- const returnStatement = predicateFunction.body.body[1];
75004
- if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
75005
- let negationCount = 0;
75006
- let expression = matchCall;
75007
- let parent = expression.parent ?? null;
75008
- while (parent && parent !== returnStatement) {
75009
- if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "!") {
75010
- negationCount += 1;
75011
- expression = parent;
75012
- parent = parent.parent ?? null;
75013
- continue;
75014
- }
75015
- if (TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) || isNodeOfType(parent, "ChainExpression")) {
75016
- expression = parent;
75017
- parent = parent.parent ?? null;
75018
- continue;
75019
- }
75020
- return false;
75021
- }
75022
- return parent === returnStatement && returnStatement.argument === expression && negationCount % 2 === 0;
75023
- };
75024
73889
  const isStringTypeofGuardForPath = (test, expectedPath) => {
75025
73890
  const target = stripParenExpression(test);
75026
73891
  if (!isNodeOfType(target, "BinaryExpression") || target.operator !== "===") return false;
@@ -75061,26 +73926,6 @@ const pathUsesOptionalAccess = (node) => {
75061
73926
  current = current.object;
75062
73927
  }
75063
73928
  };
75064
- const getNormalizedClassNameRoot = (expression) => {
75065
- const conditional = stripParenExpression(expression);
75066
- if (!isNodeOfType(conditional, "ConditionalExpression")) return null;
75067
- const consequent = stripParenExpression(conditional.consequent);
75068
- const rootIdentifier = getRootIdentifier(consequent);
75069
- if (!rootIdentifier || receiverPathKey(consequent) !== `${rootIdentifier.name}.className`) return null;
75070
- const test = stripParenExpression(conditional.test);
75071
- if (!isNodeOfType(test, "BinaryExpression") || test.operator !== "===") return null;
75072
- const testOperands = [test.left, test.right].map((operand) => stripParenExpression(operand));
75073
- const typeofOperand = testOperands.find((operand) => isNodeOfType(operand, "UnaryExpression"));
75074
- const stringOperand = testOperands.find((operand) => isNodeOfType(operand, "Literal"));
75075
- if (!typeofOperand || !isNodeOfType(typeofOperand, "UnaryExpression") || typeofOperand.operator !== "typeof" || receiverPathKey(typeofOperand.argument) !== `${rootIdentifier.name}.className` || !stringOperand || !isNodeOfType(stringOperand, "Literal") || stringOperand.value !== "string") return null;
75076
- const alternate = stripParenExpression(conditional.alternate);
75077
- if (!isNodeOfType(alternate, "LogicalExpression") || alternate.operator !== "??") return null;
75078
- const fallback = stripParenExpression(alternate.right);
75079
- const attributeCall = stripParenExpression(alternate.left);
75080
- 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;
75081
- const attributeName = attributeCall.arguments[0] ? stripParenExpression(attributeCall.arguments[0]) : null;
75082
- return attributeName && isNodeOfType(attributeName, "Literal") && attributeName.value === "class" ? rootIdentifier : null;
75083
- };
75084
73929
  const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
75085
73930
  const resultIdentifier = getRootIdentifier(matchReceiver);
75086
73931
  const resultPath = receiverPathKey(matchReceiver);
@@ -75089,17 +73934,11 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
75089
73934
  if (!isDirectFinderMatchReturn(assertion) && !isOptionalResultPath) return false;
75090
73935
  const resultSymbol = context.scopes.symbolFor(resultIdentifier);
75091
73936
  const initializer = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
75092
- if (resultSymbol?.kind !== "const" || !initializer) return false;
75093
- const finderCall = isNodeOfType(initializer, "CallExpression") ? initializer : isNodeOfType(initializer, "ConditionalExpression") ? (() => {
75094
- const alternate = stripParenExpression(initializer.alternate);
75095
- const consequent = stripParenExpression(initializer.consequent);
75096
- return (isNodeOfType(alternate, "Literal") && alternate.value === null || isNodeOfType(alternate, "Identifier") && alternate.name === "undefined" && context.scopes.isGlobalReference(alternate)) && isNodeOfType(consequent, "CallExpression") ? consequent : null;
75097
- })() : null;
75098
- if (!finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
73937
+ if (resultSymbol?.kind !== "const" || !initializer || !isNodeOfType(initializer, "CallExpression")) return false;
75099
73938
  if (!isOptionalResultPath && !isImmediatelyGuardedFinderResult(assertion, resultSymbol, resultPath, context)) return false;
75100
- const finderCallee = stripParenExpression(finderCall.callee);
73939
+ const finderCallee = stripParenExpression(initializer.callee);
75101
73940
  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;
75102
- const predicateArgument = finderCall.arguments[1];
73941
+ const predicateArgument = initializer.arguments[1];
75103
73942
  if (!predicateArgument) return false;
75104
73943
  const predicateFunction = resolveExactLocalFunction(predicateArgument, context.scopes);
75105
73944
  if (!predicateFunction || !isFunctionLike$1(predicateFunction)) return false;
@@ -75123,43 +73962,6 @@ const isMatchProvenByFindUpUntilPredicate = (assertion, matchReceiver, assertedP
75123
73962
  });
75124
73963
  return didProveMatch;
75125
73964
  };
75126
- const isMatchProvenByNormalizedFindUpUntilPredicate = (assertion, matchReceiver, assertedPattern, context) => {
75127
- const normalizedReceiver = stripParenExpression(matchReceiver);
75128
- if (!isNodeOfType(normalizedReceiver, "Identifier")) return false;
75129
- const normalizedReceiverSymbol = context.scopes.symbolFor(normalizedReceiver);
75130
- const normalizedReceiverInitializer = normalizedReceiverSymbol?.initializer ? stripParenExpression(normalizedReceiverSymbol.initializer) : null;
75131
- const resultIdentifier = normalizedReceiverInitializer ? getNormalizedClassNameRoot(normalizedReceiverInitializer) : null;
75132
- if (normalizedReceiverSymbol?.kind !== "const" || normalizedReceiverSymbol.references.some((reference) => reference.flag !== "read") || !resultIdentifier) return false;
75133
- const resultSymbol = context.scopes.symbolFor(resultIdentifier);
75134
- const finderCall = resultSymbol?.initializer ? stripParenExpression(resultSymbol.initializer) : null;
75135
- if (resultSymbol?.kind !== "const" || resultSymbol.references.some((reference) => reference.flag !== "read") || !finderCall || !isNodeOfType(finderCall, "CallExpression")) return false;
75136
- const finderCallee = stripParenExpression(finderCall.callee);
75137
- if (!isNodeOfType(finderCallee, "Identifier") || context.scopes.symbolFor(finderCallee)?.kind !== "import" || getImportedNameFromModule(assertion, finderCallee.name, CLOUDSCAPE_DOM_MODULE) !== "findUpUntil") return false;
75138
- if (!isPresenceProvenBeforeNode(assertion, (test) => {
75139
- const expression = stripParenExpression(test);
75140
- return isNodeOfType(expression, "Identifier") && context.scopes.symbolFor(expression)?.id === resultSymbol.id;
75141
- })) return false;
75142
- const predicateArgument = finderCall.arguments[1];
75143
- const predicateFunction = predicateArgument ? resolveExactLocalFunction(predicateArgument, context.scopes) : null;
75144
- if (!predicateFunction || !isFunctionLike$1(predicateFunction) || predicateFunction.async || predicateFunction.generator) return false;
75145
- const predicateParameter = predicateFunction.params[0];
75146
- if (!isNodeOfType(predicateParameter, "Identifier")) return false;
75147
- let didProveNormalizedMatch = false;
75148
- walkAst(predicateFunction.body, (child) => {
75149
- if (didProveNormalizedMatch || isFunctionLike$1(child)) return false;
75150
- 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;
75151
- const predicateReceiver = stripParenExpression(child.callee.object);
75152
- if (!isNodeOfType(predicateReceiver, "Identifier")) return;
75153
- const predicateReceiverSymbol = context.scopes.symbolFor(predicateReceiver);
75154
- const predicateReceiverInitializer = predicateReceiverSymbol?.initializer ? stripParenExpression(predicateReceiverSymbol.initializer) : null;
75155
- const predicateRoot = predicateReceiverInitializer ? getNormalizedClassNameRoot(predicateReceiverInitializer) : null;
75156
- if (predicateReceiverSymbol?.kind === "const" && predicateReceiverSymbol.references.every((reference) => reference.flag === "read") && predicateRoot?.name === predicateParameter.name) {
75157
- didProveNormalizedMatch = true;
75158
- return false;
75159
- }
75160
- });
75161
- return didProveNormalizedMatch;
75162
- };
75163
73965
  const scopeProvesFindMatch = (assertion, findReceiver, findPredicate, context) => {
75164
73966
  if (!isStablePredicate(findPredicate, context)) return false;
75165
73967
  return isPresenceProvenBeforeNode(assertion, (test) => testPositivelyContainsCall(test, (call) => {
@@ -75260,123 +74062,6 @@ const isEnsureThenFind = (assertion, findReceiver, findPredicate) => {
75260
74062
  }
75261
74063
  return false;
75262
74064
  };
75263
- const getReceiverRootIdentifier = (node) => {
75264
- let target = stripParenExpression(node);
75265
- while (isNodeOfType(target, "MemberExpression")) target = stripParenExpression(target.object);
75266
- return isNodeOfType(target, "Identifier") ? target : null;
75267
- };
75268
- const getReceiverStatePath = (node) => {
75269
- const target = stripParenExpression(node);
75270
- if (isNodeOfType(target, "Identifier")) return target.name;
75271
- if (!isNodeOfType(target, "MemberExpression")) return null;
75272
- const objectPath = getReceiverStatePath(target.object);
75273
- if (!objectPath) return null;
75274
- return `${objectPath}.${getStaticPropertyName(target) ?? "*"}`;
75275
- };
75276
- const doesReceiverStateChangeBeforeAssertion = (ownerFunction, receiver, startOffset, assertion, context) => {
75277
- const receiverRoot = getReceiverRootIdentifier(receiver);
75278
- const receiverSymbol = receiverRoot ? context.scopes.symbolFor(receiverRoot) : null;
75279
- const receiverPath = getReceiverStatePath(receiver);
75280
- if (!receiverRoot || !receiverSymbol || !receiverPath) return true;
75281
- const receiverAliasPaths = new Map([[receiverSymbol.id, receiverRoot.name]]);
75282
- let didAddAlias = true;
75283
- while (didAddAlias) {
75284
- didAddAlias = false;
75285
- walkAst(ownerFunction, (child) => {
75286
- if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75287
- if (!isNodeOfType(child, "VariableDeclarator") || !isNodeOfType(child.id, "Identifier") || !child.init) return;
75288
- const initializer = stripParenExpression(child.init);
75289
- if (!isNodeOfType(initializer, "Identifier") && !isNodeOfType(initializer, "MemberExpression")) return;
75290
- const initializerRoot = getReceiverRootIdentifier(initializer);
75291
- const initializerSymbol = initializerRoot ? context.scopes.symbolFor(initializerRoot) : null;
75292
- const initializerBasePath = initializerSymbol ? receiverAliasPaths.get(initializerSymbol.id) : null;
75293
- const initializerPath = getReceiverStatePath(initializer);
75294
- if (!initializerRoot || !initializerBasePath || !initializerPath) return;
75295
- const aliasSymbol = context.scopes.symbolFor(child.id);
75296
- if (aliasSymbol && !receiverAliasPaths.has(aliasSymbol.id)) {
75297
- const initializerSuffix = initializerPath.slice(initializerRoot.name.length);
75298
- receiverAliasPaths.set(aliasSymbol.id, `${initializerBasePath}${initializerSuffix}`);
75299
- didAddAlias = true;
75300
- }
75301
- });
75302
- }
75303
- let didChangeReceiverState = false;
75304
- walkAst(ownerFunction, (child) => {
75305
- if (didChangeReceiverState) return false;
75306
- if (child !== ownerFunction && isFunctionLike$1(child)) return false;
75307
- if (child.range[0] <= startOffset || child.range[0] >= assertion.range[0]) return;
75308
- if (isNodeOfType(child, "CallExpression")) {
75309
- didChangeReceiverState = true;
75310
- return false;
75311
- }
75312
- const mutationTarget = isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete" ? stripParenExpression(isNodeOfType(child, "AssignmentExpression") ? child.left : child.argument) : null;
75313
- const mutationRoot = mutationTarget ? getReceiverRootIdentifier(mutationTarget) : null;
75314
- const mutationSymbol = mutationRoot ? context.scopes.symbolFor(mutationRoot) : null;
75315
- const mutationBasePath = mutationSymbol ? receiverAliasPaths.get(mutationSymbol.id) : null;
75316
- const mutationPath = mutationTarget ? getReceiverStatePath(mutationTarget) : null;
75317
- if (mutationRoot && mutationBasePath && mutationPath) {
75318
- const canonicalMutationPath = `${mutationBasePath}${mutationPath.slice(mutationRoot.name.length)}`;
75319
- if (canonicalMutationPath !== receiverPath && !canonicalMutationPath.startsWith(`${receiverPath}.`) && !receiverPath.startsWith(`${canonicalMutationPath}.`)) return;
75320
- didChangeReceiverState = true;
75321
- return false;
75322
- }
75323
- });
75324
- return didChangeReceiverState;
75325
- };
75326
- const isFindProvenByGuardedMaximum = (assertion, findReceiver, findPredicate, context) => {
75327
- const findLookup = findEqualityLookupParts(findPredicate);
75328
- const maximumIdentifier = findLookup ? stripParenExpression(findLookup.comparedValue) : null;
75329
- if (!findLookup || !maximumIdentifier || !isNodeOfType(maximumIdentifier, "Identifier")) return false;
75330
- const maximumSymbol = context.scopes.symbolFor(maximumIdentifier);
75331
- const maximumInitializer = maximumSymbol?.initializer ? stripParenExpression(maximumSymbol.initializer) : null;
75332
- 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;
75333
- const filterCall = stripParenExpression(maximumInitializer.callee.object);
75334
- if (!isNodeOfType(filterCall, "CallExpression") || !isNodeOfType(filterCall.callee, "MemberExpression") || getStaticPropertyName(filterCall.callee) !== "filter" || !areNodesLooselyEqual(filterCall.callee.object, findReceiver)) return false;
75335
- const filterPredicate = filterCall.arguments[0] ? stripParenExpression(filterCall.arguments[0]) : null;
75336
- if (!filterPredicate || !isStablePredicate(filterPredicate, context)) return false;
75337
- const reducerArgument = maximumInitializer.arguments[0] ? stripParenExpression(maximumInitializer.arguments[0]) : null;
75338
- const reducerFunction = reducerArgument ? resolveExactLocalFunction(reducerArgument, context.scopes) : null;
75339
- const initialValue = maximumInitializer.arguments[1] ? stripParenExpression(maximumInitializer.arguments[1]) : null;
75340
- if (!reducerFunction || !isFunctionLike$1(reducerFunction) || reducerFunction.async || reducerFunction.generator || !initialValue || !isNodeOfType(initialValue, "Literal") || typeof initialValue.value !== "number") return false;
75341
- const accumulatorParameter = reducerFunction.params[0];
75342
- const itemParameter = reducerFunction.params[1];
75343
- const reducerBody = singleExpressionPredicateBody(reducerFunction);
75344
- if (!isNodeOfType(accumulatorParameter, "Identifier") || !isNodeOfType(itemParameter, "Identifier") || !reducerBody || !isNodeOfType(reducerBody, "CallExpression") || !isNodeOfType(reducerBody.callee, "MemberExpression") || getStaticPropertyName(reducerBody.callee) !== "max") return false;
75345
- const mathReceiver = stripParenExpression(reducerBody.callee.object);
75346
- if (!isNodeOfType(mathReceiver, "Identifier") || mathReceiver.name !== "Math" || !context.scopes.isGlobalReference(mathReceiver) || reducerBody.arguments.length !== 2) return false;
75347
- const accumulatorArgument = reducerBody.arguments.find((argument) => {
75348
- const expression = stripParenExpression(argument);
75349
- return isNodeOfType(expression, "Identifier") && expression.name === accumulatorParameter.name;
75350
- });
75351
- const itemMemberArgument = reducerBody.arguments.find((argument) => {
75352
- const expression = stripParenExpression(argument);
75353
- const rootIdentifier = getRootIdentifier(expression);
75354
- return isNodeOfType(expression, "MemberExpression") && rootIdentifier?.name === itemParameter.name && receiverPathKey(expression)?.slice(itemParameter.name.length + 1) === findLookup.propertyName;
75355
- });
75356
- if (!accumulatorArgument || !itemMemberArgument) return false;
75357
- if (!receiverPathKey(findReceiver)) return false;
75358
- let ownerFunction = assertion.parent ?? null;
75359
- while (ownerFunction && !isFunctionLike$1(ownerFunction)) ownerFunction = ownerFunction.parent ?? null;
75360
- if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
75361
- const maximumEnd = maximumInitializer.range[1];
75362
- if (doesReceiverStateChangeBeforeAssertion(ownerFunction.body, findReceiver, maximumEnd, assertion, context)) return false;
75363
- return isPresenceProvenBeforeNode(assertion, (test) => {
75364
- const comparison = stripParenExpression(test);
75365
- if (!isNodeOfType(comparison, "BinaryExpression")) return false;
75366
- return [[
75367
- comparison.left,
75368
- comparison.right,
75369
- comparison.operator
75370
- ], [
75371
- comparison.right,
75372
- comparison.left,
75373
- comparison.operator === "<" ? ">" : comparison.operator === ">" ? "<" : comparison.operator
75374
- ]].some(([candidateMaximum, candidateInitial, operator]) => {
75375
- const candidateMaximumIdentifier = stripParenExpression(candidateMaximum);
75376
- return operator === ">" && isNodeOfType(candidateMaximumIdentifier, "Identifier") && context.scopes.symbolFor(candidateMaximumIdentifier)?.id === maximumSymbol.id && areNodesLooselyEqual(stripParenExpression(candidateInitial), initialValue);
75377
- });
75378
- });
75379
- };
75380
74065
  const isDefinitelyNonNullishMapValue = (value) => {
75381
74066
  if (!value) return false;
75382
74067
  const expression = stripParenExpression(value);
@@ -75400,15 +74085,15 @@ const unwrapFalseBooleanGuard = (test) => {
75400
74085
  };
75401
74086
  const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
75402
74087
  const stableLookupKey = stripParenExpression(lookupKey);
75403
- const lookupKeyRoot = isNodeOfType(stableLookupKey, "MemberExpression") ? getRootIdentifier(stableLookupKey) : null;
75404
- const lookupKeySymbol = isNodeOfType(stableLookupKey, "Identifier") ? context.scopes.symbolFor(stableLookupKey) : lookupKeyRoot ? context.scopes.symbolFor(lookupKeyRoot) : null;
75405
- if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal") && (!isNodeOfType(stableLookupKey, "MemberExpression") || !lookupKeyRoot || lookupKeySymbol?.kind !== "const")) return false;
74088
+ if (!isNodeOfType(stableLookupKey, "Identifier") && !isNodeOfType(stableLookupKey, "Literal")) return false;
75406
74089
  const receiverSymbol = context.scopes.symbolFor(receiver);
75407
74090
  if (!receiverSymbol) return false;
75408
74091
  const receiverMatches = (candidate) => {
75409
74092
  const target = stripParenExpression(candidate);
75410
74093
  return isNodeOfType(target, "Identifier") && context.scopes.symbolFor(target)?.id === receiverSymbol.id;
75411
74094
  };
74095
+ const lookupKeyExpression = stripParenExpression(lookupKey);
74096
+ const lookupKeySymbol = isNodeOfType(lookupKeyExpression, "Identifier") ? context.scopes.symbolFor(lookupKeyExpression) : null;
75412
74097
  let child = assertion;
75413
74098
  let ancestor = assertion.parent ?? null;
75414
74099
  while (ancestor && !isFunctionLike$1(ancestor)) {
@@ -75428,7 +74113,7 @@ const isEnsureThenMapGet = (assertion, receiver, lookupKey, context) => {
75428
74113
  const populationCall = populationCalls[0];
75429
74114
  if (!populationCall) continue;
75430
74115
  const populationCallStart = populationCall.range[0];
75431
- 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;
74116
+ if (Boolean(lookupKeySymbol?.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0]))) continue;
75432
74117
  if (receiverSymbol.references.some((reference) => reference.flag !== "read" && reference.identifier.range[0] > populationCallStart && reference.identifier.range[0] < assertion.range[0])) continue;
75433
74118
  if (!indexedRelevantCalls(ancestor).some((laterCall) => {
75434
74119
  if (laterCall.range[0] <= populationCallStart || laterCall.range[0] >= assertion.range[0] || !isNodeOfType(laterCall.callee, "MemberExpression") || !receiverMatches(laterCall.callee.object)) return false;
@@ -75538,7 +74223,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
75538
74223
  const findReceiver = callee.object;
75539
74224
  if (predicate && isExhaustiveLiteralTupleMapping(findReceiver, predicate, context)) return;
75540
74225
  if (predicate && scopeProvesFindMatch(node, findReceiver, predicate, context)) return;
75541
- if (predicate && isFindProvenByGuardedMaximum(node, findReceiver, predicate, context)) return;
75542
74226
  if (predicate && isEnsureThenFind(node, findReceiver, predicate)) return;
75543
74227
  }
75544
74228
  if (methodName === "match") {
@@ -75548,7 +74232,6 @@ const noNonNullAssertionOnMaybeUndefinedResult = defineRule({
75548
74232
  const regexKey = pattern ? regexComparableKey(pattern, context) : null;
75549
74233
  if (pattern && isGuardedAnchoredCharacterMatch(node, matchReceiver, pattern)) return;
75550
74234
  if (pattern && regexKey && isMatchProvenByFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
75551
- if (pattern && regexKey && isMatchProvenByNormalizedFindUpUntilPredicate(node, matchReceiver, pattern, context)) return;
75552
74235
  if (regexKey && scopeProvesMatchTested(node, regexKey, matchReceiver, context)) return;
75553
74236
  }
75554
74237
  if (methodName === "get") {
@@ -89688,11 +88371,6 @@ const noUndersizedIconButton = defineRule({
89688
88371
  //#region src/plugin/rules/correctness/no-unescaped-dynamic-string-in-regexp.ts
89689
88372
  const TEST_CONTEXT_FILE_PATTERN = /(\.test\.|\.spec\.|__tests__|(^|\/)(test|tests|e2e|cypress|playwright)\/)/;
89690
88373
  const SEARCH_TERM_NAME_PATTERN = /search|query|highlight|filter|term(?!in)|keyword/i;
89691
- const REGEX_SOURCE_WORDS = [
89692
- "pattern",
89693
- "regex",
89694
- "regexp"
89695
- ];
89696
88374
  const ESCAPE_HELPER_NAME_PATTERN = /escape.*reg|safe.*reg/i;
89697
88375
  const SANITIZED_NAME_PATTERN = /escap|sanitiz/i;
89698
88376
  const INITIALIZER_RESOLUTION_HOPS = 2;
@@ -89846,70 +88524,14 @@ const isTypePositionIdentifier = (identifier) => {
89846
88524
  }
89847
88525
  return false;
89848
88526
  };
89849
- const identifierNameHasPathSegmentSemantics = (identifierName) => {
89850
- const identifierWords = identifierName.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[\s_]+/).map((word) => word.toLowerCase());
89851
- if (identifierWords.some((word) => REGEX_SOURCE_WORDS.includes(word))) return false;
89852
- return identifierWords.some((word) => [
89853
- "path",
89854
- "folder",
89855
- "directory",
89856
- "root"
89857
- ].includes(word)) || identifierWords.includes("top") && identifierWords.includes("level");
89858
- };
89859
- const flattenPatternParts = (expression) => {
89860
- const inner = stripParenExpression(expression);
89861
- const staticValue = literalStringValue(inner);
89862
- if (staticValue !== null) return [staticValue];
89863
- if (isNodeOfType(inner, "Identifier")) return [inner];
89864
- if (isNodeOfType(inner, "TemplateLiteral")) {
89865
- const parts = [];
89866
- for (let index = 0; index < inner.quasis.length; index += 1) {
89867
- const quasi = inner.quasis[index];
89868
- if (quasi) parts.push(quasi.value.cooked ?? quasi.value.raw);
89869
- const templateExpression = inner.expressions[index];
89870
- if (templateExpression) parts.push(stripParenExpression(templateExpression));
89871
- }
89872
- return parts;
89873
- }
89874
- if (isNodeOfType(inner, "BinaryExpression") && inner.operator === "+") {
89875
- const leftParts = flattenPatternParts(inner.left);
89876
- const rightParts = flattenPatternParts(inner.right);
89877
- return leftParts && rightParts ? [...leftParts, ...rightParts] : null;
89878
- }
89879
- if (isNodeOfType(inner, "CallExpression") && isNodeOfType(inner.callee, "MemberExpression") && getStaticPropertyName(inner.callee) === "concat") {
89880
- const receiverParts = flattenPatternParts(inner.callee.object);
89881
- if (!receiverParts) return null;
89882
- const parts = [...receiverParts];
89883
- for (const argument of inner.arguments) {
89884
- if (isNodeOfType(argument, "SpreadElement")) return null;
89885
- const argumentParts = flattenPatternParts(argument);
89886
- if (!argumentParts) return null;
89887
- parts.push(...argumentParts);
89888
- }
89889
- return parts;
89890
- }
89891
- return null;
89892
- };
89893
- const isIdentifierAnAnchoredPathSegment = (argument, identifier) => {
89894
- if (!identifierNameHasPathSegmentSemantics(identifier.name)) return false;
89895
- const parts = flattenPatternParts(argument);
89896
- if (!parts) return false;
89897
- const identifierPartIndex = parts.findIndex((part) => part === identifier);
89898
- if (identifierPartIndex < 0) return false;
89899
- const precedingParts = parts.slice(0, identifierPartIndex);
89900
- if (precedingParts.some((part) => typeof part !== "string")) return false;
89901
- const staticPrefix = precedingParts.join("");
89902
- const followingPart = parts[identifierPartIndex + 1];
89903
- return staticPrefix === "^" && typeof followingPart === "string" && followingPart.startsWith("/");
89904
- };
89905
- const collectRawDynamicLiteralIdentifiers = (argument) => {
89906
- const rawDynamicLiteralIdentifiers = [];
88527
+ const collectRawSearchTermIdentifiers = (argument) => {
88528
+ const rawSearchTermIdentifiers = [];
89907
88529
  walkAst(argument, (child) => {
89908
88530
  if (isEscapingCall(child) || isRegexSourceAccess(child)) return false;
89909
88531
  if (isLiteralReturningGetterCall(child)) return false;
89910
- if (isNodeOfType(child, "Identifier") && (SEARCH_TERM_NAME_PATTERN.test(child.name) || isIdentifierAnAnchoredPathSegment(argument, child)) && !isPropertyNamePosition(child) && !isTypePositionIdentifier(child)) rawDynamicLiteralIdentifiers.push(child);
88532
+ if (isNodeOfType(child, "Identifier") && SEARCH_TERM_NAME_PATTERN.test(child.name) && !isPropertyNamePosition(child) && !isTypePositionIdentifier(child)) rawSearchTermIdentifiers.push(child);
89911
88533
  });
89912
- return rawDynamicLiteralIdentifiers;
88534
+ return rawSearchTermIdentifiers;
89913
88535
  };
89914
88536
  const collectLeafIdentifiers = (node) => {
89915
88537
  const leafIdentifiers = [];
@@ -89920,11 +88542,8 @@ const collectLeafIdentifiers = (node) => {
89920
88542
  return leafIdentifiers;
89921
88543
  };
89922
88544
  const compositeInitializerResolvesEscaped = (strippedInitializer, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames) => {
89923
- if (isNodeOfType(strippedInitializer, "ConditionalExpression")) return [strippedInitializer.consequent, strippedInitializer.alternate].every((branch) => initializerLooksEscaped(branch, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
89924
- if (isNodeOfType(strippedInitializer, "BinaryExpression") || isNodeOfType(strippedInitializer, "LogicalExpression")) return [strippedInitializer.left, strippedInitializer.right].every((operand) => initializerLooksEscaped(operand, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
89925
- if (isNodeOfType(strippedInitializer, "TemplateLiteral")) return strippedInitializer.expressions.every((expression) => initializerLooksEscaped(expression, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames));
89926
88545
  let didResolveAnyLeafEscaped = false;
89927
- for (const leafIdentifier of collectLeafIdentifiers(strippedInitializer)) if (identifierResolvesToEscapedValue(leafIdentifier, remainingHops - 1, scopes, regexpObjectSymbolIds, globalRegExpObjectNames)) didResolveAnyLeafEscaped = true;
88546
+ for (const leafIdentifier of collectLeafIdentifiers(strippedInitializer)) if (identifierResolvesToEscapedValue(leafIdentifier, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames)) didResolveAnyLeafEscaped = true;
89928
88547
  else if (SEARCH_TERM_NAME_PATTERN.test(leafIdentifier.name)) return false;
89929
88548
  return didResolveAnyLeafEscaped;
89930
88549
  };
@@ -89936,7 +88555,7 @@ const initializerLooksEscaped = (initializer, remainingHops, scopes, regexpObjec
89936
88555
  if (isNodeOfType(strippedInitializer, "CallExpression") && (isEscapingCall(strippedInitializer) || calleeBindingBodyEscapes(strippedInitializer))) return true;
89937
88556
  if (remainingHops > 0) {
89938
88557
  if (isNodeOfType(strippedInitializer, "Identifier")) return identifierResolvesToEscapedValue(strippedInitializer, remainingHops - 1, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
89939
- return compositeInitializerResolvesEscaped(strippedInitializer, remainingHops, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
88558
+ return compositeInitializerResolvesEscaped(strippedInitializer, remainingHops - 1, scopes, regexpObjectSymbolIds, globalRegExpObjectNames);
89940
88559
  }
89941
88560
  return false;
89942
88561
  };
@@ -90128,7 +88747,7 @@ const noUnescapedDynamicStringInRegexp = defineRule({
90128
88747
  severity: "warn",
90129
88748
  category: "Correctness",
90130
88749
  tags: ["test-noise"],
90131
- recommendation: "A dynamic literal string such as a search term or path segment dropped straight into `new RegExp(...)` lets its regex metacharacters act as operators, so values containing `.` or `(` over-match or throw. Escape the value with an `escapeRegExp` helper before constructing the pattern.",
88750
+ recommendation: "A search/filter/highlight term dropped straight into `new RegExp(...)` lets its regex metacharacters act as operators, so a user typing `.` or `(` over-matches or throws. Escape the value with an `escapeRegExp` helper before constructing the pattern.",
90132
88751
  create: (context) => {
90133
88752
  if (TEST_CONTEXT_FILE_PATTERN.test(context.filename ?? "")) return {};
90134
88753
  let regexpObjectIndex = null;
@@ -90145,10 +88764,10 @@ const noUnescapedDynamicStringInRegexp = defineRule({
90145
88764
  regexpObjectIndex = buildRegExpObjectIndex(programRoot, context.scopes);
90146
88765
  }
90147
88766
  const currentRegExpObjectIndex = regexpObjectIndex;
90148
- if (!collectRawDynamicLiteralIdentifiers(firstArgument).some((identifier) => !identifierResolvesToEscapedValue(identifier, INITIALIZER_RESOLUTION_HOPS, context.scopes, currentRegExpObjectIndex.regexpObjectSymbolIds, currentRegExpObjectIndex.globalRegExpObjectNames) && !isShapeTestedByDominatingGuard(node, identifier, context.scopes) && !isParameterFedOnlyMetacharacterFreeLiterals(identifier, context.scopes))) return;
88767
+ if (!collectRawSearchTermIdentifiers(firstArgument).some((identifier) => !identifierResolvesToEscapedValue(identifier, INITIALIZER_RESOLUTION_HOPS, context.scopes, currentRegExpObjectIndex.regexpObjectSymbolIds, currentRegExpObjectIndex.globalRegExpObjectNames) && !isShapeTestedByDominatingGuard(node, identifier, context.scopes) && !isParameterFedOnlyMetacharacterFreeLiterals(identifier, context.scopes))) return;
90149
88768
  context.report({
90150
88769
  node,
90151
- message: "This builds a `RegExp` from a dynamic literal string without escaping it, so regex metacharacters in the value act as operators and over-match or throw. Escape the value with an `escapeRegExp` helper first."
88770
+ message: "This builds a `RegExp` from a dynamic search/filter term without escaping it, so regex metacharacters in the value act as operators and over-match or throw. Escape the value with an `escapeRegExp` helper first."
90152
88771
  });
90153
88772
  };
90154
88773
  return {
@@ -109890,6 +108509,9 @@ const rerenderFunctionalSetstate = defineRule({
109890
108509
  } })
109891
108510
  });
109892
108511
  //#endregion
108512
+ //#region src/plugin/utils/is-trivial-built-in-construction.ts
108513
+ const isTrivialBuiltInConstruction = (expression) => isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && TRIVIAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && (expression.arguments ?? []).length === 0;
108514
+ //#endregion
109893
108515
  //#region src/plugin/rules/state-and-effects/rerender-lazy-ref-init.ts
109894
108516
  const rerenderLazyRefInit = defineRule({
109895
108517
  id: "rerender-lazy-ref-init",
@@ -109908,6 +108530,7 @@ const rerenderLazyRefInit = defineRule({
109908
108530
  const memberPropertyName = isNodeOfType(callee, "MemberExpression") && (isNodeOfType(callee.property, "Identifier") || isNodeOfType(callee.property, "PrivateIdentifier")) ? callee.property.name : null;
109909
108531
  const calleeName = isNodeOfType(callee, "Identifier") ? callee.name : memberPropertyName ?? "fn";
109910
108532
  if (TRIVIAL_INITIALIZER_NAMES.has(calleeName)) return;
108533
+ if (isTrivialBuiltInConstruction(initializer)) return;
109911
108534
  if (isPlainCall && isReactHookName(calleeName)) return;
109912
108535
  const callShape = isNewCall ? `new ${calleeName}()` : `${calleeName}()`;
109913
108536
  context.report({