oxlint-plugin-react-doctor 0.8.1-dev.fe241a9 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -762,7 +762,7 @@ const EXTERNAL_SYNC_MEMBER_METHOD_NAMES = new Set([
762
762
  "put",
763
763
  "patch"
764
764
  ]);
765
- new Set([
765
+ const EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS = new Set([
766
766
  ...FETCH_MEMBER_OBJECTS,
767
767
  "api",
768
768
  "client",
@@ -9008,16 +9008,6 @@ const hasVisibleBindingNamed = (node, bindingName, scopes) => {
9008
9008
  }
9009
9009
  };
9010
9010
  //#endregion
9011
- //#region src/plugin/utils/is-global-match-media-call.ts
9012
- const isGlobalMatchMediaCall = (node, scopes) => {
9013
- if (!isNodeOfType(node, "CallExpression")) return false;
9014
- const callee = stripParenExpression(node.callee);
9015
- if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
9016
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
9017
- const receiver = stripParenExpression(callee.object);
9018
- return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
9019
- };
9020
- //#endregion
9021
9011
  //#region src/plugin/utils/is-proven-browser-api-receiver.ts
9022
9012
  const DOM_EVENT_TARGET_TYPE_NAMES = new Set([
9023
9013
  "AbortSignal",
@@ -9164,10 +9154,8 @@ const getProvenDomEventTargetPrototypeOwnerNames = (rawExpression, scopes, visit
9164
9154
  if (memberName === "body" || memberName === "documentElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9165
9155
  }
9166
9156
  if (isNodeOfType(expression, "CallExpression")) {
9167
- if (isGlobalMatchMediaCall(expression, scopes)) return getDomPrototypeOwnerNamesForType("MediaQueryList");
9168
9157
  const callee = stripParenExpression(expression.callee);
9169
9158
  const methodName = isNodeOfType(callee, "MemberExpression") ? getStaticPropertyName(callee) : null;
9170
- if (methodName === "matchMedia" && isNodeOfType(callee, "MemberExpression") && getProvenDomEventTargetPrototypeOwnerNames(callee.object, scopes, visitedSymbolIds).includes("Window")) return getDomPrototypeOwnerNamesForType("MediaQueryList");
9171
9159
  if (methodName === "createElement") return getDomPrototypeOwnerNamesForType("HTMLElement");
9172
9160
  if (methodName === "createElementNS") return getDomPrototypeOwnerNamesForType("SVGElement");
9173
9161
  if (methodName && DOM_EVENT_TARGET_FACTORY_METHOD_NAMES.has(methodName)) return getDomPrototypeOwnerNamesForType("Element");
@@ -13864,19 +13852,14 @@ const isAstDescendant = (inner, outer) => {
13864
13852
  };
13865
13853
  //#endregion
13866
13854
  //#region src/plugin/utils/react-ref-origin.ts
13867
- const REACT_REF_API_NAMES = new Set(["createRef", "useRef"]);
13868
- const resolveReactRefSymbol = (memberExpression, scopes, options = {}) => {
13855
+ const resolveReactRefSymbol = (memberExpression, scopes) => {
13869
13856
  const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
13870
13857
  if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
13871
13858
  const symbol = resolveConstIdentifierAlias(receiver, scopes);
13872
13859
  if (!symbol?.initializer) return null;
13873
13860
  const initializer = stripParenExpression(symbol.initializer);
13874
13861
  if (!isNodeOfType(initializer, "CallExpression")) return null;
13875
- return isReactApiCall(initializer, options.includeCreateRef ? REACT_REF_API_NAMES : "useRef", scopes, {
13876
- allowGlobalReactNamespace: true,
13877
- allowUnboundBareCalls: options.allowUnboundBareCalls,
13878
- resolveNamedAliases: options.resolveNamedAliases
13879
- }) ? symbol : null;
13862
+ return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
13880
13863
  };
13881
13864
  const resolveReactRefCurrentOriginSymbol = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
13882
13865
  const expression = stripParenExpression(node);
@@ -13978,18 +13961,6 @@ const isNodeReachableWithinFunction = (node, context) => {
13978
13961
  //#region src/plugin/rules/state-and-effects/effect-needs-cleanup.ts
13979
13962
  const CLEANUP_EFFECT_HOOK_NAMES = new Set([...EFFECT_HOOK_NAMES$1, "useInsertionEffect"]);
13980
13963
  const REPLAYABLE_ITERATOR_COLLECTION_CACHE = /* @__PURE__ */ new WeakMap();
13981
- const REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES = new Set([
13982
- "pop",
13983
- "shift",
13984
- "splice",
13985
- "fill",
13986
- "copyWithin"
13987
- ]);
13988
- const REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES = new Set([
13989
- "clear",
13990
- "delete",
13991
- "set"
13992
- ]);
13993
13964
  const REACT_REF_EFFECT_ANALYSIS_CACHE = /* @__PURE__ */ new WeakMap();
13994
13965
  const RESOURCE_NOUN_BY_KIND = {
13995
13966
  subscribe: "subscription",
@@ -14030,92 +14001,6 @@ const resolveExpressionKey = (expression, context, visitedSymbolIds = /* @__PURE
14030
14001
  }
14031
14002
  return null;
14032
14003
  };
14033
- const resolveForEachProjection = (expression, context) => {
14034
- if (!expression) return null;
14035
- let currentExpression = stripParenExpression(expression);
14036
- const memberNames = [];
14037
- while (isNodeOfType(currentExpression, "MemberExpression") && !currentExpression.computed) {
14038
- if (!isNodeOfType(currentExpression.property, "Identifier")) return null;
14039
- memberNames.unshift(currentExpression.property.name);
14040
- currentExpression = stripParenExpression(currentExpression.object);
14041
- }
14042
- if (!isNodeOfType(currentExpression, "Identifier")) return null;
14043
- const symbol = context.scopes.symbolFor(currentExpression);
14044
- if (!symbol || symbol.kind !== "parameter") return null;
14045
- let callbackNode = symbol.bindingIdentifier.parent;
14046
- while (callbackNode && !isFunctionLike$1(callbackNode)) callbackNode = callbackNode.parent;
14047
- if (!callbackNode || !isFunctionLike$1(callbackNode)) return null;
14048
- const forEachCall = findEnclosingForEachCall(callbackNode);
14049
- if (!forEachCall) return null;
14050
- const forEachCallee = stripParenExpression(forEachCall.callee);
14051
- if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
14052
- const collectionKey = resolveExpressionKey(forEachCallee.object, context);
14053
- if (!collectionKey) return null;
14054
- const firstParameter = callbackNode.params[0];
14055
- const bindingProperty = symbol.bindingIdentifier.parent;
14056
- const propertyName = isNodeOfType(firstParameter, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.parent === firstParameter && bindingProperty.value === symbol.bindingIdentifier ? getStaticPropertyKeyName(bindingProperty) : null;
14057
- const parameterProjection = firstParameter === symbol.bindingIdentifier ? "value" : propertyName;
14058
- if (!parameterProjection) return null;
14059
- return {
14060
- collectionKey,
14061
- projectionKey: [parameterProjection, ...memberNames].join(".")
14062
- };
14063
- };
14064
- const resolveForEachProjectionKey = (expression, context) => {
14065
- const projection = resolveForEachProjection(expression, context);
14066
- return projection ? `forEach:${projection.collectionKey}:${projection.projectionKey}` : null;
14067
- };
14068
- const resolveResourceIdentityKey = (expression, context) => resolveForEachProjectionKey(expression, context) ?? resolveExpressionKey(expression, context);
14069
- const resolveEventListenerCaptureIdentityKey = (optionsNode, context, allowOpaqueOptionsIdentity) => {
14070
- const capture = resolveEventListenerCapture(optionsNode, { allowIndeterminateEntries: true });
14071
- if (capture !== null) return `capture:${String(capture)}`;
14072
- if (!optionsNode) return null;
14073
- const unwrappedOptions = stripParenExpression(optionsNode);
14074
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) {
14075
- const optionsKey = allowOpaqueOptionsIdentity ? resolveResourceIdentityKey(unwrappedOptions, context) : null;
14076
- return optionsKey ? `options:${optionsKey}` : null;
14077
- }
14078
- let captureKey = "capture:false";
14079
- for (const property of unwrappedOptions.properties ?? []) {
14080
- if (!isNodeOfType(property, "Property")) {
14081
- captureKey = null;
14082
- continue;
14083
- }
14084
- const propertyName = getStaticPropertyKeyName(property);
14085
- if (propertyName === null || !property.computed && propertyName === "__proto__") {
14086
- captureKey = null;
14087
- continue;
14088
- }
14089
- if (propertyName === "capture") {
14090
- const propertyValueKey = resolveResourceIdentityKey(property.value, context);
14091
- captureKey = propertyValueKey ? `capture-value:${propertyValueKey}` : null;
14092
- }
14093
- }
14094
- return captureKey;
14095
- };
14096
- const resolveEventListenerCaptureProjection = (optionsNode, context) => {
14097
- if (!optionsNode) return null;
14098
- const unwrappedOptions = stripParenExpression(optionsNode);
14099
- if (!isNodeOfType(unwrappedOptions, "ObjectExpression")) return resolveForEachProjection(unwrappedOptions, context);
14100
- let captureProjection = null;
14101
- for (const property of unwrappedOptions.properties ?? []) {
14102
- if (!isNodeOfType(property, "Property")) {
14103
- captureProjection = null;
14104
- continue;
14105
- }
14106
- const propertyName = getStaticPropertyKeyName(property);
14107
- if (propertyName === null || !property.computed && propertyName === "__proto__") {
14108
- captureProjection = null;
14109
- continue;
14110
- }
14111
- if (propertyName === "capture") captureProjection = resolveForEachProjection(property.value, context);
14112
- }
14113
- return captureProjection;
14114
- };
14115
- const doEventListenerCapturesMatch = (registrationOptions, releaseOptions, context, allowOpaqueOptionsIdentity = false) => {
14116
- const registrationCaptureKey = resolveEventListenerCaptureIdentityKey(registrationOptions, context, allowOpaqueOptionsIdentity);
14117
- return registrationCaptureKey !== null && registrationCaptureKey === resolveEventListenerCaptureIdentityKey(releaseOptions, context, allowOpaqueOptionsIdentity);
14118
- };
14119
14004
  const findAssignedResourceKey = (resourceNode, context) => {
14120
14005
  let currentNode = resourceNode;
14121
14006
  let parentNode = currentNode.parent;
@@ -14127,30 +14012,6 @@ const findAssignedResourceKey = (resourceNode, context) => {
14127
14012
  if (isNodeOfType(parentNode, "AssignmentExpression") && parentNode.right === currentNode) return resolveExpressionKey(parentNode.left, context);
14128
14013
  return null;
14129
14014
  };
14130
- const resolveStableMediaQueryListenerIdentityKey = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
14131
- if (!expression) return null;
14132
- const unwrappedExpression = stripParenExpression(expression);
14133
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
14134
- const symbol = context.scopes.symbolFor(unwrappedExpression);
14135
- if (!symbol || visitedSymbolIds.has(symbol.id) || !symbol.references.every((reference) => reference.flag === "read" && !isWithinAssignmentTarget(reference.identifier))) return null;
14136
- const initializer = getDirectUnreassignedInitializer(symbol);
14137
- if (!initializer) return `symbol:${symbol.id}`;
14138
- const unwrappedInitializer = stripParenExpression(initializer);
14139
- if (!isNodeOfType(unwrappedInitializer, "Identifier")) return `symbol:${symbol.id}`;
14140
- const nextVisitedSymbolIds = new Set(visitedSymbolIds);
14141
- nextVisitedSymbolIds.add(symbol.id);
14142
- return resolveStableMediaQueryListenerIdentityKey(unwrappedInitializer, context, nextVisitedSymbolIds) ?? `symbol:${symbol.id}`;
14143
- }
14144
- if (isFunctionLike$1(unwrappedExpression)) {
14145
- const rangeStart = getRangeStart(unwrappedExpression);
14146
- return rangeStart === null ? null : `function:${rangeStart}`;
14147
- }
14148
- return null;
14149
- };
14150
- const isProvenLegacyMediaQueryListMethodCall = (callNode, methodName, context) => {
14151
- const callee = stripParenExpression(callNode.callee);
14152
- return callNode.arguments?.length === 1 && isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName && getProvenDomEventTargetPrototypeOwnerNames(callee.object, context.scopes).includes("MediaQueryList");
14153
- };
14154
14015
  const getCallRegistrationDetails = (callNode, context) => {
14155
14016
  const callee = stripParenExpression(callNode.callee);
14156
14017
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return {
@@ -14159,17 +14020,11 @@ const getCallRegistrationDetails = (callNode, context) => {
14159
14020
  eventKey: null,
14160
14021
  handlerKey: null
14161
14022
  };
14162
- if (isProvenLegacyMediaQueryListMethodCall(callNode, "addListener", context)) return {
14163
- receiverKey: resolveStableMediaQueryListenerIdentityKey(callee.object, context),
14164
- registrationVerbName: callee.property.name,
14165
- eventKey: null,
14166
- handlerKey: resolveStableMediaQueryListenerIdentityKey(callNode.arguments?.[0], context)
14167
- };
14168
14023
  return {
14169
- receiverKey: resolveResourceIdentityKey(callee.object, context),
14024
+ receiverKey: resolveExpressionKey(callee.object, context),
14170
14025
  registrationVerbName: callee.property.name,
14171
- eventKey: resolveResourceIdentityKey(callNode.arguments?.[0], context),
14172
- handlerKey: resolveResourceIdentityKey(callNode.arguments?.[1], context)
14026
+ eventKey: resolveExpressionKey(callNode.arguments?.[0], context),
14027
+ handlerKey: resolveExpressionKey(callNode.arguments?.[1], context)
14173
14028
  };
14174
14029
  };
14175
14030
  const findSubscribeLikeUsages = (callback, context) => {
@@ -14594,8 +14449,8 @@ const isSynchronousIteratorCallback = (functionNode) => {
14594
14449
  return SYNCHRONOUS_ITERATOR_METHOD_NAMES$2.has(callee.property.name) && callNode.arguments?.[0] === functionNode;
14595
14450
  };
14596
14451
  const findEnclosingForEachCall = (node) => {
14597
- const callbackNode = isFunctionLike$1(node) ? node : findEnclosingFunction$1(node);
14598
- if (!callbackNode || !isFunctionLike$1(callbackNode) || callbackNode.async || callbackNode.generator) return null;
14452
+ const callbackNode = findEnclosingFunction$1(node);
14453
+ if (!callbackNode) return null;
14599
14454
  const callNode = callbackNode.parent;
14600
14455
  if (!isNodeOfType(callNode, "CallExpression") || callNode.arguments?.[0] !== callbackNode) return null;
14601
14456
  const callee = stripParenExpression(callNode.callee);
@@ -15244,7 +15099,9 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15244
15099
  const nodeParameterKey = resolveExpressionKey(usageFunction.params?.[0], context);
15245
15100
  const releaseReceiverKey = resolveExpressionKey(releaseCallee.object, context);
15246
15101
  if (registrationReceiverKey === null || registrationReceiverKey !== nodeParameterKey || releaseReceiverKey === null || usage.eventKey === null || usage.eventKey !== resolveExpressionKey(releaseCall.arguments?.[0], context) || usage.handlerKey === null || usage.handlerKey !== resolveExpressionKey(releaseCall.arguments?.[1], context)) return false;
15247
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], releaseCall.arguments?.[2], context)) return false;
15102
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15103
+ const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15104
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15248
15105
  const releaseStart = getRangeStart(releaseCall);
15249
15106
  const matchingOwnershipAssignments = [];
15250
15107
  const usageFunctionBody = usageFunction.body;
@@ -15256,126 +15113,6 @@ const isReactRefListenerReplacementRelease = (releaseCall, usage, context) => {
15256
15113
  const safeOwnershipAssignments = matchingOwnershipAssignments.filter((assignment) => doMatchingNodesCoverEveryPathBeforeUsage(assignment, [releaseAnchor], usageFunction, context));
15257
15114
  return doMatchingNodesCoverEveryPathFromFunctionEntry(usageFunction, [releaseAnchor], context) && doMatchingNodesCoverEveryPathBeforeUsage(usage.node, safeOwnershipAssignments, usageFunction, context);
15258
15115
  };
15259
- const findDirectExhaustiveForEachCleanupFunction = (releaseNode, requiredCollectionKeys, context) => {
15260
- let currentNode = findTransparentExpressionRoot(releaseNode);
15261
- const visitedFunctions = /* @__PURE__ */ new Set();
15262
- const replayedCollectionKeys = /* @__PURE__ */ new Set();
15263
- while (true) {
15264
- const ownerFunction = findEnclosingFunction$1(currentNode);
15265
- if (!ownerFunction || !isFunctionLike$1(ownerFunction) || visitedFunctions.has(ownerFunction)) return null;
15266
- visitedFunctions.add(ownerFunction);
15267
- const isDirectConciseBody = ownerFunction.body === currentNode;
15268
- const statementNode = currentNode.parent;
15269
- const isDirectBlockStatement = isNodeOfType(ownerFunction.body, "BlockStatement") && isNodeOfType(statementNode, "ExpressionStatement") && statementNode.parent === ownerFunction.body;
15270
- if (!isDirectConciseBody && !isDirectBlockStatement || !doMatchingNodesCoverEveryPathFromFunctionEntry(ownerFunction, [isDirectBlockStatement ? statementNode : currentNode], context)) return null;
15271
- const forEachCall = findEnclosingForEachCall(ownerFunction);
15272
- if (!forEachCall) return replayedCollectionKeys.size === requiredCollectionKeys.size && isReturnedEffectCleanupFunction(ownerFunction, context) ? ownerFunction : null;
15273
- const forEachCallee = stripParenExpression(forEachCall.callee);
15274
- if (!isNodeOfType(forEachCallee, "MemberExpression")) return null;
15275
- const collectionKey = resolveExpressionKey(forEachCallee.object, context);
15276
- if (!collectionKey || !requiredCollectionKeys.has(collectionKey)) return null;
15277
- replayedCollectionKeys.add(collectionKey);
15278
- currentNode = findTransparentExpressionRoot(forEachCall);
15279
- }
15280
- };
15281
- const collectReplayOwnerFunctions = (usageNode) => {
15282
- const ownerFunctions = /* @__PURE__ */ new Set();
15283
- let currentNode = usageNode;
15284
- while (true) {
15285
- const ownerFunction = findEnclosingFunction$1(currentNode);
15286
- if (!ownerFunction || !isFunctionLike$1(ownerFunction) || ownerFunctions.has(ownerFunction)) break;
15287
- ownerFunctions.add(ownerFunction);
15288
- const forEachCall = findEnclosingForEachCall(ownerFunction);
15289
- if (!forEachCall) break;
15290
- currentNode = forEachCall;
15291
- }
15292
- return ownerFunctions;
15293
- };
15294
- const hasCollectionMutationBeforeRelease = (usageNode, releaseNode, collectionKeys, context) => {
15295
- const usageStart = getRangeStart(usageNode);
15296
- const releaseStart = getRangeStart(releaseNode);
15297
- if (usageStart === null || releaseStart === null) return true;
15298
- const setupOwnerFunctions = collectReplayOwnerFunctions(usageNode);
15299
- const cleanupOwnerFunctions = collectReplayOwnerFunctions(releaseNode);
15300
- let programNode = usageNode;
15301
- while (programNode.parent) programNode = programNode.parent;
15302
- let didFindMutation = false;
15303
- walkAst(programNode, (child) => {
15304
- if (didFindMutation) return false;
15305
- const childStart = getRangeStart(child);
15306
- if (childStart === null) return;
15307
- const ownerFunction = context.cfg.enclosingFunction(child);
15308
- if (!ownerFunction) return;
15309
- const isAfterRegistration = setupOwnerFunctions.has(ownerFunction) && childStart > usageStart;
15310
- const isBeforeRelease = cleanupOwnerFunctions.has(ownerFunction) && childStart < releaseStart;
15311
- if (!isAfterRegistration && !isBeforeRelease) return;
15312
- if (isNodeOfType(child, "AssignmentExpression")) {
15313
- const assignmentKey = resolveExpressionKey(child.left, context);
15314
- const assignmentTarget = stripParenExpression(child.left);
15315
- if (assignmentKey && [...collectionKeys].some((collectionKey) => assignmentKey === collectionKey || assignmentKey === `${collectionKey}.length`) || isNodeOfType(assignmentTarget, "MemberExpression") && assignmentTarget.computed && collectionKeys.has(resolveExpressionKey(assignmentTarget.object, context) ?? "")) {
15316
- didFindMutation = true;
15317
- return false;
15318
- }
15319
- return;
15320
- }
15321
- if (isNodeOfType(child, "UnaryExpression") && child.operator === "delete") {
15322
- const deletedMember = stripParenExpression(child.argument);
15323
- if (!isNodeOfType(deletedMember, "MemberExpression")) return;
15324
- if (collectionKeys.has(resolveExpressionKey(deletedMember.object, context) ?? "")) {
15325
- didFindMutation = true;
15326
- return false;
15327
- }
15328
- return;
15329
- }
15330
- if (isNodeOfType(child, "UpdateExpression")) {
15331
- const updatedKey = resolveExpressionKey(child.argument, context);
15332
- if (updatedKey && [...collectionKeys].some((collectionKey) => updatedKey === `${collectionKey}.length`)) {
15333
- didFindMutation = true;
15334
- return false;
15335
- }
15336
- return;
15337
- }
15338
- if (!isNodeOfType(child, "CallExpression")) return;
15339
- const callee = stripParenExpression(child.callee);
15340
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || !REPLAY_ENTRY_DROPPING_ARRAY_METHOD_NAMES.has(callee.property.name) && !REPLAY_ENTRY_DROPPING_COLLECTION_METHOD_NAMES.has(callee.property.name) || !collectionKeys.has(resolveExpressionKey(callee.object, context) ?? "")) return;
15341
- didFindMutation = true;
15342
- return false;
15343
- });
15344
- return didFindMutation;
15345
- };
15346
- const usesUnaryListenerSignature = (registrationCall, releaseCall) => getCalleeName$1(registrationCall) === "addListener" && registrationCall.arguments?.length === 1 && releaseCall.arguments?.length === 1;
15347
- const hasSafeForEachProjectionCleanup = (registrationCall, releaseCall, context) => {
15348
- const registrationCallee = stripParenExpression(registrationCall.callee);
15349
- const releaseCallee = stripParenExpression(releaseCall.callee);
15350
- if (!isNodeOfType(registrationCallee, "MemberExpression") || !isNodeOfType(releaseCallee, "MemberExpression")) return true;
15351
- const registrationVerbName = getCalleeName$1(registrationCall);
15352
- const releaseVerbName = getCalleeName$1(releaseCall);
15353
- const releaseHandler = releaseCall.arguments?.[usesUnaryListenerSignature(registrationCall, releaseCall) ? 0 : 1];
15354
- const releaseFunction = findEnclosingFunction$1(releaseCall);
15355
- const registrationEventKey = resolveResourceIdentityKey(registrationCall.arguments?.[0], context);
15356
- const releaseEventKey = resolveResourceIdentityKey(releaseCall.arguments?.[0], context);
15357
- const doesHandlerlessOffReleaseEveryRegistration = releaseVerbName === "off" && !releaseHandler && (releaseCall.arguments?.length === 0 || registrationEventKey !== null && registrationEventKey === releaseEventKey);
15358
- if (Boolean(releaseFunction && isFunctionLike$1(releaseFunction) && isReturnedEffectCleanupFunction(releaseFunction, context) && doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseCall], context)) && (releaseVerbName !== null && UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName) || doesHandlerlessOffReleaseEveryRegistration)) return true;
15359
- const projections = [
15360
- registrationCallee.object,
15361
- registrationCall.arguments?.[0],
15362
- registrationCall.arguments?.[1],
15363
- releaseCallee.object,
15364
- releaseCall.arguments?.[0],
15365
- releaseCall.arguments?.[1]
15366
- ].flatMap((expression) => {
15367
- const projection = resolveForEachProjection(expression, context);
15368
- return projection ? [projection] : [];
15369
- });
15370
- if (registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener") for (const optionsNode of [registrationCall.arguments?.[2], releaseCall.arguments?.[2]]) {
15371
- const captureProjection = resolveEventListenerCaptureProjection(optionsNode, context);
15372
- if (captureProjection) projections.push(captureProjection);
15373
- }
15374
- if (projections.length === 0) return true;
15375
- const collectionKeys = new Set(projections.map((projection) => projection.collectionKey));
15376
- if (!findDirectExhaustiveForEachCleanupFunction(releaseCall, collectionKeys, context)) return false;
15377
- return !hasCollectionMutationBeforeRelease(registrationCall, releaseCall, collectionKeys, context);
15378
- };
15379
15116
  const doesReleaseCallMatchUsage = (node, usage, context) => {
15380
15117
  const callNode = isNodeOfType(node, "ChainExpression") ? node.expression : node;
15381
15118
  if (!isNodeOfType(callNode, "CallExpression")) return false;
@@ -15391,8 +15128,8 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15391
15128
  const releaseVerbName = getReleaseVerbName(callNode);
15392
15129
  if (!releaseVerbName) return false;
15393
15130
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier")) return false;
15394
- const releaseReceiverKey = resolveResourceIdentityKey(callee.object, context);
15395
- const releaseEventKey = resolveResourceIdentityKey(callNode.arguments?.[0], context);
15131
+ const releaseReceiverKey = resolveExpressionKey(callee.object, context);
15132
+ const releaseEventKey = resolveExpressionKey(callNode.arguments?.[0], context);
15396
15133
  const pairedReleaseVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
15397
15134
  const pushedResourceCollectionKey = findPushedResourceCollectionKey(usage, context);
15398
15135
  const releaseReceiverForOfStatement = findForOfStatementForIteratorExpression(callee.object, context);
@@ -15402,13 +15139,7 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15402
15139
  if (usage.kind === "socket") return usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
15403
15140
  if (usage.handleKey !== null && releaseReceiverKey === usage.handleKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName))) return true;
15404
15141
  if (releaseVerbName === "abort" && releaseReceiverKey === getListenerAbortControllerKey(usage, context)) return true;
15405
- 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;
15406
15142
  if (releaseVerbName === "abort" && isRetainedAbortControllerRefRelease(callee.object, usage, context)) return true;
15407
- if (usage.registrationVerbName === "addEventListener" && releaseVerbName === "removeEventListener" && isNodeOfType(usage.node, "CallExpression")) {
15408
- if (!isNodeOfType(stripParenExpression(usage.node.callee), "MemberExpression")) return false;
15409
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context, true)) return false;
15410
- }
15411
- if (isNodeOfType(usage.node, "CallExpression") && !hasSafeForEachProjectionCleanup(usage.node, callNode, context)) return false;
15412
15143
  if (usage.receiverKey === null || releaseReceiverKey !== usage.receiverKey) return false;
15413
15144
  if (usage.registrationVerbName === "subscribe" && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub") && usage.handleKey !== null && resolveExpressionKey(callNode.arguments?.[0], context) === usage.handleKey) return true;
15414
15145
  const pairedVerbNames = usage.registrationVerbName ? PAIRED_RELEASE_VERB_NAMES_BY_REGISTRATION_VERB.get(usage.registrationVerbName) : null;
@@ -15418,9 +15149,6 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15418
15149
  if (isAssignmentFormForOfIteratorReference(usageEventArgument, context) || isAssignmentFormForOfIteratorReference(releaseEventArgument, context)) return false;
15419
15150
  if (usage.eventKey !== null && releaseEventKey !== null && usage.eventKey !== releaseEventKey) {
15420
15151
  if (!isNodeOfType(usage.node, "CallExpression")) return false;
15421
- const registrationEventProjectionKey = resolveForEachProjectionKey(usage.node.arguments?.[0], context);
15422
- const releaseEventProjectionKey = resolveForEachProjectionKey(callNode.arguments?.[0], context);
15423
- if ((registrationEventProjectionKey !== null || releaseEventProjectionKey !== null) && registrationEventProjectionKey !== releaseEventProjectionKey) return false;
15424
15152
  const usageForOfStatement = findForOfStatementForIteratorExpression(usageEventArgument, context);
15425
15153
  const releaseForOfStatement = findForOfStatementForIteratorExpression(releaseEventArgument, context);
15426
15154
  if (usageForOfStatement === null !== (releaseForOfStatement === null)) return false;
@@ -15434,7 +15162,9 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15434
15162
  const registrationCallee = stripParenExpression(usage.node.callee);
15435
15163
  if (!isNodeOfType(registrationCallee, "MemberExpression")) return false;
15436
15164
  if (!isStableLoopReceiver(registrationCallee.object, context) || !isStableLoopReceiver(callee.object, context) || registrationHandlerSymbolId === null || registrationHandlerSymbolId !== releaseHandlerSymbolId) return false;
15437
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], callNode.arguments?.[2], context)) return false;
15165
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15166
+ const releaseCapture = resolveEventListenerCapture(callNode.arguments?.[2], { allowIndeterminateEntries: true });
15167
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15438
15168
  if (!isDirectExhaustiveForOfRelease(callNode, releaseForOfStatement)) return false;
15439
15169
  }
15440
15170
  }
