oxlint-plugin-react-doctor 0.7.5-dev.3afd146 → 0.7.5-dev.70eff9a

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +374 -127
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5934,6 +5934,21 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5934
5934
  }
5935
5935
  });
5936
5936
  //#endregion
5937
+ //#region src/plugin/utils/get-static-property-key-name.ts
5938
+ const getStaticPropertyKeyName = (node, options = {}) => {
5939
+ if (!isNodeOfType(node, "Property")) return null;
5940
+ if (node.computed) {
5941
+ if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
5942
+ return null;
5943
+ }
5944
+ if (isNodeOfType(node.key, "Identifier")) return node.key.name;
5945
+ if (isNodeOfType(node.key, "Literal")) {
5946
+ if (typeof node.key.value === "string") return node.key.value;
5947
+ if (options.stringifyNonStringLiterals) return String(node.key.value);
5948
+ }
5949
+ return null;
5950
+ };
5951
+ //#endregion
5937
5952
  //#region src/plugin/utils/is-presentation-role.ts
5938
5953
  const isPresentationRole = (openingElement) => {
5939
5954
  const roleAttribute = hasJsxPropIgnoreCase(openingElement.attributes, "role");
@@ -5978,6 +5993,21 @@ const isPureEventBlockerHandler = (attribute) => {
5978
5993
  return false;
5979
5994
  };
5980
5995
  //#endregion
5996
+ //#region src/plugin/utils/resolve-const-identifier-alias.ts
5997
+ const resolveConstIdentifierAlias = (identifier, scopes) => {
5998
+ if (!isNodeOfType(identifier, "Identifier")) return null;
5999
+ const visitedSymbolIds = /* @__PURE__ */ new Set();
6000
+ let symbol = scopes.symbolFor(identifier);
6001
+ while (symbol?.kind === "const") {
6002
+ if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
6003
+ visitedSymbolIds.add(symbol.id);
6004
+ const initializer = stripParenExpression(symbol.initializer);
6005
+ if (!isNodeOfType(initializer, "Identifier")) return symbol;
6006
+ symbol = scopes.symbolFor(initializer);
6007
+ }
6008
+ return symbol;
6009
+ };
6010
+ //#endregion
5981
6011
  //#region src/plugin/rules/a11y/click-events-have-key-events.ts
5982
6012
  const MESSAGE$57 = "Keyboard users can't trigger this click handler because there's no keyboard one, so add `onKeyUp`, `onKeyDown`, or `onKeyPress`.";
5983
6013
  const KEY_HANDLERS = [
@@ -5988,6 +6018,63 @@ const KEY_HANDLERS = [
5988
6018
  "onKeyDownCapture",
5989
6019
  "onKeyPressCapture"
5990
6020
  ];
6021
+ const CLICK_HANDLERS = ["onClick", "onClickCapture"];
6022
+ const TRANSPARENT_SPREAD_EVENT_NAMES = new Set([...CLICK_HANDLERS, ...KEY_HANDLERS].map((eventName) => eventName.toLowerCase()));
6023
+ const CONSERVATIVE_SPREAD_PROP_NAMES = new Set([
6024
+ "aria-hidden",
6025
+ "onmouseenter",
6026
+ "onmouseover",
6027
+ "role"
6028
+ ]);
6029
+ const resolveSpreadObjectExpression = (expression, scopes) => {
6030
+ const innerExpression = stripParenExpression(expression);
6031
+ if (isNodeOfType(innerExpression, "ObjectExpression")) return innerExpression;
6032
+ if (!isNodeOfType(innerExpression, "Identifier")) return null;
6033
+ const symbol = resolveConstIdentifierAlias(innerExpression, scopes);
6034
+ if (symbol?.kind !== "const" || !symbol.initializer) return null;
6035
+ const initializer = stripParenExpression(symbol.initializer);
6036
+ return isNodeOfType(initializer, "ObjectExpression") ? initializer : null;
6037
+ };
6038
+ const collectTransparentSpreadEventNames = (expression, scopes, eventValues, visitedObjectExpressions) => {
6039
+ const objectExpression = resolveSpreadObjectExpression(expression, scopes);
6040
+ if (!objectExpression || visitedObjectExpressions.has(objectExpression)) return false;
6041
+ visitedObjectExpressions.add(objectExpression);
6042
+ let isTransparent = true;
6043
+ for (const property of objectExpression.properties) {
6044
+ if (isNodeOfType(property, "SpreadElement")) {
6045
+ if (!collectTransparentSpreadEventNames(property.argument, scopes, eventValues, visitedObjectExpressions)) {
6046
+ isTransparent = false;
6047
+ break;
6048
+ }
6049
+ continue;
6050
+ }
6051
+ if (!isNodeOfType(property, "Property")) {
6052
+ isTransparent = false;
6053
+ break;
6054
+ }
6055
+ const propertyName = getStaticPropertyKeyName(property, { allowComputedString: true });
6056
+ if (!propertyName) {
6057
+ isTransparent = false;
6058
+ break;
6059
+ }
6060
+ const normalizedPropertyName = propertyName.toLowerCase();
6061
+ if (CONSERVATIVE_SPREAD_PROP_NAMES.has(normalizedPropertyName)) {
6062
+ isTransparent = false;
6063
+ break;
6064
+ }
6065
+ if (TRANSPARENT_SPREAD_EVENT_NAMES.has(normalizedPropertyName)) eventValues.set(normalizedPropertyName, property.value);
6066
+ }
6067
+ visitedObjectExpressions.delete(objectExpression);
6068
+ return isTransparent;
6069
+ };
6070
+ const getTransparentSpreadEventValues = (attributes, scopes) => {
6071
+ const eventValues = /* @__PURE__ */ new Map();
6072
+ for (const attribute of attributes) {
6073
+ if (!isNodeOfType(attribute, "JSXSpreadAttribute")) continue;
6074
+ if (!collectTransparentSpreadEventNames(attribute.argument, scopes, eventValues, /* @__PURE__ */ new Set())) return null;
6075
+ }
6076
+ return eventValues;
6077
+ };
5991
6078
  const FOCUSLESS_CONTAINER_TAGS = new Set([
5992
6079
  "tr",
5993
6080
  "td",
@@ -6035,7 +6122,10 @@ const isFocusForwardingFunctionBody = (body) => {
6035
6122
  };
6036
6123
  const resolveHandlerFunction = (attribute) => {
6037
6124
  if (!attribute.value || !isNodeOfType(attribute.value, "JSXExpressionContainer")) return null;
6038
- let expression = attribute.value.expression;
6125
+ return resolveHandlerFunctionExpression(attribute.value.expression);
6126
+ };
6127
+ const resolveHandlerFunctionExpression = (handlerExpression) => {
6128
+ let expression = handlerExpression;
6039
6129
  if (isNodeOfType(expression, "Identifier")) {
6040
6130
  const binding = findVariableInitializer(expression, expression.name);
6041
6131
  if (!binding?.initializer) return null;
@@ -6134,18 +6224,22 @@ const clickEventsHaveKeyEvents = defineRule({
6134
6224
  if (!HTML_TAGS.has(tag)) return;
6135
6225
  if (tag === "label") return;
6136
6226
  if (!FOCUSLESS_CONTAINER_TAGS.has(tag) && isInteractiveElement(tag, node)) return;
6227
+ const spreadEventValues = getTransparentSpreadEventValues(node.attributes, context.scopes);
6228
+ if (!spreadEventValues) return;
6137
6229
  const onClick = hasJsxPropIgnoreCase(node.attributes, "onClick") ?? hasJsxPropIgnoreCase(node.attributes, "onClickCapture");
6138
- if (!onClick) return;
6139
- if (isPureEventBlockerHandler(onClick)) return;
6140
- if (isFocusForwardingHandler(onClick)) return;
6141
- if (hasJsxSpreadAttribute$1(node.attributes)) return;
6230
+ const spreadOnClickExpression = CLICK_HANDLERS.map((name) => spreadEventValues.get(name.toLowerCase())).find((expression) => expression !== void 0);
6231
+ if (!onClick && !spreadOnClickExpression) return;
6232
+ if (onClick && isPureEventBlockerHandler(onClick)) return;
6233
+ if (onClick && isFocusForwardingHandler(onClick)) return;
6234
+ const spreadHandlerFunction = spreadOnClickExpression ? resolveHandlerFunctionExpression(spreadOnClickExpression) : null;
6235
+ if (spreadHandlerFunction && (isFocusForwardingFunctionBody(spreadHandlerFunction.body ?? null) || containsBackdropDismissComparison(spreadHandlerFunction.body ?? null))) return;
6142
6236
  if (hasCompositeItemRole(node)) return;
6143
6237
  if (isHoverSelectionListItem(tag, node)) return;
6144
- if (isBackdropDismissHandler(onClick)) return;
6238
+ if (onClick && isBackdropDismissHandler(onClick)) return;
6145
6239
  if (containsKeyboardActivatableDescendant(node.parent)) return;
6146
6240
  if (isHiddenFromScreenReader(node, context.settings)) return;
6147
6241
  if (isPresentationRole(node)) return;
6148
- if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler))) return;
6242
+ if (KEY_HANDLERS.some((handler) => hasJsxPropIgnoreCase(node.attributes, handler) || spreadEventValues.has(handler.toLowerCase()))) return;
6149
6243
  context.report({
6150
6244
  node: node.name,
6151
6245
  message: MESSAGE$57
@@ -7618,7 +7712,7 @@ const isCreateClassLikeCall = (node) => {
7618
7712
  if (isNodeOfType(callee, "MemberExpression")) return getStaticMemberName$2(callee) === "createClass";
7619
7713
  return false;
7620
7714
  };
7621
- const isCreateContextCall = (node) => {
7715
+ const isCreateContextCall$1 = (node) => {
7622
7716
  if (!isNodeOfType(node, "CallExpression")) return false;
7623
7717
  const callee = node.callee;
7624
7718
  if (isNodeOfType(callee, "Identifier")) return callee.name === "createContext";
@@ -7795,7 +7889,7 @@ const displayName = defineRule({
7795
7889
  }
7796
7890
  },
7797
7891
  CallExpression(node) {
7798
- if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall(node)) {
7892
+ if (settings.checkContextObjects && isReactVersionAtLeast$1(settings.reactVersion, 16, 3) && isCreateContextCall$1(node)) {
7799
7893
  const assignedName = getAssignedName(node);
7800
7894
  if (assignedName) {
7801
7895
  const programRoot = findProgramRoot(node);
@@ -7842,21 +7936,6 @@ const displayName = defineRule({
7842
7936
  }
7843
7937
  });
7844
7938
  //#endregion
7845
- //#region src/plugin/utils/get-static-property-key-name.ts
7846
- const getStaticPropertyKeyName = (node, options = {}) => {
7847
- if (!isNodeOfType(node, "Property")) return null;
7848
- if (node.computed) {
7849
- if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
7850
- return null;
7851
- }
7852
- if (isNodeOfType(node.key, "Identifier")) return node.key.name;
7853
- if (isNodeOfType(node.key, "Literal")) {
7854
- if (typeof node.key.value === "string") return node.key.value;
7855
- if (options.stringifyNonStringLiterals) return String(node.key.value);
7856
- }
7857
- return null;
7858
- };
7859
- //#endregion
7860
7939
  //#region src/plugin/utils/read-static-boolean.ts
7861
7940
  const readStaticBoolean = (node) => {
7862
7941
  if (!node) return null;
@@ -7865,21 +7944,6 @@ const readStaticBoolean = (node) => {
7865
7944
  return unwrappedNode.value;
7866
7945
  };
7867
7946
  //#endregion
7868
- //#region src/plugin/utils/resolve-const-identifier-alias.ts
7869
- const resolveConstIdentifierAlias = (identifier, scopes) => {
7870
- if (!isNodeOfType(identifier, "Identifier")) return null;
7871
- const visitedSymbolIds = /* @__PURE__ */ new Set();
7872
- let symbol = scopes.symbolFor(identifier);
7873
- while (symbol?.kind === "const") {
7874
- if (visitedSymbolIds.has(symbol.id) || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || symbol.declarationNode.id !== symbol.bindingIdentifier) return null;
7875
- visitedSymbolIds.add(symbol.id);
7876
- const initializer = stripParenExpression(symbol.initializer);
7877
- if (!isNodeOfType(initializer, "Identifier")) return symbol;
7878
- symbol = scopes.symbolFor(initializer);
7879
- }
7880
- return symbol;
7881
- };
7882
- //#endregion
7883
7947
  //#region src/plugin/utils/is-react-api-call.ts
7884
7948
  const includesApiName = (apiNames, apiName) => typeof apiNames === "string" ? apiNames === apiName : apiNames.has(apiName);
7885
7949
  const isImportedFromReact = (symbol) => {
@@ -9006,6 +9070,30 @@ const doMatchingNodesCoverEveryPathAfterUsage = (usageNode, matchingNodes, conte
9006
9070
  }
9007
9071
  return matchingBlocks.size > 0;
9008
9072
  };
9073
+ const doMatchingNodesCoverEveryPathFromFunctionEntry = (owner, matchingNodes, context) => {
9074
+ const functionCfg = context.cfg.cfgFor(owner);
9075
+ if (!functionCfg) return false;
9076
+ const matchingBlocks = new Set(matchingNodes.flatMap((matchingNode) => {
9077
+ if (context.cfg.enclosingFunction(matchingNode) !== owner) return [];
9078
+ const matchingBlock = functionCfg.blockOf(matchingNode);
9079
+ return matchingBlock ? [matchingBlock] : [];
9080
+ }));
9081
+ if (matchingBlocks.size === 0) return false;
9082
+ const visitedBlocks = new Set([functionCfg.entry]);
9083
+ const pendingBlocks = [functionCfg.entry];
9084
+ while (pendingBlocks.length > 0) {
9085
+ const currentBlock = pendingBlocks.pop();
9086
+ if (!currentBlock) break;
9087
+ if (matchingBlocks.has(currentBlock)) continue;
9088
+ for (const edge of currentBlock.successors) {
9089
+ if (edge.to === functionCfg.exit) return false;
9090
+ if (visitedBlocks.has(edge.to)) continue;
9091
+ visitedBlocks.add(edge.to);
9092
+ pendingBlocks.push(edge.to);
9093
+ }
9094
+ }
9095
+ return true;
9096
+ };
9009
9097
  const removeSynchronouslyReleasedUsages = (callback, usages, context) => {
9010
9098
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return usages;
9011
9099
  if (!isNodeOfType(callback.body, "BlockStatement")) return usages;
@@ -9206,6 +9294,83 @@ const doesCleanupFunctionReleaseUsage = (cleanupFunction, usage, context, visite
9206
9294
  });
9207
9295
  return didCleanupFunctionMatch;
9208
9296
  };
9297
+ const doesBoundCleanupReleaseUsage = (expression, usage, context) => {
9298
+ const callExpression = stripParenExpression(expression);
9299
+ if (!isNodeOfType(callExpression, "CallExpression")) return false;
9300
+ const bindCallee = stripParenExpression(callExpression.callee);
9301
+ if (!isNodeOfType(bindCallee, "MemberExpression") || bindCallee.computed || !isNodeOfType(bindCallee.property, "Identifier") || bindCallee.property.name !== "bind") return false;
9302
+ const releaseMember = stripParenExpression(bindCallee.object);
9303
+ if (!isNodeOfType(releaseMember, "MemberExpression") || releaseMember.computed || !isNodeOfType(releaseMember.property, "Identifier")) return false;
9304
+ const releaseReceiverKey = resolveExpressionKey$1(releaseMember.object, context);
9305
+ if (releaseReceiverKey === null || releaseReceiverKey !== resolveExpressionKey$1(callExpression.arguments?.[0], context)) return false;
9306
+ const releaseVerbName = releaseMember.property.name;
9307
+ if (usage.kind === "socket") return usage.handleKey === releaseReceiverKey && (SOCKET_RELEASE_VERB_NAMES.has(releaseVerbName) || UNIVERSAL_RELEASE_VERB_NAMES.has(releaseVerbName));
9308
+ return usage.kind === "subscribe" && usage.handleKey === releaseReceiverKey && (releaseVerbName === "unsubscribe" || releaseVerbName === "unsub" || releaseVerbName === "close" || releaseVerbName === "unwatch" || releaseVerbName === "unlisten" || BOUND_RESOURCE_RELEASE_METHOD_NAMES.has(releaseVerbName));
9309
+ };
9310
+ const callbackReturnsCleanupForUsage = (callback, usage, context) => {
9311
+ if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
9312
+ const doesReturnedValueReleaseUsage = (returnedValue) => {
9313
+ if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) return true;
9314
+ const cleanupFunction = resolveStableValue(returnedValue, context);
9315
+ if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) return true;
9316
+ return Boolean(cleanupFunction && isFunctionLike$1(cleanupFunction) && doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context));
9317
+ };
9318
+ if (!isNodeOfType(callback.body, "BlockStatement")) return doesReturnedValueReleaseUsage(stripParenExpression(callback.body));
9319
+ const matchingCleanupReturns = [];
9320
+ walkInsideStatementBlocks(callback.body, (child) => {
9321
+ if (isNodeOfType(child, "ReturnStatement") && child.argument && doesReturnedValueReleaseUsage(stripParenExpression(child.argument))) matchingCleanupReturns.push(child);
9322
+ });
9323
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingCleanupReturns, context);
9324
+ };
9325
+ const hasRerunReleaseBeforeUsage = (callback, usage, context) => {
9326
+ if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression") || !isNodeOfType(callback.body, "BlockStatement")) return false;
9327
+ const functionCfg = context.cfg.cfgFor(callback);
9328
+ const usageBlock = functionCfg?.blockOf(usage.node);
9329
+ const usageStart = getRangeStart(usage.node);
9330
+ if (!functionCfg || !usageBlock || usageStart === null) return false;
9331
+ const findHandleGuard = (releaseCall) => {
9332
+ if (usage.handleKey === null) return null;
9333
+ let ancestor = releaseCall.parent;
9334
+ while (ancestor && ancestor !== callback.body) {
9335
+ if (isNodeOfType(ancestor, "IfStatement")) return ancestor.alternate === null && resolveExpressionKey$1(ancestor.test, context) === usage.handleKey && getRangeStart(ancestor) !== null && (getRangeStart(ancestor) ?? usageStart) < usageStart ? ancestor : null;
9336
+ ancestor = ancestor.parent;
9337
+ }
9338
+ return null;
9339
+ };
9340
+ const matchingReleaseAnchors = [];
9341
+ walkInsideStatementBlocks(callback.body, (child) => {
9342
+ if (!isNodeOfType(child, "CallExpression")) return;
9343
+ const releaseStart = getRangeStart(child);
9344
+ const handleGuard = findHandleGuard(child);
9345
+ if (releaseStart === null || releaseStart >= usageStart || functionCfg.blockOf(child) !== usageBlock && !handleGuard) return;
9346
+ if (doesReleaseCallMatchUsage(child, usage, context)) {
9347
+ matchingReleaseAnchors.push(handleGuard ?? child);
9348
+ return;
9349
+ }
9350
+ const helperFunction = resolveStableValue(child.callee, context);
9351
+ if (helperFunction && isFunctionLike$1(helperFunction) && doesCleanupFunctionReleaseUsage(helperFunction, usage, context)) matchingReleaseAnchors.push(handleGuard ?? child);
9352
+ });
9353
+ return doMatchingNodesCoverEveryPathFromFunctionEntry(callback, matchingReleaseAnchors, context);
9354
+ };
9355
+ const hasStableUnmountCleanupForUsage = (callback, usage, context) => {
9356
+ const componentFunction = findEnclosingFunction(callback);
9357
+ if (!componentFunction || !isNodeOfType(componentFunction, "ArrowFunctionExpression") && !isNodeOfType(componentFunction, "FunctionExpression") && !isNodeOfType(componentFunction, "FunctionDeclaration")) return false;
9358
+ let didFindUnmountCleanup = false;
9359
+ walkAst(componentFunction.body, (child) => {
9360
+ if (didFindUnmountCleanup) return false;
9361
+ if (!isNodeOfType(child, "CallExpression") || findEnclosingFunction(child) !== componentFunction) return;
9362
+ if (!isHookCall$2(child, CLEANUP_EFFECT_HOOK_NAMES)) return;
9363
+ const dependencyList = child.arguments?.[1];
9364
+ if (!isNodeOfType(dependencyList, "ArrayExpression") || dependencyList.elements.length > 0) return;
9365
+ const cleanupCallback = getEffectCallback(child);
9366
+ if (cleanupCallback && cleanupCallback !== callback && callbackReturnsCleanupForUsage(cleanupCallback, usage, context)) {
9367
+ didFindUnmountCleanup = true;
9368
+ return false;
9369
+ }
9370
+ });
9371
+ return didFindUnmountCleanup;
9372
+ };
9373
+ const hasSplitLifecycleCleanup = (callback, usage, context) => usage.handleKey !== null && hasRerunReleaseBeforeUsage(callback, usage, context) && hasStableUnmountCleanupForUsage(callback, usage, context);
9209
9374
  const effectHasCleanupForUsage = (callback, usage, context) => {
9210
9375
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
9211
9376
  if (usage.kind === "subscribe" && findEnclosingFunction(usage.node) === callback && doesResourceResultEscape(usage.node, true) && isCleanupReturningSubscribeLikeCallExpression(usage.node)) return true;
@@ -9218,6 +9383,10 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
9218
9383
  if (returnStart !== null && usageStart !== null && returnStart < usageStart) return;
9219
9384
  const returnedValue = child.argument ? stripParenExpression(child.argument) : null;
9220
9385
  if (!returnedValue) return;
9386
+ if (doesBoundCleanupReleaseUsage(returnedValue, usage, context)) {
9387
+ matchingCleanupReturns.push(child);
9388
+ return;
9389
+ }
9221
9390
  if (usage.kind === "subscribe" && (returnedValue === usage.node || getRangeStart(returnedValue) !== null && getRangeStart(returnedValue) === getRangeStart(usage.node)) && isCleanupReturningSubscribeLikeCallExpression(returnedValue)) {
9222
9391
  matchingCleanupReturns.push(child);
9223
9392
  return;
@@ -9233,13 +9402,17 @@ const effectHasCleanupForUsage = (callback, usage, context) => {
9233
9402
  if (!context.scopes.symbolFor(returnedValue)?.initializer) return;
9234
9403
  }
9235
9404
  const cleanupFunction = resolveStableValue(returnedValue, context);
9405
+ if (cleanupFunction && doesBoundCleanupReleaseUsage(cleanupFunction, usage, context)) {
9406
+ matchingCleanupReturns.push(child);
9407
+ return;
9408
+ }
9236
9409
  if (!cleanupFunction || !isFunctionLike$1(cleanupFunction)) return;
9237
9410
  if (doesCleanupFunctionReleaseUsage(cleanupFunction, usage, context)) matchingCleanupReturns.push(child);
9238
9411
  });
9239
9412
  return doMatchingNodesCoverEveryPathAfterUsage(resolveCleanupPathAnchor(usage.node, callback, context), matchingCleanupReturns, context);
9240
9413
  };
9241
9414
  const findFirstUsageWithoutCleanup = (callback, usages, context) => {
9242
- for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context)) return usage;
9415
+ for (const usage of usages) if (!effectHasCleanupForUsage(callback, usage, context) && !hasSplitLifecycleCleanup(callback, usage, context)) return usage;
9243
9416
  return null;
9244
9417
  };
9245
9418
  const isLocalAbortControllerExpression = (expression, context, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
@@ -23044,61 +23217,73 @@ const PURE_GLOBAL_CALLEE_NAMES = new Set([
23044
23217
  "Array",
23045
23218
  "BigInt",
23046
23219
  "Boolean",
23220
+ "encodeURIComponent",
23047
23221
  "Number",
23048
23222
  "Object",
23049
23223
  "String",
23050
23224
  "parseFloat",
23051
- "parseInt"
23225
+ "parseInt",
23226
+ "structuredClone"
23227
+ ]);
23228
+ const PURE_GLOBAL_CONSTRUCTOR_NAMES = new Set(["Date", "Set"]);
23229
+ const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([
23230
+ ["Array", new Set(["from"])],
23231
+ ["JSON", new Set([
23232
+ "isRawJSON",
23233
+ "parse",
23234
+ "rawJSON",
23235
+ "stringify"
23236
+ ])],
23237
+ ["Math", new Set([
23238
+ "abs",
23239
+ "acos",
23240
+ "acosh",
23241
+ "asin",
23242
+ "asinh",
23243
+ "atan",
23244
+ "atan2",
23245
+ "atanh",
23246
+ "cbrt",
23247
+ "ceil",
23248
+ "clz32",
23249
+ "cos",
23250
+ "cosh",
23251
+ "exp",
23252
+ "floor",
23253
+ "fround",
23254
+ "hypot",
23255
+ "imul",
23256
+ "log",
23257
+ "log10",
23258
+ "log1p",
23259
+ "log2",
23260
+ "max",
23261
+ "min",
23262
+ "pow",
23263
+ "round",
23264
+ "sign",
23265
+ "sin",
23266
+ "sinh",
23267
+ "sqrt",
23268
+ "tan",
23269
+ "tanh",
23270
+ "trunc"
23271
+ ])],
23272
+ ["Object", new Set(["assign"])]
23052
23273
  ]);
23053
- const PURE_GLOBAL_NAMESPACE_NAMES = new Set(["JSON", "Math"]);
23054
- const PURE_HELPER_NAMESPACE_MEMBER_NAMES = new Map([["JSON", new Set([
23055
- "isRawJSON",
23056
- "parse",
23057
- "rawJSON",
23058
- "stringify"
23059
- ])], ["Math", new Set([
23060
- "abs",
23061
- "acos",
23062
- "acosh",
23063
- "asin",
23064
- "asinh",
23065
- "atan",
23066
- "atan2",
23067
- "atanh",
23068
- "cbrt",
23069
- "ceil",
23070
- "clz32",
23071
- "cos",
23072
- "cosh",
23073
- "exp",
23074
- "floor",
23075
- "fround",
23076
- "hypot",
23077
- "imul",
23078
- "log",
23079
- "log10",
23080
- "log1p",
23081
- "log2",
23082
- "max",
23083
- "min",
23084
- "pow",
23085
- "round",
23086
- "sign",
23087
- "sin",
23088
- "sinh",
23089
- "sqrt",
23090
- "tan",
23091
- "tanh",
23092
- "trunc"
23093
- ])]]);
23094
23274
  const PURE_MEMBER_TRANSFORM_NAMES = new Set([
23095
23275
  "concat",
23096
23276
  "filter",
23277
+ "flatMap",
23097
23278
  "join",
23098
23279
  "map",
23280
+ "reduce",
23281
+ "replace",
23282
+ "slice",
23099
23283
  "split",
23100
23284
  "toLowerCase",
23101
23285
  "toString",
23286
+ "toSorted",
23102
23287
  "toUpperCase",
23103
23288
  "trim"
23104
23289
  ]);
@@ -23480,6 +23665,11 @@ const analyzeHelperExpression = (expression, environment, usedParameterIndices)
23480
23665
  const callbackSummary = analyzeHelperStatements(node.body.body ?? [], callbackEnvironment, usedParameterIndices, analyzeHelperExpression);
23481
23666
  return callbackSummary.isValid && !callbackSummary.canContinue;
23482
23667
  }
23668
+ if (isNodeOfType(node, "NewExpression")) {
23669
+ const callee = stripParenExpression(node.callee);
23670
+ if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || environment.parameterIndices.has(callee.name) || environment.recursiveNames.has(callee.name) || environment.shadowedGlobalNames.has(callee.name)) return false;
23671
+ return (node.arguments ?? []).every((argument) => analyzeHelperExpression(argument, environment, usedParameterIndices));
23672
+ }
23483
23673
  if (isNodeOfType(node, "CallExpression")) {
23484
23674
  const callee = stripParenExpression(node.callee);
23485
23675
  const calleeRoot = getMemberRoot(callee);
@@ -23682,7 +23872,7 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23682
23872
  }
23683
23873
  const callee = stripParenExpression(node.callee);
23684
23874
  const calleeRoot = getMemberRoot(callee);
23685
- const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(calleeRoot, "Identifier") && PURE_GLOBAL_NAMESPACE_NAMES.has(calleeRoot.name) && getIdentifierBindingIdentity(analysis, calleeRoot) === null;
23875
+ const isPureGlobalCall = isNodeOfType(callee, "Identifier") && PURE_GLOBAL_CALLEE_NAMES.has(callee.name) && getIdentifierBindingIdentity(analysis, callee) === null || isNodeOfType(callee, "MemberExpression") && isNodeOfType(calleeRoot, "Identifier") && getIdentifierBindingIdentity(analysis, calleeRoot) === null && PURE_HELPER_NAMESPACE_MEMBER_NAMES.get(calleeRoot.name)?.has(getStaticMemberName$1(callee) ?? "") === true;
23686
23876
  const isPureMemberTransform = isNodeOfType(callee, "MemberExpression") && PURE_MEMBER_TRANSFORM_NAMES.has(getStaticMemberName$1(callee) ?? "");
23687
23877
  if (isPureGlobalCall || isPureMemberTransform) {
23688
23878
  if (isPureMemberTransform && isNodeOfType(callee, "MemberExpression")) mergeEvidence(evidence, collectValueEvidence(analysis, callee.object, frame, remainingCallFrames, new Set(visitedBindings)));
@@ -23735,6 +23925,15 @@ const collectValueEvidence = (analysis, expression, frame, remainingCallFrames,
23735
23925
  }
23736
23926
  return evidence;
23737
23927
  }
23928
+ if (isNodeOfType(node, "NewExpression")) {
23929
+ const callee = stripParenExpression(node.callee);
23930
+ if (!isNodeOfType(callee, "Identifier") || !PURE_GLOBAL_CONSTRUCTOR_NAMES.has(callee.name) || getIdentifierBindingIdentity(analysis, callee) !== null) {
23931
+ evidence.hasUnknownSource = true;
23932
+ return evidence;
23933
+ }
23934
+ for (const argument of node.arguments ?? []) mergeEvidence(evidence, collectValueEvidence(analysis, argument, frame, remainingCallFrames, new Set(visitedBindings)));
23935
+ return evidence;
23936
+ }
23738
23937
  if (isFunctionLike$1(node) || isNodeOfType(node, "AwaitExpression")) {
23739
23938
  evidence.hasUnknownSource = true;
23740
23939
  return evidence;
@@ -26277,6 +26476,19 @@ const noCloneElement = defineRule({
26277
26476
  } })
26278
26477
  });
26279
26478
  //#endregion
26479
+ //#region src/plugin/utils/get-call-method-name.ts
26480
+ /**
26481
+ * Returns the static method name of a call's callee when it's a
26482
+ * non-computed MemberExpression (`obj.method` → `"method"`), or
26483
+ * `null` otherwise. Used by `no-pass-data-to-parent` and
26484
+ * `no-pass-live-state-to-parent` to match the receiver-bound
26485
+ * method-call shape against an allow/block list.
26486
+ */
26487
+ const getCallMethodName = (callee) => {
26488
+ if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
26489
+ return null;
26490
+ };
26491
+ //#endregion
26280
26492
  //#region src/plugin/rules/state-and-effects/no-create-context-in-render.ts
26281
26493
  const MESSAGE$31 = "createContext() builds a new context every render, so every consumer gets cut off & resets.";
26282
26494
  const CONTEXT_MODULES = [
@@ -26284,23 +26496,35 @@ const CONTEXT_MODULES = [
26284
26496
  "use-context-selector",
26285
26497
  "react-tracked"
26286
26498
  ];
26287
- const isCreateContextCallee = (callee) => {
26499
+ const getSupportedContextImportSource = (symbol) => {
26500
+ if (symbol?.kind !== "import") return null;
26501
+ const importDeclaration = symbol.declarationNode.parent;
26502
+ if (!importDeclaration || !isNodeOfType(importDeclaration, "ImportDeclaration") || typeof importDeclaration.source.value !== "string" || !CONTEXT_MODULES.includes(importDeclaration.source.value)) return null;
26503
+ return importDeclaration.source.value;
26504
+ };
26505
+ const isSupportedNamespaceSymbol = (symbol) => getSupportedContextImportSource(symbol) !== null && Boolean(symbol && (isNodeOfType(symbol.declarationNode, "ImportDefaultSpecifier") || isNodeOfType(symbol.declarationNode, "ImportNamespaceSpecifier")));
26506
+ const isDestructuredCreateContextBinding = (identifier, scopes) => {
26507
+ if (!isNodeOfType(identifier, "Identifier")) return false;
26508
+ const symbol = scopes.symbolFor(identifier);
26509
+ if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "ObjectPattern")) return false;
26510
+ const property = symbol.bindingIdentifier.parent;
26511
+ if (!property || !isNodeOfType(property, "Property") || getStaticPropertyKeyName(property, { allowComputedString: true }) !== "createContext") return false;
26512
+ const initializer = stripParenExpression(symbol.initializer);
26513
+ return isNodeOfType(initializer, "Identifier") && isSupportedNamespaceSymbol(resolveConstIdentifierAlias(initializer, scopes));
26514
+ };
26515
+ const isCreateContextCall = (node, scopes) => {
26516
+ const callee = stripParenExpression(node.callee);
26288
26517
  if (isNodeOfType(callee, "Identifier")) {
26289
- const binding = getImportBindingForName(callee, callee.name);
26290
- return binding !== null && binding.exportedName === "createContext" && CONTEXT_MODULES.includes(binding.source);
26518
+ if (isDestructuredCreateContextBinding(callee, scopes)) return true;
26519
+ const symbol = resolveConstIdentifierAlias(callee, scopes);
26520
+ return getSupportedContextImportSource(symbol) !== null && Boolean(symbol && getImportedName(symbol.declarationNode) === "createContext");
26291
26521
  }
26292
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed) {
26293
- const namespaceIdentifier = callee.object;
26294
- const propertyIdentifier = callee.property;
26295
- if (!isNodeOfType(namespaceIdentifier, "Identifier")) return false;
26296
- if (!isNodeOfType(propertyIdentifier, "Identifier")) return false;
26297
- if (propertyIdentifier.name !== "createContext") return false;
26298
- const namespaceName = namespaceIdentifier.name;
26299
- if (isCanonicalReactNamespaceName(namespaceName)) return true;
26300
- const importSource = getImportSourceForName(namespaceIdentifier, namespaceName);
26301
- return importSource !== null && CONTEXT_MODULES.includes(importSource);
26302
- }
26303
- return false;
26522
+ if (!isNodeOfType(callee, "MemberExpression")) return false;
26523
+ if ((getCallMethodName(callee) ?? (callee.computed && isNodeOfType(callee.property, "Literal") && typeof callee.property.value === "string" ? callee.property.value : null)) !== "createContext") return false;
26524
+ const receiver = stripParenExpression(callee.object);
26525
+ if (!isNodeOfType(receiver, "Identifier")) return false;
26526
+ if (receiver.name === "React" && scopes.isGlobalReference(receiver)) return true;
26527
+ return isSupportedNamespaceSymbol(resolveConstIdentifierAlias(receiver, scopes));
26304
26528
  };
