oxlint-plugin-react-doctor 0.7.6-dev.0a8d30b → 0.7.6-dev.17418

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 +226 -986
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1180,8 +1180,8 @@ const stripParenExpression = (node) => {
1180
1180
  return current;
1181
1181
  };
1182
1182
  //#endregion
1183
- //#region src/plugin/utils/get-root-identifier.ts
1184
- const getRootIdentifier$1 = (node, options) => {
1183
+ //#region src/plugin/utils/get-root-identifier-name.ts
1184
+ const getRootIdentifierName = (node, options) => {
1185
1185
  if (!node) return null;
1186
1186
  const followCallChains = options?.followCallChains === true;
1187
1187
  let cursor = node;
@@ -1192,18 +1192,16 @@ const getRootIdentifier$1 = (node, options) => {
1192
1192
  continue;
1193
1193
  }
1194
1194
  if (followCallChains && isNodeOfType(cursor, "CallExpression")) {
1195
- if (!isNodeOfType(cursor.callee, "MemberExpression")) return null;
1196
- cursor = cursor.callee.object;
1195
+ const callee = cursor.callee;
1196
+ if (!isNodeOfType(callee, "MemberExpression")) return null;
1197
+ cursor = callee.object;
1197
1198
  continue;
1198
1199
  }
1199
1200
  break;
1200
1201
  }
1201
- return isNodeOfType(cursor, "Identifier") ? cursor : null;
1202
+ return isNodeOfType(cursor, "Identifier") ? cursor.name : null;
1202
1203
  };
1203
1204
  //#endregion
1204
- //#region src/plugin/utils/get-root-identifier-name.ts
1205
- const getRootIdentifierName = (node, options) => getRootIdentifier$1(node, options)?.name ?? null;
1206
- //#endregion
1207
1205
  //#region src/plugin/rules/state-and-effects/advanced-event-handler-refs.ts