@@ -15443,28 +15173,27 @@ const doesReleaseCallMatchUsage = (node, usage, context) => {
15443
15173
  return isNodeOfType(handlerArgument, "Literal") && handlerArgument.value === null;
15444
15174
  }
15445
15175
  if (releaseVerbName === "removeEventListener" || releaseVerbName === "removeListener" || releaseVerbName === "off") {
15446
- const usesUnaryListenerSignatureForCalls = isNodeOfType(usage.node, "CallExpression") && usesUnaryListenerSignature(usage.node, callNode);
15447
- const releaseHandler = usesUnaryListenerSignatureForCalls ? callNode.arguments?.[0] : callNode.arguments?.[1];
15176
+ const usesUnaryListenerSignature = usage.registrationVerbName === "addListener" && isNodeOfType(usage.node, "CallExpression") && usage.node.arguments?.length === 1 && callNode.arguments?.length === 1;
15177
+ const releaseHandler = usesUnaryListenerSignature ? callNode.arguments?.[0] : callNode.arguments?.[1];
15448
15178
  if (!releaseHandler) return releaseVerbName === "off";
15449
- const expectedHandlerKey = usesUnaryListenerSignatureForCalls ? usage.eventKey : usage.handlerKey;
15450
- const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignatureForCalls ? 0 : 1] : null;
15451
- return expectedHandlerKey !== null && resolveResourceIdentityKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
15179
+ const expectedHandlerKey = usesUnaryListenerSignature ? usage.eventKey : usage.handlerKey;
15180
+ const registrationHandler = isNodeOfType(usage.node, "CallExpression") ? usage.node.arguments?.[usesUnaryListenerSignature ? 0 : 1] : null;
15181
+ return expectedHandlerKey !== null && resolveExpressionKey(releaseHandler, context) === expectedHandlerKey || registrationHandler !== null && resolveStableValue(releaseHandler, context) === resolveStableValue(registrationHandler, context);
15452
15182
  }
15453
15183
  if (releaseVerbName === "unobserve" && usage.eventKey !== null) return releaseEventKey === usage.eventKey;
15454
15184
  return true;
15455
15185
  };
15456
15186
  const matchesPairedReleaseVerb = (releaseVerbName, pairedVerbNames) => pairedVerbNames.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName);
15457
15187
  const isReturnedEffectCleanupFunction = (functionNode, context) => {
15458
- const effectCallback = findEnclosingFunction$1(functionNode);
15459
- if (!effectCallback || !isFunctionLike$1(effectCallback)) return false;
15460
- const effectCall = effectCallback.parent;
15461
- if (!isNodeOfType(effectCall, "CallExpression") || !isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes)) return false;
15462
- if (!isNodeOfType(effectCallback.body, "BlockStatement")) return resolveStableValue(effectCallback.body, context) === functionNode;
15463
- let isReturned = false;
15464
- walkInsideStatementBlocks(effectCallback.body, (child) => {
15465
- if (isNodeOfType(child, "ReturnStatement") && child.argument && resolveStableValue(child.argument, context) === functionNode) isReturned = true;
15466
- });
15467
- return isReturned;
15188
+ let currentNode = functionNode;
15189
+ let parentNode = currentNode.parent;
15190
+ while (isNodeOfType(parentNode, "ChainExpression") || isNodeOfType(parentNode, "TSAsExpression") || isNodeOfType(parentNode, "TSNonNullExpression")) {
15191
+ currentNode = parentNode;
15192
+ parentNode = currentNode.parent;
15193
+ }
15194
+ const effectCallback = isNodeOfType(parentNode, "ReturnStatement") && parentNode.argument === currentNode ? findEnclosingFunction$1(parentNode) : isNodeOfType(parentNode, "ArrowFunctionExpression") && parentNode.body === currentNode ? parentNode : null;
15195
+ const effectCall = effectCallback?.parent;
15196
+ return Boolean(effectCallback && isNodeOfType(effectCall, "CallExpression") && isReactHookCall(effectCall, CLEANUP_EFFECT_HOOK_NAMES, context.scopes));
15468
15197
  };