26305
26529
  const noCreateContextInRender = defineRule({
26306
26530
  id: "no-create-context-in-render",
@@ -26309,7 +26533,7 @@ const noCreateContextInRender = defineRule({
26309
26533
  category: "Correctness",
26310
26534
  recommendation: "Move `createContext(...)` outside the component, to the top level of the file, so it stays the same on every render.",
26311
26535
  create: (context) => ({ CallExpression(node) {
26312
- if (!isCreateContextCallee(node.callee)) return;
26536
+ if (!isCreateContextCall(node, context.scopes)) return;
26313
26537
  const componentOrHookName = enclosingComponentOrHookName(node);
26314
26538
  if (!componentOrHookName) return;
26315
26539
  context.report({
@@ -28259,20 +28483,31 @@ const noDocumentStartViewTransition = defineRule({
28259
28483
  } })
28260
28484
  });
28261
28485
  //#endregion
28486
+ //#region src/plugin/utils/get-static-property-name.ts
28487
+ const getStaticPropertyName = (memberExpression) => {
28488
+ const property = memberExpression.property;
28489
+ if (!memberExpression.computed && isNodeOfType(property, "Identifier")) return property.name;
28490
+ if (memberExpression.computed && isNodeOfType(property, "Literal")) return typeof property.value === "string" ? property.value : null;
28491
+ if (memberExpression.computed && isNodeOfType(property, "TemplateLiteral") && property.expressions.length === 0) return property.quasis[0]?.value.cooked ?? property.quasis[0]?.value.raw ?? null;
28492
+ return null;
28493
+ };
28494
+ //#endregion
28262
28495
  //#region src/plugin/rules/js-performance/no-document-write.ts
28263
28496
  const MESSAGE$24 = "`document.write()` blocks parsing, is ignored (or wipes the page) after load, and is flagged by browsers as a performance anti-pattern. Build DOM nodes or set `innerHTML`/`textContent` on a target element instead.";
28264
28497
  const WRITE_METHODS = new Set(["write", "writeln"]);
28498
+ const isGlobalDocumentReference = (node, context) => node.name === "document" && context.scopes.isGlobalReference(node);
28265
28499
  const noDocumentWrite = defineRule({
28266
28500
  id: "no-document-write",
28267
28501
  title: "document.write/writeln",
28268
28502
  severity: "warn",
28269
28503
  recommendation: "Don't use `document.write()`/`document.writeln()`. Append DOM nodes or set `innerHTML`/`textContent` on a specific element instead.",
28270
28504
  create: (context) => ({ CallExpression(node) {
28271
- const callee = node.callee;
28272
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return;
28505
+ const callee = stripParenExpression(node.callee);
28506
+ if (!isNodeOfType(callee, "MemberExpression")) return;
28273
28507
  const receiver = stripParenExpression(callee.object);
28274
- if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "document") return;
28275
- if (!isNodeOfType(callee.property, "Identifier") || !WRITE_METHODS.has(callee.property.name)) return;
28508
+ if (!isNodeOfType(receiver, "Identifier") || !isGlobalDocumentReference(receiver, context)) return;
28509
+ const methodName = getStaticPropertyName(callee);
28510
+ if (methodName === null || !WRITE_METHODS.has(methodName)) return;
28276
28511
  context.report({
28277
28512
  node,
28278
28513
  message: MESSAGE$24
@@ -29079,6 +29314,14 @@ const noEffectWithFreshDeps = defineRule({
29079
29314
  //#endregion
29080
29315
  //#region src/plugin/rules/security/no-eval.ts
29081
29316
  const SANDBOX_SURFACE_PATH_PATTERN = /(?:^|[/-])sandbox(?:$|[/-])|(?:^|\/)[\w.]+-sandbox(?:\/|\.[cm]?[jt]sx?$)/i;
29317
+ const getExecutableGlobalName = (node, context) => {
29318
+ const expression = stripParenExpression(node);
29319
+ if (isNodeOfType(expression, "Identifier")) return context.scopes.isGlobalReference(expression) ? expression.name : null;
29320
+ if (!isNodeOfType(expression, "MemberExpression")) return null;
29321
+ const receiver = stripParenExpression(expression.object);
29322
+ if (!isNodeOfType(receiver, "Identifier") || receiver.name !== "globalThis" || !context.scopes.isGlobalReference(receiver)) return null;
29323
+ return getStaticPropertyName(expression);
29324
+ };
29082
29325
  const noEval = defineRule({
29083
29326
  id: "no-eval",
29084
29327
  title: "eval() runs untrusted code strings",
@@ -29088,20 +29331,21 @@ const noEval = defineRule({
29088
29331
  if (SANDBOX_SURFACE_PATH_PATTERN.test(normalizeFilename(context.filename ?? ""))) return {};
29089
29332
  return {
29090
29333
  CallExpression(node) {
29091
- if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "eval") {
29334
+ const executableGlobalName = getExecutableGlobalName(node.callee, context);
29335
+ if (executableGlobalName === "eval") {
29092
29336
  context.report({
29093
29337
  node,
29094
29338
  message: "eval() is a code-injection vulnerability: it runs any string as code."
29095
29339
  });
29096
29340
  return;
29097
29341
  }
29098
- if (isNodeOfType(node.callee, "Identifier") && (node.callee.name === "setTimeout" || node.callee.name === "setInterval") && isNodeOfType(node.arguments?.[0], "Literal") && typeof node.arguments[0].value === "string") context.report({
29342
+ if ((executableGlobalName === "setTimeout" || executableGlobalName === "setInterval") && isNodeOfType(node.arguments?.[0], "Literal") && typeof node.arguments[0].value === "string") context.report({
29099
29343
  node,
29100
- message: `Passing a string to ${node.callee.name}() is a code-injection vulnerability, since it runs that string as code.`
29344
+ message: `Passing a string to ${executableGlobalName}() is a code-injection vulnerability, since it runs that string as code.`
29101
29345
  });
29102
29346
  },
29103
29347
  NewExpression(node) {
29104
- if (isNodeOfType(node.callee, "Identifier") && node.callee.name === "Function") {
29348
+ if (getExecutableGlobalName(node.callee, context) === "Function") {
29105
29349
  const onlyArgument = node.arguments.length === 1 ? node.arguments[0] : void 0;
29106
29350
  if (isNodeOfType(onlyArgument, "Literal") && typeof onlyArgument.value === "string" && onlyArgument.value.trim() === "return this") return;
29107
29351
  context.report({
@@ -34574,19 +34818,6 @@ const DATA_SINK_METHOD_NAMES = new Set([
34574
34818
  "deserialize"
34575
34819
  ]);
34576
34820
  //#endregion
34577
- //#region src/plugin/utils/get-call-method-name.ts
34578
- /**
34579
- * Returns the static method name of a call's callee when it's a
34580
- * non-computed MemberExpression (`obj.method` → `"method"`), or
34581
- * `null` otherwise. Used by `no-pass-data-to-parent` and
34582
- * `no-pass-live-state-to-parent` to match the receiver-bound
34583
- * method-call shape against an allow/block list.
34584
- */
34585
- const getCallMethodName = (callee) => {
34586
- if (isNodeOfType(callee, "MemberExpression") && !callee.computed && isNodeOfType(callee.property, "Identifier")) return callee.property.name;
34587
- return null;
34588
- };
34589
- //#endregion
34590
34821
  //#region src/plugin/rules/state-and-effects/no-pass-data-to-parent.ts
34591
34822
  const isUseStateIdentifier = (identifier) => {
34592
34823
  if (!isNodeOfType(identifier, "Identifier")) return false;
@@ -34831,6 +35062,14 @@ const isHandlerBagArgument = (analysis, argument) => {
34831
35062
  };
34832
35063
  const getFunctionalUpdaterDataRefs = (analysis, updater) => getDownstreamRefs(analysis, updater).filter((updaterRef) => !updaterRef.resolved?.defs.some((def) => def.type === "Parameter" && def.node === updater));
34833
35064
  const HOOK_NAME_PATTERN$1 = /^use[A-Z0-9]/;
35065
+ const EXTERNAL_SUBSCRIPTION_HOOK_NAMES = new Set([
35066
+ "useIntersectionObserver",
35067
+ "useMatchMedia",
35068
+ "useMediaQuery",
35069
+ "useResizeObserver",
35070
+ "useVisibility",
35071
+ "useWindowSize"
35072
+ ]);
34834
35073
  const isParentWiredHookResultRef = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
34835
35074
  const node = def.node;
34836
35075
  if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
@@ -34853,6 +35092,19 @@ const isParentWiredHookCalleeRef = (analysis, ref) => {
34853
35092
  if (!parent || !isNodeOfType(parent, "CallExpression") || parent.callee !== identifier) return false;
34854
35093
  return (parent.arguments ?? []).some((hookArgument) => getDownstreamRefs(analysis, hookArgument).some((downstreamRef) => isProp(analysis, downstreamRef)));
34855
35094
  };
35095
+ const isExternalSubscriptionHookRef = (ref) => {
35096
+ const identifier = ref.identifier;
35097
+ if (!isNodeOfType(identifier, "Identifier")) return false;
35098
+ if (EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(identifier.name) && isCalleePosition(identifier)) return true;
35099
+ return Boolean(ref.resolved?.defs.some((def) => {
35100
+ const node = def.node;
35101
+ if (!isNodeOfType(node, "VariableDeclarator") || !node.init) return false;
35102
+ const initializer = stripParenExpression(node.init);
35103
+ if (!isNodeOfType(initializer, "CallExpression")) return false;
35104
+ const callee = stripParenExpression(initializer.callee);
35105
+ return isNodeOfType(callee, "Identifier") && EXTERNAL_SUBSCRIPTION_HOOK_NAMES.has(callee.name);
35106
+ }));
35107
+ };
34856
35108
  const isImportBindingRef = (ref) => Boolean(ref.resolved?.defs.some((def) => def.type === "ImportBinding"));
34857
35109
  const isCalleePosition = (identifier) => {
34858
35110
  const parent = identifier.parent;
@@ -34914,10 +35166,11 @@ const noPassDataToParent = defineRule({
34914
35166
  if (argumentRef && resolveToFunction(argumentRef)) return [];
34915
35167
  }
34916
35168
  return getDownstreamRefs(analysis, argument);
34917
- }).flatMap((argumentRef) => getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
35169
+ }).flatMap((argumentRef) => isExternallyDrivenState(analysis, argumentRef) ? [] : getUpstreamRefs(analysis, argumentRef)).filter(isLeafRef);
34918
35170
  if (calleeNode === identifier && isWrapperHookCallbackRef(analysis, ref)) argsUpstreamRefs.push(...getArgsUpstreamRefs(analysis, ref).filter(isLeafRef));
34919
35171
  if (!argsUpstreamRefs.some((argRef) => {
34920
35172
  if (isUseStateIdentifier(argRef.identifier)) return false;
35173
+ if (isExternalSubscriptionHookRef(argRef)) return false;
34921
35174
  if (isProp(analysis, argRef)) return false;
34922
35175
  if (isUseRefIdentifier(argRef.identifier)) return false;
34923
35176
  if (isRefCurrent(argRef)) return false;
@@ -54675,12 +54928,6 @@ const webhookSignatureRisk = defineRule({
54675
54928
  //#region src/plugin/rules/zod/utils/zod-ast.ts
54676
54929
  const ZOD_MODULE = "zod";
54677
54930
  const ZOD_MODULE_SOURCES = [ZOD_MODULE];
54678
- const getStaticPropertyName = (member) => {
54679
- const property = member.property;
54680
- if (!member.computed && isNodeOfType(property, "Identifier")) return property.name;
54681
- if (member.computed && isNodeOfType(property, "Literal") && typeof property.value === "string") return property.value;
54682
- return null;
54683
- };
54684
54931
  const importInfoCache = /* @__PURE__ */ new WeakMap();
54685
54932
  const getImportInfoForIdentifier = (identifier) => {
54686
54933
  if (importInfoCache.has(identifier)) return importInfoCache.get(identifier) ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.5-dev.3afd146",
3
+ "version": "0.7.5-dev.70eff9a",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",