1208
1206
  const STABLE_HANDLER_HOOK_NAMES = new Set([
1209
1207
  "useCallback",
@@ -1520,37 +1518,6 @@ const hasJsxPropIgnoreCase = (attributes, targetProp) => {
1520
1518
  }
1521
1519
  };
1522
1520
  //#endregion
1523
- //#region src/plugin/utils/is-uppercase-name.ts
1524
- const isUppercaseName = (name) => UPPERCASE_PATTERN.test(name);
1525
- //#endregion
1526
- //#region src/plugin/utils/resolve-jsx-element-type.ts
1527
- const resolveConstantStringBinding = (name) => {
1528
- const binding = findVariableInitializer(name, name.name);
1529
- if (!binding?.initializer) return null;
1530
- const declarator = binding.bindingIdentifier.parent;
1531
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return null;
1532
- if (declarator.id !== binding.bindingIdentifier) return null;
1533
- const declaration = declarator.parent;
1534
- if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return null;
1535
- if (declaration.kind !== "const") return null;
1536
- const initializer = stripParenExpression(binding.initializer);
1537
- return isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
1538
- };
1539
- const flattenJsxName$2 = (name) => {
1540
- if (isNodeOfType(name, "JSXIdentifier")) return name.name;
1541
- if (isNodeOfType(name, "JSXMemberExpression")) return `${flattenJsxName$2(name.object)}.${name.property.name}`;
1542
- if (isNodeOfType(name, "JSXNamespacedName")) return `${name.namespace.name}:${name.name.name}`;
1543
- return "";
1544
- };
1545
- const resolveJsxElementType = (openingElement) => {
1546
- const name = openingElement.name;
1547
- if (isNodeOfType(name, "JSXIdentifier")) {
1548
- if (!isUppercaseName(name.name)) return name.name;
1549
- return resolveConstantStringBinding(name) ?? name.name;
1550
- }
1551
- return flattenJsxName$2(name);
1552
- };
1553
- //#endregion
1554
1521
  //#region src/plugin/utils/get-element-type.ts
1555
1522
  const EMPTY_JSX_A11Y_SETTINGS = Object.freeze({});
1556
1523
  const jsxA11ySettingsCache = /* @__PURE__ */ new WeakMap();
@@ -1563,8 +1530,14 @@ const readJsxA11ySettings = (settings) => {
1563
1530
  jsxA11ySettingsCache.set(settings, a11ySettings);
1564
1531
  return a11ySettings;
1565
1532
  };
1533
+ const flattenJsxName$2 = (name) => {
1534
+ if (isNodeOfType(name, "JSXIdentifier")) return name.name;
1535
+ if (isNodeOfType(name, "JSXMemberExpression")) return `${flattenJsxName$2(name.object)}.${name.property.name}`;
1536
+ if (isNodeOfType(name, "JSXNamespacedName")) return `${name.namespace.name}:${name.name.name}`;
1537
+ return "";
1538
+ };
1566
1539
  const computeElementType = (openingElement, a11ySettings) => {
1567
- const baseName = resolveJsxElementType(openingElement);
1540
+ const baseName = flattenJsxName$2(openingElement.name);
1568
1541
  if (a11ySettings.polymorphicPropName) {
1569
1542
  const polymorphicAttribute = hasJsxPropIgnoreCase(openingElement.attributes, a11ySettings.polymorphicPropName);
1570
1543
  if (polymorphicAttribute) {
@@ -2399,9 +2372,8 @@ const anchorIsValid = defineRule({
2399
2372
  const isTestlikeFile = isTestlikeFilename(context.filename);
2400
2373
  return { JSXOpeningElement(node) {
2401
2374
  if (isTestlikeFile) return;
2402
- const tag = getElementType(node, context.settings);
2403
- if (!fileHasJsxA11ySettings && tag !== "a") return;
2404
- if (tag !== "a") return;
2375
+ if (!fileHasJsxA11ySettings && (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a")) return;
2376
+ if (getElementType(node, context.settings) !== "a") return;
2405
2377
  let hrefAttribute;
2406
2378
  for (const attributeName of settings.hrefAttributeNames) {
2407
2379
  hrefAttribute = hasJsxPropIgnoreCase(node.attributes, attributeName);
@@ -5756,7 +5728,7 @@ const buttonHasType = defineRule({
5756
5728
  return {
5757
5729
  JSXOpeningElement(node) {
5758
5730
  if (isTestlikeFile) return;
5759
- if (resolveJsxElementType(node) !== "button") return;
5731
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "button") return;
5760
5732
  const typeAttr = hasJsxPropIgnoreCase(node.attributes, "type");
5761
5733
  if (!typeAttr) {
5762
5734
  if (hasJsxSpreadAttribute$1(node.attributes)) {
@@ -5946,7 +5918,7 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5946
5918
  };
5947
5919
  return {
5948
5920
  JSXOpeningElement(node) {
5949
- if (resolveJsxElementType(node) !== "input") return;
5921
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
5950
5922
  reportFromPresence(collectFromJsxAttributes(node.attributes));
5951
5923
  },
5952
5924
  CallExpression(node) {
@@ -5964,7 +5936,7 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5964
5936
  //#endregion
5965
5937
  //#region src/plugin/utils/get-static-property-key-name.ts
5966
5938
  const getStaticPropertyKeyName = (node, options = {}) => {
5967
- if (!isNodeOfType(node, "Property") && !isNodeOfType(node, "MethodDefinition")) return null;
5939
+ if (!isNodeOfType(node, "Property")) return null;
5968
5940
  if (node.computed) {
5969
5941
  if (options.allowComputedString && isNodeOfType(node.key, "Literal") && typeof node.key.value === "string") return node.key.value;
5970
5942
  return null;
@@ -6023,7 +5995,7 @@ const isPureEventBlockerHandler = (attribute) => {
6023
5995
  //#endregion
6024
5996
  //#region src/plugin/utils/resolve-const-identifier-alias.ts
6025
5997
  const resolveConstIdentifierAlias = (identifier, scopes) => {
6026
- if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
5998
+ if (!isNodeOfType(identifier, "Identifier")) return null;
6027
5999
  const visitedSymbolIds = /* @__PURE__ */ new Set();
6028
6000
  let symbol = scopes.symbolFor(identifier);
6029
6001
  while (symbol?.kind === "const") {
@@ -6298,131 +6270,6 @@ const isGlobalMethodCall = (node, objectName, methodName) => {
6298
6270
  return isNodeOfType(receiver, "Identifier") && receiver.name === objectName && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName;
6299
6271
  };
6300
6272
  //#endregion
6301
- //#region src/plugin/utils/collect-safely-validated-local-storage-keys.ts
6302
- const isLocalStorageCall = (node, methodName) => {
6303
- if (!isNodeOfType(node, "CallExpression")) return false;
6304
- const callee = stripParenExpression(node.callee);
6305
- if (!isNodeOfType(callee, "MemberExpression")) return false;
6306
- const receiver = stripParenExpression(callee.object);
6307
- return isNodeOfType(receiver, "Identifier") && receiver.name === "localStorage" && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName;
6308
- };
6309
- const catchReturnsFallback = (tryStatement) => {
6310
- const handler = tryStatement.handler;
6311
- if (!handler) return false;
6312
- let hasReturn = false;
6313
- let hasThrow = false;
6314
- walkAst(handler.body, (child) => {
6315
- if (isFunctionLike$1(child)) return false;
6316
- if (isNodeOfType(child, "ReturnStatement")) hasReturn = true;
6317
- if (isNodeOfType(child, "ThrowStatement")) hasThrow = true;
6318
- });
6319
- return hasReturn && !hasThrow;
6320
- };
6321
- const resolveValidatorFunction = (callee, scopes) => {
6322
- const unwrappedCallee = stripParenExpression(callee);
6323
- if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
6324
- const symbol = scopes.symbolFor(unwrappedCallee);
6325
- if (!symbol) return null;
6326
- const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
6327
- return isFunctionLike$1(candidate) ? candidate : null;
6328
- };
6329
- const validatorChecksPayloadProperties = (validatorFunction, scopes) => {
6330
- if (!isFunctionLike$1(validatorFunction)) return false;
6331
- const firstParameter = validatorFunction.params?.[0];
6332
- if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
6333
- const parameterSymbol = scopes.symbolFor(firstParameter);
6334
- if (!parameterSymbol) return false;
6335
- const payloadSymbolIds = new Set([parameterSymbol.id]);
6336
- let hasPropertyTypeCheck = false;
6337
- walkAst(validatorFunction.body, (child) => {
6338
- if (isFunctionLike$1(child)) return false;
6339
- if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.init) {
6340
- const initializer = stripParenExpression(child.init);
6341
- if (isNodeOfType(initializer, "Identifier")) {
6342
- const initializerSymbol = scopes.symbolFor(initializer);
6343
- if (initializerSymbol && payloadSymbolIds.has(initializerSymbol.id)) {
6344
- const aliasSymbol = scopes.symbolFor(child.id);
6345
- if (aliasSymbol) payloadSymbolIds.add(aliasSymbol.id);
6346
- }
6347
- }
6348
- }
6349
- if (!isNodeOfType(child, "UnaryExpression") || child.operator !== "typeof") return;
6350
- const checkedValue = stripParenExpression(child.argument);
6351
- if (!isNodeOfType(checkedValue, "MemberExpression")) return;
6352
- const receiver = stripParenExpression(checkedValue.object);
6353
- if (!isNodeOfType(receiver, "Identifier")) return;
6354
- const receiverSymbol = scopes.symbolFor(receiver);
6355
- if (receiverSymbol && payloadSymbolIds.has(receiverSymbol.id)) hasPropertyTypeCheck = true;
6356
- });
6357
- return hasPropertyTypeCheck;
6358
- };
6359
- const incrementCount = (counts, keyValue) => {
6360
- counts.set(keyValue, (counts.get(keyValue) ?? 0) + 1);
6361
- };
6362
- const isReturnedExpression = (expression) => {
6363
- const parent = expression.parent;
6364
- return Boolean(parent && isNodeOfType(parent, "ReturnStatement") && parent.argument === expression);
6365
- };
6366
- const collectSafelyValidatedLocalStorageKeys = ({ programNode, scopes, resolveKey }) => {
6367
- const readCountsByKey = /* @__PURE__ */ new Map();
6368
- const safeReadCountsByKey = /* @__PURE__ */ new Map();
6369
- const safeReadSymbolIds = /* @__PURE__ */ new Set();
6370
- walkAst(programNode, (child) => {
6371
- if (!isNodeOfType(child, "CallExpression") || !isLocalStorageCall(child, "getItem")) return;
6372
- const keyArgument = child.arguments?.[0];
6373
- if (!keyArgument) return;
6374
- const keyValue = resolveKey(keyArgument);
6375
- if (keyValue !== null) incrementCount(readCountsByKey, keyValue);
6376
- });
6377
- walkAst(programNode, (child) => {
6378
- if (!isNodeOfType(child, "TryStatement") || !catchReturnsFallback(child)) return;
6379
- const rawValueKeys = /* @__PURE__ */ new Map();
6380
- const parsedValueSources = /* @__PURE__ */ new Map();
6381
- walkAst(child.block, (tryChild) => {
6382
- if (isFunctionLike$1(tryChild)) return false;
6383
- if (isNodeOfType(tryChild, "VariableDeclarator") && isNodeOfType(tryChild.id, "Identifier") && tryChild.init) {
6384
- const initializer = stripParenExpression(tryChild.init);
6385
- if (isNodeOfType(initializer, "CallExpression") && isLocalStorageCall(initializer, "getItem")) {
6386
- const keyArgument = initializer.arguments?.[0];
6387
- const bindingSymbol = scopes.symbolFor(tryChild.id);
6388
- if (keyArgument && bindingSymbol) {
6389
- const keyValue = resolveKey(keyArgument);
6390
- if (keyValue !== null) rawValueKeys.set(bindingSymbol.id, keyValue);
6391
- }
6392
- }
6393
- if (isNodeOfType(initializer, "CallExpression") && isGlobalMethodCall(initializer, "JSON", "parse")) {
6394
- const rawValueArgument = initializer.arguments?.[0];
6395
- const parsedValueSymbol = scopes.symbolFor(tryChild.id);
6396
- if (rawValueArgument && isNodeOfType(rawValueArgument, "Identifier") && parsedValueSymbol) {
6397
- const rawValueSymbol = scopes.symbolFor(rawValueArgument);
6398
- if (rawValueSymbol && rawValueKeys.has(rawValueSymbol.id)) parsedValueSources.set(parsedValueSymbol.id, rawValueSymbol.id);
6399
- }
6400
- }
6401
- }
6402
- if (!isNodeOfType(tryChild, "ConditionalExpression") || !isReturnedExpression(tryChild)) return;
6403
- const test = stripParenExpression(tryChild.test);
6404
- if (!isNodeOfType(test, "CallExpression")) return;
6405
- const testedValue = test.arguments?.[0];
6406
- if (!testedValue || !isNodeOfType(testedValue, "Identifier")) return;
6407
- const parsedValueSymbol = scopes.symbolFor(testedValue);
6408
- if (!parsedValueSymbol) return;
6409
- const rawValueSymbolId = parsedValueSources.get(parsedValueSymbol.id);
6410
- if (rawValueSymbolId === void 0) return;
6411
- const returnedValue = stripParenExpression(tryChild.consequent);
6412
- if (!isNodeOfType(returnedValue, "Identifier") || scopes.symbolFor(returnedValue)?.id !== parsedValueSymbol.id) return;
6413
- const validatorFunction = resolveValidatorFunction(test.callee, scopes);
6414
- if (!validatorFunction || !validatorChecksPayloadProperties(validatorFunction, scopes)) return;
6415
- const keyValue = rawValueKeys.get(rawValueSymbolId);
6416
- if (keyValue === void 0 || safeReadSymbolIds.has(rawValueSymbolId)) return;
6417
- safeReadSymbolIds.add(rawValueSymbolId);
6418
- incrementCount(safeReadCountsByKey, keyValue);
6419
- });
6420
- });
6421
- const safeKeys = /* @__PURE__ */ new Set();
6422
- for (const [keyValue, readCount] of readCountsByKey) if (safeReadCountsByKey.get(keyValue) === readCount) safeKeys.add(keyValue);
6423
- return safeKeys;
6424
- };
6425
- //#endregion
6426
6273
  //#region src/plugin/rules/client/client-localstorage-no-version.ts
6427
6274
  const VERSIONED_KEY_PATTERN = /(?:[._:-]v\d+|@\d+|\bv\d+\b)/i;
6428
6275
  const CAMEL_CASE_VERSIONED_KEY_PATTERN = /[a-z]V\d+/;
@@ -6444,39 +6291,26 @@ const clientLocalstorageNoVersion = defineRule({
6444
6291
  severity: "warn",
6445
6292
  category: "Correctness",
6446
6293
  recommendation: "Put a version in the storage key (e.g. \"myKey:v1\"). If you change the data shape later, old saved data can be ignored instead of crashing the app.",
6447
- create: (context) => {
6448
- let safelyValidatedStorageKeys = /* @__PURE__ */ new Set();
6449
- return {
6450
- Program(node) {
6451
- safelyValidatedStorageKeys = collectSafelyValidatedLocalStorageKeys({
6452
- programNode: node,
6453
- scopes: context.scopes,
6454
- resolveKey: (keyNode) => resolveStringKey(keyNode, context)
6455
- });
6456
- },
6457
- CallExpression(node) {
6458
- if (!isNodeOfType(node.callee, "MemberExpression")) return;
6459
- const receiver = stripParenExpression(node.callee.object);
6460
- if (!isNodeOfType(receiver, "Identifier")) return;
6461
- if (receiver.name !== "localStorage") return;
6462
- if (!isNodeOfType(node.callee.property, "Identifier")) return;
6463
- if (node.callee.property.name !== "setItem") return;
6464
- const keyArg = node.arguments?.[0];
6465
- if (!keyArg) return;
6466
- const keyValue = resolveStringKey(keyArg, context);
6467
- if (keyValue === null) return;
6468
- if (isVersionedKey(keyValue)) return;
6469
- if (safelyValidatedStorageKeys.has(keyValue)) return;
6470
- const valueArg = node.arguments?.[1];
6471
- if (!valueArg) return;
6472
- if (!isJsonStringifyCall(valueArg)) return;
6473
- context.report({
6474
- node: keyArg,
6475
- message: `${receiver.name}.setItem("${keyValue}", JSON.stringify(...)) has no version, so changing the data shape later crashes your users' saved sessions. Add one to the key (e.g. "${keyValue}:v1").`
6476
- });
6477
- }
6478
- };
6479
- }
6294
+ create: (context) => ({ CallExpression(node) {
6295
+ if (!isNodeOfType(node.callee, "MemberExpression")) return;
6296
+ const receiver = stripParenExpression(node.callee.object);
6297
+ if (!isNodeOfType(receiver, "Identifier")) return;
6298
+ if (receiver.name !== "localStorage") return;
6299
+ if (!isNodeOfType(node.callee.property, "Identifier")) return;
6300
+ if (node.callee.property.name !== "setItem") return;
6301
+ const keyArg = node.arguments?.[0];
6302
+ if (!keyArg) return;
6303
+ const keyValue = resolveStringKey(keyArg, context);
6304
+ if (keyValue === null) return;
6305
+ if (isVersionedKey(keyValue)) return;
6306
+ const valueArg = node.arguments?.[1];
6307
+ if (!valueArg) return;
6308
+ if (!isJsonStringifyCall(valueArg)) return;
6309
+ context.report({
6310
+ node: keyArg,
6311
+ message: `${receiver.name}.setItem("${keyValue}", JSON.stringify(...)) has no version, so changing the data shape later crashes your users' saved sessions. Add one to the key (e.g. "${keyValue}:v1").`
6312
+ });
6313
+ } })
6480
6314
  });
6481
6315
  //#endregion
6482
6316
  //#region src/plugin/rules/client/client-passive-event-listeners.ts
@@ -7312,17 +7146,12 @@ const findVisibleIdentifierDeclaration = (identifier, sinkIndex, fileContent) =>
7312
7146
  }
7313
7147
  return nearestDeclaration;
7314
7148
  };
7315
- const isDeclarationStable = (identifier, declaration, usageIndex, fileContent) => {
7316
- if (declaration.isImmutable) return true;
7317
- const textAfterInitializer = fileContent.slice(declaration.initializerStartIndex + declaration.initializer.length, usageIndex);
7318
- return !new RegExp(`(?:^|[^\\w$.])${escapeRegExp(identifier)}\\s*=(?!=)`).test(textAfterInitializer);
7319
- };
7320
7149
  const isTrustedHighlighterValue = (valueExpression, fileContent, sinkIndex) => {
7321
7150
  if (!/highlight/i.test(valueExpression)) return false;
7322
7151
  const identifier = valueExpression.match(/^([\w$]+)/)?.[1];
7323
7152
  if (identifier === void 0) return false;
7324
7153
  const declaration = findVisibleIdentifierDeclaration(identifier, sinkIndex, fileContent);
7325
- if (declaration !== null) return isDeclarationStable(identifier, declaration, sinkIndex, fileContent) && SERIALIZER_CALL_PROVENANCE_PATTERN.test(declaration.initializer);
7154
+ if (declaration !== null) return SERIALIZER_CALL_PROVENANCE_PATTERN.test(declaration.initializer);
7326
7155
  return /highlighted/i.test(valueExpression) || HIGHLIGHTER_LIBRARY_PATTERN.test(fileContent);
7327
7156
  };
7328
7157
  const isExplicitlyTrustedHtmlValue = (valueExpression, fileContent, sinkIndex, visitedIdentifiers = /* @__PURE__ */ new Set()) => {
@@ -7334,7 +7163,7 @@ const isExplicitlyTrustedHtmlValue = (valueExpression, fileContent, sinkIndex, v
7334
7163
  const identifier = trimmedExpression.match(/^([\w$]+)(?:(?:\??\.[\w$]+)|(?:\[\s*(?:"[^"\n]*"|'[^'\n]*'|\d+)\s*\]))*$/)?.[1];
7335
7164
  if (identifier && !visitedIdentifiers.has(identifier)) {
7336
7165
  const declaration = findVisibleIdentifierDeclaration(identifier, sinkIndex, fileContent);
7337
- if (declaration && isDeclarationStable(identifier, declaration, sinkIndex, fileContent)) {
7166
+ if (declaration) {
7338
7167
  const nextVisitedIdentifiers = new Set(visitedIdentifiers);
7339
7168
  nextVisitedIdentifiers.add(identifier);
7340
7169
  return isExplicitlyTrustedHtmlValue(declaration.initializer, fileContent, declaration.initializerStartIndex, nextVisitedIdentifiers);
@@ -7401,16 +7230,6 @@ const splitTopLevelArguments = (argumentText) => {
7401
7230
  argumentsList.push(argumentText.slice(startIndex).trim());
7402
7231
  return argumentsList;
7403
7232
  };
7404
- const isBackedByFunctionParameter = (expression, expressionIndex, fileContent, visitedIdentifiers = /* @__PURE__ */ new Set()) => {
7405
- const identifier = expression.trim().match(/^([\w$]+)$/)?.[1];
7406
- if (identifier === void 0 || visitedIdentifiers.has(identifier)) return false;
7407
- if (findContainingFunctionParameterSource(identifier, expressionIndex, fileContent) !== null) return true;
7408
- const declaration = findVisibleIdentifierDeclaration(identifier, expressionIndex, fileContent);
7409
- if (!declaration || !isDeclarationStable(identifier, declaration, expressionIndex, fileContent)) return false;
7410
- const nextVisitedIdentifiers = new Set(visitedIdentifiers);
7411
- nextVisitedIdentifiers.add(identifier);
7412
- return isBackedByFunctionParameter(declaration.initializer, declaration.initializerStartIndex, fileContent, nextVisitedIdentifiers);
7413
- };
7414
7233
  const isHtmlTainted = (expression, fileContent, sinkIndex, visitedIdentifiers, visitedCallSites) => {
7415
7234
  const trimmedExpression = expression.trim();
7416
7235
  if (isExplicitlyTrustedHtmlValue(trimmedExpression, fileContent, sinkIndex)) return false;
@@ -7421,11 +7240,8 @@ const isHtmlTainted = (expression, fileContent, sinkIndex, visitedIdentifiers, v
7421
7240
  const declaration = findVisibleIdentifierDeclaration(identifier, sinkIndex, fileContent);
7422
7241
  const parameterSource = findContainingFunctionParameterSource(identifier, sinkIndex, fileContent);
7423
7242
  if (declaration !== null && (parameterSource === null || declaration.declarationIndex > parameterSource.declarationNameIndex)) {
7424
- if (isDeclarationStable(identifier, declaration, sinkIndex, fileContent) && isExplicitlyTrustedHtmlValue(declaration.initializer, fileContent, declaration.initializerStartIndex)) return false;
7425
- if (!isDeclarationStable(identifier, declaration, sinkIndex, fileContent)) return true;
7426
- if (isHtmlTainted(declaration.initializer, fileContent, declaration.initializerStartIndex, visitedIdentifiers, visitedCallSites)) return true;
7427
- if (isBackedByFunctionParameter(declaration.initializer, declaration.initializerStartIndex, fileContent)) return false;
7428
- return HTML_TAINT_PATTERN.test(trimmedExpression);
7243
+ if (isExplicitlyTrustedHtmlValue(declaration.initializer, fileContent, declaration.initializerStartIndex)) return false;
7244
+ return isHtmlTainted(declaration.initializer, fileContent, declaration.initializerStartIndex, visitedIdentifiers, visitedCallSites) || HTML_TAINT_PATTERN.test(trimmedExpression);
7429
7245
  }
7430
7246
  if (parameterSource !== null && parameterSource.functionName.length > 0) {
7431
7247
  const callPattern = new RegExp(`\\b${escapeRegExp(parameterSource.functionName)}\\s*\\(`, "g");
@@ -7507,13 +7323,12 @@ const dangerousHtmlSink = defineRule({
7507
7323
  if (isTrustedHighlighterValue(valueExpression, file.content, sinkIndex)) continue;
7508
7324
  if (valueIdentifier !== void 0) {
7509
7325
  const escapedIdentifier = escapeRegExp(valueIdentifier);
7510
- const visibleDeclaration = findVisibleIdentifierDeclaration(valueIdentifier, sinkIndex, file.content);
7511
- const visibleInitializer = visibleDeclaration?.initializer;
7326
+ const visibleInitializer = findVisibleIdentifierDeclaration(valueIdentifier, sinkIndex, file.content)?.initializer;
7512
7327
  if (!(visibleInitializer !== void 0 && (splitTopLevelByPlus(visibleInitializer).length > 1 || (visibleInitializer.match(/\$\{/g)?.length ?? 0) > 1))) {
7513
7328
  const fromSerializer = new RegExp(`\\b${escapedIdentifier}\\b\\s*${SERIALIZER_ASSIGNMENT_PATTERN.source}`, "i");
7514
- if (visibleInitializer === void 0 ? fromSerializer.test(file.content) : visibleDeclaration !== null && isDeclarationStable(valueIdentifier, visibleDeclaration, sinkIndex, file.content) && SERIALIZER_CALL_PROVENANCE_PATTERN.test(visibleInitializer)) continue;
7329
+ if (visibleInitializer === void 0 ? fromSerializer.test(file.content) : SERIALIZER_CALL_PROVENANCE_PATTERN.test(visibleInitializer)) continue;
7515
7330
  const fromSanitizer = new RegExp(`\\b${escapedIdentifier}\\b\\s*${SANITIZED_ASSIGNMENT_PATTERN.source}`, "i");
7516
- if (visibleInitializer === void 0 ? fromSanitizer.test(file.content) : visibleDeclaration !== null && isDeclarationStable(valueIdentifier, visibleDeclaration, sinkIndex, file.content) && SANITIZED_ASSIGNMENT_PATTERN.test(`=${visibleInitializer}`)) continue;
7331
+ if (visibleInitializer === void 0 ? fromSanitizer.test(file.content) : SANITIZED_ASSIGNMENT_PATTERN.test(`=${visibleInitializer}`)) continue;
7517
7332
  }
7518
7333
  if (new RegExp(`\\b${escapedIdentifier}\\b\\s*${DOM_CONTENT_ASSIGNMENT_PATTERN.source}`).test(file.content)) continue;
7519
7334
  if (new RegExp(`highlight[\\w$]*\\s*\\.map\\(\\s*(?:async\\s+)?\\(?\\s*${escapedIdentifier}\\b`, "i").test(file.content) && HIGHLIGHTER_LIBRARY_PATTERN.test(file.content)) continue;
@@ -7599,7 +7414,7 @@ const findJsxAttribute = (attributes, attributeName) => attributes?.find((attrib
7599
7414
  const getOpeningElementTagName = (openingElement) => {
7600
7415
  if (!openingElement) return null;
7601
7416
  if (!isNodeOfType(openingElement, "JSXOpeningElement")) return null;
7602
- if (isNodeOfType(openingElement.name, "JSXIdentifier")) return resolveJsxElementType(openingElement);
7417
+ if (isNodeOfType(openingElement.name, "JSXIdentifier")) return openingElement.name.name;
7603
7418
  if (isNodeOfType(openingElement.name, "JSXMemberExpression")) {
7604
7419
  let cursor = openingElement.name;
7605
7420
  while (isNodeOfType(cursor, "JSXMemberExpression")) cursor = cursor.property;
@@ -7851,7 +7666,7 @@ const dialogHasAccessibleName = defineRule({
7851
7666
  if (isTestlikeFilename(context.filename)) return {};
7852
7667
  return { JSXOpeningElement(node) {
7853
7668
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
7854
- const tagName = resolveJsxElementType(node);
7669
+ const tagName = node.name.name;
7855
7670
  if (tagName[0] !== tagName[0]?.toLowerCase()) return;
7856
7671
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
7857
7672
  const roleValue = roleAttribute ? getJsxPropStringValue(roleAttribute) : null;
@@ -12083,7 +11898,7 @@ const forbidDomProps = defineRule({
12083
11898
  return { JSXOpeningElement(node) {
12084
11899
  if (forbidMap.size === 0) return;
12085
11900
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12086
- const elementName = resolveJsxElementType(node);
11901
+ const elementName = node.name.name;
12087
11902
  if (isReactComponentName(elementName)) return;
12088
11903
  for (const attribute of node.attributes) {
12089
11904
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -12161,7 +11976,7 @@ const forbidElements = defineRule({
12161
11976
  return {
12162
11977
  JSXOpeningElement(node) {
12163
11978
  if (forbidMap.size === 0) return;
12164
- const fullName = resolveJsxElementType(node);
11979
+ const fullName = flattenJsxName$1(node.name);
12165
11980
  if (!fullName || !forbidMap.has(fullName)) return;
12166
11981
  context.report({
12167
11982
  node: node.name,
@@ -12523,7 +12338,8 @@ const buildMessage$22 = (childTagName) => `Your users get reshuffled HTML becaus
12523
12338
  const isParagraphElement = (candidate) => {
12524
12339
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12525
12340
  const opening = candidate.openingElement;
12526
- return resolveJsxElementType(opening) === "p";
12341
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12342
+ return opening.name.name === "p";
12527
12343
  };
12528
12344
  const findEnclosingParagraph = (openingElement) => {
12529
12345
  const owningElement = openingElement.parent;
@@ -12544,7 +12360,8 @@ const htmlNoInvalidParagraphChild = defineRule({
12544
12360
  severity: "warn",
12545
12361
  recommendation: "Swap the `<p>` for a `<div>`, or move the child outside the paragraph.",
12546
12362
  create: (context) => ({ JSXOpeningElement(node) {
12547
- const childTagName = resolveJsxElementType(node);
12363
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12364
+ const childTagName = node.name.name;
12548
12365
  if (!BLOCK_LEVEL_ELEMENTS.has(childTagName)) return;
12549
12366
  if (!findEnclosingParagraph(node)) return;
12550
12367
  context.report({
@@ -12575,7 +12392,7 @@ const getHostTagName = (jsxElement) => {
12575
12392
  if (!isNodeOfType(jsxElement, "JSXElement")) return null;
12576
12393
  const opening = jsxElement.openingElement;
12577
12394
  if (!isNodeOfType(opening.name, "JSXIdentifier")) return null;
12578
- const tagName = resolveJsxElementType(opening);
12395
+ const tagName = opening.name.name;
12579
12396
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return null;
12580
12397
  return tagName;
12581
12398
  };
@@ -12605,7 +12422,7 @@ const findClosestHostAncestor = (jsxElement) => {
12605
12422
  if (isNodeOfType(ancestor, "JSXElement")) {
12606
12423
  const opening = ancestor.openingElement;
12607
12424
  if (isNodeOfType(opening.name, "JSXIdentifier")) {
12608
- const ancestorTag = resolveJsxElementType(opening);
12425
+ const ancestorTag = opening.name.name;
12609
12426
  if (ancestorTag.length === 0) {
12610
12427
  previous = ancestor;
12611
12428
  ancestor = ancestor.parent ?? null;
@@ -12687,7 +12504,8 @@ const buildMessage$20 = (tagName) => `Your users get broken clicks, focus & scre
12687
12504
  const isJsxElementWithTagName = (candidate, tagName) => {
12688
12505
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12689
12506
  const opening = candidate.openingElement;
12690
- return resolveJsxElementType(opening) === tagName;
12507
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12508
+ return opening.name.name === tagName;
12691
12509
  };
12692
12510
  const findEnclosingSameTag = (openingElement, tagName) => {
12693
12511
  const owningElement = openingElement.parent;
@@ -12708,7 +12526,8 @@ const htmlNoNestedInteractive = defineRule({
12708
12526
  severity: "warn",
12709
12527
  recommendation: "Move the inner `<a>` or `<button>` so it's a sibling, or change the outer one to a plain `<div>` or `<span>`.",
12710
12528
  create: (context) => ({ JSXOpeningElement(node) {
12711
- const tagName = resolveJsxElementType(node);
12529
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12530
+ const tagName = node.name.name;
12712
12531
  if (tagName !== "a" && tagName !== "button") return;
12713
12532
  if (!findEnclosingSameTag(node, tagName)) return;
12714
12533
  context.report({
@@ -12823,7 +12642,7 @@ const iframeMissingSandbox = defineRule({
12823
12642
  matchByOccurrence: true,
12824
12643
  create: skipNonProductionFiles((context) => ({
12825
12644
  JSXOpeningElement(node) {
12826
- if (resolveJsxElementType(node) !== "iframe") return;
12645
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "iframe") return;
12827
12646
  const sandboxAttr = hasJsxPropIgnoreCase(node.attributes, "sandbox");
12828
12647
  if (!sandboxAttr) {
12829
12648
  const hasExplicitSrc = Boolean(hasJsxPropIgnoreCase(node.attributes, "src"));
@@ -13815,28 +13634,6 @@ const jsAsyncReduceWithoutAwaitedAcc = defineRule({
13815
13634
  } })
13816
13635
  });
13817
13636
  //#endregion
13818
- //#region src/plugin/utils/unwrap-discarded-expression.ts
13819
- const unwrapDiscardedExpression = (node) => {
13820
- let expression = isNodeOfType(node, "ExpressionStatement") ? node.expression : node;
13821
- for (;;) {
13822
- expression = stripParenExpression(expression);
13823
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") {
13824
- expression = expression.argument;
13825
- continue;
13826
- }
13827
- if (isNodeOfType(expression, "SequenceExpression")) {
13828
- const expressions = expression.expressions ?? [];
13829
- const finalExpression = expressions.at(-1);
13830
- const prefixExpressions = expressions.slice(0, -1);
13831
- if (finalExpression && prefixExpressions.length > 0 && prefixExpressions.every((prefixExpression) => isNodeOfType(stripParenExpression(prefixExpression), "Literal"))) {
13832
- expression = finalExpression;
13833
- continue;
13834
- }
13835
- }
13836
- return expression;
13837
- }
13838
- };
13839
- //#endregion
13840
13637
  //#region src/plugin/rules/js-performance/js-batch-dom-css.ts
13841
13638
  const ITERATOR_METHOD_NAMES$2 = new Set([
13842
13639
  "forEach",
@@ -14021,19 +13818,9 @@ const hasAttachmentBefore = (scopeOwner, elementName, beforeStart) => {
14021
13818
  });
14022
13819
  return foundAttachment;
14023
13820
  };
14024
- const getStyleAssignment = (node) => {
14025
- if (!isNodeOfType(node, "ExpressionStatement")) return null;
14026
- const expression = unwrapDiscardedExpression(node);
14027
- if (!isNodeOfType(expression, "AssignmentExpression")) return null;
14028
- if (!isNodeOfType(expression.left, "MemberExpression")) return null;
14029
- if (!isNodeOfType(expression.left.object, "MemberExpression")) return null;
14030
- if (!isNodeOfType(expression.left.object.property, "Identifier")) return null;
14031
- return expression.left.object.property.name === "style" ? expression : null;
14032
- };
14033
13821
  const isProvablyDetachedAtWrite = (styleWriteStatement) => {
14034
- const assignment = getStyleAssignment(styleWriteStatement);
14035
- if (!assignment || !isNodeOfType(assignment.left, "MemberExpression") || !isNodeOfType(assignment.left.object, "MemberExpression")) return false;
14036
- const elementExpression = assignment.left.object.object;
13822
+ if (!isNodeOfType(styleWriteStatement, "ExpressionStatement") || !isNodeOfType(styleWriteStatement.expression, "AssignmentExpression") || !isNodeOfType(styleWriteStatement.expression.left, "MemberExpression") || !isNodeOfType(styleWriteStatement.expression.left.object, "MemberExpression")) return false;
13823
+ const elementExpression = styleWriteStatement.expression.left.object.object;
14037
13824
  const creationRoot = resolveDetachedCreationRoot(elementExpression, 0);
14038
13825
  if (!creationRoot) return false;
14039
13826
  return !hasAttachmentBefore(creationRoot.scopeOwner, creationRoot.rootName, getNodeStart$1(styleWriteStatement));
@@ -14045,17 +13832,15 @@ const jsBatchDomCss = defineRule({
14045
13832
  severity: "warn",
14046
13833
  recommendation: "Do all your reads first, then all your writes. Mixing them inside a loop makes the browser recalculate the layout again and again, which is slow",
14047
13834
  create: (context) => {
14048
- const writesLayoutAffectingProperty = (node) => {
14049
- const assignment = getStyleAssignment(node);
14050
- return assignment !== null && isNodeOfType(assignment.left, "MemberExpression") && isNodeOfType(assignment.left.property, "Identifier") && !LAYOUT_NEUTRAL_STYLE_PROPERTY_NAMES.has(assignment.left.property.name);
14051
- };
13835
+ const isStyleAssignment = (node) => isNodeOfType(node, "ExpressionStatement") && isNodeOfType(node.expression, "AssignmentExpression") && isNodeOfType(node.expression.left, "MemberExpression") && isNodeOfType(node.expression.left.object, "MemberExpression") && isNodeOfType(node.expression.left.object.property, "Identifier") && node.expression.left.object.property.name === "style";
13836
+ const writesLayoutAffectingProperty = (node) => isNodeOfType(node, "ExpressionStatement") && isNodeOfType(node.expression, "AssignmentExpression") && isNodeOfType(node.expression.left, "MemberExpression") && isNodeOfType(node.expression.left.property, "Identifier") && !LAYOUT_NEUTRAL_STYLE_PROPERTY_NAMES.has(node.expression.left.property.name);
14052
13837
  return { BlockStatement(node) {
14053
13838
  const perIterationBody = findEnclosingPerIterationBody(node);
14054
13839
  if (!perIterationBody) return;
14055
13840
  const statements = node.body ?? [];
14056
13841
  let layoutReads = null;
14057
13842
  for (let statementIndex = 1; statementIndex < statements.length; statementIndex++) {
14058
- if (getStyleAssignment(statements[statementIndex]) === null || getStyleAssignment(statements[statementIndex - 1]) === null) continue;
13843
+ if (!isStyleAssignment(statements[statementIndex]) || !isStyleAssignment(statements[statementIndex - 1])) continue;
14059
13844
  if (!writesLayoutAffectingProperty(statements[statementIndex]) && !writesLayoutAffectingProperty(statements[statementIndex - 1])) continue;
14060
13845
  layoutReads ??= scanPerIterationLayoutReads(perIterationBody);
14061
13846
  if (!layoutReads.hasUsedLayoutRead || layoutReads.hasDeliberateForcedReflow) return;
@@ -14751,18 +14536,6 @@ const jsHoistRegexp = defineRule({
14751
14536
  }, { treatIteratorCallbacksAsLoops: true })
14752
14537
  });
14753
14538
  //#endregion
14754
- //#region src/plugin/utils/resolve-first-argument-binding.ts
14755
- const resolveFirstArgumentBinding = (firstParameter) => {
14756
- if (!firstParameter) return null;
14757
- if (!isNodeOfType(firstParameter, "RestElement")) return firstParameter;
14758
- if (!isNodeOfType(firstParameter.argument, "ArrayPattern")) return null;
14759
- const elements = firstParameter.argument.elements ?? [];
14760
- if (elements.length !== 1) return null;
14761
- const firstBinding = elements[0];
14762
- if (!firstBinding || isNodeOfType(firstBinding, "RestElement")) return null;
14763
- return firstBinding;
14764
- };
14765
- //#endregion
14766
14539
  //#region src/plugin/rules/js-performance/js-index-maps.ts
14767
14540
  const referencesParameter = (expression, parameterName) => {
14768
14541
  if (!expression) return false;
@@ -14773,7 +14546,7 @@ const referencesParameter = (expression, parameterName) => {
14773
14546
  const isSingleFieldEqualityPredicate = (node) => {
14774
14547
  const callback = node.arguments?.[0];
14775
14548
  if (!isInlineFunctionExpression(callback)) return false;
14776
- const firstParameter = resolveFirstArgumentBinding(callback.params?.[0]);
14549
+ const firstParameter = callback.params?.[0];
14777
14550
  if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
14778
14551
  let predicate = null;
14779
14552
  const body = callback.body;
@@ -15465,21 +15238,9 @@ const isSmallFixedListMember = (receiver) => {
15465
15238
  };
15466
15239
  const getResolvedInitializer = (receiver) => {
15467
15240
  if (!isNodeOfType(receiver, "Identifier")) return null;
15468
- const binding = findVariableInitializer(receiver, receiver.name);
15469
- const initializer = binding?.initializer ?? null;
15470
- if (!binding || !initializer) return null;
15471
- const isDefault = isNodeOfType(binding.bindingIdentifier.parent, "AssignmentPattern");
15472
- if (isNodeOfType(initializer, "Identifier")) {
15473
- const aliased = findVariableInitializer(initializer, initializer.name);
15474
- if (aliased?.initializer) return {
15475
- initializer: aliased.initializer,
15476
- isDefault: isDefault || isNodeOfType(aliased.bindingIdentifier.parent, "AssignmentPattern")
15477
- };
15478
- }
15479
- return {
15480
- initializer,
15481
- isDefault
15482
- };
15241
+ const initializer = findVariableInitializer(receiver, receiver.name)?.initializer ?? null;
15242
+ if (initializer && isNodeOfType(initializer, "Identifier")) return findVariableInitializer(initializer, initializer.name)?.initializer ?? initializer;
15243
+ return initializer;
15483
15244
  };
15484
15245
  const isSplitCall = (expression) => {
15485
15246
  if (!expression) return false;
@@ -15645,8 +15406,8 @@ const isBoundedConstantCollection = (collection) => {
15645
15406
  if (isScreamingSnakeCaseConstantReceiver(stripped)) return true;
15646
15407
  if (isSmallInlineLiteralArray(stripped)) return true;
15647
15408
  if (isNodeOfType(stripped, "Identifier")) {
15648
- const resolved = getResolvedInitializer(stripped);
15649
- if (resolved && !resolved.isDefault && isSmallInlineLiteralArray(resolved.initializer)) return true;
15409
+ const initializer = getResolvedInitializer(stripped);
15410
+ if (initializer && isSmallInlineLiteralArray(initializer)) return true;
15650
15411
  }
15651
15412
  return false;
15652
15413
  };
@@ -15698,8 +15459,8 @@ const jsSetMapLookups = defineRule({
15698
15459
  if (isIndexedArrayElementWithStringArgument(receiver, node.arguments?.[0])) return;
15699
15460
  const resolvedInitializer = getResolvedInitializer(receiver);
15700
15461
  if (resolvedInitializer) {
15701
- if (isLikelyStringReceiver(resolvedInitializer.initializer)) return;
15702
- if (!resolvedInitializer.isDefault && isSmallInlineLiteralArray(resolvedInitializer.initializer)) return;
15462
+ if (isLikelyStringReceiver(resolvedInitializer)) return;
15463
+ if (isSmallInlineLiteralArray(resolvedInitializer)) return;
15703
15464
  }
15704
15465
  if (isStringElementOfSplitIteration(receiver)) return;
15705
15466
  if (isReceiverDeclaredInNearestLoop(receiver, node)) return;
@@ -16112,21 +15873,14 @@ const jsxFilenameExtension = defineRule({
16112
15873
  });
16113
15874
  //#endregion
16114
15875
  //#region src/plugin/utils/is-jsx-fragment-element.ts
16115
- const isJsxFragmentElement = (node, scopes) => {
15876
+ const isJsxFragmentElement = (node) => {
16116
15877
  if (!isNodeOfType(node, "JSXOpeningElement")) return false;
16117
15878
  const elementName = node.name;
16118
- if (isNodeOfType(elementName, "JSXIdentifier")) {
16119
- if (!scopes) return elementName.name === "Fragment";
16120
- const symbol = resolveConstIdentifierAlias(elementName, scopes);
16121
- if (!symbol) return elementName.name === "Fragment" && scopes.isGlobalReference(elementName);
16122
- return isImportedFromReact(symbol) && getImportedName(symbol.declarationNode) === "Fragment";
16123
- }
15879
+ if (isNodeOfType(elementName, "JSXIdentifier")) return elementName.name === "Fragment";
16124
15880
  if (isNodeOfType(elementName, "JSXMemberExpression")) {
16125
15881
  if (!isNodeOfType(elementName.object, "JSXIdentifier")) return false;
16126
- if (elementName.property.name !== "Fragment") return false;
16127
- if (!scopes) return elementName.object.name === "React";
16128
- if (isReactNamespaceImport(elementName.object, scopes)) return true;
16129
- return elementName.object.name === "React" && scopes.isGlobalReference(elementName.object);
15882
+ if (elementName.object.name !== "React") return false;
15883
+ return elementName.property.name === "Fragment";
16130
15884
  }
16131
15885
  return false;
16132
15886
  };
@@ -16152,7 +15906,7 @@ const jsxFragments = defineRule({
16152
15906
  if (mode !== "syntax") return;
16153
15907
  if (!node.closingElement) return;
16154
15908
  const openingElement = node.openingElement;
16155
- if (!isJsxFragmentElement(openingElement, context.scopes)) return;
15909
+ if (!isJsxFragmentElement(openingElement)) return;
16156
15910
  if (openingElement.attributes.length > 0) return;
16157
15911
  context.report({
16158
15912
  node: openingElement,
@@ -18833,6 +18587,10 @@ const resolveSettings$29 = (settings) => {
18833
18587
  if (typeof reactDoctor !== "object" || reactDoctor === null) return {};
18834
18588
  return reactDoctor.jsxNoScriptUrl ?? {};
18835
18589
  };
18590
+ const getElementName = (node) => {
18591
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
18592
+ return null;
18593
+ };
18836
18594
  const isLinkPropForElement = (elementName, attributeName, options) => {
18837
18595
  if (elementName === "a" && attributeName === "href") return true;
18838
18596
  const explicit = options.components?.[elementName];
@@ -18852,8 +18610,7 @@ const jsxNoScriptUrl = defineRule({
18852
18610
  create: (context) => {
18853
18611
  const options = resolveSettings$29(context.settings);
18854
18612
  return { JSXOpeningElement(node) {
18855
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
18856
- const elementName = resolveJsxElementType(node);
18613
+ const elementName = getElementName(node);
18857
18614
  if (!elementName) return;
18858
18615
  for (const attribute of node.attributes) {
18859
18616
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -19040,6 +18797,11 @@ const checkTarget = (attributeValue) => {
19040
18797
  alternate: false
19041
18798
  };
19042
18799
  };
18800
+ const getOpeningElementName = (node) => {
18801
+ const name = node.name;
18802
+ if (isNodeOfType(name, "JSXIdentifier")) return name.name;
18803
+ return null;
18804
+ };
19043
18805
  const jsxNoTargetBlank = defineRule({
19044
18806
  id: "jsx-no-target-blank",
19045
18807
  title: "Unsafe target=_blank link",
@@ -19059,8 +18821,7 @@ const jsxNoTargetBlank = defineRule({
19059
18821
  return settings.formComponents.has(tagName);
19060
18822
  };
19061
18823
  return { JSXOpeningElement(node) {
19062
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
19063
- const tagName = resolveJsxElementType(node);
18824
+ const tagName = getOpeningElementName(node);
19064
18825
  if (!tagName) return;
19065
18826
  if (!isLink(tagName) && !isForm(tagName)) return;
19066
18827
  const linkAttributeNames = settings.linkComponents.get(tagName) ?? ["href"];
@@ -19293,7 +19054,7 @@ const jsxNoUselessFragment = defineRule({
19293
19054
  return {
19294
19055
  JSXElement(node) {
19295
19056
  const openingElement = node.openingElement;
19296
- if (!isJsxFragmentElement(openingElement, context.scopes)) return;
19057
+ if (!isJsxFragmentElement(openingElement)) return;
19297
19058
  if (hasJsxKeyAttribute(openingElement)) return;
19298
19059
  if (checkChildren(node, openingElement, node.children)) return;
19299
19060
  if (isChildOfHtmlElement(node)) context.report({
@@ -20535,24 +20296,11 @@ const hasDirective = (programNode, directive) => {
20535
20296
  return Boolean(programNode.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === directive));
20536
20297
  };
20537
20298
  //#endregion
20538
- //#region src/plugin/utils/unwrap-object-integrity-expression.ts
20539
- const OBJECT_INTEGRITY_METHOD_NAMES = new Set([
20540
- "freeze",
20541
- "seal",
20542
- "preventExtensions"
20543
- ]);
20544
- const OBJECT_FREEZE_OR_SEAL_METHOD_NAMES = new Set(["freeze", "seal"]);
20545
- const unwrapObjectIntegrityExpression = (node, scopes, methodNames = OBJECT_INTEGRITY_METHOD_NAMES) => {
20546
- let expression = stripParenExpression(node);
20547
- while (isNodeOfType(expression, "CallExpression")) {
20548
- const callee = stripParenExpression(expression.callee);
20549
- if (!isNodeOfType(callee, "MemberExpression") || callee.computed || !isNodeOfType(callee.object, "Identifier") || callee.object.name !== "Object" || !scopes.isGlobalReference(callee.object) || !isNodeOfType(callee.property, "Identifier") || !methodNames.has(callee.property.name)) break;
20550
- const wrappedExpression = expression.arguments[0];
20551
- if (!wrappedExpression || isNodeOfType(wrappedExpression, "SpreadElement")) break;
20552
- expression = stripParenExpression(wrappedExpression);
20553
- }
20554
- return expression;
20555
- };
20299
+ //#region src/plugin/utils/is-uppercase-name.ts
20300
+ const isUppercaseName = (name) => UPPERCASE_PATTERN.test(name);
20301
+ //#endregion
20302
+ //#region src/plugin/utils/is-component-assignment.ts
20303
+ const isComponentAssignment = (node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && isUppercaseName(node.id.name) && Boolean(node.init) && (isNodeOfType(node.init, "ArrowFunctionExpression") || isNodeOfType(node.init, "FunctionExpression"));
20556
20304
  //#endregion
20557
20305
  //#region src/plugin/rules/nextjs/nextjs-async-client-component.ts
20558
20306
  const nextjsAsyncClientComponent = defineRule({
@@ -20578,10 +20326,10 @@ const nextjsAsyncClientComponent = defineRule({
20578
20326
  },
20579
20327
  VariableDeclarator(node) {
20580
20328
  if (!fileHasUseClient) return;
20329
+ if (!isComponentAssignment(node)) return;
20330
+ if (!isInlineFunctionExpression(node.init)) return;
20331
+ if (!node.init.async) return;
20581
20332
  if (!isNodeOfType(node.id, "Identifier")) return;
20582
- if (!isUppercaseName(node.id.name) || !node.init) return;
20583
- const componentFunction = unwrapObjectIntegrityExpression(node.init, context.scopes, OBJECT_FREEZE_OR_SEAL_METHOD_NAMES);
20584
- if (!isInlineFunctionExpression(componentFunction) || !componentFunction.async) return;
20585
20333
  context.report({
20586
20334
  node,
20587
20335
  message: `Async client component "${node.id.name}" fails to render because client components can't be async.`
@@ -20883,7 +20631,7 @@ const nextjsNoAElement = defineRule({
20883
20631
  severity: "warn",
20884
20632
  recommendation: "`import Link from 'next/link'` for client-side navigation, prefetching, and preserved scroll position",
20885
20633
  create: (context) => ({ JSXOpeningElement(node) {
20886
- if (resolveJsxElementType(node) !== "a") return;
20634
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
20887
20635
  const attributes = node.attributes ?? [];
20888
20636
  const downloadAttribute = findJsxAttribute(attributes, "download");
20889
20637
  if (downloadAttribute) {
@@ -21079,7 +20827,7 @@ const nextjsNoCssLink = defineRule({
21079
20827
  severity: "warn",
21080
20828
  recommendation: "Import CSS directly or use CSS Modules so Next.js can bundle, order, and optimize the stylesheet.",
21081
20829
  create: (context) => ({ JSXOpeningElement(node) {
21082
- if (resolveJsxElementType(node) !== "link") return;
20830
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
21083
20831
  const attributes = node.attributes ?? [];
21084
20832
  const relAttribute = findJsxAttribute(attributes, "rel");
21085
20833
  if (!relAttribute?.value) return;
@@ -21187,7 +20935,7 @@ const nextjsNoFontLink = defineRule({
21187
20935
  severity: "warn",
21188
20936
  recommendation: "`import { Inter } from \"next/font/google\"` for self-hosting, zero layout shift, and no render-blocking requests",
21189
20937
  create: (context) => ({ JSXOpeningElement(node) {
21190
- if (resolveJsxElementType(node) !== "link") return;
20938
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
21191
20939
  const attributes = node.attributes ?? [];
21192
20940
  const hrefAttribute = findJsxAttribute(attributes, "href");
21193
20941
  if (!hrefAttribute?.value) return;
@@ -21362,7 +21110,7 @@ const nextjsNoImgElement = defineRule({
21362
21110
  create: (context) => {
21363
21111
  if (isGeneratedImageRenderContext(context)) return {};
21364
21112
  return { JSXOpeningElement(node) {
21365
- if (resolveJsxElementType(node) !== "img") return;
21113
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
21366
21114
  if (isGeneratedImageRenderContext(context, node)) return;
21367
21115
  const programRoot = findProgramRoot(node);
21368
21116
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
@@ -21409,8 +21157,8 @@ const nextjsNoPolyfillScript = defineRule({
21409
21157
  severity: "warn",
21410
21158
  recommendation: "Next.js includes polyfills for fetch, Promise, Object.assign, Array.from, and 50+ others automatically",
21411
21159
  create: (context) => ({ JSXOpeningElement(node) {
21412
- const elementName = resolveJsxElementType(node);
21413
- if (elementName !== "script" && elementName !== "Script") return;
21160
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
21161
+ if (node.name.name !== "script" && node.name.name !== "Script") return;
21414
21162
  const srcAttribute = findJsxAttribute(node.attributes ?? [], "src");
21415
21163
  if (!srcAttribute?.value) return;
21416
21164
  const srcValue = isNodeOfType(srcAttribute.value, "Literal") ? srcAttribute.value.value : null;
@@ -23218,15 +22966,10 @@ const isProp = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
23218
22966
  if (!declaringNode) return false;
23219
22967
  return isReactFunctionalComponent(declaringNode) && !isReactFunctionalHOC(analysis, declaringNode) || isCustomHook(declaringNode);
23220
22968
  }));
23221
- const isWholePropsParameterBinding = (bindingNode) => {
23222
- if (isFunctionLike$1(bindingNode.parent)) return true;
23223
- let declaringFunction = bindingNode.parent;
23224
- while (declaringFunction && !isFunctionLike$1(declaringFunction)) declaringFunction = declaringFunction.parent;
23225
- return Boolean(declaringFunction && resolveFirstArgumentBinding(declaringFunction.params?.[0]) === bindingNode);
23226
- };
23227
22969
  const isWholePropsObjectReference = (analysis, ref) => isProp(analysis, ref) && Boolean(ref.resolved?.defs.some((def) => {
23228
22970
  if (def.type !== "Parameter") return false;
23229
- return isWholePropsParameterBinding(def.name);
22971
+ const bindingParent = def.name.parent;
22972
+ return isFunctionLike$1(bindingParent);
23230
22973
  }));
23231
22974
  const isIdentifierOrMemberExpression = (node) => isNodeOfType(node, "Identifier") || isNodeOfType(node, "MemberExpression");
23232
22975
  const isPropAlias = (analysis, ref) => {
@@ -26223,56 +25966,6 @@ const noBarrelImport = defineRule({
26223
25966
  }
26224
25967
  });
26225
25968
  //#endregion
26226
- //#region src/plugin/utils/function-returns-matching-expression.ts
26227
- const collectReturnedExpressions = (functionNode) => {
26228
- if (!isFunctionLike$1(functionNode)) return [];
26229
- const body = functionNode.body;
26230
- if (!body) return [];
26231
- if (!isNodeOfType(body, "BlockStatement")) return [body];
26232
- const returnedExpressions = [];
26233
- walkAst(body, (node) => {
26234
- if (node !== body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
26235
- if (isNodeOfType(node, "ReturnStatement") && node.argument) returnedExpressions.push(node.argument);
26236
- });
26237
- return returnedExpressions;
26238
- };
26239
- const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpression) => {
26240
- const visitedExpressions = /* @__PURE__ */ new Set();
26241
- const visitedFunctions = /* @__PURE__ */ new Set();
26242
- const functionMatches = (candidateFunction) => {
26243
- if (visitedFunctions.has(candidateFunction)) return false;
26244
- visitedFunctions.add(candidateFunction);
26245
- return collectReturnedExpressions(candidateFunction).some(expressionMatches);
26246
- };
26247
- const expressionMatches = (expression) => {
26248
- const unwrappedExpression = stripParenExpression(expression);
26249
- if (visitedExpressions.has(unwrappedExpression)) return false;
26250
- visitedExpressions.add(unwrappedExpression);
26251
- if (matchesExpression(unwrappedExpression)) return true;
26252
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
26253
- const symbol = scopes.symbolFor(unwrappedExpression);
26254
- if (!symbol || symbol.kind !== "const" || !symbol.initializer) return false;
26255
- const initializer = stripParenExpression(symbol.initializer);
26256
- if (isFunctionLike$1(initializer)) return false;
26257
- return expressionMatches(initializer);
26258
- }
26259
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
26260
- if (unwrappedExpression.arguments.length !== 0) return false;
26261
- if (!isNodeOfType(unwrappedExpression.callee, "Identifier")) return false;
26262
- const symbol = scopes.symbolFor(unwrappedExpression.callee);
26263
- if (!symbol || symbol.kind !== "const" && symbol.kind !== "function") return false;
26264
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
26265
- const candidateFunction = isFunctionLike$1(initializer) ? initializer : isFunctionLike$1(symbol.declarationNode) ? symbol.declarationNode : null;
26266
- if (!candidateFunction || candidateFunction.async || candidateFunction.generator || candidateFunction.params.length !== 0) return false;
26267
- return functionMatches(candidateFunction);
26268
- }
26269
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return expressionMatches(unwrappedExpression.consequent) || expressionMatches(unwrappedExpression.alternate);
26270
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) return expressionMatches(unwrappedExpression.left) || expressionMatches(unwrappedExpression.right);
26271
- return false;
26272
- };
26273
- return functionMatches(functionNode);
26274
- };
26275
- //#endregion
26276
25969
  //#region src/plugin/utils/function-contains-react-render-output.ts
26277
25970
  const NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES = new Set([
26278
25971
  "FunctionDeclaration",
@@ -26292,13 +25985,12 @@ const isCallArgumentFunctionExpression = (node) => {
26292
25985
  return parent.arguments.some((argumentNode) => argumentNode === node);
26293
25986
  };
26294
25987
  const isNestedRenderEvidenceBoundary = (node) => NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES.has(node.type) && !isCallArgumentFunctionExpression(node);
26295
- const isRenderOutputExpression = (node, scopes) => node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS);
26296
25988
  const containsRenderOutput$1 = (rootNode, scopes) => {
26297
25989
  let hasRenderOutput = false;
26298
25990
  walkAst(rootNode, (node) => {
26299
25991
  if (hasRenderOutput) return false;
26300
25992
  if (node !== rootNode && isNestedRenderEvidenceBoundary(node)) return false;
26301
- if (isRenderOutputExpression(node, scopes)) {
25993
+ if (node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS)) {
26302
25994
  hasRenderOutput = true;
26303
25995
  return false;
26304
25996
  }
@@ -26309,7 +26001,7 @@ const renderOutputCache = /* @__PURE__ */ new WeakMap();
26309
26001
  const functionContainsReactRenderOutput = (functionNode, scopes) => {
26310
26002
  const cachedEntry = renderOutputCache.get(functionNode);
26311
26003
  if (cachedEntry && cachedEntry.scopes === scopes) return cachedEntry.hasRenderOutput;
26312
- const hasRenderOutput = containsRenderOutput$1(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => isRenderOutputExpression(expression, scopes));
26004
+ const hasRenderOutput = containsRenderOutput$1(functionNode, scopes);
26313
26005
  renderOutputCache.set(functionNode, {
26314
26006
  scopes,
26315
26007
  hasRenderOutput
@@ -26317,9 +26009,6 @@ const functionContainsReactRenderOutput = (functionNode, scopes) => {
26317
26009
  return hasRenderOutput;
26318
26010
  };
26319
26011
  //#endregion
26320
- //#region src/plugin/utils/is-component-assignment.ts
26321
- const isComponentAssignment = (node) => isNodeOfType(node, "VariableDeclarator") && isNodeOfType(node.id, "Identifier") && isUppercaseName(node.id.name) && Boolean(node.init) && (isNodeOfType(node.init, "ArrowFunctionExpression") || isNodeOfType(node.init, "FunctionExpression"));
26322
- //#endregion
26323
26012
  //#region src/plugin/utils/is-component-declaration.ts
26324
26013
  const isComponentDeclaration = (node) => isNodeOfType(node, "FunctionDeclaration") && node.id !== null && Boolean(node.id?.name) && isUppercaseName(node.id.name);
26325
26014
  //#endregion
@@ -28777,7 +28466,7 @@ const noDisabledZoom = defineRule({
28777
28466
  category: "Accessibility",
28778
28467
  recommendation: "Remove `user-scalable=no` and `maximum-scale` from the viewport meta tag. If the layout breaks at 200% zoom, fix the layout instead.",
28779
28468
  create: (context) => ({ JSXOpeningElement(node) {
28780
- if (resolveJsxElementType(node) !== "meta") return;
28469
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "meta") return;
28781
28470
  const nameAttr = findJsxAttribute(node.attributes ?? [], "name");
28782
28471
  if (!nameAttr?.value) return;
28783
28472
  if ((isNodeOfType(nameAttr.value, "Literal") ? nameAttr.value.value : null) !== "viewport") return;
@@ -29167,7 +28856,7 @@ const findTopLevelEffectCalls = (componentBody) => {
29167
28856
  if (!isNodeOfType(componentBody, "BlockStatement")) return effectCalls;
29168
28857
  for (const statement of componentBody.body ?? []) {
29169
28858
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
29170
- const expression = unwrapDiscardedExpression(statement);
28859
+ const expression = statement.expression;
29171
28860
  if (!isNodeOfType(expression, "CallExpression")) continue;
29172
28861
  if (!isHookCall$2(expression, EFFECT_HOOK_NAMES$1)) continue;
29173
28862
  effectCalls.push(expression);
@@ -31949,7 +31638,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
31949
31638
  severity: "warn",
31950
31639
  recommendation: "Don't combine `loading=\"lazy\"` with `fetchPriority=\"high\"`. A high-priority image (usually the LCP) should load eagerly; a lazy image is by definition not high priority.",
31951
31640
  create: (context) => ({ JSXOpeningElement(node) {
31952
- if (resolveJsxElementType(node) !== "img") return;
31641
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
31953
31642
  const loadingAttribute = hasJsxPropIgnoreCase(node.attributes, "loading");
31954
31643
  if (!loadingAttribute || getJsxPropStringValue(loadingAttribute)?.toLowerCase() !== "lazy") return;
31955
31644
  const fetchPriorityAttribute = hasJsxPropIgnoreCase(node.attributes, "fetchPriority");
@@ -31961,26 +31650,6 @@ const noImgLazyWithHighFetchpriority = defineRule({
31961
31650
  } })
31962
31651
  });
31963
31652
  //#endregion
31964
- //#region src/plugin/utils/react-ref-origin.ts
31965
- const resolveReactRefSymbol = (memberExpression, scopes) => {
31966
- const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
31967
- if (!isNodeOfType(memberExpression, "MemberExpression") || getStaticPropertyName(memberExpression) !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
31968
- const symbol = resolveConstIdentifierAlias(receiver, scopes);
31969
- if (!symbol?.initializer) return null;
31970
- const initializer = stripParenExpression(symbol.initializer);
31971
- if (!isNodeOfType(initializer, "CallExpression")) return null;
31972
- return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
31973
- };
31974
- const hasReactRefCurrentOrigin = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
31975
- const expression = stripParenExpression(node);
31976
- if (resolveReactRefSymbol(expression, scopes)) return true;
31977
- if (!isNodeOfType(expression, "Identifier")) return false;
31978
- const symbol = resolveConstIdentifierAlias(expression, scopes);
31979
- if (!symbol?.initializer || visitedSymbolIds.has(symbol.id)) return false;
31980
- visitedSymbolIds.add(symbol.id);
31981
- return hasReactRefCurrentOrigin(symbol.initializer, scopes, visitedSymbolIds);
31982
- };
31983
- //#endregion
31984
31653
  //#region src/plugin/rules/state-and-effects/no-impure-state-updater.ts
31985
31654
  const TIMER_FUNCTION_NAMES = new Set([
31986
31655
  "cancelAnimationFrame",
@@ -32012,33 +31681,6 @@ const NOTIFICATION_METHOD_NAMES = new Set([
32012
31681
  "success",
32013
31682
  "warning"
32014
31683
  ]);
32015
- const NOTIFICATION_MODULE_SOURCES = new Set([
32016
- "@chakra-ui/react",
32017
- "@heroui/react",
32018
- "@mantine/notifications",
32019
- "antd",
32020
- "react-hot-toast",
32021
- "react-toastify",
32022
- "sonner"
32023
- ]);
32024
- const SYNCHRONOUS_ARRAY_METHOD_NAMES = new Set([
32025
- "every",
32026
- "filter",
32027
- "find",
32028
- "findIndex",
32029
- "flatMap",
32030
- "forEach",
32031
- "map",
32032
- "reduce",
32033
- "reduceRight",
32034
- "some",
32035
- "sort"
32036
- ]);
32037
- const isNotificationModuleSource = (source) => {
32038
- if (!source) return false;
32039
- for (const moduleSource of NOTIFICATION_MODULE_SOURCES) if (source === moduleSource || source.startsWith(`${moduleSource}/`)) return true;
32040
- return /(?:^|[/_.-])(?:notification|toast)s?(?:$|[/_.-])/i.test(source);
32041
- };
32042
31684
  const getMemberCall = (node) => {
32043
31685
  if (!isNodeOfType(node, "CallExpression")) return null;
32044
31686
  if (!isNodeOfType(node.callee, "MemberExpression") || node.callee.computed) return null;
@@ -32050,37 +31692,27 @@ const getMemberCall = (node) => {
32050
31692
  };
32051
31693
  const isNotificationReceiver = (receiver, scopes) => {
32052
31694
  if (!isNodeOfType(receiver, "Identifier")) return false;
32053
- const importBinding = getImportBindingForName(receiver, receiver.name);
32054
- if (importBinding) return isNotificationModuleSource(importBinding.source) && (importBinding.exportedName === "default" || importBinding.exportedName !== null && NOTIFICATION_RECEIVER_NAMES.has(importBinding.exportedName));
31695
+ if (!NOTIFICATION_RECEIVER_NAMES.has(receiver.name)) return false;
32055
31696
  const symbol = scopes.symbolFor(receiver);
31697
+ if (symbol?.kind === "import") return true;
32056
31698
  if (!isNodeOfType(symbol?.initializer, "CallExpression")) return false;
32057
31699
  const callee = symbol.initializer.callee;
32058
- if (!isNodeOfType(callee, "Identifier")) return false;
32059
- const hookImport = getImportBindingForName(callee, callee.name);
32060
- return Boolean(hookImport && isNotificationModuleSource(hookImport.source) && hookImport.exportedName !== null && /^use(?:Message|Notification|Toast)$/.test(hookImport.exportedName));
32061
- };
32062
- const isKnownGlobalObject = (node, expectedName, scopes) => isNodeOfType(node, "Identifier") && node.name === expectedName && scopes.isGlobalReference(node);
32063
- const isGlobalMember = (node, expectedPropertyName, scopes) => isNodeOfType(node, "MemberExpression") && !node.computed && isNodeOfType(node.property, "Identifier") && node.property.name === expectedPropertyName && (isKnownGlobalObject(node.object, "window", scopes) || isKnownGlobalObject(node.object, "globalThis", scopes));
32064
- const isStorageReceiver = (node, scopes) => {
32065
- if (isNodeOfType(node, "Identifier") && STORAGE_RECEIVER_NAMES.has(node.name) && scopes.isGlobalReference(node)) return true;
32066
- for (const receiverName of STORAGE_RECEIVER_NAMES) if (isGlobalMember(node, receiverName, scopes)) return true;
32067
- return false;
31700
+ return isNodeOfType(callee, "Identifier") && /^use(?:Message|Notification|Toast)$/.test(callee.name);
32068
31701
  };
32069
31702
  const getKnownImpureCall = (callExpression, scopes) => {
32070
31703
  if (isNodeOfType(callExpression.callee, "Identifier") && TIMER_FUNCTION_NAMES.has(callExpression.callee.name) && scopes.isGlobalReference(callExpression.callee)) return `${callExpression.callee.name}()`;
32071
- if (isNodeOfType(callExpression.callee, "MemberExpression") && !callExpression.callee.computed && isNodeOfType(callExpression.callee.property, "Identifier") && TIMER_FUNCTION_NAMES.has(callExpression.callee.property.name) && (isKnownGlobalObject(callExpression.callee.object, "window", scopes) || isKnownGlobalObject(callExpression.callee.object, "globalThis", scopes))) return `${callExpression.callee.property.name}()`;
32072
31704
  const memberCall = getMemberCall(callExpression);
32073
31705
  if (!memberCall) return null;
32074
31706
  const { methodName, receiver } = memberCall;
32075
- if (STORAGE_MUTATION_METHOD_NAMES.has(methodName) && isStorageReceiver(receiver, scopes)) return `${methodName}()`;
32076
- if (EXTERNAL_READ_METHOD_NAMES.has(methodName) && hasReactRefCurrentOrigin(receiver, scopes)) return `.${methodName}()`;
32077
- const receiverRoot = getRootIdentifier$1(receiver);
32078
- if (EXTERNAL_READ_METHOD_NAMES.has(methodName) && receiverRoot !== null && isKnownGlobalObject(receiverRoot, "document", scopes)) return `.${methodName}()`;
32079
- if (NOTIFICATION_METHOD_NAMES.has(methodName) && isNotificationReceiver(receiver, scopes)) return `${methodName}()`;
31707
+ if (STORAGE_MUTATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && STORAGE_RECEIVER_NAMES.has(receiver.name) && scopes.isGlobalReference(receiver)) return `${receiver.name}.${methodName}()`;
31708
+ if (EXTERNAL_READ_METHOD_NAMES.has(methodName)) return `.${methodName}()`;
31709
+ if (NOTIFICATION_METHOD_NAMES.has(methodName) && isNodeOfType(receiver, "Identifier") && isNotificationReceiver(receiver, scopes)) return `${receiver.name}.${methodName}()`;
32080
31710
  return null;
32081
31711
  };
32082
31712
  const getExternalAssignmentDescription = (assignmentTarget, updater, scopes) => {
32083
- const rootIdentifier = getRootIdentifier$1(assignmentTarget);
31713
+ let rootIdentifier = null;
31714
+ if (isNodeOfType(assignmentTarget, "Identifier")) rootIdentifier = assignmentTarget;
31715
+ else if (isNodeOfType(assignmentTarget, "MemberExpression") && isNodeOfType(assignmentTarget.object, "Identifier")) rootIdentifier = assignmentTarget.object;
32084
31716
  if (!rootIdentifier) return null;
32085
31717
  const updaterScope = scopes.ownScopeFor(updater);
32086
31718
  if (!updaterScope) return null;
@@ -32089,26 +31721,12 @@ const getExternalAssignmentDescription = (assignmentTarget, updater, scopes) =>
32089
31721
  if (symbol.kind === "parameter" && symbol.scope === updaterScope) return `the updater argument "${rootIdentifier.name}"`;
32090
31722
  return isDescendantScope(symbol.scope, updaterScope) ? null : `the captured value "${rootIdentifier.name}"`;
32091
31723
  };
32092
- const isArrayValue = (node, scopes) => {
32093
- const expression = stripParenExpression(node);
32094
- if (isNodeOfType(expression, "ArrayExpression")) return true;
32095
- if (!isNodeOfType(expression, "Identifier")) return false;
32096
- return isNodeOfType(resolveConstIdentifierAlias(expression, scopes)?.initializer, "ArrayExpression");
32097
- };
32098
- const isDefinitelySynchronousCallback = (callback, scopes) => {
32099
- const parent = callback.parent;
32100
- if (!parent) return false;
32101
- if (isNodeOfType(parent, "CallExpression") && parent.callee === callback) return true;
32102
- if (!isNodeOfType(parent, "CallExpression") || !parent.arguments?.some((argument) => argument === callback)) return false;
32103
- if (isNodeOfType(parent.callee, "MemberExpression") && !parent.callee.computed && isNodeOfType(parent.callee.property, "Identifier") && SYNCHRONOUS_ARRAY_METHOD_NAMES.has(parent.callee.property.name) && isArrayValue(parent.callee.object, scopes)) return true;
32104
- return isNodeOfType(parent.callee, "MemberExpression") && !parent.callee.computed && isNodeOfType(parent.callee.object, "Identifier") && parent.callee.object.name === "Array" && scopes.isGlobalReference(parent.callee.object) && isNodeOfType(parent.callee.property, "Identifier") && parent.callee.property.name === "from" && parent.arguments[1] === callback;
32105
- };
32106
31724
  const findImpureUpdaterOperation = (updater, scopes) => {
32107
31725
  const analysis = getProgramAnalysis(updater);
32108
31726
  let operation = null;
32109
31727
  walkAst(updater, (child) => {
32110
31728
  if (operation) return false;
32111
- if (child !== updater && isFunctionLike$1(child) && !isDefinitelySynchronousCallback(child, scopes)) return false;
31729
+ if (child !== updater && isFunctionLike$1(child) && !executesDuringRender(child, scopes)) return false;
32112
31730
  if (isNodeOfType(child, "CallExpression")) {
32113
31731
  if (isNodeOfType(child.callee, "Identifier") && analysis) {
32114
31732
  const calleeReference = getRef(analysis, child.callee);
@@ -32140,25 +31758,18 @@ const noImpureStateUpdater = defineRule({
32140
31758
  severity: "error",
32141
31759
  recommendation: "Keep state updater callbacks pure and return only the next state. Move notifications, storage, timers, ref writes, and other external work into the event or effect that queues the update.",
32142
31760
  create: (context) => ({ CallExpression(node) {
32143
- const updaterArgument = node.arguments?.[0];
32144
- if (!updaterArgument) return;
31761
+ const updater = node.arguments?.[0];
31762
+ if (!updater || !isFunctionLike$1(updater)) return;
32145
31763
  const analysis = getProgramAnalysis(node);
32146
31764
  if (!analysis || !isNodeOfType(node.callee, "Identifier")) return;
32147
31765
  const calleeReference = getRef(analysis, node.callee);
32148
31766
  if (!calleeReference || !isStateSetterCall(analysis, calleeReference)) return;
32149
31767
  const stateDeclarator = getUseStateDecl(analysis, calleeReference);
32150
31768
  if (!isNodeOfType(stateDeclarator, "VariableDeclarator") || !isNodeOfType(stateDeclarator.init, "CallExpression") || !isReactApiCall(stateDeclarator.init, "useState", context.scopes, { allowGlobalReactNamespace: true })) return;
32151
- let updater = null;
32152
- if (isFunctionLike$1(updaterArgument)) updater = updaterArgument;
32153
- else if (isNodeOfType(updaterArgument, "Identifier")) {
32154
- const updaterReference = getRef(analysis, updaterArgument);
32155
- if (updaterReference) updater = resolveToFunction(updaterReference);
32156
- }
32157
- if (!updater) return;
32158
31769
  const operation = findImpureUpdaterOperation(updater, context.scopes);
32159
31770
  if (!operation) return;
32160
31771
  context.report({
32161
- node: updaterArgument,
31772
+ node: updater,
32162
31773
  message: `This state updater performs ${operation}. React may run updater functions more than once, so side effects here can repeat or observe inconsistent external state.`
32163
31774
  });
32164
31775
  } })
@@ -32268,7 +31879,7 @@ const noIndeterminateAttribute = defineRule({
32268
31879
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
32269
31880
  return {
32270
31881
  JSXOpeningElement(node) {
32271
- if (resolveJsxElementType(node) !== "input") return;
31882
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
32272
31883
  let typeAttribute = null;
32273
31884
  let typeAttributeIndex = null;
32274
31885
  let indeterminateAttribute = null;
@@ -32447,12 +32058,11 @@ const isMemoCall = (node) => {
32447
32058
  };
32448
32059
  const isDefaultEquivalentComparator = (comparator) => isNodeOfType(comparator, "Identifier") && (comparator.name === "undefined" || comparator.name === "shallowEqual");
32449
32060
  const hasCustomComparator = (node) => isNodeOfType(node, "CallExpression") && (node.arguments?.length ?? 0) >= 2 && !isDefaultEquivalentComparator(node.arguments?.[1]);
32450
- const isInlineReference = (node, scopes) => {
32451
- const referenceNode = unwrapObjectIntegrityExpression(node, scopes);
32452
- 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";
32453
- if (isNodeOfType(referenceNode, "ObjectExpression")) return "objects";
32454
- if (isNodeOfType(referenceNode, "ArrayExpression")) return "Arrays";
32455
- if (isNodeOfType(referenceNode, "JSXElement") || isNodeOfType(referenceNode, "JSXFragment")) return "JSX";
32061
+ const isInlineReference = (node) => {
32062
+ if (isNodeOfType(node, "ArrowFunctionExpression") || isNodeOfType(node, "FunctionExpression") || isNodeOfType(node, "CallExpression") && isNodeOfType(node.callee, "MemberExpression") && isNodeOfType(node.callee.property, "Identifier") && node.callee.property.name === "bind") return "functions";
32063
+ if (isNodeOfType(node, "ObjectExpression")) return "objects";
32064
+ if (isNodeOfType(node, "ArrayExpression")) return "Arrays";
32065
+ if (isNodeOfType(node, "JSXElement") || isNodeOfType(node, "JSXFragment")) return "JSX";
32456
32066
  return null;
32457
32067
  };
32458
32068
  const noInlinePropOnMemoComponent = defineRule({
@@ -32482,7 +32092,7 @@ const noInlinePropOnMemoComponent = defineRule({
32482
32092
  let elementName = null;
32483
32093
  if (isNodeOfType(openingElement.name, "JSXIdentifier")) elementName = openingElement.name.name;
32484
32094
  if (!elementName || !memoizedComponentNames.has(elementName)) return;
32485
- const propType = isInlineReference(node.value.expression, context.scopes);
32095
+ const propType = isInlineReference(node.value.expression);
32486
32096
  if (propType) context.report({
32487
32097
  node: node.value.expression,
32488
32098
  message: `This redraws ${elementName} on every render because the prop is ${propType} built right here, so memo() can't skip it. Move it to a stable value with useMemo, useCallback, or module scope`
@@ -33003,18 +32613,6 @@ const INTL_FORMAT_METHOD_NAMES = new Set([
33003
32613
  "formatToParts",
33004
32614
  "formatRange"
33005
32615
  ]);
33006
- const ABSENT_PROPERTY_PROOF = { status: "absent" };
33007
- const PRESENT_PROPERTY_PROOF = { status: "present" };
33008
- const UNDEFINED_PROPERTY_PROOF = { status: "undefined" };
33009
- const UNKNOWN_PROPERTY_PROOF = { status: "unknown" };
33010
- const READ_ONLY_OBJECT_METHOD_NAMES = new Set([
33011
- "hasOwnProperty",
33012
- "isPrototypeOf",
33013
- "propertyIsEnumerable",
33014
- "toLocaleString",
33015
- "toString",
33016
- "valueOf"
33017
- ]);
33018
32616
  const isProvableDateExpression = (expression) => {
33019
32617
  if (!expression) return false;
33020
32618
  const unwrapped = stripParenExpression(expression);
@@ -33029,290 +32627,32 @@ const receiverNameLooksDateFlavored = (expression) => {
33029
32627
  if (isNodeOfType(unwrapped, "CallExpression")) return receiverNameLooksDateFlavored(unwrapped.callee);
33030
32628
  return false;
33031
32629
  };
33032
- const isStaticUndefined = (node, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
33033
- const expression = stripParenExpression(node);
33034
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") return true;
33035
- if (!isNodeOfType(expression, "Identifier")) return false;
33036
- if (expression.name === "undefined" && scopes.isGlobalReference(expression)) return true;
33037
- const symbol = scopes.symbolFor(expression);
33038
- if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "Identifier") || visitedSymbolIds.has(symbol.id)) return false;
33039
- visitedSymbolIds.add(symbol.id);
33040
- return isStaticUndefined(symbol.initializer, scopes, visitedSymbolIds);
33041
- };
33042
- const isNestedAssignmentTarget = (expression) => {
33043
- let target = expression;
33044
- let parent = target.parent;
33045
- while (parent) {
33046
- if (isNodeOfType(parent, "AssignmentExpression")) return parent.left === target;
33047
- if (isNodeOfType(parent, "ForInStatement") || isNodeOfType(parent, "ForOfStatement")) return parent.left === target;
33048
- if (isNodeOfType(parent, "AssignmentPattern")) {
33049
- if (parent.left !== target) return false;
33050
- } else if (isNodeOfType(parent, "RestElement")) {
33051
- if (parent.argument !== target) return false;
33052
- } else if (isNodeOfType(parent, "ArrayPattern")) {
33053
- if (!parent.elements?.some((element) => element === target)) return false;
33054
- } else if (isNodeOfType(parent, "Property")) {
33055
- if (parent.value !== target || !isNodeOfType(parent.parent, "ObjectPattern")) return false;
33056
- } else if (isNodeOfType(parent, "ObjectPattern")) {
33057
- if (!parent.properties?.some((property) => property === target)) return false;
33058
- } else return false;
33059
- target = parent;
33060
- parent = target.parent;
33061
- }
33062
- return false;
33063
- };
33064
- const isPotentialMutationReference = (identifier, readNode) => {
33065
- let expression = identifier;
33066
- let parent = expression.parent;
33067
- while (parent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) && "expression" in parent && parent.expression === expression) {
33068
- expression = parent;
33069
- parent = expression.parent;
33070
- }
33071
- const rootExpression = expression;
33072
- let memberDepth = 0;
33073
- while (parent && isNodeOfType(parent, "MemberExpression") && parent.object === expression) {
33074
- memberDepth += 1;
33075
- expression = parent;
33076
- parent = expression.parent;
33077
- while (parent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) && "expression" in parent && parent.expression === expression) {
33078
- expression = parent;
33079
- parent = expression.parent;
33080
- }
33081
- }
33082
- if (!parent || isAstDescendant(identifier, readNode)) return false;
33083
- if (isNestedAssignmentTarget(expression)) return true;
33084
- if (isNodeOfType(parent, "AssignmentExpression") && parent.left === expression) return true;
33085
- if (isNodeOfType(parent, "UpdateExpression") && parent.argument === expression) return true;
33086
- if (isNodeOfType(parent, "UnaryExpression") && parent.operator === "delete" && parent.argument === expression) return true;
33087
- if (isNodeOfType(parent, "CallExpression")) {
33088
- if (parent.callee === expression) {
33089
- if (memberDepth === 0) return true;
33090
- const memberExpression = stripParenExpression(expression);
33091
- return memberDepth === 1 && isNodeOfType(memberExpression, "MemberExpression") && !READ_ONLY_OBJECT_METHOD_NAMES.has(getStaticPropertyName(memberExpression) ?? "");
33092
- }
33093
- return memberDepth === 0 && parent.arguments?.some((argument) => argument === rootExpression);
33094
- }
33095
- if (isNodeOfType(parent, "NewExpression")) return memberDepth === 0 && parent.arguments?.some((argument) => argument === rootExpression);
33096
- if (isNodeOfType(parent, "AssignmentExpression") && parent.right === rootExpression) return true;
33097
- return false;
33098
- };
33099
- const getSimpleAlias = (identifier, scopes) => {
33100
- let expression = identifier;
33101
- let parent = expression.parent;
33102
- while (parent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) && "expression" in parent && parent.expression === expression) {
33103
- expression = parent;
33104
- parent = expression.parent;
33105
- }
33106
- if (!isNodeOfType(parent, "VariableDeclarator") || parent.init !== expression || !isNodeOfType(parent.id, "Identifier")) return null;
33107
- const symbol = scopes.symbolFor(parent.id);
33108
- return symbol ? {
33109
- symbol,
33110
- readNode: parent.id
33111
- } : null;
33112
- };
33113
- const getDirectCallForExpression = (expression) => {
33114
- let callee = expression;
33115
- let parent = callee.parent;
33116
- while (parent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(parent.type) && "expression" in parent && parent.expression === callee) {
33117
- callee = parent;
33118
- parent = callee.parent;
33119
- }
33120
- return isNodeOfType(parent, "CallExpression") && parent.callee === callee ? parent : null;
33121
- };
33122
- const getMethodOwner = (functionNode, scopes) => {
33123
- const methodNode = functionNode.parent;
33124
- if (isNodeOfType(methodNode, "Property") && methodNode.value === functionNode && isNodeOfType(methodNode.parent, "ObjectExpression")) {
33125
- const objectParent = methodNode.parent.parent;
33126
- if (!isNodeOfType(objectParent, "VariableDeclarator") || objectParent.init !== methodNode.parent || !isNodeOfType(objectParent.id, "Identifier")) return null;
33127
- const ownerSymbol = scopes.symbolFor(objectParent.id);
33128
- const methodName = getStaticPropertyKeyName(methodNode, { allowComputedString: true });
33129
- return ownerSymbol && methodName ? {
33130
- methodName,
33131
- ownerKind: "object",
33132
- ownerSymbol,
33133
- isStatic: false
33134
- } : null;
33135
- }
33136
- if (!isNodeOfType(methodNode, "MethodDefinition") || methodNode.value !== functionNode || !isNodeOfType(methodNode.parent, "ClassBody")) return null;
33137
- const classNode = methodNode.parent.parent;
33138
- if (!isNodeOfType(classNode, "ClassDeclaration") && !isNodeOfType(classNode, "ClassExpression")) return null;
33139
- let bindingIdentifier = isNodeOfType(classNode.id, "Identifier") ? classNode.id : null;
33140
- if (!bindingIdentifier && isNodeOfType(classNode.parent, "VariableDeclarator")) bindingIdentifier = isNodeOfType(classNode.parent.id, "Identifier") ? classNode.parent.id : null;
33141
- if (!bindingIdentifier) return null;
33142
- const ownerSymbol = scopes.symbolFor(bindingIdentifier);
33143
- const methodName = getStaticPropertyKeyName(methodNode, { allowComputedString: true });
33144
- return ownerSymbol && methodName ? {
33145
- methodName,
33146
- ownerKind: "class",
33147
- ownerSymbol,
33148
- isStatic: Boolean(methodNode.static)
33149
- } : null;
33150
- };
33151
- const doesSymbolResolveToOwner = (symbol, ownerSymbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
33152
- if (!symbol) return false;
33153
- if (symbol.declarationNode === ownerSymbol.declarationNode) return true;
33154
- if (symbol.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return false;
33155
- visitedSymbolIds.add(symbol.id);
33156
- const initializer = stripParenExpression(symbol.initializer);
33157
- return isNodeOfType(initializer, "Identifier") && doesSymbolResolveToOwner(scopes.symbolFor(initializer), ownerSymbol, scopes, visitedSymbolIds);
33158
- };
33159
- const isClassInstanceExpression = (expression, ownerSymbol, scopes, visitedSymbolIds = /* @__PURE__ */ new Set()) => {
33160
- const candidate = stripParenExpression(expression);
33161
- if (isNodeOfType(candidate, "NewExpression") && isNodeOfType(candidate.callee, "Identifier")) return doesSymbolResolveToOwner(scopes.symbolFor(candidate.callee), ownerSymbol, scopes);
33162
- if (!isNodeOfType(candidate, "Identifier")) return false;
33163
- const symbol = scopes.symbolFor(candidate);
33164
- if (symbol?.kind !== "const" || !symbol.initializer || visitedSymbolIds.has(symbol.id)) return false;
33165
- visitedSymbolIds.add(symbol.id);
33166
- return isClassInstanceExpression(symbol.initializer, ownerSymbol, scopes, visitedSymbolIds);
33167
- };
33168
- const getMethodCalls = (functionNode, scopes) => {
33169
- const owner = getMethodOwner(functionNode, scopes);
33170
- if (!owner) return [];
33171
- const calls = [];
33172
- walkAst(scopes.rootScope.node, (child) => {
33173
- if (!isNodeOfType(child, "MemberExpression")) return;
33174
- if (getStaticPropertyName(child) !== owner.methodName) return;
33175
- const receiver = stripParenExpression(child.object);
33176
- let doesReceiverMatch = isNodeOfType(receiver, "Identifier") && doesSymbolResolveToOwner(scopes.symbolFor(receiver), owner.ownerSymbol, scopes);
33177
- if (owner.ownerKind === "class" && !owner.isStatic) doesReceiverMatch = isClassInstanceExpression(receiver, owner.ownerSymbol, scopes);
33178
- if (!doesReceiverMatch) return;
33179
- const call = getDirectCallForExpression(child);
33180
- if (call) calls.push(call);
33181
- });
33182
- return calls;
33183
- };
33184
- const isFunctionInvokedBeforeUsage = (functionNode, usageNode, usageBoundary, scopes, visitedSymbolIds, visitedFunctionNodes = /* @__PURE__ */ new Set()) => {
33185
- if (visitedFunctionNodes.has(functionNode)) return false;
33186
- visitedFunctionNodes.add(functionNode);
33187
- const usageFunction = findEnclosingFunction(usageNode);
33188
- const immediateCall = getDirectCallForExpression(functionNode);
33189
- if (immediateCall) {
33190
- const immediateCallFunction = findEnclosingFunction(immediateCall);
33191
- if (immediateCallFunction === usageFunction) {
33192
- const immediateCallStart = getRangeStart(immediateCall);
33193
- return immediateCallStart === null || immediateCallStart < usageBoundary;
33194
- }
33195
- if (!immediateCallFunction) return usageFunction !== null;
33196
- return isFunctionInvokedBeforeUsage(immediateCallFunction, usageNode, usageBoundary, scopes, visitedSymbolIds, new Set(visitedFunctionNodes));
33197
- }
33198
- for (const methodCall of getMethodCalls(functionNode, scopes)) {
33199
- const methodCallFunction = findEnclosingFunction(methodCall);
33200
- if (methodCallFunction === usageFunction) {
33201
- const methodCallStart = getRangeStart(methodCall);
33202
- if (methodCallStart === null || methodCallStart < usageBoundary) return true;
33203
- continue;
33204
- }
33205
- if (!methodCallFunction) {
33206
- if (usageFunction) return true;
33207
- continue;
33208
- }
33209
- if (isFunctionInvokedBeforeUsage(methodCallFunction, usageNode, usageBoundary, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes))) return true;
33210
- }
33211
- const bindingIdentifier = getFunctionBindingIdentifier(functionNode);
33212
- if (!bindingIdentifier) return false;
33213
- const symbol = scopes.symbolFor(bindingIdentifier);
33214
- if (!symbol || visitedSymbolIds.has(symbol.id)) return false;
33215
- visitedSymbolIds.add(symbol.id);
33216
- let wasInvokedBeforeUsage = false;
33217
- walkAst(scopes.rootScope.node, (child) => {
33218
- if (wasInvokedBeforeUsage || !isNodeOfType(child, "Identifier")) return;
33219
- if (scopes.symbolFor(child)?.declarationNode !== symbol.declarationNode) return;
33220
- const call = getDirectCallForExpression(child);
33221
- if (!call) return false;
33222
- const callFunction = findEnclosingFunction(call);
33223
- if (callFunction === usageFunction) {
33224
- const callStart = getRangeStart(call);
33225
- wasInvokedBeforeUsage = callStart === null || callStart < usageBoundary;
33226
- return;
33227
- }
33228
- if (!callFunction) {
33229
- wasInvokedBeforeUsage = usageFunction !== null;
33230
- return;
33231
- }
33232
- wasInvokedBeforeUsage = isFunctionInvokedBeforeUsage(callFunction, usageNode, usageBoundary, scopes, new Set(visitedSymbolIds), new Set(visitedFunctionNodes));
33233
- });
33234
- return wasInvokedBeforeUsage;
33235
- };
33236
- const getMutationUsageBoundary = (usageNode, readNode) => {
33237
- const readStart = getRangeStart(readNode);
33238
- let readExpression = readNode;
33239
- let readParent = readExpression.parent;
33240
- while (readParent && TRANSPARENT_EXPRESSION_WRAPPER_TYPES.has(readParent.type) && "expression" in readParent && readParent.expression === readExpression) {
33241
- readExpression = readParent;
33242
- readParent = readExpression.parent;
33243
- }
33244
- if ((isNodeOfType(usageNode, "CallExpression") || isNodeOfType(usageNode, "NewExpression")) && usageNode.arguments?.some((argument) => argument === readExpression)) return usageNode.range?.[1] ?? null;
33245
- if (isAstDescendant(readNode, usageNode)) return readStart;
33246
- return getRangeStart(usageNode);
33247
- };
33248
- const wasMutatedBeforeUsage = (symbol, usageNode, readNode, scopes, visitedMutationSymbolIds = /* @__PURE__ */ new Set(), inheritedUsageBoundary, readNodesBySymbolId = /* @__PURE__ */ new Map()) => {
33249
- if (visitedMutationSymbolIds.has(symbol.id)) return false;
33250
- visitedMutationSymbolIds.add(symbol.id);
33251
- const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, readNode) : inheritedUsageBoundary;
33252
- if (typeof usageBoundary !== "number") return true;
33253
- const usageFunction = findEnclosingFunction(usageNode);
33254
- return symbol.references.some((reference) => {
33255
- const referenceStart = getRangeStart(reference.identifier);
33256
- const simpleAlias = getSimpleAlias(reference.identifier, scopes);
33257
- if (simpleAlias) return wasMutatedBeforeUsage(simpleAlias.symbol, usageNode, readNodesBySymbolId.get(simpleAlias.symbol.id) ?? simpleAlias.readNode, scopes, new Set(visitedMutationSymbolIds), usageBoundary, readNodesBySymbolId);
33258
- if (!isPotentialMutationReference(reference.identifier, readNode)) return false;
33259
- if (referenceStart === null) return true;
33260
- const mutationFunction = findEnclosingFunction(reference.identifier);
33261
- if (mutationFunction === usageFunction) return referenceStart < usageBoundary;
33262
- if (!mutationFunction) return usageFunction !== null;
33263
- if (usageFunction && isAstDescendant(usageFunction, mutationFunction)) return true;
33264
- return isFunctionInvokedBeforeUsage(mutationFunction, usageNode, usageBoundary, scopes, /* @__PURE__ */ new Set());
33265
- });
33266
- };
33267
- const getObjectPropertyProof = (objectExpression, propertyName, scopes, usageNode, visitedSymbolIds = /* @__PURE__ */ new Set(), inheritedUsageBoundary, readNodesBySymbolId = /* @__PURE__ */ new Map()) => {
33268
- if (!objectExpression) return ABSENT_PROPERTY_PROOF;
32630
+ const objectLiteralHasProperty = (objectExpression, propertyName) => {
32631
+ if (!objectExpression) return false;
33269
32632
  const unwrapped = stripParenExpression(objectExpression);
33270
- if (isNodeOfType(unwrapped, "Literal") || isStaticUndefined(unwrapped, scopes)) return ABSENT_PROPERTY_PROOF;
33271
- if (isNodeOfType(unwrapped, "Identifier")) {
33272
- const symbol = scopes.symbolFor(unwrapped);
33273
- const nextReadNodesBySymbolId = new Map(readNodesBySymbolId);
33274
- if (symbol) nextReadNodesBySymbolId.set(symbol.id, unwrapped);
33275
- const usageBoundary = inheritedUsageBoundary === void 0 ? getMutationUsageBoundary(usageNode, unwrapped) : inheritedUsageBoundary;
33276
- if (symbol?.kind !== "const" || !symbol.initializer || !isNodeOfType(symbol.declarationNode, "VariableDeclarator") || !isNodeOfType(symbol.declarationNode.id, "Identifier") || visitedSymbolIds.has(symbol.id) || wasMutatedBeforeUsage(symbol, usageNode, unwrapped, scopes, /* @__PURE__ */ new Set(), usageBoundary, nextReadNodesBySymbolId)) return UNKNOWN_PROPERTY_PROOF;
33277
- visitedSymbolIds.add(symbol.id);
33278
- return getObjectPropertyProof(symbol.initializer, propertyName, scopes, usageNode, visitedSymbolIds, usageBoundary, nextReadNodesBySymbolId);
33279
- }
33280
- if (isNodeOfType(unwrapped, "ConditionalExpression")) {
33281
- const consequent = getObjectPropertyProof(unwrapped.consequent, propertyName, scopes, usageNode, new Set(visitedSymbolIds), inheritedUsageBoundary, new Map(readNodesBySymbolId));
33282
- const alternate = getObjectPropertyProof(unwrapped.alternate, propertyName, scopes, usageNode, new Set(visitedSymbolIds), inheritedUsageBoundary, new Map(readNodesBySymbolId));
33283
- return consequent.status === alternate.status ? consequent : UNKNOWN_PROPERTY_PROOF;
33284
- }
33285
- if (isNodeOfType(unwrapped, "CallExpression") && isNodeOfType(unwrapped.callee, "MemberExpression") && !unwrapped.callee.computed && isNodeOfType(unwrapped.callee.object, "Identifier") && unwrapped.callee.object.name === "Object" && scopes.isGlobalReference(unwrapped.callee.object) && isNodeOfType(unwrapped.callee.property, "Identifier") && unwrapped.callee.property.name === "freeze") return getObjectPropertyProof(unwrapped.arguments?.[0], propertyName, scopes, usageNode, visitedSymbolIds, inheritedUsageBoundary, readNodesBySymbolId);
33286
- if (!isNodeOfType(unwrapped, "ObjectExpression")) return UNKNOWN_PROPERTY_PROOF;
33287
- const properties = unwrapped.properties ?? [];
33288
- for (let propertyIndex = properties.length - 1; propertyIndex >= 0; propertyIndex -= 1) {
33289
- const property = properties[propertyIndex];
33290
- if (!property) continue;
33291
- if (isNodeOfType(property, "SpreadElement")) {
33292
- const spreadProof = getObjectPropertyProof(property.argument, propertyName, scopes, unwrapped, new Set(visitedSymbolIds), void 0);
33293
- if (spreadProof.status !== "absent") return spreadProof;
33294
- continue;
33295
- }
33296
- if (!isNodeOfType(property, "Property") || getStaticPropertyKeyName(property) !== propertyName) continue;
33297
- return isStaticUndefined(property.value, scopes) ? UNDEFINED_PROPERTY_PROOF : PRESENT_PROPERTY_PROOF;
32633
+ if (!isNodeOfType(unwrapped, "ObjectExpression")) return false;
32634
+ for (const property of unwrapped.properties ?? []) {
32635
+ if (!isNodeOfType(property, "Property")) continue;
32636
+ if (property.computed) continue;
32637
+ if (isNodeOfType(property.key, "Identifier") && property.key.name === propertyName) return true;
32638
+ if (isNodeOfType(property.key, "Literal") && property.key.value === propertyName) return true;
33298
32639
  }
33299
- return ABSENT_PROPERTY_PROOF;
32640
+ return false;
33300
32641
  };
33301
- const objectHasExplicitProperty = (objectExpression, propertyName, scopes, usageNode) => getObjectPropertyProof(objectExpression, propertyName, scopes, usageNode).status === "present";
33302
- const hasExplicitLocaleArgument = (argument, scopes) => {
32642
+ const hasExplicitLocaleArgument = (argument) => {
33303
32643
  if (!argument) return false;
33304
32644
  const unwrapped = stripParenExpression(argument);
33305
- if (isNodeOfType(unwrapped, "Identifier") && unwrapped.name === "undefined" && scopes.isGlobalReference(unwrapped) || isNodeOfType(unwrapped, "UnaryExpression") && unwrapped.operator === "void") return false;
32645
+ if (isNodeOfType(unwrapped, "Identifier") && unwrapped.name === "undefined") return false;
33306
32646
  return true;
33307
32647
  };
33308
- const isDeterministicLocaleMethodCall = (call, methodName, receiverIsProvablyDate, scopes) => {
32648
+ const isDeterministicLocaleMethodCall = (call, methodName, receiverIsProvablyDate) => {
33309
32649
  const localeArgument = call.arguments?.[0];
33310
- if (!hasExplicitLocaleArgument(localeArgument, scopes)) return false;
32650
+ if (!hasExplicitLocaleArgument(localeArgument)) return false;
33311
32651
  const optionsArgument = call.arguments?.[1];
33312
- if (objectHasExplicitProperty(optionsArgument, "timeZone", scopes, call)) return true;
32652
+ if (objectLiteralHasProperty(optionsArgument, "timeZone")) return true;
33313
32653
  return !DATE_ONLY_LOCALE_METHOD_NAMES.has(methodName) && !receiverIsProvablyDate;
33314
32654
  };
33315
- const matchLocaleMethodCall = (call, scopes) => {
32655
+ const matchLocaleMethodCall = (call) => {
33316
32656
  const callee = call.callee;
33317
32657
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return null;
33318
32658
  if (!isNodeOfType(callee.property, "Identifier")) return null;
@@ -33320,7 +32660,7 @@ const matchLocaleMethodCall = (call, scopes) => {
33320
32660
  if (!LOCALE_FORMAT_METHOD_NAMES.has(methodName)) return null;
33321
32661
  const receiverIsProvablyDate = isProvableDateExpression(callee.object);
33322
32662
  if (methodName === "toLocaleString" && !receiverIsProvablyDate && !receiverNameLooksDateFlavored(callee.object)) return null;
33323
- if (isDeterministicLocaleMethodCall(call, methodName, receiverIsProvablyDate, scopes)) return null;
32663
+ if (isDeterministicLocaleMethodCall(call, methodName, receiverIsProvablyDate)) return null;
33324
32664
  return {
33325
32665
  node: call,
33326
32666
  display: `${methodName}()`
@@ -33336,13 +32676,13 @@ const getIntlFormatterName = (expression) => {
33336
32676
  if (!isNodeOfType(callee.property, "Identifier")) return null;
33337
32677
  return INTL_FORMATTER_NAMES.has(callee.property.name) ? callee.property.name : null;
33338
32678
  };
33339
- const isDeterministicIntlConstruction = (construction, formatterName, scopes) => {
32679
+ const isDeterministicIntlConstruction = (construction, formatterName) => {
33340
32680
  if (!isNodeOfType(construction, "CallExpression") && !isNodeOfType(construction, "NewExpression")) return false;
33341
- if (!hasExplicitLocaleArgument(construction.arguments?.[0], scopes)) return false;
32681
+ if (!hasExplicitLocaleArgument(construction.arguments?.[0])) return false;
33342
32682
  if (formatterName !== "DateTimeFormat") return true;
33343
- return objectHasExplicitProperty(construction.arguments?.[1], "timeZone", scopes, construction);
32683
+ return objectLiteralHasProperty(construction.arguments?.[1], "timeZone");
33344
32684
  };
33345
- const matchIntlFormatCall = (call, scopes) => {
32685
+ const matchIntlFormatCall = (call) => {
33346
32686
  const callee = call.callee;
33347
32687
  if (!isNodeOfType(callee, "MemberExpression") || callee.computed) return null;
33348
32688
  if (!isNodeOfType(callee.property, "Identifier")) return null;
@@ -33355,7 +32695,7 @@ const matchIntlFormatCall = (call, scopes) => {
33355
32695
  if (!construction) return null;
33356
32696
  const formatterName = getIntlFormatterName(construction);
33357
32697
  if (!formatterName) return null;
33358
- if (isDeterministicIntlConstruction(construction, formatterName, scopes)) return null;
32698
+ if (isDeterministicIntlConstruction(construction, formatterName)) return null;
33359
32699
  return {
33360
32700
  node: call,
33361
32701
  display: `Intl.${formatterName}().${callee.property.name}()`
@@ -33424,7 +32764,7 @@ const noLocaleFormatInRender = defineRule({
33424
32764
  fileIsEmailTemplate = hasEmailTemplateImport(node);
33425
32765
  },
33426
32766
  CallExpression(node) {
33427
- const match = matchLocaleMethodCall(node, context.scopes) ?? matchIntlFormatCall(node, context.scopes) ?? matchDateDefaultStringification(node);
32767
+ const match = matchLocaleMethodCall(node) ?? matchIntlFormatCall(node) ?? matchDateDefaultStringification(node);
33428
32768
  if (match) reportIfRenderPhase(match);
33429
32769
  },
33430
32770
  TemplateLiteral(node) {
@@ -33444,7 +32784,7 @@ const noLocaleFormatInRender = defineRule({
33444
32784
  walkAst(helperNode.body ?? helperNode, (child) => {
33445
32785
  if (isFunctionLike$1(child)) return false;
33446
32786
  if (!isNodeOfType(child, "CallExpression")) return;
33447
- const match = matchLocaleMethodCall(child, context.scopes) ?? matchIntlFormatCall(child, context.scopes);
32787
+ const match = matchLocaleMethodCall(child) ?? matchIntlFormatCall(child);
33448
32788
  if (!match || reportedNodes.has(match.node)) return;
33449
32789
  if (fileIsEmailTemplate) return;
33450
32790
  if (!hasClientRenderEvidence(componentOrHookNode, fileHasUseClientDirective)) return;
@@ -33611,13 +32951,11 @@ const noManyBooleanProps = defineRule({
33611
32951
  };
33612
32952
  const checkComponent = (functionNode, param, body, componentName, reportNode) => {
33613
32953
  if (!param) return;
33614
- const propsBinding = resolveFirstArgumentBinding(param);
33615
- if (!propsBinding) return;
33616
32954
  if (!functionContainsReactRenderOutput(functionNode, context.scopes)) return;
33617
- if (isNodeOfType(propsBinding, "ObjectPattern")) {
32955
+ if (isNodeOfType(param, "ObjectPattern")) {
33618
32956
  const callbackUsedNames = collectCallbackUsedNames(body, param, context.scopes);
33619
32957
  const booleanLikePropNames = [];
33620
- for (const property of propsBinding.properties ?? []) {
32958
+ for (const property of param.properties ?? []) {
33621
32959
  if (!isNodeOfType(property, "Property")) continue;
33622
32960
  const keyName = isNodeOfType(property.key, "Identifier") ? property.key.name : null;
33623
32961
  if (!keyName) continue;
@@ -33628,7 +32966,7 @@ const noManyBooleanProps = defineRule({
33628
32966
  reportIfMany(booleanLikePropNames, componentName, reportNode);
33629
32967
  return;
33630
32968
  }
33631
- if (isNodeOfType(propsBinding, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, propsBinding.name)], componentName, reportNode);
32969
+ if (isNodeOfType(param, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, param.name)], componentName, reportNode);
33632
32970
  };
33633
32971
  return {
33634
32972
  FunctionDeclaration(node) {
@@ -33750,7 +33088,7 @@ const noMirrorPropEffect = defineRule({
33750
33088
  if (mirrorBindings.length === 0) return;
33751
33089
  for (const statement of componentBody.body ?? []) {
33752
33090
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
33753
- const effectCall = unwrapDiscardedExpression(statement);
33091
+ const effectCall = statement.expression;
33754
33092
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
33755
33093
  if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
33756
33094
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
@@ -33764,7 +33102,7 @@ const noMirrorPropEffect = defineRule({
33764
33102
  const bodyStatements = getCallbackStatements(callback);
33765
33103
  if (bodyStatements.length !== 1) continue;
33766
33104
  const onlyStatement = bodyStatements[0];
33767
- const expression = unwrapDiscardedExpression(onlyStatement);
33105
+ const expression = isNodeOfType(onlyStatement, "ExpressionStatement") ? onlyStatement.expression : onlyStatement;
33768
33106
  if (!isNodeOfType(expression, "CallExpression")) continue;
33769
33107
  if (!isNodeOfType(expression.callee, "Identifier")) continue;
33770
33108
  if (!isSetterIdentifier(expression.callee.name)) continue;
@@ -36380,7 +35718,7 @@ const noPreventDefault = defineRule({
36380
35718
  const isServerActionsFramework = hasCapability(context.settings, "server-actions");
36381
35719
  const formMessage = isServerActionsFramework ? FORM_MESSAGE_SERVER_CAPABLE : FORM_MESSAGE_GENERIC;
36382
35720
  return { JSXOpeningElement(node) {
36383
- const elementName = resolveJsxElementType(node);
35721
+ const elementName = isNodeOfType(node.name, "JSXIdentifier") ? node.name.name : null;
36384
35722
  if (!elementName) return;
36385
35723
  const targetEventProps = PREVENT_DEFAULT_ELEMENTS.get(elementName);
36386
35724
  if (!targetEventProps) return;
@@ -37270,85 +36608,28 @@ const noRedundantShouldComponentUpdate = defineRule({
37270
36608
  });
37271
36609
  //#endregion
37272
36610
  //#region src/plugin/rules/state-and-effects/no-ref-current-in-render.ts
37273
- const REPEATED_ANCESTOR_TYPES = new Set([
37274
- "DoWhileStatement",
37275
- "ForInStatement",
37276
- "ForOfStatement",
37277
- "ForStatement",
37278
- "WhileStatement"
37279
- ]);
36611
+ const resolveReactRefSymbol = (memberExpression, scopes) => {
36612
+ const receiver = isNodeOfType(memberExpression, "MemberExpression") ? stripParenExpression(memberExpression.object) : null;
36613
+ if (!isNodeOfType(memberExpression, "MemberExpression") || memberExpression.computed || !isNodeOfType(memberExpression.property, "Identifier") || memberExpression.property.name !== "current" || !isNodeOfType(receiver, "Identifier")) return null;
36614
+ const symbol = resolveConstIdentifierAlias(receiver, scopes);
36615
+ if (!symbol?.initializer) return null;
36616
+ const initializer = stripParenExpression(symbol.initializer);
36617
+ if (!isNodeOfType(initializer, "CallExpression")) return null;
36618
+ return isReactApiCall(initializer, "useRef", scopes, { allowGlobalReactNamespace: true }) ? symbol : null;
36619
+ };
37280
36620
  const isSameRefCurrentMember = (node, refSymbol, scopes) => {
37281
- if (!isNodeOfType(node, "MemberExpression") || getStaticPropertyName(node) !== "current") return false;
36621
+ if (!isNodeOfType(node, "MemberExpression") || node.computed || !isNodeOfType(node.property, "Identifier") || node.property.name !== "current") return false;
37282
36622
  const receiver = stripParenExpression(node.object);
37283
36623
  return isNodeOfType(receiver, "Identifier") && resolveConstIdentifierAlias(receiver, scopes)?.id === refSymbol.id;
37284
36624
  };
37285
- const isSameRefCurrentAlias = (node, refSymbol, scopes) => {
37286
- if (isSameRefCurrentMember(node, refSymbol, scopes)) return true;
37287
- if (!isNodeOfType(node, "Identifier")) return false;
37288
- const aliasSymbol = scopes.symbolFor(node);
37289
- return aliasSymbol?.kind === "const" && aliasSymbol.initializer !== null && isSameRefCurrentMember(stripParenExpression(aliasSymbol.initializer), refSymbol, scopes);
37290
- };
37291
- const isEmptySentinel = (node, scopes) => isNodeOfType(node, "Literal") && node.value === null || isNodeOfType(node, "Identifier") && node.name === "undefined" && scopes.isGlobalReference(node);
37292
- const hasRepeatedExecutionAncestor = (node, stop) => {
37293
- let ancestor = node.parent;
37294
- while (ancestor && ancestor !== stop) {
37295
- if (isFunctionLike$1(ancestor) || REPEATED_ANCESTOR_TYPES.has(ancestor.type)) return true;
37296
- ancestor = ancestor.parent;
37297
- }
37298
- return ancestor !== stop;
37299
- };
37300
- const getBranchConstraints = (node, branchRoot) => {
37301
- const constraints = /* @__PURE__ */ new Map();
37302
- let descendant = node;
37303
- let ancestor = descendant.parent;
37304
- while (ancestor && descendant !== branchRoot) {
37305
- if (isNodeOfType(ancestor, "IfStatement")) {
37306
- if (ancestor.consequent === descendant) constraints.set(ancestor, true);
37307
- if (ancestor.alternate === descendant) constraints.set(ancestor, false);
37308
- }
37309
- descendant = ancestor;
37310
- ancestor = ancestor.parent;
37311
- }
37312
- return constraints;
37313
- };
37314
- const canExecuteTogether = (firstConstraints, secondConstraints) => {
37315
- for (const [statement, branch] of firstConstraints) {
37316
- const otherBranch = secondConstraints.get(statement);
37317
- if (otherBranch !== void 0 && otherBranch !== branch) return false;
37318
- }
37319
- return true;
37320
- };
37321
- const hasNoPriorCoExecutableWrite = (assignmentExpression, branchRoot, refSymbol, scopes) => {
37322
- const assignmentConstraints = getBranchConstraints(assignmentExpression, branchRoot);
37323
- const assignmentStart = getRangeStart(assignmentExpression);
37324
- let hasCoExecutableWrite = false;
37325
- walkAst(branchRoot, (child) => {
37326
- if (hasCoExecutableWrite) return false;
37327
- const childStart = getRangeStart(child);
37328
- if (child === assignmentExpression || !isNodeOfType(child, "AssignmentExpression") || assignmentStart === null || childStart === null || childStart >= assignmentStart || resolveReactRefSymbol(child.left, scopes)?.id !== refSymbol.id || hasRepeatedExecutionAncestor(child, branchRoot)) return;
37329
- if (canExecuteTogether(assignmentConstraints, getBranchConstraints(child, branchRoot))) {
37330
- hasCoExecutableWrite = true;
37331
- return false;
37332
- }
37333
- });
37334
- return !hasCoExecutableWrite;
37335
- };
37336
36625
  const isDocumentedLazyInitialization = (assignmentExpression, refSymbol, scopes) => {
37337
- if (assignmentExpression.operator === "??=" || assignmentExpression.operator === "||=") return true;
37338
- if (assignmentExpression.operator !== "=") return false;
36626
+ if (assignmentExpression.operator !== "=" || !isNodeOfType(assignmentExpression.right, "NewExpression")) return false;
37339
36627
  let descendant = assignmentExpression;
37340
36628
  let ancestor = descendant.parent;
37341
36629
  while (ancestor) {
37342
- if (isNodeOfType(ancestor, "IfStatement") && isNodeOfType(ancestor.test, "BinaryExpression") && [
37343
- "===",
37344
- "==",
37345
- "!==",
37346
- "!="
37347
- ].includes(ancestor.test.operator)) {
36630
+ if (isNodeOfType(ancestor, "IfStatement") && ancestor.consequent === descendant && isNodeOfType(ancestor.test, "BinaryExpression") && (ancestor.test.operator === "===" || ancestor.test.operator === "==")) {
37348
36631
  const { left, right } = ancestor.test;
37349
- const comparesEmptySentinel = isSameRefCurrentAlias(left, refSymbol, scopes) && isEmptySentinel(right, scopes) || isSameRefCurrentAlias(right, refSymbol, scopes) && isEmptySentinel(left, scopes);
37350
- const guardedBranch = ancestor.test.operator === "===" || ancestor.test.operator === "==" ? ancestor.consequent : ancestor.alternate;
37351
- if (comparesEmptySentinel && guardedBranch === descendant && guardedBranch && !hasRepeatedExecutionAncestor(assignmentExpression, guardedBranch) && hasNoPriorCoExecutableWrite(assignmentExpression, guardedBranch, refSymbol, scopes)) return true;
36632
+ if (isSameRefCurrentMember(left, refSymbol, scopes) && isNodeOfType(right, "Literal") && right.value === null || isSameRefCurrentMember(right, refSymbol, scopes) && isNodeOfType(left, "Literal") && left.value === null) return true;
37352
36633
  }
37353
36634
  descendant = ancestor;
37354
36635
  ancestor = descendant.parent;
@@ -37922,7 +37203,7 @@ const isNonSettlingSetterArgument = (setterCall, stateName) => {
37922
37203
  return expressionReadsStateValue(argument, stateName);
37923
37204
  };
37924
37205
  const getUnconditionalSetterCall = (statement, setterNames) => {
37925
- const expression = unwrapDiscardedExpression(statement);
37206
+ const expression = stripParenExpression(isNodeOfType(statement, "ExpressionStatement") ? statement.expression : statement);
37926
37207
  if (!isNodeOfType(expression, "CallExpression")) return null;
37927
37208
  if (!isNodeOfType(expression.callee, "Identifier")) return null;
37928
37209
  if (!setterNames.has(expression.callee.name)) return null;
@@ -38271,7 +37552,7 @@ const noSelfUpdatingEffect = defineRule({
38271
37552
  const setterNames = new Set(setterNameToStateName.keys());
38272
37553
  for (const statement of functionBody.body ?? []) {
38273
37554
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
38274
- const effectCall = unwrapDiscardedExpression(statement);
37555
+ const effectCall = statement.expression;
38275
37556
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
38276
37557
  if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
38277
37558
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
@@ -38901,10 +38182,9 @@ const noStringFalseOnBooleanAttribute = defineRule({
38901
38182
  recommendation: "Use the boolean form on boolean attributes: `disabled` / `disabled={true}` / `disabled={false}`, not `disabled=\"false\"`. A non-empty string is truthy, so `=\"false\"` actually turns the attribute ON.",
38902
38183
  create: (context) => ({ JSXOpeningElement(node) {
38903
38184
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
38904
- const elementName = resolveJsxElementType(node);
38905
- const firstCharacter = elementName.charCodeAt(0);
38185
+ const firstCharacter = node.name.name.charCodeAt(0);
38906
38186
  if (firstCharacter < 97 || firstCharacter > 122) return;
38907
- if (elementName.includes("-")) return;
38187
+ if (node.name.name.includes("-")) return;
38908
38188
  for (const attribute of node.attributes) {
38909
38189
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
38910
38190
  if (!isNodeOfType(attribute.name, "JSXIdentifier")) continue;
@@ -39292,7 +38572,8 @@ const noUncontrolledInput = defineRule({
39292
38572
  const undefinedInitialStateNames = isNodeOfType(componentBody, "BlockStatement") ? collectUndefinedInitialStateNames(componentBody) : /* @__PURE__ */ new Set();
39293
38573
  walkAst(componentBody, (child) => {
39294
38574
  if (!isNodeOfType(child, "JSXOpeningElement")) return;
39295
- const tagName = resolveJsxElementType(child);
38575
+ if (!isNodeOfType(child.name, "JSXIdentifier")) return;
38576
+ const tagName = child.name.name;
39296
38577
  if (!UNCONTROLLED_INPUT_TAGS.has(tagName)) return;
39297
38578
  const attributes = child.attributes ?? [];
39298
38579
  if (hasJsxSpreadAttribute(attributes)) return;
@@ -39353,7 +38634,7 @@ const noUndeferredThirdParty = defineRule({
39353
38634
  severity: "warn",
39354
38635
  recommendation: "Use `next/script` with `strategy=\"lazyOnload\"`, or add the `defer` attribute.",
39355
38636
  create: (context) => ({ JSXOpeningElement(node) {
39356
- if (resolveJsxElementType(node) !== "script") return;
38637
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "script") return;
39357
38638
  const attributes = node.attributes ?? [];
39358
38639
  const srcAttribute = findJsxAttribute(attributes, "src");
39359
38640
  if (!srcAttribute) return;
@@ -40568,7 +39849,7 @@ const noUnknownProperty = defineRule({
40568
39849
  }
40569
39850
  if (fileIsNonReactJsx) return;
40570
39851
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
40571
- const elementType = resolveJsxElementType(node);
39852
+ const elementType = node.name.name;
40572
39853
  const firstCharacter = elementType.charCodeAt(0);
40573
39854
  if (!(firstCharacter >= 97 && firstCharacter <= 122) || elementType === "fbt" || elementType === "fbs") return;
40574
39855
  let isValidHtmlTag = isKnownDomTag(elementType);
@@ -40746,21 +40027,20 @@ const expressionContainsJsxOrCreateElement = (root) => {
40746
40027
  });
40747
40028
  return found;
40748
40029
  };
40749
- const functionContainsJsxOrCreateElement = (functionNode, scopes) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement);
40750
40030
  const isReactClassComponent = (classNode) => {
40751
40031
  if (isEs6Component(classNode)) return true;
40752
40032
  return expressionContainsJsxOrCreateElement(classNode);
40753
40033
  };
40754
- const findEnclosingComponent = (node, scopes) => {
40034
+ const findEnclosingComponent = (node) => {
40755
40035
  let walker = node.parent;
40756
40036
  while (walker) {
40757
40037
  if (isFunctionLike$1(walker)) {
40758
40038
  const componentName = inferFunctionLikeName(walker);
40759
- if (componentName && isReactComponentName(componentName) && functionContainsJsxOrCreateElement(walker, scopes)) return {
40039
+ if (componentName && isReactComponentName(componentName) && expressionContainsJsxOrCreateElement(walker)) return {
40760
40040
  component: walker,
40761
40041
  name: componentName
40762
40042
  };
40763
- if (!componentName && functionContainsJsxOrCreateElement(walker, scopes) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40043
+ if (!componentName && expressionContainsJsxOrCreateElement(walker) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40764
40044
  component: walker,
40765
40045
  name: null
40766
40046
  };
@@ -40999,7 +40279,7 @@ const noUnstableNestedComponents = defineRule({
40999
40279
  if (renderPropRegex.test(propInfo.propName)) return;
41000
40280
  if (settings.allowAsProps) return;
41001
40281
  }
41002
- const enclosing = findEnclosingComponent(candidateNode, context.scopes);
40282
+ const enclosing = findEnclosingComponent(candidateNode);
41003
40283
  if (!enclosing) return;
41004
40284
  const gatedName = propInfo ? null : requiredInstantiationName;
41005
40285
  queuedReports.push({
@@ -41010,7 +40290,7 @@ const noUnstableNestedComponents = defineRule({
41010
40290
  });
41011
40291
  };
41012
40292
  const checkFunctionLike = (node) => {
41013
- if (!functionContainsJsxOrCreateElement(node, context.scopes)) return;
40293
+ if (!expressionContainsJsxOrCreateElement(node)) return;
41014
40294
  const inferredName = inferFunctionLikeName(node);
41015
40295
  const propInfo = isComponentDeclaredInProp(node);
41016
40296
  const isObjectCallback = isObjectCallbackCandidate(node);
@@ -42270,7 +41550,7 @@ const preactPreferOndblclick = defineRule({
42270
41550
  recommendation: "Rename `onDoubleClick` to `onDblClick` because Preact core listens for the DOM `dblclick` event name and `onDoubleClick` never fires.",
42271
41551
  create: (context) => ({ JSXOpeningElement(node) {
42272
41552
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
42273
- const tagName = resolveJsxElementType(node);
41553
+ const tagName = node.name.name;
42274
41554
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
42275
41555
  const onDoubleClickAttribute = findJsxAttribute(node.attributes, "onDoubleClick");
42276
41556
  if (!onDoubleClickAttribute) return;
@@ -42290,7 +41570,7 @@ const COMPAT_EXEMPT_INPUT_TYPES = new Set([
42290
41570
  ]);
42291
41571
  const isTextLikeInput = (openingElement) => {
42292
41572
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
42293
- const tagName = resolveJsxElementType(openingElement);
41573
+ const tagName = openingElement.name.name;
42294
41574
  if (tagName === "textarea") return true;
42295
41575
  if (tagName !== "input") return false;
42296
41576
  const typeAttribute = findJsxAttribute(openingElement.attributes, "type");
@@ -42447,9 +41727,8 @@ const CROSS_CUTTING_STATE_BOOLEAN_NAMES = new Set([
42447
41727
  ]);
42448
41728
  const collectBooleanPropBindings = (param) => {
42449
41729
  const bindings = /* @__PURE__ */ new Set();
42450
- const propsBinding = resolveFirstArgumentBinding(param);
42451
- if (!isNodeOfType(propsBinding, "ObjectPattern")) return bindings;
42452
- for (const property of propsBinding.properties ?? []) {
41730
+ if (!param || !isNodeOfType(param, "ObjectPattern")) return bindings;
41731
+ for (const property of param.properties ?? []) {
42453
41732
  if (!isNodeOfType(property, "Property")) continue;
42454
41733
  if (property.computed) continue;
42455
41734
  if (!isNodeOfType(property.key, "Identifier")) continue;
@@ -42710,7 +41989,7 @@ const preferHtmlDialog = defineRule({
42710
41989
  },
42711
41990
  JSXOpeningElement(node) {
42712
41991
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
42713
- const tagName = resolveJsxElementType(node);
41992
+ const tagName = node.name.name;
42714
41993
  if (tagName === "dialog") return;
42715
41994
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
42716
41995
  if (!HTML_TAGS.has(tagName)) return;
@@ -44257,13 +43536,13 @@ const resolveTanstackQueryHookName = (callExpression) => {
44257
43536
  }
44258
43537
  return null;
44259
43538
  };
44260
- const resolveHookNameFromInitializer = (initializer, scopes) => {
43539
+ const resolveHookNameFromInitializer = (initializer) => {
44261
43540
  if (isNodeOfType(initializer, "CallExpression")) return resolveTanstackQueryHookName(initializer);
44262
43541
  if (!isNodeOfType(initializer, "Identifier")) return null;
44263
- const resolvedSymbol = resolveConstIdentifierAlias(initializer, scopes);
44264
- if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
44265
- if (!isNodeOfType(resolvedSymbol.initializer, "CallExpression")) return null;
44266
- return resolveTanstackQueryHookName(resolvedSymbol.initializer);
43542
+ const binding = findVariableInitializer(initializer, initializer.name);
43543
+ if (!binding?.initializer || !isConstDeclaredBinding(binding)) return null;
43544
+ if (!isNodeOfType(binding.initializer, "CallExpression")) return null;
43545
+ return resolveTanstackQueryHookName(binding.initializer);
44267
43546
  };
44268
43547
  const queryNoRestDestructuring = defineRule({
44269
43548
  id: "query-no-rest-destructuring",
@@ -44276,7 +43555,7 @@ const queryNoRestDestructuring = defineRule({
44276
43555
  if (!isNodeOfType(node.id, "ObjectPattern")) return;
44277
43556
  if (!node.init) return;
44278
43557
  if (!node.id.properties?.some((property) => isNodeOfType(property, "RestElement"))) return;
44279
- const hookName = resolveHookNameFromInitializer(node.init, context.scopes);
43558
+ const hookName = resolveHookNameFromInitializer(node.init);
44280
43559
  if (!hookName) return;
44281
43560
  context.report({
44282
43561
  node: node.id,
@@ -44747,7 +44026,7 @@ const renderingAnimateSvgWrapper = defineRule({
44747
44026
  severity: "warn",
44748
44027
  recommendation: "Wrap the SVG in a motion element so animation props apply to a stable wrapper instead of the SVG node itself.",
44749
44028
  create: (context) => ({ JSXOpeningElement(node) {
44750
- if (resolveJsxElementType(node) !== "svg") return;
44029
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "svg") return;
44751
44030
  if (node.attributes?.some((attribute) => isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && MOTION_ANIMATE_PROPS.has(attribute.name.name))) context.report({
44752
44031
  node,
44753
44032
  message: "This is slow to render because you animate <svg> directly, so wrap it in a <div> or <motion.div> & animate that instead"
@@ -45631,7 +44910,7 @@ const DEFERRABLE_HOOK_NAMES = new Set([
45631
44910
  "useParams",
45632
44911
  "usePathname"
45633
44912
  ]);
45634
- const findHookCallBindings = (componentBody, scopes) => {
44913
+ const findHookCallBindings = (componentBody) => {
45635
44914
  const bindings = [];
45636
44915
  if (!isNodeOfType(componentBody, "BlockStatement")) return bindings;
45637
44916
  for (const statement of componentBody.body ?? []) {
@@ -45642,10 +44921,8 @@ const findHookCallBindings = (componentBody, scopes) => {
45642
44921
  const callee = declarator.init.callee;
45643
44922
  if (!isNodeOfType(callee, "Identifier")) continue;
45644
44923
  if (!DEFERRABLE_HOOK_NAMES.has(callee.name)) continue;
45645
- const valueSymbol = scopes.symbolFor(declarator.id);
45646
- if (!valueSymbol) continue;
45647
44924
  bindings.push({
45648
- valueSymbol,
44925
+ valueName: declarator.id.name,
45649
44926
  hookName: callee.name,
45650
44927
  declarator
45651
44928
  });
@@ -45653,49 +44930,6 @@ const findHookCallBindings = (componentBody, scopes) => {
45653
44930
  }
45654
44931
  return bindings;
45655
44932
  };
45656
- const collectExactAliasSymbols = (componentBody, sourceSymbol, scopes) => {
45657
- const symbols = [sourceSymbol];
45658
- const symbolIds = new Set([sourceSymbol.id]);
45659
- const aliasSourceIdentifiers = /* @__PURE__ */ new Set();
45660
- if (!isNodeOfType(componentBody, "BlockStatement")) return {
45661
- symbols,
45662
- aliasSourceIdentifiers
45663
- };
45664
- let didFindAlias = true;
45665
- while (didFindAlias) {
45666
- didFindAlias = false;
45667
- for (const statement of componentBody.body ?? []) {
45668
- if (!isNodeOfType(statement, "VariableDeclaration") || statement.kind !== "const") continue;
45669
- for (const declarator of statement.declarations ?? []) {
45670
- let aliasIdentifier = null;
45671
- let sourceIdentifier = null;
45672
- const initializer = declarator.init ? stripParenExpression(declarator.init) : null;
45673
- const arrayBinding = isNodeOfType(declarator.id, "ArrayPattern") ? declarator.id.elements[0] : null;
45674
- const arrayValueNode = isNodeOfType(initializer, "ArrayExpression") ? initializer.elements[0] : null;
45675
- const arrayValue = arrayValueNode && !isNodeOfType(arrayValueNode, "SpreadElement") ? stripParenExpression(arrayValueNode) : null;
45676
- if (isNodeOfType(declarator.id, "Identifier") && isNodeOfType(initializer, "Identifier")) {
45677
- aliasIdentifier = declarator.id;
45678
- sourceIdentifier = initializer;
45679
- } else if (isNodeOfType(declarator.id, "ArrayPattern") && declarator.id.elements.length === 1 && isNodeOfType(arrayBinding, "Identifier") && isNodeOfType(initializer, "ArrayExpression") && initializer.elements.length === 1 && isNodeOfType(arrayValue, "Identifier")) {
45680
- aliasIdentifier = arrayBinding;
45681
- sourceIdentifier = arrayValue;
45682
- }
45683
- if (!aliasIdentifier || !sourceIdentifier) continue;
45684
- const referencedSymbol = scopes.symbolFor(sourceIdentifier);
45685
- const aliasSymbol = scopes.symbolFor(aliasIdentifier);
45686
- if (!referencedSymbol || !symbolIds.has(referencedSymbol.id) || !aliasSymbol || aliasSymbol.kind !== "const" || symbolIds.has(aliasSymbol.id)) continue;
45687
- symbolIds.add(aliasSymbol.id);
45688
- symbols.push(aliasSymbol);
45689
- aliasSourceIdentifiers.add(sourceIdentifier);
45690
- didFindAlias = true;
45691
- }
45692
- }
45693
- }
45694
- return {
45695
- symbols,
45696
- aliasSourceIdentifiers
45697
- };
45698
- };
45699
44933
  const rerenderDeferReadsHook = defineRule({
45700
44934
  id: "rerender-defer-reads-hook",
45701
44935
  title: "URL hook value only read in handlers",
@@ -45706,13 +44940,15 @@ const rerenderDeferReadsHook = defineRule({
45706
44940
  create: (context) => {
45707
44941
  const checkComponent = (componentBody) => {
45708
44942
  if (!componentBody || !isNodeOfType(componentBody, "BlockStatement")) return;
45709
- const bindings = findHookCallBindings(componentBody, context.scopes);
44943
+ const bindings = findHookCallBindings(componentBody);
45710
44944
  if (bindings.length === 0) return;
45711
44945
  const handlerBindingNames = collectHandlerBindingNames(componentBody);
45712
44946
  for (const binding of bindings) {
45713
- const { symbols, aliasSourceIdentifiers } = collectExactAliasSymbols(componentBody, binding.valueSymbol, context.scopes);
45714
44947
  const referenceLocations = [];
45715
- for (const symbol of symbols) for (const reference of symbol.references) if (!aliasSourceIdentifiers.has(reference.identifier)) referenceLocations.push(reference.identifier);
44948
+ walkAst(componentBody, (child) => {
44949
+ if (isNodeOfType(binding.declarator, "VariableDeclarator") && child === binding.declarator.id) return;
44950
+ if (isNodeOfType(child, "Identifier") && child.name === binding.valueName) referenceLocations.push(child);
44951
+ });
45716
44952
  if (referenceLocations.length === 0) continue;
45717
44953
  if (!referenceLocations.every((ref) => isInsideEventHandler(ref, handlerBindingNames))) continue;
45718
44954
  context.report({
@@ -46083,10 +45319,14 @@ const rerenderLazyStateInit = defineRule({
46083
45319
  //#endregion
46084
45320
  //#region src/plugin/rules/performance/rerender-memo-before-early-return.ts
46085
45321
  const isJsxExpression = (node) => Boolean(node && isJsxElementOrFragment(stripParenExpression(node)));
46086
- const callbackReturnsJsx = (callback, scopes) => {
45322
+ const callbackReturnsJsx = (callback) => {
46087
45323
  if (!callback) return false;
46088
45324
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
46089
- return functionReturnsMatchingExpression(callback, scopes, isJsxExpression);
45325
+ const body = callback.body;
45326
+ if (isJsxExpression(body)) return true;
45327
+ if (!isNodeOfType(body, "BlockStatement")) return false;
45328
+ for (const stmt of body.body ?? []) if (isNodeOfType(stmt, "ReturnStatement") && isJsxExpression(stmt.argument)) return true;
45329
+ return false;
46090
45330
  };
46091
45331
  const returnArgumentUsesAnyName = (returnStatement, names) => {
46092
45332
  if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
@@ -46164,7 +45404,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
46164
45404
  if (!isNodeOfType(stmt, "VariableDeclaration")) continue;
46165
45405
  for (const declarator of stmt.declarations ?? []) {
46166
45406
  const init = declarator.init;
46167
- if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0], context.scopes)) {
45407
+ if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0])) {
46168
45408
  memoNode = declarator;
46169
45409
  callbackGuardTests = collectLeadingCallbackGuardTests(init.arguments?.[0]);
46170
45410
  if (isNodeOfType(declarator.id, "Identifier")) memoConsumerNames.add(declarator.id.name);
@@ -46230,11 +45470,8 @@ const collectFromObjectPattern = (pattern, bindings) => {
46230
45470
  const collectDefaultedEmptyBindings = (functionNode) => {
46231
45471
  const bindings = /* @__PURE__ */ new Map();
46232
45472
  const params = functionNode.params ?? [];
46233
- for (const [parameterIndex, param] of params.entries()) {
46234
- const parameterBinding = parameterIndex === 0 ? resolveFirstArgumentBinding(param) : param;
46235
- if (parameterBinding) collectFromObjectPattern(parameterBinding, bindings);
46236
- }
46237
- const propsParam = resolveFirstArgumentBinding(params[0]);
45473
+ for (const param of params) collectFromObjectPattern(param, bindings);
45474
+ const propsParam = params[0];
46238
45475
  const body = functionNode.body;
46239
45476
  if (!propsParam || !isNodeOfType(propsParam, "Identifier") || !body) return bindings;
46240
45477
  if (!isNodeOfType(body, "BlockStatement")) return bindings;
@@ -46740,8 +45977,8 @@ const rnAnimationReactionAsDerived = defineRule({
46740
45977
  if (statements.length !== 1) return;
46741
45978
  const onlyStatement = statements[0];
46742
45979
  if (!isNodeOfType(onlyStatement, "ExpressionStatement")) return;
46743
- singleAssignment = unwrapDiscardedExpression(onlyStatement);
46744
- } else if (body) singleAssignment = unwrapDiscardedExpression(body);
45980
+ singleAssignment = onlyStatement.expression;
45981
+ } else if (body) singleAssignment = body;
46745
45982
  if (!singleAssignment) return;
46746
45983
  const isValueAssignment = isNodeOfType(singleAssignment, "AssignmentExpression") && isNodeOfType(singleAssignment.left, "MemberExpression") && isNodeOfType(singleAssignment.left.object, "Identifier") && isNodeOfType(singleAssignment.left.property, "Identifier") && singleAssignment.left.property.name === "value";
46747
45984
  const isSetCall = isNodeOfType(singleAssignment, "CallExpression") && isNodeOfType(singleAssignment.callee, "MemberExpression") && isNodeOfType(singleAssignment.callee.property, "Identifier") && singleAssignment.callee.property.name === "set" && (singleAssignment.arguments?.length ?? 0) === 1;
@@ -53661,8 +52898,7 @@ const serverCacheWithObjectLiteral = defineRule({
53661
52898
  if (!isNodeOfType(node.callee, "Identifier")) return;
53662
52899
  if (!cachedFunctionNames.has(node.callee.name)) return;
53663
52900
  const firstArg = node.arguments?.[0];
53664
- if (!firstArg || isNodeOfType(firstArg, "SpreadElement")) return;
53665
- if (!isNodeOfType(unwrapObjectIntegrityExpression(firstArg, context.scopes, OBJECT_FREEZE_OR_SEAL_METHOD_NAMES), "ObjectExpression")) return;
52901
+ if (!isNodeOfType(firstArg, "ObjectExpression")) return;
53666
52902
  context.report({
53667
52903
  node,
53668
52904
  message: `Passing a new object to React.cache() each render misses the cache, so it refetches every request.`
@@ -54349,6 +53585,10 @@ const isInvalidStyleExpression = (expression) => {
54349
53585
  }
54350
53586
  return false;
54351
53587
  };
53588
+ const getJsxOpeningElementName = (node) => {
53589
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
53590
+ return null;
53591
+ };
54352
53592
  const stylePropObject = defineRule({
54353
53593
  id: "style-prop-object",
54354
53594
  title: "Style prop is not an object",
@@ -54360,8 +53600,7 @@ const stylePropObject = defineRule({
54360
53600
  const allowSet = new Set(allow);
54361
53601
  return {
54362
53602
  JSXOpeningElement(node) {
54363
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
54364
- const elementName = resolveJsxElementType(node);
53603
+ const elementName = getJsxOpeningElementName(node);
54365
53604
  if (elementName && allowSet.has(elementName)) return;
54366
53605
  if (elementName) {
54367
53606
  const firstCharCode = elementName.charCodeAt(0);
@@ -55066,7 +54305,7 @@ const tanstackStartNoAnchorElement = defineRule({
55066
54305
  create: (context) => {
55067
54306
  if (!isInProjectDirectory(context, "routes")) return {};
55068
54307
  return { JSXOpeningElement(node) {
55069
- if (resolveJsxElementType(node) !== "a") return;
54308
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
55070
54309
  const attributes = node.attributes ?? [];
55071
54310
  const hrefAttribute = findJsxAttribute(attributes, "href");
55072
54311
  if (!hrefAttribute?.value) return;
@@ -55687,7 +54926,8 @@ const voidDomElementsNoChildren = defineRule({
55687
54926
  create: (context) => ({
55688
54927
  JSXElement(node) {
55689
54928
  const openingElement = node.openingElement;
55690
- const tagName = resolveJsxElementType(openingElement);
54929
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return;
54930
+ const tagName = openingElement.name.name;
55691
54931
  if (!VOID_DOM_ELEMENTS.has(tagName)) return;
55692
54932
  const hasChildrenContent = node.children.some(isMeaningfulJsxChild);
55693
54933
  const hasChildrenLikeProp = findChildrenLikePropName(openingElement.attributes);