15469
15198
  const isPotentiallyReachableFunction = (functionNode, context) => {
15470
15199
  if (isInlineRetainedHandlerFunction(functionNode, context) || isReturnedEffectCleanupFunction(functionNode, context)) return true;
@@ -15607,9 +15336,11 @@ const isRetainedDisposerRefRelease = (releaseNode, usage, context) => {
15607
15336
  };
15608
15337
  const isSelfReleasingListenerRelease = (releaseNode, releaseFunction, usage, context) => {
15609
15338
  if (usage.kind !== "subscribe" || usage.registrationVerbName !== "addEventListener" || usage.receiverKey === null || usage.eventKey === null || !isNodeOfType(usage.node, "CallExpression") || !isFunctionLike$1(releaseFunction) || releaseFunction.async || releaseFunction.generator || !isNodeOfType(releaseFunction.body, "BlockStatement") || !doMatchingNodesCoverEveryPathFromFunctionEntry(releaseFunction, [releaseNode], context)) return false;
15339
+ const registrationCapture = resolveEventListenerCapture(usage.node.arguments?.[2], { allowIndeterminateEntries: true });
15610
15340
  const releaseCall = isNodeOfType(releaseNode, "ChainExpression") ? releaseNode.expression : releaseNode;
15611
15341
  if (!isNodeOfType(releaseCall, "CallExpression")) return false;
15612
- if (!doEventListenerCapturesMatch(usage.node.arguments?.[2], releaseCall.arguments?.[2], context)) return false;
15342
+ const releaseCapture = resolveEventListenerCapture(releaseCall.arguments?.[2], { allowIndeterminateEntries: true });
15343
+ if (registrationCapture === null || releaseCapture === null || registrationCapture !== releaseCapture) return false;
15613
15344
  const ownerFunction = findEnclosingFunction$1(releaseFunction);
15614
15345
  if (!ownerFunction || !isFunctionLike$1(ownerFunction)) return false;
15615
15346
  const triggerRegistrations = [];
@@ -19079,12 +18810,11 @@ const forbidDomProps = defineRule({
19079
18810
  * carrying their own near-identical inlined implementation.
19080
18811
  */
19081
18812
  const flattenCalleeName = (callee) => {
19082
- const unwrappedCallee = stripParenExpression(callee);
19083
- if (isNodeOfType(unwrappedCallee, "Identifier")) return unwrappedCallee.name;
19084
- if (isNodeOfType(unwrappedCallee, "MemberExpression") && !unwrappedCallee.computed) {
19085
- const objectName = flattenCalleeName(unwrappedCallee.object);
18813
+ if (isNodeOfType(callee, "Identifier")) return callee.name;
18814
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
18815
+ const objectName = flattenCalleeName(callee.object);
19086
18816
  if (!objectName) return null;
19087
- if (isNodeOfType(unwrappedCallee.property, "Identifier")) return `${objectName}.${unwrappedCallee.property.name}`;
18817
+ if (isNodeOfType(callee.property, "Identifier")) return `${objectName}.${callee.property.name}`;
19088
18818
  }
19089
18819
  return null;
19090
18820
  };
@@ -22484,49 +22214,25 @@ const jsLengthCheckFirst = defineRule({
22484
22214
  });
22485
22215
  //#endregion
22486
22216
  //#region src/plugin/utils/is-proven-global-namespace-reference.ts
22487
- const GLOBAL_SELF_PROPERTY_NAMES = new Set([
22488
- "global",
22489
- "globalThis",
22490
- "self",
22491
- "window"
22492
- ]);
22493
22217
  const isProvenGlobalObjectReference = (expression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
22494
- let currentExpression = expression;
22495
- while (true) {
22496
- const strippedExpression = stripParenExpression(currentExpression);
22497
- if (!isNodeOfType(strippedExpression, "Identifier")) return false;
22498
- if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
22499
- const symbol = scopes.symbolFor(strippedExpression);
22500
- if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id) || !isNodeOfType(symbol.declarationNode, "VariableDeclarator")) return false;
22501
- const declaration = symbol.declarationNode;
22502
- const initializer = declaration.init;
22503
- if (!initializer) return false;
22504
- const isDirectIdentifierAlias = isNodeOfType(declaration.id, "Identifier") && declaration.id === symbol.bindingIdentifier && initializer === symbol.initializer;
22505
- const bindingProperty = symbol.bindingIdentifier.parent;
22506
- const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
22507
- const isDirectGlobalSelfAlias = isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === symbol.bindingIdentifier && bindingProperty.parent === declaration.id && destructuredPropertyName !== null && GLOBAL_SELF_PROPERTY_NAMES.has(destructuredPropertyName);
22508
- if (!isDirectIdentifierAlias && !isDirectGlobalSelfAlias) return false;
22509
- visitedSymbolIds.add(symbol.id);
22510
- currentExpression = initializer;
22511
- }
22218
+ const strippedExpression = stripParenExpression(expression);
22219
+ if (!isNodeOfType(strippedExpression, "Identifier")) return false;
22220
+ if ((strippedExpression.name === "globalThis" || strippedExpression.name === "window" || strippedExpression.name === "self" || strippedExpression.name === "global") && scopes.isGlobalReference(strippedExpression)) return true;
22221
+ const symbol = scopes.symbolFor(strippedExpression);
22222
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22223
+ visitedSymbolIds.add(symbol.id);
22224
+ return isProvenGlobalObjectReference(symbol.initializer, scopes, visitedSymbolIds);
22512
22225
  };
22513
22226
  const isProvenGlobalNamespaceReference = (expression, namespaceName, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
22514
- let currentExpression = expression;
22515
- while (true) {
22516
- const strippedExpression = stripParenExpression(currentExpression);
22517
- if (!isNodeOfType(strippedExpression, "Identifier")) return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
22227
+ const strippedExpression = stripParenExpression(expression);
22228
+ if (isNodeOfType(strippedExpression, "Identifier")) {
22518
22229
  if (strippedExpression.name === namespaceName && scopes.isGlobalReference(strippedExpression)) return true;
22519
22230
  const symbol = scopes.symbolFor(strippedExpression);
22520
- if (!symbol || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22231
+ if (!symbol?.initializer || symbol.kind !== "const" || visitedSymbolIds.has(symbol.id)) return false;
22521
22232
  visitedSymbolIds.add(symbol.id);
22522
- const declaration = symbol.declarationNode;
22523
- const bindingIdentifier = symbol.bindingIdentifier;
22524
- const bindingProperty = bindingIdentifier.parent;
22525
- if (getDestructuredBindingPropertyName(bindingIdentifier) === namespaceName && isNodeOfType(declaration, "VariableDeclarator") && isNodeOfType(declaration.id, "ObjectPattern") && isNodeOfType(bindingProperty, "Property") && bindingProperty.value === bindingIdentifier && bindingProperty.parent === declaration.id && declaration.init) return isProvenGlobalObjectReference(declaration.init, scopes);
22526
- const directInitializer = getDirectConstInitializer(symbol);
22527
- if (!directInitializer) return false;
22528
- currentExpression = directInitializer;
22233
+ return isProvenGlobalNamespaceReference(symbol.initializer, namespaceName, scopes, visitedSymbolIds);
22529
22234
  }
22235
+ return isNodeOfType(strippedExpression, "MemberExpression") && getStaticPropertyName(strippedExpression) === namespaceName && isProvenGlobalObjectReference(strippedExpression.object, scopes);
22530
22236
  };
22531
22237
  //#endregion
22532
22238
  //#region src/plugin/rules/js-performance/js-min-max-loop.ts
@@ -24881,14 +24587,9 @@ const collectContextBindings = (programRoot) => {
24881
24587
  }
24882
24588
  return bindings;
24883
24589
  };
24884
- const isCreateContextBindingJsxName = (node, contextBindings, scopes) => {
24590
+ const isCreateContextBindingJsxName = (node, contextBindings) => {
24885
24591
  if (!isNodeOfType(node, "JSXIdentifier")) return false;
24886
- if (!contextBindings.has(node.name)) {
24887
- const symbol = scopes.symbolFor(node);
24888
- if (!symbol || symbol.kind !== "import") return false;
24889
- const importedName = getImportedName(symbol.declarationNode);
24890
- return getImportSourceForName(node, node.name)?.toLowerCase().includes("context") === true && (node.name.endsWith("Context") || importedName?.endsWith("Context") === true);
24891
- }
24592
+ if (!contextBindings.has(node.name)) return false;
24892
24593
  const binding = findVariableInitializer(node, node.name);
24893
24594
  if (!binding) return false;
24894
24595
  return binding.scopeOwner.type === "Program";
@@ -24912,7 +24613,7 @@ const jsxNoConstructedContextValues = defineRule({
24912
24613
  if (isTestlikeFile) return;
24913
24614
  const nameNode = node.name;
24914
24615
  const isLegacyProvider = isProviderMemberName(nameNode);
24915
- const isReact19Shorthand = isCreateContextBindingJsxName(nameNode, contextBindings, context.scopes);
24616
+ const isReact19Shorthand = isCreateContextBindingJsxName(nameNode, contextBindings);
24916
24617
  if (!isLegacyProvider && !isReact19Shorthand) return;
24917
24618
  if (!isInsideFunctionScope(node)) return;
24918
24619
  for (const attribute of node.attributes) {
@@ -25183,29 +24884,6 @@ const buildSameFileJsxSlotPropRegistry = (program, memoRegistry, scopes) => {
25183
24884
  return registry;
25184
24885
  };
25185
24886
  //#endregion
25186
- //#region src/plugin/utils/has-custom-memo-comparator.ts
25187
- const MEMO_CALLEE_NAMES$1 = new Set(["memo", "React.memo"]);
25188
- const isIdentitySensitiveMemoComparator = (comparatorNode, scopes) => {
25189
- if (isNodeOfType(comparatorNode, "Identifier")) {
25190
- if (comparatorNode.name === "undefined") return scopes.isGlobalReference(comparatorNode);
25191
- return getImportedNameFromModule(comparatorNode, comparatorNode.name, "react-redux") === "shallowEqual";
25192
- }
25193
- if (!isNodeOfType(comparatorNode, "MemberExpression") || comparatorNode.computed || !isNodeOfType(comparatorNode.object, "Identifier") || !isNodeOfType(comparatorNode.property, "Identifier") || comparatorNode.property.name !== "shallowEqual") return false;
25194
- return isNamespaceImportFromModule$1(comparatorNode, comparatorNode.object.name, "react-redux");
25195
- };
25196
- const hasCustomMemoComparator = (openingName, scopes) => {
25197
- if (!openingName || !isNodeOfType(openingName, "JSXIdentifier")) return false;
25198
- const binding = findVariableInitializer(openingName, openingName.name);
25199
- if (!binding || !binding.initializer) return false;
25200
- const initializer = binding.initializer;
25201
- if (!isNodeOfType(initializer, "CallExpression")) return false;
25202
- const calleeName = flattenCalleeName(initializer.callee);
25203
- if (calleeName === null || !MEMO_CALLEE_NAMES$1.has(calleeName)) return false;
25204
- const comparatorNode = (initializer.arguments ?? [])[1];
25205
- if (!comparatorNode) return false;
25206
- return !isIdentitySensitiveMemoComparator(comparatorNode, scopes);
25207
- };
25208
- //#endregion
25209
24887
  //#region src/plugin/utils/is-on-intrinsic-html-element.ts
25210
24888
  const isJsxAttributeOnIntrinsicHtmlElement = (attribute) => {
25211
24889
  const openingElement = attribute.parent;
@@ -25491,7 +25169,6 @@ const jsxNoJsxAsProp = defineRule({
25491
25169
  const parentJsxOpening = node.parent;
25492
25170
  const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
25493
25171
  if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
25494
- if (hasCustomMemoComparator(openingName, context.scopes)) return;
25495
25172
  const openingSymbol = openingName && isNodeOfType(openingName, "JSXIdentifier") ? context.scopes.symbolFor(openingName) : null;
25496
25173
  if (openingSymbol && isNodeOfType(node.name, "JSXIdentifier") && jsxSlotPropRegistry?.get(openingSymbol.id)?.has(node.name.name)) return;
25497
25174
  if (isNodeOfType(node.name, "JSXIdentifier") && isSlotPropName(node.name.name)) return;
@@ -25511,6 +25188,19 @@ const jsxNoJsxAsProp = defineRule({
25511
25188
  }
25512
25189
  });
25513
25190
  //#endregion
25191
+ //#region src/plugin/utils/has-custom-memo-comparator.ts
25192
+ const MEMO_CALLEE_NAMES = new Set(["memo", "React.memo"]);
25193
+ const hasCustomMemoComparator = (openingName) => {
25194
+ if (!openingName || !isNodeOfType(openingName, "JSXIdentifier")) return false;
25195
+ const binding = findVariableInitializer(openingName, openingName.name);
25196
+ if (!binding || !binding.initializer) return false;
25197
+ const initializer = binding.initializer;
25198
+ if (!isNodeOfType(initializer, "CallExpression")) return false;
25199
+ const calleeName = flattenCalleeName(initializer.callee);
25200
+ if (calleeName === null || !MEMO_CALLEE_NAMES.has(calleeName)) return false;
25201
+ return (initializer.arguments ?? []).length >= 2;
25202
+ };
25203
+ //#endregion
25514
25204
  //#region src/plugin/rules/react-builtins/jsx-no-new-array-as-prop-tables.ts
25515
25205
  const DATA_ARRAY_PROP_NAMES = new Set([
25516
25206
  "data",
@@ -25869,7 +25559,7 @@ const jsxNoNewArrayAsProp = defineRule({
25869
25559
  const parentJsxOpening = node.parent;
25870
25560
  const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
25871
25561
  if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
25872
- if (hasCustomMemoComparator(openingName, context.scopes)) return;
25562
+ if (hasCustomMemoComparator(openingName)) return;
25873
25563
  if (isNodeOfType(node.name, "JSXIdentifier") && isDataArrayPropName(node.name.name)) return;
25874
25564
  if (!isInsideFunctionScope(node)) return;
25875
25565
  const value = node.value;
@@ -26333,7 +26023,6 @@ const jsxNoNewFunctionAsProp = defineRule({
26333
26023
  const parentJsxOpening = node.parent;
26334
26024
  const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
26335
26025
  if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
26336
- if (hasCustomMemoComparator(openingName, context.scopes)) return;
26337
26026
  if (isNodeOfType(node.name, "JSXIdentifier") && isOneShotHandlerName(node.name.name)) return;
26338
26027
  if (!isInsideFunctionScope(node)) return;
26339
26028
  const value = node.value;
@@ -26641,7 +26330,7 @@ const jsxNoNewObjectAsProp = defineRule({
26641
26330
  const parentJsxOpening = node.parent;
26642
26331
  const openingName = parentJsxOpening && isNodeOfType(parentJsxOpening, "JSXOpeningElement") ? parentJsxOpening.name : null;
26643
26332
  if (memoStatusForJsxOpeningName(memoRegistry, openingName) !== "memoised") return;
26644
- if (hasCustomMemoComparator(openingName, context.scopes)) return;
26333
+ if (hasCustomMemoComparator(openingName)) return;
26645
26334
  if (!isInsideFunctionScope(node)) return;
26646
26335
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
26647
26336
  if (ALWAYS_FRESH_OBJECT_PROPS.has(node.name.name)) return;
@@ -29673,14 +29362,6 @@ const nextjsNoImgElement = defineRule({
29673
29362
  });
29674
29363
  //#endregion
29675
29364
  //#region src/plugin/rules/nextjs/nextjs-no-native-script.ts
29676
- const hasEnabledBooleanAttribute = (attributes, attributeName) => {
29677
- const attribute = findJsxAttribute(attributes, attributeName);
29678
- if (!attribute) return false;
29679
- if (attribute.value === null) return true;
29680
- if (isNodeOfType(attribute.value, "Literal")) return true;
29681
- if (!isNodeOfType(attribute.value, "JSXExpressionContainer")) return false;
29682
- return isNodeOfType(attribute.value.expression, "Literal") && Boolean(attribute.value.expression.value);
29683
- };
29684
29365
  const nextjsNoNativeScript = defineRule({
29685
29366
  id: "nextjs-no-native-script",
29686
29367
  title: "Plain script can block Next.js rendering",
@@ -29693,9 +29374,6 @@ const nextjsNoNativeScript = defineRule({
29693
29374
  const typeAttribute = findJsxAttribute(node.attributes ?? [], "type");
29694
29375
  const typeValue = isNodeOfType(typeAttribute?.value, "Literal") ? typeAttribute.value.value : null;
29695
29376
  if (typeof typeValue === "string" && !EXECUTABLE_SCRIPT_TYPES.has(typeValue)) return;
29696
- if (hasEnabledBooleanAttribute(node.attributes ?? [], "async")) return;
29697
- if (hasEnabledBooleanAttribute(node.attributes ?? [], "defer")) return;
29698
- if (typeValue === "module") return;
29699
29377
  const hasSrcAttribute = Boolean(findJsxAttribute(node.attributes ?? [], "src"));
29700
29378
  if (Boolean(findJsxAttribute(node.attributes ?? [], "dangerouslySetInnerHTML")) && !hasSrcAttribute) return;
29701
29379
  context.report({
@@ -34788,19 +34466,6 @@ const noCascadingSetState = defineRetiredRule({
34788
34466
  recommendation: "Retired: React batches synchronous state updates from one effect into the same follow-up commit, so setter count does not prove repeated redraws."
34789
34467
  });
34790
34468
  //#endregion
34791
- //#region src/plugin/utils/get-react-use-callback-call.ts
34792
- const getReactUseCallbackCall = (expression, scopes) => {
34793
- const identifier = stripParenExpression(expression);
34794
- if (!isNodeOfType(identifier, "Identifier")) return null;
34795
- const symbol = resolveConstIdentifierAlias(identifier, scopes);
34796
- if (symbol?.kind !== "const" || !symbol.initializer) return null;
34797
- const initializer = stripParenExpression(symbol.initializer);
34798
- return isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", scopes, {
34799
- allowGlobalReactNamespace: true,
34800
- resolveNamedAliases: true
34801
- }) ? initializer : null;
34802
- };
34803
- //#endregion
34804
34469
  //#region src/plugin/rules/state-and-effects/utils/create-state-trigger-reachability.ts
34805
34470
  const getRefCurrentSymbol = (node, context) => {
34806
34471
  const expression = stripParenExpression(node);
@@ -35329,8 +34994,19 @@ const isBuiltinNamespaceCallee = (callee) => {
35329
34994
  }
35330
34995
  return false;
35331
34996
  };
34997
+ const getReactUseCallbackSource = (reference, context) => {
34998
+ const identifier = reference.identifier;
34999
+ const symbol = resolveConstIdentifierAlias(identifier, context.scopes);
35000
+ if (!symbol || symbol.kind !== "const" || !symbol.initializer) return null;
35001
+ const initializer = stripParenExpression(symbol.initializer);
35002
+ if (isNodeOfType(initializer, "CallExpression") && isReactApiCall(initializer, "useCallback", context.scopes, {
35003
+ allowGlobalReactNamespace: true,
35004
+ resolveNamedAliases: true
35005
+ })) return initializer;
35006
+ return null;
35007
+ };
35332
35008
  const getDependencyStateRefs = (analysis, context, dependencyReference) => {
35333
- const useCallbackCall = getReactUseCallbackCall(dependencyReference.identifier, context.scopes);
35009
+ const useCallbackCall = getReactUseCallbackSource(dependencyReference, context);
35334
35010
  if (!useCallbackCall) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
35335
35011
  const dependencyList = useCallbackCall.arguments?.[1];
35336
35012
  if (!dependencyList || !isNodeOfType(dependencyList, "ArrayExpression")) return getUpstreamRefs(analysis, dependencyReference).filter((reference) => isState(analysis, reference));
@@ -38844,17 +38520,6 @@ const noDynamicImportPath = defineRule({
38844
38520
  })
38845
38521
  });
38846
38522
  //#endregion
38847
- //#region src/plugin/utils/get-require-call-source.ts
38848
- const getRequireCallSource = (expression) => {
38849
- const unwrappedExpression = stripParenExpression(expression);
38850
- if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
38851
- if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
38852
- if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
38853
- const [firstArgument] = unwrappedExpression.arguments ?? [];
38854
- if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
38855
- return typeof firstArgument.value === "string" ? firstArgument.value : null;
38856
- };
38857
- //#endregion
38858
38523
  //#region src/plugin/rules/state-and-effects/utils/is-cleanup-return.ts
38859
38524
  const ITERATOR_CALLBACK_METHOD_NAMES = new Set([
38860
38525
  "each",
@@ -38999,16 +38664,7 @@ const collectDependencyStateSymbolIds = (effectNode, stateSymbolIds, scopes) =>
38999
38664
  }
39000
38665
  return dependencyStateSymbolIds;
39001
38666
  };
39002
- const resolveSynchronouslyInvokedFunction = (expression, scopes) => {
39003
- const localFunction = resolveExactLocalFunction(expression, scopes);
39004
- if (localFunction) return localFunction;
39005
- const useCallbackCall = getReactUseCallbackCall(expression, scopes);
39006
- if (!useCallbackCall) return null;
39007
- const callback = useCallbackCall.arguments[0];
39008
- if (!callback || isNodeOfType(callback, "SpreadElement")) return null;
39009
- return resolveExactLocalFunction(callback, scopes);
39010
- };
39011
- const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeStableCallbacks = true) => {
38667
+ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes) => {
39012
38668
  const analysisFunctions = new Set([effectCallback]);
39013
38669
  const pendingFunctions = [effectCallback];
39014
38670
  while (pendingFunctions.length > 0) {
@@ -39016,9 +38672,9 @@ const collectSynchronouslyInvokedFunctions = (effectCallback, scopes, includeSta
39016
38672
  if (!currentFunction || !isFunctionLike$1(currentFunction)) continue;
39017
38673
  walkInsideStatementBlocks(currentFunction.body, (child) => {
39018
38674
  if (!isNodeOfType(child, "CallExpression")) return;
39019
- const invokedFunction = includeStableCallbacks ? resolveSynchronouslyInvokedFunction(child.callee, scopes) : resolveExactLocalFunction(child.callee, scopes);
38675
+ const invokedFunction = resolveExactLocalFunction(child.callee, scopes);
39020
38676
  if (!invokedFunction || analysisFunctions.has(invokedFunction)) return;
39021
- if (isFunctionLike$1(invokedFunction) && (invokedFunction.async || invokedFunction.generator)) return;
38677
+ if (isFunctionLike$1(invokedFunction) && invokedFunction.async) return;
39022
38678
  analysisFunctions.add(invokedFunction);
39023
38679
  pendingFunctions.push(invokedFunction);
39024
38680
  });
@@ -39031,51 +38687,7 @@ const visitSynchronousFunctionBodies = (analysisFunctions, visitor) => {
39031
38687
  walkInsideStatementBlocks(analysisFunction.body, visitor);
39032
38688
  }
39033
38689
  };
39034
- const getStateNameForSetterCall = (node, setterSymbolIdToStateName, scopes) => {
39035
- if (!isNodeOfType(node, "CallExpression") || !isNodeOfType(node.callee, "Identifier")) return null;
39036
- const setterSymbol = resolveConstIdentifierAlias(node.callee, scopes, true);
39037
- return setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) ?? null : null;
39038
- };
39039
- const collectStateWriteAnalysisFunctions = (effectCallback, scopes, setterSymbolIdToStateName) => {
39040
- const ordinaryAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes, false);
39041
- const fullAnalysisFunctions = collectSynchronouslyInvokedFunctions(effectCallback, scopes);
39042
- const stableAnalysisFunctions = [...fullAnalysisFunctions].filter((analysisFunction) => !ordinaryAnalysisFunctions.has(analysisFunction));
39043
- if (stableAnalysisFunctions.length === 0) return ordinaryAnalysisFunctions;
39044
- let hasUnprovenObservableWork = stableAnalysisFunctions.some((analysisFunction) => {
39045
- if (!isFunctionLike$1(analysisFunction)) return false;
39046
- let hasExecutableDefault = false;
39047
- for (const parameter of analysisFunction.params) {
39048
- walkInsideStatementBlocks(parameter, (child) => {
39049
- if (isNodeOfType(child, "CallExpression") || isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasExecutableDefault = true;
39050
- });
39051
- if (hasExecutableDefault) return true;
39052
- }
39053
- return false;
39054
- });
39055
- const stableWrittenStateNames = /* @__PURE__ */ new Set();
39056
- const allWrittenStateNames = /* @__PURE__ */ new Set();
39057
- visitSynchronousFunctionBodies(fullAnalysisFunctions, (child) => {
39058
- const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
39059
- if (writtenStateName) allWrittenStateNames.add(writtenStateName);
39060
- });
39061
- visitSynchronousFunctionBodies(new Set(stableAnalysisFunctions), (child) => {
39062
- if (hasUnprovenObservableWork) return;
39063
- if (isNodeOfType(child, "CallExpression")) {
39064
- const writtenStateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
39065
- if (writtenStateName) {
39066
- stableWrittenStateNames.add(writtenStateName);
39067
- return;
39068
- }
39069
- const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
39070
- if (invokedFunction && fullAnalysisFunctions.has(invokedFunction)) return;
39071
- hasUnprovenObservableWork = true;
39072
- return;
39073
- }
39074
- if (isNodeOfType(child, "AssignmentExpression") || isNodeOfType(child, "UpdateExpression") || isNodeOfType(child, "ImportExpression") || isNodeOfType(child, "NewExpression") || isNodeOfType(child, "TaggedTemplateExpression") || isNodeOfType(child, "ThrowStatement") || isNodeOfType(child, "UnaryExpression") && child.operator === "delete") hasUnprovenObservableWork = true;
39075
- });
39076
- return !hasUnprovenObservableWork && stableWrittenStateNames.size === 1 && allWrittenStateNames.size === 1 ? fullAnalysisFunctions : ordinaryAnalysisFunctions;
39077
- };
39078
- const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set(), additionalStateValues = /* @__PURE__ */ new Map()) => {
38690
+ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39079
38691
  const unwrappedExpression = stripParenExpression(expression);
39080
38692
  if (isNodeOfType(unwrappedExpression, "Literal")) {
39081
38693
  const literalValue = unwrappedExpression.value;
@@ -39083,49 +38695,46 @@ const readStaticEffectValue = (expression, scopes, stateSymbolId, stateValue, vi
39083
38695
  return null;
39084
38696
  }
39085
38697
  if (isNodeOfType(unwrappedExpression, "Identifier")) {
39086
- const symbol = scopes.symbolFor(unwrappedExpression);
39087
- if (symbol?.id === stateSymbolId) return stateValue;
39088
- if (symbol && additionalStateValues.has(symbol.id)) return additionalStateValues.get(symbol.id) ?? null;
38698
+ if (scopes.symbolFor(unwrappedExpression)?.id === stateSymbolId) return stateValue;
39089
38699
  if (unwrappedExpression.name === "undefined" && scopes.isGlobalReference(unwrappedExpression)) return { value: void 0 };
39090
- if (unwrappedExpression.name === "NaN" && scopes.isGlobalReference(unwrappedExpression)) return { value: NaN };
39091
38700
  const immutableSymbol = scopes.symbolFor(unwrappedExpression);
39092
38701
  if (immutableSymbol?.kind !== "const" || !immutableSymbol.initializer || !isNodeOfType(immutableSymbol.declarationNode, "VariableDeclarator") || immutableSymbol.declarationNode.id !== immutableSymbol.bindingIdentifier || immutableSymbol.declarationNode.init !== immutableSymbol.initializer || immutableSymbol.references.some((reference) => reference.flag !== "read") || visitedSymbolIds.has(immutableSymbol.id)) return null;
39093
- return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id), additionalStateValues);
38702
+ return readStaticEffectValue(immutableSymbol.initializer, scopes, stateSymbolId, stateValue, new Set(visitedSymbolIds).add(immutableSymbol.id));
39094
38703
  }
39095
38704
  if (isNodeOfType(unwrappedExpression, "UnaryExpression")) {
39096
38705
  if (unwrappedExpression.operator === "void") return { value: void 0 };
39097
38706
  if (unwrappedExpression.operator !== "!") return null;
39098
- const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38707
+ const argumentValue = readStaticEffectValue(unwrappedExpression.argument, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39099
38708
  return argumentValue ? { value: !argumentValue.value } : null;
39100
38709
  }
39101
38710
  if (isNodeOfType(unwrappedExpression, "CallExpression")) {
39102
38711
  if (isNodeOfType(unwrappedExpression.callee, "Identifier") && unwrappedExpression.callee.name === "Boolean" && scopes.isGlobalReference(unwrappedExpression.callee) && unwrappedExpression.arguments.length === 1 && unwrappedExpression.arguments[0] && !isNodeOfType(unwrappedExpression.arguments[0], "SpreadElement")) {
39103
- const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38712
+ const argumentValue = readStaticEffectValue(unwrappedExpression.arguments[0], scopes, stateSymbolId, stateValue, visitedSymbolIds);
39104
38713
  return argumentValue ? { value: Boolean(argumentValue.value) } : null;
39105
38714
  }
39106
38715
  return null;
39107
38716
  }
39108
38717
  if (isNodeOfType(unwrappedExpression, "LogicalExpression")) {
39109
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38718
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39110
38719
  if (!leftValue) return null;
39111
38720
  if (unwrappedExpression.operator === "&&" && !leftValue.value) return leftValue;
39112
38721
  if (unwrappedExpression.operator === "||" && leftValue.value) return leftValue;
39113
38722
  if (unwrappedExpression.operator === "??" && leftValue.value !== null && leftValue.value !== void 0) return leftValue;
39114
- return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38723
+ return readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39115
38724
  }
39116
38725
  if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) {
39117
- const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38726
+ const testValue = readStaticEffectValue(unwrappedExpression.test, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39118
38727
  if (!testValue) return null;
39119
- return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38728
+ return readStaticEffectValue(testValue.value ? unwrappedExpression.consequent : unwrappedExpression.alternate, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39120
38729
  }
39121
38730
  if (isNodeOfType(unwrappedExpression, "MemberExpression") && unwrappedExpression.optional) {
39122
- const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38731
+ const objectValue = readStaticEffectValue(unwrappedExpression.object, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39123
38732
  if (objectValue?.value === null || objectValue?.value === void 0) return { value: void 0 };
39124
38733
  return null;
39125
38734
  }
39126
38735
  if (isNodeOfType(unwrappedExpression, "BinaryExpression")) {
39127
- const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
39128
- const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds, additionalStateValues);
38736
+ const leftValue = readStaticEffectValue(unwrappedExpression.left, scopes, stateSymbolId, stateValue, visitedSymbolIds);
38737
+ const rightValue = readStaticEffectValue(unwrappedExpression.right, scopes, stateSymbolId, stateValue, visitedSymbolIds);
39129
38738
  if (!leftValue || !rightValue) return null;
39130
38739
  if (unwrappedExpression.operator === "===" || unwrappedExpression.operator === "!==") {
39131
38740
  const areEqual = leftValue.value === rightValue.value;
@@ -39164,7 +38773,8 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39164
38773
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
39165
38774
  if (!isNodeOfType(child, "CallExpression")) return;
39166
38775
  if (!isNodeOfType(child.callee, "Identifier")) return;
39167
- const stateName = getStateNameForSetterCall(child, setterSymbolIdToStateName, scopes);
38776
+ const setterSymbol = resolveConstIdentifierAlias(child.callee, scopes, true);
38777
+ const stateName = setterSymbol ? setterSymbolIdToStateName.get(setterSymbol.id) : void 0;
39168
38778
  if (!stateName) return;
39169
38779
  const writeInfo = stateWrites.get(stateName) ?? {
39170
38780
  values: /* @__PURE__ */ new Set(),
@@ -39180,27 +38790,27 @@ const collectStateWritesInEffect = (analysisFunctions, setterSymbolIdToStateName
39180
38790
  const isGlobalBooleanCall = (node, scopes) => {
39181
38791
  return isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "Identifier") && node.callee.name === "Boolean" && scopes.isGlobalReference(node.callee);
39182
38792
  };
39183
- const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
38793
+ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, scopes) => {
39184
38794
  let currentNode = workNode;
39185
38795
  while (currentNode.parent) {
39186
38796
  const parentNode = currentNode.parent;
39187
38797
  if (isFunctionLike$1(parentNode)) break;
39188
38798
  if (isNodeOfType(parentNode, "IfStatement")) {
39189
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38799
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39190
38800
  if (testValue) {
39191
38801
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39192
38802
  if (currentNode === parentNode.alternate && testValue.value) return false;
39193
38803
  }
39194
38804
  }
39195
38805
  if (isNodeOfType(parentNode, "ConditionalExpression")) {
39196
- const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38806
+ const testValue = readStaticEffectValue(parentNode.test, scopes, stateSymbolId, stateValue);
39197
38807
  if (testValue) {
39198
38808
  if (currentNode === parentNode.consequent && !testValue.value) return false;
39199
38809
  if (currentNode === parentNode.alternate && testValue.value) return false;
39200
38810
  }
39201
38811
  }
39202
38812
  if (isNodeOfType(parentNode, "LogicalExpression") && currentNode === parentNode.right) {
39203
- const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues);
38813
+ const leftValue = readStaticEffectValue(parentNode.left, scopes, stateSymbolId, stateValue);
39204
38814
  if (leftValue) {
39205
38815
  if (parentNode.operator === "&&" && !leftValue.value) return false;
39206
38816
  if (parentNode.operator === "||" && leftValue.value) return false;
@@ -39212,7 +38822,7 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39212
38822
  if (statementIndex >= 0) for (let index = 0; index < statementIndex; index += 1) {
39213
38823
  const earlierStatement = parentNode.body[index];
39214
38824
  if (!isNodeOfType(earlierStatement, "IfStatement") || earlierStatement.alternate || !statementAlwaysExits(earlierStatement.consequent)) continue;
39215
- if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), additionalStateValues)?.value) return false;
38825
+ if (readStaticEffectValue(earlierStatement.test, scopes, stateSymbolId, stateValue)?.value) return false;
39216
38826
  }
39217
38827
  }
39218
38828
  currentNode = parentNode;
@@ -39222,93 +38832,24 @@ const isWorkNodeReachableForStateValue = (workNode, stateSymbolId, stateValue, s
39222
38832
  const isReaderWorkNode = (node, analysisFunctions, scopes) => {
39223
38833
  if (isNodeOfType(node, "CallExpression")) {
39224
38834
  if (isGlobalBooleanCall(node, scopes)) return false;
39225
- const invokedFunction = resolveSynchronouslyInvokedFunction(node.callee, scopes);
38835
+ const invokedFunction = resolveExactLocalFunction(node.callee, scopes);
39226
38836
  return !invokedFunction || !analysisFunctions.has(invokedFunction);
39227
38837
  }
39228
38838
  return isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression") || isNodeOfType(node, "NewExpression") || isNodeOfType(node, "TaggedTemplateExpression") || isNodeOfType(node, "ThrowStatement") || isNodeOfType(node, "UnaryExpression") && node.operator === "delete";
39229
38839
  };
39230
- const mergeKnownStateValues = (additionalStateValues, parameterStateValues) => {
39231
- if (parameterStateValues.size === 0) return additionalStateValues;
39232
- const knownStateValues = new Map(additionalStateValues);
39233
- for (const [symbolId, value] of parameterStateValues) knownStateValues.set(symbolId, value);
39234
- return knownStateValues;
39235
- };
39236
- const buildInvokedFunctionParameterStateValues = (invokedFunction, invocation, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map(), callerParameterStateValues = /* @__PURE__ */ new Map()) => {
39237
- if (!isFunctionLike$1(invokedFunction)) return /* @__PURE__ */ new Map();
39238
- const parameterStateValues = /* @__PURE__ */ new Map();
39239
- const callerKnownStateValues = mergeKnownStateValues(additionalStateValues, callerParameterStateValues);
39240
- for (let parameterIndex = 0; parameterIndex < invokedFunction.params.length; parameterIndex += 1) {
39241
- const rawParameter = invokedFunction.params[parameterIndex];
39242
- const rawArgument = invocation.arguments[parameterIndex];
39243
- if (rawArgument && isNodeOfType(rawArgument, "SpreadElement")) break;
39244
- const parameter = isNodeOfType(rawParameter, "AssignmentPattern") ? rawParameter.left : rawParameter;
39245
- if (!isNodeOfType(parameter, "Identifier")) continue;
39246
- const parameterSymbol = scopes.symbolFor(parameter);
39247
- if (!parameterSymbol) continue;
39248
- let argumentValue = null;
39249
- if (rawArgument) {
39250
- argumentValue = readStaticEffectValue(rawArgument, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), callerKnownStateValues);
39251
- if (argumentValue !== null && argumentValue.value === void 0 && isNodeOfType(rawParameter, "AssignmentPattern")) {
39252
- const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
39253
- argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
39254
- }
39255
- } else if (isNodeOfType(rawParameter, "AssignmentPattern")) {
39256
- const defaultKnownStateValues = mergeKnownStateValues(callerKnownStateValues, parameterStateValues);
39257
- argumentValue = readStaticEffectValue(rawParameter.right, scopes, stateSymbolId, stateValue, /* @__PURE__ */ new Set(), defaultKnownStateValues);
39258
- } else argumentValue = { value: void 0 };
39259
- if (argumentValue) parameterStateValues.set(parameterSymbol.id, argumentValue);
39260
- }
39261
- return parameterStateValues;
39262
- };
39263
- const haveEqualParameterStateValues = (leftValues, rightValues) => {
39264
- if (leftValues.size !== rightValues.size) return false;
39265
- for (const [symbolId, leftValue] of leftValues) {
39266
- const rightValue = rightValues.get(symbolId);
39267
- if (!rightValue || !Object.is(leftValue.value, rightValue.value)) return false;
39268
- }
39269
- return true;
39270
- };
39271
- const canReaderWorkRunForStateValues = (readerEffect, stateSymbolId, stateValue, scopes, additionalStateValues = /* @__PURE__ */ new Map()) => {
39272
- const pendingFrames = [{
39273
- functionNode: readerEffect.callback,
39274
- parameterStateValues: /* @__PURE__ */ new Map()
39275
- }];
39276
- const visitedParameterStateValues = /* @__PURE__ */ new Map();
39277
- while (pendingFrames.length > 0) {
39278
- const frame = pendingFrames.pop();
39279
- if (!frame || !isFunctionLike$1(frame.functionNode)) continue;
39280
- const functionParameterStateValues = visitedParameterStateValues.get(frame.functionNode) ?? [];
39281
- if (functionParameterStateValues.some((previousValues) => haveEqualParameterStateValues(previousValues, frame.parameterStateValues))) continue;
39282
- functionParameterStateValues.push(frame.parameterStateValues);
39283
- visitedParameterStateValues.set(frame.functionNode, functionParameterStateValues);
39284
- const knownStateValues = mergeKnownStateValues(additionalStateValues, frame.parameterStateValues);
38840
+ const canStateWriteReachReaderWork = (writeInfo, readerEffect, stateSymbolId, scopes) => {
38841
+ if (writeInfo.hasUnknownValue || stateSymbolId === null) return true;
38842
+ for (const writtenValue of writeInfo.values) {
38843
+ const stateValue = { value: writtenValue };
39285
38844
  let didFindReachableWork = false;
39286
- walkInsideStatementBlocks(frame.functionNode.body, (child) => {
39287
- if (didFindReachableWork) return;
39288
- if (isNodeOfType(child, "CallExpression")) {
39289
- const invokedFunction = resolveSynchronouslyInvokedFunction(child.callee, scopes);
39290
- if (invokedFunction && readerEffect.analysisFunctions.has(invokedFunction)) {
39291
- if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) pendingFrames.push({
39292
- functionNode: invokedFunction,
39293
- parameterStateValues: buildInvokedFunctionParameterStateValues(invokedFunction, child, stateSymbolId, stateValue, scopes, additionalStateValues, frame.parameterStateValues)
39294
- });
39295
- return;
39296
- }
39297
- }
39298
- if (isReaderWorkNode(child, readerEffect.analysisFunctions, scopes) && isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes, knownStateValues)) didFindReachableWork = true;
38845
+ visitSynchronousFunctionBodies(readerEffect.analysisFunctions, (child) => {
38846
+ if (didFindReachableWork || !isReaderWorkNode(child, readerEffect.analysisFunctions, scopes)) return;
38847
+ if (isWorkNodeReachableForStateValue(child, stateSymbolId, stateValue, scopes)) didFindReachableWork = true;
39299
38848
  });
39300
38849
  if (didFindReachableWork) return true;
39301
38850
  }
39302
38851
  return false;
39303
38852
  };
39304
- const canStateWriteReachReaderWork = (writtenStateName, writerEffect, readerEffect, stateSymbolIds, scopes) => {
39305
- const writeInfo = writerEffect.stateWrites.get(writtenStateName);
39306
- const stateSymbolId = stateSymbolIds.get(writtenStateName);
39307
- if (!writeInfo || stateSymbolId === void 0) return true;
39308
- if (writeInfo.hasUnknownValue) return true;
39309
- for (const writtenValue of writeInfo.values) if (canReaderWorkRunForStateValues(readerEffect, stateSymbolId, { value: writtenValue }, scopes)) return true;
39310
- return false;
39311
- };
39312
38853
  const EMPTY_CLEANUP_NAME_SET = /* @__PURE__ */ new Set();
39313
38854
  const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39314
38855
  "clear",
@@ -39319,499 +38860,26 @@ const NON_CONTAMINATING_MAP_METHOD_NAMES = new Set([
39319
38860
  "keys",
39320
38861
  "values"
39321
38862
  ]);
39322
- const EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES = new Map([
39323
- ["fetch", new Set([
39324
- "cross-fetch",
39325
- "node-fetch",
39326
- "undici"
39327
- ])],
39328
- ["ky", new Set(["ky"])],
39329
- ["got", new Set(["got"])],
39330
- ["wretch", new Set(["wretch"])],
39331
- ["ofetch", new Set(["ofetch"])],
39332
- ["setTimeout", new Set([
39333
- "node:timers",
39334
- "node:timers/promises",
39335
- "timers",
39336
- "timers/promises"
39337
- ])],
39338
- ["setInterval", new Set([
39339
- "node:timers",
39340
- "node:timers/promises",
39341
- "timers",
39342
- "timers/promises"
39343
- ])]
39344
- ]);
39345
- const EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES = new Map([
39346
- ["axios", "axios"],
39347
- ["cross-fetch", "fetch"],
39348
- ["got", "got"],
39349
- ["ky", "ky"],
39350
- ["node-fetch", "fetch"],
39351
- ["wretch", "wretch"]
39352
- ]);
39353
- const EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES = new Set([
39354
- "axios",
39355
- "cross-fetch",
39356
- "got",
39357
- "ky",
39358
- "node-fetch",
39359
- "ofetch",
39360
- "undici",
39361
- "wretch"
39362
- ]);
39363
- const EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES = new Set([
39364
- "axios",
39365
- "got",
39366
- "ky",
39367
- "ofetch",
39368
- "wretch"
39369
- ]);
39370
- const EXTERNAL_SYNC_HTTP_METHOD_NAMES = new Set([
39371
- ...EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES,
39372
- "fetch",
39373
- "patch",
39374
- "post",
39375
- "put",
39376
- "request"
39377
- ]);
39378
- const TANSTACK_QUERY_CLIENT_MODULE_SOURCES = new Set(["@tanstack/query-core", "@tanstack/react-query"]);
39379
- const TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES = new Set(["fetchQuery", "prefetchQuery"]);
39380
- const EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES = new Set([
39381
- ...EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS,
39382
- ...SOCKET_CONSTRUCTOR_NAMES_REQUIRING_CLEANUP,
39383
- "EventTarget",
39384
- "XMLHttpRequest"
39385
- ]);
39386
- const getDestructuredBindingDepth = (bindingIdentifier) => {
39387
- let bindingNode = bindingIdentifier;
39388
- let depth = 0;
39389
- while (true) {
39390
- if (isNodeOfType(bindingNode.parent, "AssignmentPattern") && bindingNode.parent.left === bindingNode) bindingNode = bindingNode.parent;
39391
- const property = bindingNode.parent;
39392
- if (!property || !isNodeOfType(property, "Property") || property.value !== bindingNode || !property.parent || !isNodeOfType(property.parent, "ObjectPattern")) return depth;
39393
- depth += 1;
39394
- bindingNode = property.parent;
39395
- }
39396
- };
39397
- const isMutatedMemberExpression = (rawMemberExpression) => {
39398
- let expression = findTransparentExpressionRoot(rawMemberExpression);
39399
- while (expression.parent) {
39400
- const parent = expression.parent;
39401
- if (isNodeOfType(parent, "MemberExpression") && parent.object === expression) {
39402
- expression = findTransparentExpressionRoot(parent);
39403
- continue;
39404
- }
39405
- if (isNodeOfType(parent, "Property") && parent.value === expression && isNodeOfType(parent.parent, "ObjectPattern")) {
39406
- expression = findTransparentExpressionRoot(parent);
39407
- continue;
39408
- }
39409
- if (isNodeOfType(parent, "ObjectPattern") && parent.properties.some((property) => property === expression)) {
39410
- expression = findTransparentExpressionRoot(parent);
39411
- continue;
39412
- }
39413
- if (isNodeOfType(parent, "ArrayPattern") && parent.elements.some((element) => element === expression)) {
39414
- expression = findTransparentExpressionRoot(parent);
39415
- continue;
39416
- }
39417
- if (isNodeOfType(parent, "RestElement") && parent.argument === expression || isNodeOfType(parent, "AssignmentPattern") && parent.left === expression) {
39418
- expression = findTransparentExpressionRoot(parent);
39419
- continue;
39420
- }
39421
- break;
39422
- }
39423
- const parent = expression.parent;
39424
- return Boolean(isNodeOfType(parent, "AssignmentExpression") && parent.left === expression || isNodeOfType(parent, "UpdateExpression") && parent.argument === expression || isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expression || (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) && parent.left === expression);
39425
- };
39426
- const isReactHookDependencyArgument = (expression, scopes) => {
39427
- const call = expression.parent;
39428
- return Boolean(isNodeOfType(call, "CallExpression") && call.arguments[1] === expression && isReactApiCall(call, HOOKS_WITH_DEPS, scopes, {
39429
- allowGlobalReactNamespace: true,
39430
- allowUnboundBareCalls: true,
39431
- resolveNamedAliases: true
39432
- }));
39433
- };
39434
- const isPlainDependencyObjectExpression = (expression) => isNodeOfType(expression, "ObjectExpression") && expression.properties.every((property) => isNodeOfType(property, "Property") && property.kind === "init" && !property.computed && !property.method);
39435
- const getContainingReactDependencyValue = (rawExpression, containerKind) => {
39436
- const expression = findTransparentExpressionRoot(rawExpression);
39437
- const parent = expression.parent;
39438
- if (isNodeOfType(parent, "ArrayExpression")) return {
39439
- expression: parent,
39440
- containerKind: "array"
39441
- };
39442
- if (containerKind === "array" && isNodeOfType(parent, "SpreadElement") && parent.argument === expression && isNodeOfType(parent.parent, "ArrayExpression")) return {
39443
- expression: parent.parent,
39444
- containerKind: "array"
39445
- };
39446
- if (isNodeOfType(parent, "Property") && (parent.value === expression || parent.shorthand && parent.key === expression) && parent.parent && isPlainDependencyObjectExpression(parent.parent)) return {
39447
- expression: parent.parent,
39448
- containerKind: "object"
39449
- };
39450
- return null;
39451
- };
39452
- const getFullArrayRestCopySymbol = (initializer, scopes) => {
39453
- const declaration = initializer.parent;
39454
- if (!isNodeOfType(declaration, "VariableDeclarator") || declaration.init !== initializer || !isNodeOfType(declaration.id, "ArrayPattern") || declaration.id.elements.length !== 1) return null;
39455
- const restElement = declaration.id.elements[0];
39456
- if (!isNodeOfType(restElement, "RestElement") || !isNodeOfType(restElement.argument, "Identifier")) return null;
39457
- const symbol = scopes.symbolFor(restElement.argument);
39458
- return symbol?.kind === "const" && symbol.declarationNode === declaration && symbol.references.every((reference) => reference.flag === "read") ? symbol : null;
39459
- };
39460
- const isReactDependencyArrayReference = (initialExpression, scopes) => {
39461
- const pendingPaths = [{
39462
- expression: initialExpression,
39463
- containerKind: null
39464
- }];
39465
- const visitedExpressions = /* @__PURE__ */ new Set();
39466
- let didFindReactDependencyArgument = false;
39467
- while (pendingPaths.length > 0) {
39468
- const pendingPath = pendingPaths.pop();
39469
- if (!pendingPath) continue;
39470
- const expression = findTransparentExpressionRoot(pendingPath.expression);
39471
- if (visitedExpressions.has(expression)) continue;
39472
- visitedExpressions.add(expression);
39473
- if (pendingPath.containerKind === "array" && isReactHookDependencyArgument(expression, scopes)) {
39474
- didFindReactDependencyArgument = true;
39475
- continue;
39476
- }
39477
- const containingValue = getContainingReactDependencyValue(expression, pendingPath.containerKind);
39478
- if (containingValue) {
39479
- pendingPaths.push(containingValue);
39480
- continue;
39481
- }
39482
- const declaration = expression.parent;
39483
- if (isNodeOfType(declaration, "VariableDeclarator") && declaration.init === expression && isNodeOfType(declaration.id, "Identifier")) {
39484
- const symbol = scopes.symbolFor(declaration.id);
39485
- if (!symbol || getDirectUnreassignedInitializer(symbol) !== expression) return false;
39486
- if (symbol.references.length === 0) return false;
39487
- for (const reference of symbol.references) pendingPaths.push({
39488
- expression: reference.identifier,
39489
- containerKind: pendingPath.containerKind
39490
- });
39491
- continue;
39492
- }
39493
- if (pendingPath.containerKind === "array") {
39494
- const restCopySymbol = getFullArrayRestCopySymbol(expression, scopes);
39495
- if (restCopySymbol) {
39496
- if (restCopySymbol.references.length === 0) return false;
39497
- for (const reference of restCopySymbol.references) pendingPaths.push({
39498
- expression: reference.identifier,
39499
- containerKind: "array"
39500
- });
39501
- continue;
39502
- }
39503
- }
39504
- return false;
39505
- }
39506
- return didFindReactDependencyArgument;
39507
- };
39508
- const hasSafeReceiverAliases = (rootSymbols, scopes) => {
39509
- const pendingSymbols = [...rootSymbols];
39510
- const visitedSymbolIds = /* @__PURE__ */ new Set();
39511
- while (pendingSymbols.length > 0) {
39512
- const symbol = pendingSymbols.pop();
39513
- if (!symbol || visitedSymbolIds.has(symbol.id)) continue;
39514
- visitedSymbolIds.add(symbol.id);
39515
- for (const reference of symbol.references) {
39516
- const expression = findTransparentExpressionRoot(reference.identifier);
39517
- const container = expression.parent;
39518
- if (isReactDependencyArrayReference(expression, scopes)) continue;
39519
- if (isNodeOfType(container, "MemberExpression") && container.object === expression) {
39520
- if (isMutatedMemberExpression(container)) return false;
39521
- continue;
39522
- }
39523
- if (isNodeOfType(container, "CallExpression") && container.callee === expression) continue;
39524
- if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "ObjectPattern")) continue;
39525
- if (isNodeOfType(container, "AssignmentExpression") && container.right === expression && isNodeOfType(container.left, "ObjectPattern")) continue;
39526
- if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
39527
- const aliasSymbol = scopes.symbolFor(container.id);
39528
- const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
39529
- if (!aliasSymbol || aliasInitializer !== expression) return false;
39530
- pendingSymbols.push(aliasSymbol);
39531
- continue;
39532
- }
39533
- return false;
38863
+ const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
38864
+ if (isNodeOfType(returnedValue, "ArrowFunctionExpression") || isNodeOfType(returnedValue, "FunctionExpression")) return true;
38865
+ if (isNodeOfType(returnedValue, "CallExpression")) {
38866
+ if (isNodeOfType(returnedValue.callee, "Identifier")) {
38867
+ const setterSymbol = resolveConstIdentifierAlias(returnedValue.callee, scopes, true);
38868
+ if (setterToStateName.has(returnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
38869
+ if (isSetterIdentifier(returnedValue.callee.name)) return true;
39534
38870
  }
38871
+ return isCleanupReturn(returnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
39535
38872
  }
39536
- return true;
39537
- };
39538
- const hasProvenGlobalNamespaceReference = (expression, namespaceNames, scopes) => {
39539
- for (const namespaceName of namespaceNames) if (isProvenGlobalNamespaceReference(expression, namespaceName, scopes)) return true;
38873
+ if (isNodeOfType(returnedValue, "Identifier")) return true;
39540
38874
  return false;
39541
38875
  };
39542
- const getTypeScriptImportEqualsSource = (symbol) => {
39543
- if (symbol.kind !== "ts-import-equals" || !isNodeOfType(symbol.declarationNode, "TSImportEqualsDeclaration")) return null;
39544
- const moduleReference = symbol.declarationNode.moduleReference;
39545
- if (!isNodeOfType(moduleReference, "TSExternalModuleReference") || !isNodeOfType(moduleReference.expression, "Literal")) return null;
39546
- return typeof moduleReference.expression.value === "string" ? moduleReference.expression.value : null;
39547
- };
39548
- const getUnshadowedRequireSource = (rawExpression, scopes) => {
39549
- let expression = stripParenExpression(rawExpression);
39550
- while (isNodeOfType(expression, "MemberExpression")) expression = stripParenExpression(expression.object);
39551
- if (!isNodeOfType(expression, "CallExpression") || !isNodeOfType(expression.callee, "Identifier") || expression.callee.name !== "require" || !scopes.isGlobalReference(expression.callee)) return null;
39552
- return getRequireCallSource(rawExpression);
39553
- };
39554
- const resolveExternalModuleBinding = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set(), ignoreCommonJsMutation = false) => {
39555
- let expression = stripParenExpression(rawExpression);
39556
- let outermostExportedName = null;
39557
- let exportedPropertyDepth = 0;
39558
- let resolvedBinding = null;
39559
- let isCommonJsModuleObject = false;
39560
- const traversedSymbolIds = new Set(visitedSymbolIds);
39561
- const commonJsModuleObjectSymbols = /* @__PURE__ */ new Set();
39562
- while (true) {
39563
- if (isNodeOfType(expression, "MemberExpression")) {
39564
- const propertyName = getStaticPropertyName(expression);
39565
- if (propertyName === null) return null;
39566
- outermostExportedName ??= propertyName;
39567
- exportedPropertyDepth += 1;
39568
- expression = stripParenExpression(expression.object);
39569
- continue;
39570
- }
39571
- const requireSource = getUnshadowedRequireSource(expression, scopes);
39572
- if (requireSource !== null) {
39573
- resolvedBinding = {
39574
- source: requireSource,
39575
- exportedName: null,
39576
- isModuleObject: true
39577
- };
39578
- isCommonJsModuleObject = true;
39579
- break;
39580
- }
39581
- if (!isNodeOfType(expression, "Identifier")) return null;
39582
- const symbol = scopes.symbolFor(expression);
39583
- if (!symbol || traversedSymbolIds.has(symbol.id)) return null;
39584
- if (symbol.kind === "import") {
39585
- const importDeclaration = symbol.declarationNode.parent;
39586
- if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string") return null;
39587
- if (isNodeOfType(symbol.declarationNode, "ImportSpecifier")) resolvedBinding = {
39588
- source: importDeclaration.source.value,
39589
- exportedName: getImportedName(symbol.declarationNode) ?? null,
39590
- isModuleObject: false
39591
- };
39592
- else if (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier")) resolvedBinding = {
39593
- source: importDeclaration.source.value,
39594
- exportedName: "default",
39595
- isModuleObject: false
39596
- };
39597
- else if (isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")) resolvedBinding = {
39598
- source: importDeclaration.source.value,
39599
- exportedName: null,
39600
- isModuleObject: true
39601
- };
39602
- break;
39603
- }
39604
- const importEqualsSource = getTypeScriptImportEqualsSource(symbol);
39605
- if (importEqualsSource !== null) {
39606
- resolvedBinding = {
39607
- source: importEqualsSource,
39608
- exportedName: null,
39609
- isModuleObject: true
39610
- };
39611
- isCommonJsModuleObject = true;
39612
- commonJsModuleObjectSymbols.add(symbol);
39613
- break;
39614
- }
39615
- const directInitializer = getDirectUnreassignedInitializer(symbol);
39616
- const destructuredPropertyName = getDestructuredBindingPropertyName(symbol.bindingIdentifier);
39617
- const initializer = directInitializer ?? (destructuredPropertyName !== null && symbol.kind === "const" && isNodeOfType(symbol.declarationNode, "VariableDeclarator") ? symbol.declarationNode.init : null);
39618
- if (!initializer) return null;
39619
- traversedSymbolIds.add(symbol.id);
39620
- outermostExportedName ??= destructuredPropertyName;
39621
- if (destructuredPropertyName !== null) exportedPropertyDepth += getDestructuredBindingDepth(symbol.bindingIdentifier);
39622
- const unwrappedInitializer = stripParenExpression(initializer);
39623
- if (destructuredPropertyName === null && !isNodeOfType(unwrappedInitializer, "MemberExpression")) commonJsModuleObjectSymbols.add(symbol);
39624
- expression = unwrappedInitializer;
39625
- }
39626
- if (!resolvedBinding) return null;
39627
- if (!ignoreCommonJsMutation && isCommonJsModuleObject && !hasSafeReceiverAliases(commonJsModuleObjectSymbols, scopes)) return null;
39628
- if (outermostExportedName === null) return resolvedBinding;
39629
- if (!resolvedBinding.isModuleObject || exportedPropertyDepth !== 1) return null;
39630
- return {
39631
- source: resolvedBinding.source,
39632
- exportedName: outermostExportedName,
39633
- isModuleObject: false
39634
- };
39635
- };
39636
- const getCanonicalExternalSyncExportName = (binding) => {
39637
- if (binding.exportedName && binding.exportedName !== "default") return binding.exportedName;
39638
- return EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.get(binding.source) ?? null;
39639
- };
39640
- const isKnownExternalSyncDirectModuleBinding = (binding) => {
39641
- const exportedName = getCanonicalExternalSyncExportName(binding);
39642
- return Boolean(exportedName && EXTERNAL_SYNC_DIRECT_IMPORT_SOURCES.get(exportedName)?.has(binding.source));
39643
- };
39644
- const DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES = new Set([
39645
- "Array",
39646
- "BigInt",
39647
- "Boolean",
39648
- "Date",
39649
- "Number",
39650
- "String",
39651
- "Symbol"
39652
- ]);
39653
- const DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES = new Set([
39654
- "Array",
39655
- "Boolean",
39656
- "Date",
39657
- "Map",
39658
- "Number",
39659
- "Promise",
39660
- "RegExp",
39661
- "Set",
39662
- "String",
39663
- "URL",
39664
- "URLSearchParams",
39665
- "WeakMap",
39666
- "WeakSet"
39667
- ]);
39668
- const PROMISE_STATIC_RESULT_METHOD_NAMES = new Set([
39669
- "all",
39670
- "allSettled",
39671
- "any",
39672
- "race",
39673
- "reject",
39674
- "resolve",
39675
- "withResolvers"
39676
- ]);
39677
- const returnsOnlyNonCleanupValues = (functionNode, setterToStateName, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
39678
- if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return false;
39679
- const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
39680
- const isNonCleanupValue = (returnValue) => {
39681
- const expression = stripParenExpression(returnValue);
39682
- if (isNodeOfType(expression, "Identifier") && expression.name === "undefined" && scopes.isGlobalReference(expression)) return true;
39683
- if (isNodeOfType(expression, "Literal") || isNodeOfType(expression, "ArrayExpression") || isNodeOfType(expression, "ObjectExpression") || isNodeOfType(expression, "JSXElement") || isNodeOfType(expression, "JSXFragment") || isNodeOfType(expression, "TemplateLiteral") || isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return true;
39684
- if (isNodeOfType(expression, "ConditionalExpression")) return isNonCleanupValue(expression.consequent) && isNonCleanupValue(expression.alternate);
39685
- if (isNodeOfType(expression, "LogicalExpression")) return isNonCleanupValue(expression.left) && isNonCleanupValue(expression.right);
39686
- if (isNodeOfType(expression, "SequenceExpression")) {
39687
- const finalExpression = expression.expressions.at(-1);
39688
- return Boolean(finalExpression && isNonCleanupValue(finalExpression));
39689
- }
39690
- if (isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CONSTRUCTOR_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
39691
- if (!isNodeOfType(expression, "CallExpression")) return false;
39692
- if (isNodeOfType(expression.callee, "Identifier") && DEFINITELY_NON_FUNCTION_GLOBAL_CALL_NAMES.has(expression.callee.name) && scopes.isGlobalReference(expression.callee)) return true;
39693
- if (isNodeOfType(expression.callee, "MemberExpression") && isNodeOfType(expression.callee.object, "Identifier") && expression.callee.object.name === "Promise" && scopes.isGlobalReference(expression.callee.object) && PROMISE_STATIC_RESULT_METHOD_NAMES.has(getStaticPropertyName(expression.callee) ?? "")) return true;
39694
- if (isNodeOfType(expression.callee, "Identifier") && setterToStateName.has(expression.callee.name)) return true;
39695
- const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
39696
- return Boolean(invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes, nextVisitedFunctions));
39697
- };
39698
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return isNonCleanupValue(functionNode.body);
39699
- let returnsOnlyNonCleanup = true;
39700
- walkAst(functionNode.body, (child) => {
39701
- if (!returnsOnlyNonCleanup) return false;
39702
- if (child !== functionNode.body && isFunctionLike$1(child)) return false;
39703
- if (!isNodeOfType(child, "ReturnStatement")) return;
39704
- if (child.argument && !isNonCleanupValue(child.argument)) {
39705
- returnsOnlyNonCleanup = false;
39706
- return false;
39707
- }
39708
- });
39709
- return returnsOnlyNonCleanup;
39710
- };
39711
- const getResolvedFunctionCleanupReturnProof = (functionNode, scopes, visitedFunctions = /* @__PURE__ */ new Set()) => {
39712
- if (!isFunctionLike$1(functionNode) || functionNode.async || functionNode.generator || visitedFunctions.has(functionNode)) return {
39713
- hasCleanup: false,
39714
- isValid: false
39715
- };
39716
- const nextVisitedFunctions = new Set(visitedFunctions).add(functionNode);
39717
- const getCleanupReturnProof = (returnValue) => {
39718
- const expression = stripParenExpression(returnValue);
39719
- if (isNodeOfType(expression, "ArrowFunctionExpression") || isNodeOfType(expression, "FunctionExpression")) return {
39720
- hasCleanup: true,
39721
- isValid: true
39722
- };
39723
- if (isNodeOfType(expression, "Identifier")) {
39724
- if (expression.name === "undefined" && scopes.isGlobalReference(expression)) return {
39725
- hasCleanup: false,
39726
- isValid: true
39727
- };
39728
- const localFunction = resolveExactLocalFunction(expression, scopes);
39729
- return {
39730
- hasCleanup: Boolean(localFunction),
39731
- isValid: Boolean(localFunction)
39732
- };
39733
- }
39734
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return {
39735
- hasCleanup: false,
39736
- isValid: true
39737
- };
39738
- if (isNodeOfType(expression, "ConditionalExpression")) {
39739
- const consequentProof = getCleanupReturnProof(expression.consequent);
39740
- const alternateProof = getCleanupReturnProof(expression.alternate);
39741
- return {
39742
- hasCleanup: consequentProof.hasCleanup || alternateProof.hasCleanup,
39743
- isValid: consequentProof.isValid && alternateProof.isValid
39744
- };
39745
- }
39746
- if (isNodeOfType(expression, "LogicalExpression")) {
39747
- const leftProof = getCleanupReturnProof(expression.left);
39748
- const rightProof = getCleanupReturnProof(expression.right);
39749
- return {
39750
- hasCleanup: leftProof.hasCleanup || rightProof.hasCleanup,
39751
- isValid: leftProof.isValid && rightProof.isValid
39752
- };
39753
- }
39754
- if (isNodeOfType(expression, "SequenceExpression")) {
39755
- const finalExpression = expression.expressions.at(-1);
39756
- return finalExpression ? getCleanupReturnProof(finalExpression) : {
39757
- hasCleanup: false,
39758
- isValid: false
39759
- };
39760
- }
39761
- if (!isNodeOfType(expression, "CallExpression")) return {
39762
- hasCleanup: false,
39763
- isValid: false
39764
- };
39765
- const invokedFunction = resolveSynchronouslyInvokedFunction(expression.callee, scopes);
39766
- return invokedFunction ? getResolvedFunctionCleanupReturnProof(invokedFunction, scopes, nextVisitedFunctions) : {
39767
- hasCleanup: false,
39768
- isValid: false
39769
- };
39770
- };
39771
- if (!isNodeOfType(functionNode.body, "BlockStatement")) return getCleanupReturnProof(functionNode.body);
39772
- let cleanupReturnProof = {
39773
- hasCleanup: false,
39774
- isValid: true
39775
- };
39776
- walkAst(functionNode.body, (child) => {
39777
- if (!cleanupReturnProof.isValid) return false;
39778
- if (child !== functionNode.body && isFunctionLike$1(child)) return false;
39779
- if (!isNodeOfType(child, "ReturnStatement")) return;
39780
- if (!child.argument) return;
39781
- const returnProof = getCleanupReturnProof(child.argument);
39782
- if (!returnProof.isValid) {
39783
- cleanupReturnProof = {
39784
- hasCleanup: false,
39785
- isValid: false
39786
- };
39787
- return false;
39788
- }
39789
- cleanupReturnProof.hasCleanup ||= returnProof.hasCleanup;
39790
- });
39791
- return cleanupReturnProof;
39792
- };
39793
- const isFunctionShapedReturn = (returnedValue, setterToStateName, setterSymbolIdToStateName, scopes, isExplicitReturnStatement) => {
39794
- const unwrappedReturnedValue = stripParenExpression(returnedValue);
39795
- if (isNodeOfType(unwrappedReturnedValue, "ArrowFunctionExpression") || isNodeOfType(unwrappedReturnedValue, "FunctionExpression")) return true;
39796
- if (isNodeOfType(unwrappedReturnedValue, "CallExpression")) {
39797
- if (isNodeOfType(unwrappedReturnedValue.callee, "Identifier")) {
39798
- const setterSymbol = resolveConstIdentifierAlias(unwrappedReturnedValue.callee, scopes, true);
39799
- if (setterToStateName.has(unwrappedReturnedValue.callee.name) || setterSymbol && setterSymbolIdToStateName.has(setterSymbol.id)) return false;
39800
- }
39801
- const invokedFunction = resolveSynchronouslyInvokedFunction(unwrappedReturnedValue.callee, scopes);
39802
- if (invokedFunction && returnsOnlyNonCleanupValues(invokedFunction, setterToStateName, scopes)) return false;
39803
- if (invokedFunction) {
39804
- const cleanupReturnProof = getResolvedFunctionCleanupReturnProof(invokedFunction, scopes);
39805
- if (cleanupReturnProof.isValid) return cleanupReturnProof.hasCleanup;
39806
- }
39807
- if (!invokedFunction && isNodeOfType(unwrappedReturnedValue.callee, "Identifier") && isSetterIdentifier(unwrappedReturnedValue.callee.name)) return true;
39808
- return isCleanupReturn(unwrappedReturnedValue, EMPTY_CLEANUP_NAME_SET, EMPTY_CLEANUP_NAME_SET, { allowOpaqueReturn: isExplicitReturnStatement });
39809
- }
39810
- if (isNodeOfType(unwrappedReturnedValue, "Identifier")) return true;
38876
+ const STORAGE_GLOBAL_NAMES = new Set(["localStorage", "sessionStorage"]);
38877
+ const isBrowserStorageReceiver = (receiver) => {
38878
+ if (!receiver) return false;
38879
+ if (isNodeOfType(receiver, "Identifier")) return STORAGE_GLOBAL_NAMES.has(receiver.name);
38880
+ if (isNodeOfType(receiver, "MemberExpression")) return isNodeOfType(receiver.property, "Identifier") && STORAGE_GLOBAL_NAMES.has(receiver.property.name);
39811
38881
  return false;
39812
38882
  };
39813
- const STORAGE_GLOBAL_NAMES = ["localStorage", "sessionStorage"];
39814
- const isBrowserStorageReceiver = (receiver, scopes) => Boolean(receiver && STORAGE_GLOBAL_NAMES.some((storageName) => isProvenGlobalNamespaceReference(receiver, storageName, scopes)));
39815
38883
  const STORAGE_HOOK_PATTERN = /^use\w*Storage/i;
39816
38884
  const collectStorageHookSetterNames = (componentBody) => {
39817
38885
  const setterNames = /* @__PURE__ */ new Set();
@@ -39843,16 +38911,23 @@ const callsOpaqueExternalSetter = (analysisFunctions, setterToStateName) => {
39843
38911
  });
39844
38912
  return didFindOpaqueSetterCall;
39845
38913
  };
39846
- const isReactUseRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "useRef", scopes, {
38914
+ const isReactRefCall = (expression, scopes) => isNodeOfType(expression, "CallExpression") && (isReactApiCall(expression, "useRef", scopes, {
39847
38915
  allowGlobalReactNamespace: true,
39848
38916
  allowUnboundBareCalls: true,
39849
38917
  resolveNamedAliases: true
39850
- });
39851
- const isReactRefCall = (expression, scopes) => isReactUseRefCall(expression, scopes) || isNodeOfType(expression, "CallExpression") && isReactApiCall(expression, "createRef", scopes, {
38918
+ }) || isReactApiCall(expression, "createRef", scopes, {
39852
38919
  allowGlobalReactNamespace: true,
39853
38920
  allowUnboundBareCalls: true,
39854
38921
  resolveNamedAliases: true
39855
- });
38922
+ }));
38923
+ const getDirectReactRefSymbol = (rawExpression, scopes) => {
38924
+ const expression = stripParenExpression(rawExpression);
38925
+ if (!isNodeOfType(expression, "Identifier")) return null;
38926
+ const symbol = scopes.symbolFor(expression);
38927
+ if (!symbol) return null;
38928
+ const initializer = getDirectUnreassignedInitializer(symbol);
38929
+ return initializer && isReactRefCall(stripParenExpression(initializer), scopes) ? symbol : null;
38930
+ };
39856
38931
  const isReactNativeJsxElement = (openingElement, scopes) => {
39857
38932
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
39858
38933
  const symbol = scopes.symbolFor(openingElement.name);
@@ -39860,33 +38935,19 @@ const isReactNativeJsxElement = (openingElement, scopes) => {
39860
38935
  return Boolean(symbol?.kind === "import" && importDeclaration && isNodeOfType(importDeclaration, "ImportDeclaration") && importDeclaration.source.value === "react-native");
39861
38936
  };
39862
38937
  const isDirectHostJsxRef = (symbol, scopes) => {
39863
- let didFindHostRef = false;
39864
- const visitedSymbolIds = /* @__PURE__ */ new Set();
39865
- const pendingSymbols = [symbol];
39866
- while (pendingSymbols.length > 0) {
39867
- const currentSymbol = pendingSymbols.pop();
39868
- if (!currentSymbol || visitedSymbolIds.has(currentSymbol.id)) continue;
39869
- visitedSymbolIds.add(currentSymbol.id);
39870
- for (const reference of currentSymbol.references) {
39871
- const expression = findTransparentExpressionRoot(reference.identifier);
39872
- const container = expression.parent;
39873
- if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
39874
- if (isNodeOfType(container, "VariableDeclarator") && container.init === expression && isNodeOfType(container.id, "Identifier")) {
39875
- const aliasSymbol = scopes.symbolFor(container.id);
39876
- const aliasInitializer = aliasSymbol && getDirectUnreassignedInitializer(aliasSymbol);
39877
- if (!aliasSymbol || aliasInitializer !== expression) return false;
39878
- pendingSymbols.push(aliasSymbol);
39879
- continue;
39880
- }
39881
- if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
39882
- const attribute = container.parent;
39883
- if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
39884
- const openingElement = attribute.parent;
39885
- if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
39886
- didFindHostRef = true;
39887
- }
39888
- }
39889
- return didFindHostRef;
38938
+ let hostRefCount = 0;
38939
+ for (const reference of symbol.references) {
38940
+ const expression = findTransparentExpressionRoot(reference.identifier);
38941
+ const container = expression.parent;
38942
+ if (isNodeOfType(container, "MemberExpression") && container.object === expression && getStaticPropertyName(container) === "current") continue;
38943
+ if (!container || !isNodeOfType(container, "JSXExpressionContainer") || container.expression !== expression) return false;
38944
+ const attribute = container.parent;
38945
+ if (!attribute || !isNodeOfType(attribute, "JSXAttribute") || getJsxAttributeName(attribute.name) !== "ref") return false;
38946
+ const openingElement = attribute.parent;
38947
+ if (!openingElement || !isNodeOfType(openingElement, "JSXOpeningElement") || !isProvenIntrinsicJsxElement(openingElement, scopes) && !isReactNativeJsxElement(openingElement, scopes)) return false;
38948
+ hostRefCount += 1;
38949
+ }
38950
+ return hostRefCount > 0;
39890
38951
  };
39891
38952
  const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39892
38953
  const identifier = stripParenExpression(expression);
@@ -39896,18 +38957,7 @@ const isIntrinsicRefCallbackParameter = (expression, scopes) => {
39896
38957
  const rawFirstParameter = callback.params?.[0];
39897
38958
  const firstParameter = isNodeOfType(rawFirstParameter, "AssignmentPattern") ? rawFirstParameter.left : rawFirstParameter;
39898
38959
  const symbol = scopes.symbolFor(identifier);
39899
- return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter && symbol.references.every((reference) => {
39900
- if (reference.flag !== "read") return false;
39901
- let referenceRoot = reference.identifier;
39902
- while (referenceRoot.parent) {
39903
- const parent = referenceRoot.parent;
39904
- if (isNodeOfType(parent, "AssignmentExpression")) return parent.left !== referenceRoot;
39905
- if (isNodeOfType(parent, "UpdateExpression")) return parent.argument !== referenceRoot;
39906
- if (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) return parent.left !== referenceRoot;
39907
- referenceRoot = parent;
39908
- }
39909
- return true;
39910
- }));
38960
+ return Boolean(firstParameter && symbol?.bindingIdentifier === firstParameter);
39911
38961
  };
39912
38962
  const getDirectReactRefCall = (symbol, scopes) => {
39913
38963
  const initializer = getDirectUnreassignedInitializer(symbol);
@@ -39915,38 +38965,15 @@ const getDirectReactRefCall = (symbol, scopes) => {
39915
38965
  const expression = stripParenExpression(initializer);
39916
38966
  return isNodeOfType(expression, "CallExpression") && isReactRefCall(expression, scopes) ? expression : null;
39917
38967
  };
39918
- const isEmptyGlobalMapConstruction = (rawExpression, scopes) => {
39919
- const expression = stripParenExpression(rawExpression);
39920
- return isNodeOfType(expression, "NewExpression") && isNodeOfType(expression.callee, "Identifier") && expression.callee.name === "Map" && scopes.isGlobalReference(expression.callee) && expression.arguments.length === 0;
39921
- };
39922
- const hasEmptyRefSentinelInitializer = (refCall, scopes) => {
39923
- if (!isReactUseRefCall(refCall, scopes) || refCall.arguments.length > 1) return false;
39924
- const [initialValue] = refCall.arguments;
39925
- return !initialValue || isNodeOfType(initialValue, "Literal") && initialValue.value === null || isNodeOfType(initialValue, "Identifier") && initialValue.name === "undefined" && scopes.isGlobalReference(initialValue);
39926
- };
39927
- const isDirectLazyEmptyMapInitialization = (currentExpression, symbol, scopes) => {
39928
- const assignment = currentExpression.parent;
39929
- if (!isNodeOfType(assignment, "AssignmentExpression") || assignment.left !== currentExpression || assignment.operator !== "??=" || !isEmptyGlobalMapConstruction(assignment.right, scopes)) return false;
39930
- const refOwner = findEnclosingFunction$1(symbol.bindingIdentifier);
39931
- return refOwner !== null && findEnclosingFunction$1(assignment) === refOwner;
39932
- };
39933
38968
  const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
39934
- const refCall = getDirectReactRefCall(symbol, scopes);
39935
- const initialValue = refCall?.arguments[0];
39936
- const hasDirectEmptyMapInitializer = Boolean(initialValue && isEmptyGlobalMapConstruction(initialValue, scopes));
39937
- const hasLazyEmptyMapInitializer = Boolean(refCall && hasEmptyRefSentinelInitializer(refCall, scopes));
39938
- if (!hasDirectEmptyMapInitializer && !hasLazyEmptyMapInitializer) return false;
38969
+ const initialValue = getDirectReactRefCall(symbol, scopes)?.arguments?.[0];
38970
+ if (!initialValue || !isNodeOfType(initialValue, "NewExpression") || !isNodeOfType(initialValue.callee, "Identifier") || initialValue.callee.name !== "Map" || !scopes.isGlobalReference(initialValue.callee) || initialValue.arguments.length !== 0) return false;
39939
38971
  let intrinsicValueWriteCount = 0;
39940
- let lazyEmptyMapInitializationCount = 0;
39941
38972
  for (const reference of symbol.references) {
39942
38973
  const identifier = findTransparentExpressionRoot(reference.identifier);
39943
38974
  const currentMember = identifier.parent;
39944
38975
  if (!isNodeOfType(currentMember, "MemberExpression") || currentMember.object !== identifier || getStaticPropertyName(currentMember) !== "current") return false;
39945
38976
  const currentExpression = findTransparentExpressionRoot(currentMember);
39946
- if (hasLazyEmptyMapInitializer && isDirectLazyEmptyMapInitialization(currentExpression, symbol, scopes)) {
39947
- lazyEmptyMapInitializationCount += 1;
39948
- continue;
39949
- }
39950
38977
  const methodMember = currentExpression.parent;
39951
38978
  if (!isNodeOfType(methodMember, "MemberExpression") || methodMember.object !== currentExpression) return false;
39952
38979
  const methodName = getStaticPropertyName(methodMember);
@@ -39959,7 +38986,7 @@ const storesOnlyIntrinsicRefCallbackValues = (symbol, scopes) => {
39959
38986
  if (!storedValue || isNodeOfType(storedValue, "SpreadElement") || !isIntrinsicRefCallbackParameter(storedValue, scopes)) return false;
39960
38987
  intrinsicValueWriteCount += 1;
39961
38988
  }
39962
- return intrinsicValueWriteCount > 0 && (hasDirectEmptyMapInitializer || lazyEmptyMapInitializationCount === 1);
38989
+ return intrinsicValueWriteCount > 0;
39963
38990
  };
39964
38991
  const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollectionValue = false, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39965
38992
  const expression = stripParenExpression(rawExpression);
@@ -39973,11 +39000,7 @@ const isDerivedFromProvenDomRefCurrent = (rawExpression, scopes, didReadCollecti
39973
39000
  }
39974
39001
  if (isNodeOfType(expression, "MemberExpression")) {
39975
39002
  if (getStaticPropertyName(expression) === "current") {
39976
- const symbol = resolveReactRefSymbol(expression, scopes, {
39977
- allowUnboundBareCalls: true,
39978
- includeCreateRef: true,
39979
- resolveNamedAliases: true
39980
- });
39003
+ const symbol = getDirectReactRefSymbol(expression.object, scopes);
39981
39004
  return Boolean(symbol && (isDirectHostJsxRef(symbol, scopes) || didReadCollectionValue && storesOnlyIntrinsicRefCallbackValues(symbol, scopes)));
39982
39005
  }
39983
39006
  return isDerivedFromProvenDomRefCurrent(expression.object, scopes, didReadCollectionValue, visitedSymbolIds);
@@ -39995,80 +39018,19 @@ const isCommittedDomSyncNode = (node, scopes) => {
39995
39018
  if (propertyName === null || !EXTERNAL_SYNC_DOM_MEMBER_METHOD_NAMES.has(propertyName)) return false;
39996
39019
  return isDerivedFromProvenDomRefCurrent(callee.object, scopes) || isProvenBrowserApiReceiver(callee.object, "dom-event-target", scopes);
39997
39020
  };
39998
- const isProvenHttpClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
39999
- const expression = stripParenExpression(rawExpression);
40000
- if (isNodeOfType(expression, "Identifier")) {
40001
- const receiverSymbol = scopes.symbolFor(expression);
40002
- if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
40003
- }
40004
- if (hasProvenGlobalNamespaceReference(expression, EXTERNAL_SYNC_GLOBAL_HTTP_CLIENT_NAMES, scopes)) return true;
40005
- const moduleBinding = resolveExternalModuleBinding(expression, scopes);
40006
- if (moduleBinding && EXTERNAL_SYNC_HTTP_CLIENT_MODULE_SOURCES.has(moduleBinding.source) && (moduleBinding.isModuleObject || moduleBinding.exportedName === "default" && EXTERNAL_SYNC_DEFAULT_IMPORT_NAMES.has(moduleBinding.source) || isKnownExternalSyncDirectModuleBinding(moduleBinding))) return true;
40007
- if (isNodeOfType(expression, "CallExpression")) {
40008
- const callee = stripParenExpression(expression.callee);
40009
- return Boolean(isNodeOfType(callee, "MemberExpression") && getStaticPropertyName(callee) === "create" && isProvenHttpClientReceiver(callee.object, scopes, visitedSymbolIds));
40010
- }
40011
- if (!isNodeOfType(expression, "Identifier")) return false;
40012
- const symbol = scopes.symbolFor(expression);
40013
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40014
- if (resolveExternalModuleBinding(expression, scopes, /* @__PURE__ */ new Set(), true)) return false;
40015
- const initializer = getDirectUnreassignedInitializer(symbol);
40016
- return Boolean(initializer && isProvenHttpClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40017
- };
40018
- const isProvenTanStackQueryClientReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40019
- const expression = stripParenExpression(rawExpression);
40020
- if (isNodeOfType(expression, "Identifier")) {
40021
- const receiverSymbol = scopes.symbolFor(expression);
40022
- if (receiverSymbol && !hasSafeReceiverAliases(new Set([receiverSymbol]), scopes)) return false;
40023
- }
40024
- if (isNodeOfType(expression, "CallExpression")) {
40025
- const binding = resolveExternalModuleBinding(expression.callee, scopes);
40026
- return Boolean(binding && binding.source === "@tanstack/react-query" && binding.exportedName === "useQueryClient");
40027
- }
40028
- if (isNodeOfType(expression, "NewExpression")) {
40029
- const binding = resolveExternalModuleBinding(expression.callee, scopes);
40030
- return Boolean(binding && TANSTACK_QUERY_CLIENT_MODULE_SOURCES.has(binding.source) && binding.exportedName === "QueryClient");
40031
- }
40032
- if (!isNodeOfType(expression, "Identifier")) return false;
40033
- const symbol = scopes.symbolFor(expression);
40034
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40035
- const initializer = getDirectUnreassignedInitializer(symbol);
40036
- return Boolean(initializer && isProvenTanStackQueryClientReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40037
- };
40038
- const isProvenExternalResourceReceiver = (rawExpression, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
40039
- const expression = stripParenExpression(rawExpression);
40040
- if (isDerivedFromProvenDomRefCurrent(expression, scopes) || isProvenBrowserApiReceiver(expression, "dom-event-target", scopes) || isProvenBrowserApiReceiver(expression, "xml-http-request", scopes) || isProvenHttpClientReceiver(expression, scopes)) return true;
40041
- if (isNodeOfType(expression, "NewExpression")) return hasProvenGlobalNamespaceReference(expression.callee, EXTERNAL_SYNC_RESOURCE_CONSTRUCTOR_NAMES, scopes);
40042
- if (!isNodeOfType(expression, "Identifier")) return false;
40043
- const symbol = scopes.symbolFor(expression);
40044
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
40045
- const initializer = getDirectUnreassignedInitializer(symbol);
40046
- return Boolean(initializer && isProvenExternalResourceReceiver(initializer, scopes, new Set(visitedSymbolIds).add(symbol.id)));
40047
- };
40048
- const isProvenExternalSyncDirectCallee = (callee, scopes) => {
40049
- if (hasProvenGlobalNamespaceReference(callee, EXTERNAL_SYNC_DIRECT_CALLEE_NAMES, scopes)) return true;
40050
- const moduleBinding = resolveExternalModuleBinding(callee, scopes);
40051
- return Boolean(moduleBinding && isKnownExternalSyncDirectModuleBinding(moduleBinding));
40052
- };
40053
- const isExternalSyncNode = (node, scopes) => {
40054
- if (isNodeOfType(node, "NewExpression")) return hasProvenGlobalNamespaceReference(node.callee, EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS, scopes);
40055
- if (isNodeOfType(node, "AssignmentExpression") || isNodeOfType(node, "UpdateExpression")) {
40056
- const mutationTarget = isNodeOfType(node, "AssignmentExpression") ? node.left : node.argument;
40057
- return isNodeOfType(mutationTarget, "MemberExpression") && getStaticPropertyName(mutationTarget) === "current" && Boolean(resolveReactRefSymbol(mutationTarget, scopes, {
40058
- allowUnboundBareCalls: true,
40059
- includeCreateRef: true,
40060
- resolveNamedAliases: true
40061
- }));
40062
- }
39021
+ const isExternalSyncNode = (node) => {
39022
+ if (isNodeOfType(node, "NewExpression")) return isNodeOfType(node.callee, "Identifier") && EXTERNAL_SYNC_OBSERVER_CONSTRUCTORS.has(node.callee.name);
39023
+ if (isNodeOfType(node, "AssignmentExpression")) return isNodeOfType(node.left, "MemberExpression") && isNodeOfType(node.left.property, "Identifier") && node.left.property.name === "current";
40063
39024
  if (!isNodeOfType(node, "CallExpression")) return false;
40064
- if (isProvenExternalSyncDirectCallee(node.callee, scopes)) return true;
39025
+ if (isNodeOfType(node.callee, "Identifier")) return EXTERNAL_SYNC_DIRECT_CALLEE_NAMES.has(node.callee.name);
40065
39026
  if (!isNodeOfType(node.callee, "MemberExpression")) return false;
40066
39027
  const propertyName = getStaticPropertyName(node.callee);
40067
39028
  if (propertyName === null) return false;
40068
- if (isBrowserStorageReceiver(node.callee.object, scopes)) return true;
40069
- if (TANSTACK_QUERY_EXTERNAL_SYNC_METHOD_NAMES.has(propertyName)) return isProvenTanStackQueryClientReceiver(node.callee.object, scopes);
40070
- if (EXTERNAL_SYNC_HTTP_METHOD_NAMES.has(propertyName)) return isProvenHttpClientReceiver(node.callee.object, scopes);
40071
- return EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName) && isProvenExternalResourceReceiver(node.callee.object, scopes);
39029
+ if (EXTERNAL_SYNC_MEMBER_METHOD_NAMES.has(propertyName)) return true;
39030
+ if (isBrowserStorageReceiver(node.callee.object)) return true;
39031
+ if (!EXTERNAL_SYNC_AMBIGUOUS_HTTP_METHOD_NAMES.has(propertyName)) return false;
39032
+ const receiverRootName = getRootIdentifierName(node.callee.object);
39033
+ return receiverRootName !== null && EXTERNAL_SYNC_HTTP_CLIENT_RECEIVERS.has(receiverRootName);
40072
39034
  };
40073
39035
  const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateName, setterSymbolIdToStateName, scopes, allowCommittedDomSync) => {
40074
39036
  if (!isFunctionLike$1(effectCallback)) return false;
@@ -40077,7 +39039,7 @@ const isExternalSyncEffect = (effectCallback, analysisFunctions, setterToStateNa
40077
39039
  } else for (const statement of effectCallback.body.body ?? []) if (isNodeOfType(statement, "ReturnStatement") && statement.argument && isFunctionShapedReturn(statement.argument, setterToStateName, setterSymbolIdToStateName, scopes, true)) return true;
40078
39040
  let didFindExternalCall = false;
40079
39041
  visitSynchronousFunctionBodies(analysisFunctions, (child) => {
40080
- if (isExternalSyncNode(child, scopes) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
39042
+ if (isExternalSyncNode(child) || allowCommittedDomSync && isCommittedDomSyncNode(child, scopes)) didFindExternalCall = true;
40081
39043
  });
40082
39044
  return didFindExternalCall;
40083
39045
  };
@@ -40099,11 +39061,11 @@ const noEffectChain = defineRule({
40099
39061
  setterToStateName.set(binding.setterName, binding.valueName);
40100
39062
  if (!isNodeOfType(binding.declarator.id, "ArrayPattern")) continue;
40101
39063
  const stateIdentifier = binding.declarator.id.elements[0];
40102
- const setterIdentifier = binding.declarator.id.elements[1];
40103
39064
  if (isNodeOfType(stateIdentifier, "Identifier")) {
40104
39065
  const stateSymbol = context.scopes.symbolFor(stateIdentifier);
40105
39066
  if (stateSymbol) stateSymbolIds.set(binding.valueName, stateSymbol.id);
40106
39067
  }
39068
+ const setterIdentifier = binding.declarator.id.elements[1];
40107
39069
  if (isNodeOfType(setterIdentifier, "Identifier")) {
40108
39070
  const setterSymbol = context.scopes.symbolFor(setterIdentifier);
40109
39071
  if (setterSymbol) setterSymbolIdToStateName.set(setterSymbol.id, binding.valueName);
@@ -40116,11 +39078,10 @@ const noEffectChain = defineRule({
40116
39078
  const callback = getEffectCallback(effectCall, context.scopes);
40117
39079
  if (!callback || !isFunctionLike$1(callback) || callback.async) continue;
40118
39080
  const analysisFunctions = collectSynchronouslyInvokedFunctions(callback, context.scopes);
40119
- const stateWrites = collectStateWritesInEffect(collectStateWriteAnalysisFunctions(callback, context.scopes, setterSymbolIdToStateName), setterSymbolIdToStateName, context.scopes);
39081
+ const stateWrites = collectStateWritesInEffect(analysisFunctions, setterSymbolIdToStateName, context.scopes);
40120
39082
  const writtenStateNames = new Set(stateWrites.keys());
40121
39083
  effectInfos.push({
40122
39084
  node: effectCall,
40123
- callback,
40124
39085
  dependencyStateSymbolIds: collectDependencyStateSymbolIds(effectCall, stateSymbolIdSet, context.scopes),
40125
39086
  stateWrites,
40126
39087
  analysisFunctions,
@@ -40137,10 +39098,10 @@ const noEffectChain = defineRule({
40137
39098
  if (readerEffect.isExternalSync) continue;
40138
39099
  if (readerEffect.dependencyStateSymbolIds.size === 0) continue;
40139
39100
  let chainedStateName = null;
40140
- for (const writtenName of writerEffect.stateWrites.keys()) {
39101
+ for (const [writtenName, writeInfo] of writerEffect.stateWrites) {
40141
39102
  const writtenStateSymbolId = stateSymbolIds.get(writtenName);
40142
39103
  if (writtenStateSymbolId === void 0 || !readerEffect.dependencyStateSymbolIds.has(writtenStateSymbolId)) continue;
40143
- if (!canStateWriteReachReaderWork(writtenName, writerEffect, readerEffect, stateSymbolIds, context.scopes)) continue;
39104
+ if (!canStateWriteReachReaderWork(writeInfo, readerEffect, stateSymbolIds.get(writtenName) ?? null, context.scopes)) continue;
40144
39105
  chainedStateName = writtenName;
40145
39106
  break;
40146
39107
  }
@@ -40529,7 +39490,7 @@ const noEffectEventInDeps = defineRule({
40529
39490
  title: "Effect Event listed in effect deps",
40530
39491
  tags: ["test-noise"],
40531
39492
  severity: "error",
40532
- recommendation: "Call the useEffectEvent function inside the effect body and don't list it in the deps. It's non-reactive on purpose, so it must be omitted.",
39493
+ recommendation: "Call the useEffectEvent function inside the effect body and don't list it in the deps. It changes on every render on purpose.",
40533
39494
  create: (context) => {
40534
39495
  const componentBindings = createComponentBindingStackTracker({ onVariableDeclarator: (declaratorNode) => {
40535
39496
  if (!isNodeOfType(declaratorNode, "VariableDeclarator")) return;
@@ -40551,7 +39512,7 @@ const noEffectEventInDeps = defineRule({
40551
39512
  if (!isNodeOfType(element, "Identifier")) continue;
40552
39513
  if (componentBindings.isBoundName(element.name)) context.report({
40553
39514
  node: element,
40554
- message: `Listing "${element.name}" in the deps defeats useEffectEvent — Effect Events are non-reactive and must be omitted from deps.`
39515
+ message: `Listing "${element.name}" in the deps re-runs your effect every render & defeats useEffectEvent.`
40555
39516
  });
40556
39517
  }
40557
39518
  }
@@ -40594,7 +39555,6 @@ const noEffectWithFreshDeps = defineRule({
40594
39555
  title: "Effect dependency recreated every render",
40595
39556
  severity: "error",
40596
39557
  category: "State & Effects",
40597
- disabledWhen: ["react-compiler"],
40598
39558
  recommendation: "Move the value inside the hook body and depend on its simple inputs instead, or wrap it in useMemo / useCallback so it stays the same between renders.",
40599
39559
  create: (context) => ({ CallExpression(node) {
40600
39560
  for (const finding of findForwardedFreshHookDependencies(node, context, EFFECT_HOOK_NAMES$1)) context.report({
@@ -43535,13 +42495,14 @@ const noInlineExhaustiveStyle = defineRule({
43535
42495
  });
43536
42496
  //#endregion
43537
42497
  //#region src/plugin/rules/performance/no-inline-prop-on-memo-component.ts
43538
- const MEMO_CALLEE_NAMES = new Set(["memo", "React.memo"]);
43539
- const isMemoCall = (node) => isNodeOfType(node, "CallExpression") && MEMO_CALLEE_NAMES.has(flattenCalleeName(node.callee) ?? "");
43540
- const hasCustomComparator = (node, scopes) => {
42498
+ const isMemoCall = (node) => {
43541
42499
  if (!isNodeOfType(node, "CallExpression")) return false;
43542
- const comparator = node.arguments?.[1];
43543
- return comparator ? !isIdentitySensitiveMemoComparator(comparator, scopes) : false;
42500
+ if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "memo") return true;
42501
+ if (isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.object, "Identifier") && node.callee.object.name === "React" && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "memo") return true;
42502
+ return false;
43544
42503
  };
42504
+ const isDefaultEquivalentComparator = (comparator) => isNodeOfType(comparator, "Identifier") && (comparator.name === "undefined" || comparator.name === "shallowEqual");
42505
+ const hasCustomComparator = (node) => isNodeOfType(node, "CallExpression") && (node.arguments?.length ?? 0) >= 2 && !isDefaultEquivalentComparator(node.arguments?.[1]);
43545
42506
  const isInlineReference = (node, scopes) => {
43546
42507
  const referenceNode = unwrapObjectIntegrityExpression(node, scopes);
43547
42508
  if (isNodeOfType(referenceNode, "ArrowFunctionExpression") || isNodeOfType(referenceNode, "FunctionExpression") || isNodeOfType(referenceNode, "CallExpression") && isNodeOfType(referenceNode.callee, "MemberExpression") && isNodeOfType(referenceNode.callee.property, "Identifier") && referenceNode.callee.property.name === "bind") return "functions";
@@ -43555,17 +42516,16 @@ const noInlinePropOnMemoComponent = defineRule({
43555
42516
  title: "Inline prop defeats memo()",
43556
42517
  tags: ["test-noise"],
43557
42518
  severity: "warn",
43558
- disabledWhen: ["react-compiler"],
43559
42519
  recommendation: "Move the inline `() => ...` / `[]` / `{}` to a stable value with useMemo, useCallback, or module scope, so the memoized child stops redrawing on every parent render",
43560
42520
  create: (context) => {
43561
42521
  const memoizedComponentNames = /* @__PURE__ */ new Set();
43562
42522
  return {
43563
42523
  VariableDeclarator(node) {
43564
42524
  if (!isNodeOfType(node.id, "Identifier") || !node.init) return;
43565
- if (isMemoCall(node.init) && !hasCustomComparator(node.init, context.scopes)) memoizedComponentNames.add(node.id.name);
42525
+ if (isMemoCall(node.init) && !hasCustomComparator(node.init)) memoizedComponentNames.add(node.id.name);
43566
42526
  },
43567
42527
  ExportDefaultDeclaration(node) {
43568
- if (node.declaration && isNodeOfType(node.declaration, "CallExpression") && isMemoCall(node.declaration) && !hasCustomComparator(node.declaration, context.scopes)) {
42528
+ if (node.declaration && isNodeOfType(node.declaration, "CallExpression") && isMemoCall(node.declaration) && !hasCustomComparator(node.declaration)) {
43569
42529
  const innerArgument = node.declaration.arguments?.[0];
43570
42530
  if (isNodeOfType(innerArgument, "Identifier")) memoizedComponentNames.add(innerArgument.name);
43571
42531
  }
@@ -44925,6 +43885,16 @@ const noManyBooleanProps = defineRule({
44925
43885
  }
44926
43886
  });
44927
43887
  //#endregion
43888
+ //#region src/plugin/utils/is-global-match-media-call.ts
43889
+ const isGlobalMatchMediaCall = (node, scopes) => {
43890
+ if (!isNodeOfType(node, "CallExpression")) return false;
43891
+ const callee = stripParenExpression(node.callee);
43892
+ if (isNodeOfType(callee, "Identifier")) return callee.name === "matchMedia" && scopes.isGlobalReference(callee);
43893
+ if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.property, "Identifier") || callee.property.name !== "matchMedia") return false;
43894
+ const receiver = stripParenExpression(callee.object);
43895
+ return isNodeOfType(receiver, "Identifier") && (receiver.name === "window" || receiver.name === "globalThis") && scopes.isGlobalReference(receiver);
43896
+ };
43897
+ //#endregion
44928
43898
  //#region src/plugin/rules/performance/no-match-media-in-state-initializer.ts
44929
43899
  const REACT_USE_STATE_OPTIONS = {
44930
43900
  allowGlobalReactNamespace: true,
@@ -56410,7 +55380,6 @@ const preferModuleScopePureFunction = defineRule({
56410
55380
  tags: ["test-noise"],
56411
55381
  severity: "warn",
56412
55382
  category: "Architecture",
56413
- disabledWhen: ["react-compiler"],
56414
55383
  recommendation: "Move the function above the component, at the top of the file. It doesn't use local state, so rebuilding it each update is wasted work.",
56415
55384
  create: (context) => {
56416
55385
  const report = (functionNode, name, componentName) => {
@@ -56450,6 +55419,17 @@ const preferModuleScopePureFunction = defineRule({
56450
55419
  }
56451
55420
  });
56452
55421
  //#endregion
55422
+ //#region src/plugin/utils/get-require-call-source.ts
55423
+ const getRequireCallSource = (expression) => {
55424
+ const unwrappedExpression = stripParenExpression(expression);
55425
+ if (isNodeOfType(unwrappedExpression, "MemberExpression")) return getRequireCallSource(unwrappedExpression.object);
55426
+ if (!isNodeOfType(unwrappedExpression, "CallExpression")) return null;
55427
+ if (!isNodeOfType(unwrappedExpression.callee, "Identifier") || unwrappedExpression.callee.name !== "require") return null;
55428
+ const [firstArgument] = unwrappedExpression.arguments ?? [];
55429
+ if (!firstArgument || !isNodeOfType(firstArgument, "Literal")) return null;
55430
+ return typeof firstArgument.value === "string" ? firstArgument.value : null;
55431
+ };
55432
+ //#endregion
56453
55433
  //#region src/plugin/utils/is-proven-node-crypto-namespace-reference.ts
56454
55434
  const NODE_CRYPTO_MODULE_SOURCES = new Set(["crypto", "node:crypto"]);
56455
55435
  const isProvenNodeCryptoNamespaceReference = (expression, scopes) => {
@@ -57041,10 +56021,10 @@ const classifyCallableReadsInsideEffect = (callableIdentifier, effectCallback, c
57041
56021
  const preferUseEffectEvent = defineRule({
57042
56022
  id: "prefer-use-effect-event",
57043
56023
  title: "Effect re-subscribes on a changing callback",
57044
- requires: ["react:19.2"],
56024
+ requires: ["react:19"],
57045
56025
  tags: ["test-noise"],
57046
56026
  severity: "warn",
57047
- recommendation: "Wrap the callback with `useEffectEvent(callback)` (React 19.2+) and call it inside the sub-handler. An Effect Event always sees the latest props and state but isn't a dependency, so the effect won't re-subscribe every time the parent redraws. See https://react.dev/reference/react/useEffectEvent",
56027
+ recommendation: "Wrap the callback with `useEffectEvent(callback)` (React 19+) and call it inside the sub-handler. An Effect Event always sees the latest props and state but isn't a dependency, so the effect won't re-subscribe every time the parent redraws. See https://react.dev/reference/react/useEffectEvent",
57048
56028
  create: (context) => {
57049
56029
  const checkComponent = (componentBody) => {
57050
56030
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
@@ -58649,6 +57629,7 @@ const reduxUseselectorInlineDerivation = defineRule({
58649
57629
  title: "useSelector derives data inline",
58650
57630
  severity: "warn",
58651
57631
  category: "Performance",
57632
+ disabledWhen: ["react-compiler"],
58652
57633
  recommendation: "Select the raw slice and memoize derivation so Redux actions do not rebuild a collection and redraw this component.",
58653
57634
  create: (context) => {
58654
57635
  let aliases = /* @__PURE__ */ new Set();
@@ -58708,6 +57689,7 @@ const reduxUseselectorReturnsNewCollection = defineRule({
58708
57689
  title: "useSelector returns a new collection",
58709
57690
  severity: "warn",
58710
57691
  category: "Performance",
57692
+ disabledWhen: ["react-compiler"],
58711
57693
  recommendation: "Return a stable selected value, split selectors, or pass `shallowEqual` so every Redux action does not redraw this component.",
58712
57694
  create: (context) => {
58713
57695
  let aliases = /* @__PURE__ */ new Set();
@@ -58833,7 +57815,6 @@ const renderingHoistJsx = defineRule({
58833
57815
  title: "Constant JSX rebuilt each render",
58834
57816
  tags: ["test-noise"],
58835
57817
  severity: "warn",
58836
- disabledWhen: ["react-compiler"],
58837
57818
  recommendation: "Move the static JSX out to the top of the file: `const ICON = <svg>...</svg>`, so it isn't rebuilt on every render",
58838
57819
  create: (context) => {
58839
57820
  let componentDepth = 0;
@@ -59280,9 +58261,8 @@ const renderingHydrationNoFlicker = defineRule({
59280
58261
  id: "rendering-hydration-no-flicker",
59281
58262
  title: "useEffect setState flashes on mount",
59282
58263
  tags: ["test-noise"],
59283
- requires: ["ssr"],
59284
58264
  severity: "warn",
59285
- recommendation: "Read the value with `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)` inside a reusable hook, or add `suppressHydrationWarning` to the element",
58265
+ recommendation: "Use `useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)` or add `suppressHydrationWarning` to the element",
59286
58266
  create: (context) => ({ CallExpression(node) {
59287
58267
  if (!isHookCall$2(node, USE_EFFECT_ONLY) || (node.arguments?.length ?? 0) < 2) return;
59288
58268
  const depsNode = node.arguments[1];
@@ -59735,7 +58715,7 @@ const renderingUsetransitionLoading = defineRule({
59735
58715
  title: "Loading useState forces extra render",
59736
58716
  tags: ["test-noise"],
59737
58717
  severity: "warn",
59738
- recommendation: "Replace with `const [isPending, startTransition] = useTransition()`, which marks the update as non-urgent and interruptible so the input stays responsive",
58718
+ recommendation: "Replace with `const [isPending, startTransition] = useTransition()`, which skips the extra render for the loading flag",
59739
58719
  create: (context) => ({ VariableDeclarator(node) {
59740
58720
  if (!isNodeOfType(node.id, "ArrayPattern") || !node.id.elements?.length) return;
59741
58721
  if (!node.init || !isHookCall$2(node.init, "useState")) return;
@@ -59767,7 +58747,7 @@ const renderingUsetransitionLoading = defineRule({
59767
58747
  }
59768
58748
  context.report({
59769
58749
  node: node.init,
59770
- message: `This makes the "${stateVariableName}" update urgent and blocking because it's a plain useState flag, so if it's a state change & not a data fetch, use useTransition to keep the UI responsive while it runs`
58750
+ message: `This adds an extra render because useState for "${stateVariableName}" re-renders just for the loading flag, so if it's a state change & not a data fetch, use useTransition instead`
59771
58751
  });
59772
58752
  } })
59773
58753
  });
@@ -60082,7 +59062,6 @@ const rerenderDependencies = defineRule({
60082
59062
  title: "Unstable value recreated every render",
60083
59063
  tags: ["test-noise"],
60084
59064
  severity: "error",
60085
- disabledWhen: ["react-compiler"],
60086
59065
  recommendation: "Move it into a useMemo, useRef, or a constant outside the component so it stays the same between renders.",
60087
59066
  create: (context) => ({ CallExpression(node) {
60088
59067
  if (!isReactHookCall(node, HOOKS_WITH_DEPS, context.scopes) || node.arguments.length < 2) return;
@@ -60495,7 +59474,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
60495
59474
  title: "useMemo before an early return",
60496
59475
  tags: ["test-noise"],
60497
59476
  severity: "warn",
60498
- recommendation: "Move the JSX into a child component rendered after the early return, so renders that take the early return never build it",
59477
+ recommendation: "Move the JSX into a child component wrapped in memo, so the parent's early return skips it",
60499
59478
  create: (context) => {
60500
59479
  const inspectFunctionBody = (statements) => {
60501
59480
  let memoNode = null;
@@ -60521,7 +59500,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
60521
59500
  if (callbackGuardTests.some((guardTest) => areConditionsStructurallyEqual(stmt.test, guardTest))) continue;
60522
59501
  context.report({
60523
59502
  node: memoNode,
60524
- message: "This rebuilds the JSX whenever its dependencies change even on renders that take the early return, so move the JSX into a child component rendered after the early return to skip it"
59503
+ message: "This runs even when the component bails out because the useMemo builds JSX before an early return, so move the JSX into a child wrapped in memo to skip it on the early return"
60525
59504
  });
60526
59505
  return;
60527
59506
  }
@@ -61024,7 +60003,6 @@ const rerenderMemoWithDefaultValue = defineRule({
61024
60003
  title: "Empty default prop breaks memo",
61025
60004
  tags: ["test-noise"],
61026
60005
  severity: "warn",
61027
- disabledWhen: ["react-compiler"],
61028
60006
  recommendation: "Move it to the top of the file: `const EMPTY_ITEMS: Item[] = []`, then use that as the default value",
61029
60007
  create: (context) => {
61030
60008
  let memoRegistry = /* @__PURE__ */ new Map();
@@ -61850,7 +60828,6 @@ const rnListDataMapped = defineRule({
61850
60828
  title: "List data rebuilt every render",
61851
60829
  tags: ["test-noise"],
61852
60830
  requires: ["react-native"],
61853
- disabledWhen: ["react-compiler"],
61854
60831
  severity: "warn",
61855
60832
  recommendation: "This builds a new array each time the parent redraws, so every row redraws too. Wrap it in `useMemo(() => items.map(...), [items])` to keep the same array.",
61856
60833
  create: (context) => ({ JSXOpeningElement(node) {
@@ -64204,66 +63181,6 @@ const roleHasRequiredAriaProps = defineRule({
64204
63181
  } })
64205
63182
  });
64206
63183
  //#endregion
64207
- //#region src/plugin/constants/global-aria-properties.ts
64208
- const GLOBAL_ARIA_PROPERTIES = new Set([
64209
- "aria-atomic",
64210
- "aria-braillelabel",
64211
- "aria-brailleroledescription",
64212
- "aria-busy",
64213
- "aria-controls",
64214
- "aria-current",
64215
- "aria-describedby",
64216
- "aria-description",
64217
- "aria-details",
64218
- "aria-disabled",
64219
- "aria-dropeffect",
64220
- "aria-errormessage",
64221
- "aria-flowto",
64222
- "aria-grabbed",
64223
- "aria-haspopup",
64224
- "aria-hidden",
64225
- "aria-invalid",
64226
- "aria-keyshortcuts",
64227
- "aria-label",
64228
- "aria-labelledby",
64229
- "aria-live",
64230
- "aria-owns",
64231
- "aria-relevant",
64232
- "aria-roledescription"
64233
- ]);
64234
- //#endregion
64235
- //#region src/plugin/constants/prohibited-aria-properties-by-role.ts
64236
- const ACCESSIBLE_NAME_PROPERTIES = new Set([
64237
- "aria-braillelabel",
64238
- "aria-label",
64239
- "aria-labelledby"
64240
- ]);
64241
- const PROHIBITED_ARIA_PROPERTIES_BY_ROLE = {
64242
- caption: ACCESSIBLE_NAME_PROPERTIES,
64243
- code: ACCESSIBLE_NAME_PROPERTIES,
64244
- definition: ACCESSIBLE_NAME_PROPERTIES,
64245
- deletion: ACCESSIBLE_NAME_PROPERTIES,
64246
- emphasis: ACCESSIBLE_NAME_PROPERTIES,
64247
- generic: new Set([
64248
- "aria-braillelabel",
64249
- "aria-brailleroledescription",
64250
- "aria-label",
64251
- "aria-labelledby",
64252
- "aria-roledescription"
64253
- ]),
64254
- insertion: ACCESSIBLE_NAME_PROPERTIES,
64255
- mark: ACCESSIBLE_NAME_PROPERTIES,
64256
- none: ACCESSIBLE_NAME_PROPERTIES,
64257
- paragraph: ACCESSIBLE_NAME_PROPERTIES,
64258
- presentation: ACCESSIBLE_NAME_PROPERTIES,
64259
- strong: ACCESSIBLE_NAME_PROPERTIES,
64260
- subscript: ACCESSIBLE_NAME_PROPERTIES,
64261
- superscript: ACCESSIBLE_NAME_PROPERTIES,
64262
- term: ACCESSIBLE_NAME_PROPERTIES,
64263
- time: ACCESSIBLE_NAME_PROPERTIES,
64264
- tooltip: ACCESSIBLE_NAME_PROPERTIES
64265
- };
64266
- //#endregion
64267
63184
  //#region src/plugin/constants/role-supports-aria-props.ts
64268
63185
  const ROLE_SUPPORTS_ARIA_PROPS = {
64269
63186
  alert: new Set([
@@ -67364,12 +66281,6 @@ const ROLE_SUPPORTS_ARIA_PROPS = {
67364
66281
  ])
67365
66282
  };
67366
66283
  //#endregion
67367
- //#region src/plugin/utils/is-aria-property-supported-by-role.ts
67368
- const isAriaPropertySupportedByRole = (role, property) => {
67369
- if (PROHIBITED_ARIA_PROPERTIES_BY_ROLE[role]?.has(property)) return false;
67370
- return ROLE_SUPPORTS_ARIA_PROPS[role]?.has(property) === true || GLOBAL_ARIA_PROPERTIES.has(property);
67371
- };
67372
- //#endregion
67373
66284
  //#region src/plugin/rules/a11y/role-supports-aria-props.ts
67374
66285
  const buildMessageDefault = (roles, propName) => {
67375
66286
  return `Screen reader users get no help from \`${propName}\` because role ${roles.map((role) => `\`${role}\``).join(" / ")} ignores it, so remove it or change the role.`;
@@ -67406,10 +66317,16 @@ const roleSupportsAriaProps = defineRule({
67406
66317
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
67407
66318
  const roleCandidates = roleAttribute ? getJsxPropStaticStringValues(roleAttribute, context.scopes) : [getImplicitRole(node, elementType, context.scopes)].filter((role) => role !== null);
67408
66319
  if (roleCandidates === null || roleCandidates.length === 0) return;
67409
- for (const role of roleCandidates) if (!VALID_ARIA_ROLES.has(role)) return;
66320
+ const supportedSets = [];
66321
+ for (const role of roleCandidates) {
66322
+ if (!VALID_ARIA_ROLES.has(role)) return;
66323
+ const supported = ROLE_SUPPORTS_ARIA_PROPS[role];
66324
+ if (!supported) return;
66325
+ supportedSets.push(supported);
66326
+ }
67410
66327
  const isImplicit = !roleAttribute;
67411
66328
  for (const { attribute, propName } of ariaAttributes) {
67412
- if (roleCandidates.some((role) => isAriaPropertySupportedByRole(role, propName))) continue;
66329
+ if (supportedSets.some((supported) => supported.has(propName))) continue;
67413
66330
  context.report({
67414
66331
  node: attribute,
67415
66332
  message: isImplicit ? buildMessageImplicit(roleCandidates[0], propName, elementType) : buildMessageDefault(roleCandidates, propName)
@@ -76191,7 +75108,7 @@ const EXTERNAL_RULES = [
76191
75108
  {
76192
75109
  key: "react-hooks-js/set-state-in-effect",
76193
75110
  source: "react-compiler",
76194
- severity: "warn"
75111
+ severity: "error"
76195
75112
  },
76196
75113
  {
76197
75114
  key: "react-hooks-js/globals",