oxlint-plugin-react-doctor 0.7.6-dev.c6f996e → 0.7.6-dev.dfd47a7

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 +144 -414
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1518,37 +1518,6 @@ const hasJsxPropIgnoreCase = (attributes, targetProp) => {
1518
1518
  }
1519
1519
  };
1520
1520
  //#endregion
1521
- //#region src/plugin/utils/is-uppercase-name.ts
1522
- const isUppercaseName = (name) => UPPERCASE_PATTERN.test(name);
1523
- //#endregion
1524
- //#region src/plugin/utils/resolve-jsx-element-type.ts
1525
- const resolveConstantStringBinding = (name) => {
1526
- const binding = findVariableInitializer(name, name.name);
1527
- if (!binding?.initializer) return null;
1528
- const declarator = binding.bindingIdentifier.parent;
1529
- if (!declarator || !isNodeOfType(declarator, "VariableDeclarator")) return null;
1530
- if (declarator.id !== binding.bindingIdentifier) return null;
1531
- const declaration = declarator.parent;
1532
- if (!declaration || !isNodeOfType(declaration, "VariableDeclaration")) return null;
1533
- if (declaration.kind !== "const") return null;
1534
- const initializer = stripParenExpression(binding.initializer);
1535
- return isNodeOfType(initializer, "Literal") && typeof initializer.value === "string" ? initializer.value : null;
1536
- };
1537
- const flattenJsxName$2 = (name) => {
1538
- if (isNodeOfType(name, "JSXIdentifier")) return name.name;
1539
- if (isNodeOfType(name, "JSXMemberExpression")) return `${flattenJsxName$2(name.object)}.${name.property.name}`;
1540
- if (isNodeOfType(name, "JSXNamespacedName")) return `${name.namespace.name}:${name.name.name}`;
1541
- return "";
1542
- };
1543
- const resolveJsxElementType = (openingElement) => {
1544
- const name = openingElement.name;
1545
- if (isNodeOfType(name, "JSXIdentifier")) {
1546
- if (!isUppercaseName(name.name)) return name.name;
1547
- return resolveConstantStringBinding(name) ?? name.name;
1548
- }
1549
- return flattenJsxName$2(name);
1550
- };
1551
- //#endregion
1552
1521
  //#region src/plugin/utils/get-element-type.ts
1553
1522
  const EMPTY_JSX_A11Y_SETTINGS = Object.freeze({});
1554
1523
  const jsxA11ySettingsCache = /* @__PURE__ */ new WeakMap();
@@ -1561,8 +1530,14 @@ const readJsxA11ySettings = (settings) => {
1561
1530
  jsxA11ySettingsCache.set(settings, a11ySettings);
1562
1531
  return a11ySettings;
1563
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
+ };
1564
1539
  const computeElementType = (openingElement, a11ySettings) => {
1565
- const baseName = resolveJsxElementType(openingElement);
1540
+ const baseName = flattenJsxName$2(openingElement.name);
1566
1541
  if (a11ySettings.polymorphicPropName) {
1567
1542
  const polymorphicAttribute = hasJsxPropIgnoreCase(openingElement.attributes, a11ySettings.polymorphicPropName);
1568
1543
  if (polymorphicAttribute) {
@@ -2397,9 +2372,8 @@ const anchorIsValid = defineRule({
2397
2372
  const isTestlikeFile = isTestlikeFilename(context.filename);
2398
2373
  return { JSXOpeningElement(node) {
2399
2374
  if (isTestlikeFile) return;
2400
- const tag = getElementType(node, context.settings);
2401
- if (!fileHasJsxA11ySettings && tag !== "a") return;
2402
- if (tag !== "a") return;
2375
+ if (!fileHasJsxA11ySettings && (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a")) return;
2376
+ if (getElementType(node, context.settings) !== "a") return;
2403
2377
  let hrefAttribute;
2404
2378
  for (const attributeName of settings.hrefAttributeNames) {
2405
2379
  hrefAttribute = hasJsxPropIgnoreCase(node.attributes, attributeName);
@@ -5754,7 +5728,7 @@ const buttonHasType = defineRule({
5754
5728
  return {
5755
5729
  JSXOpeningElement(node) {
5756
5730
  if (isTestlikeFile) return;
5757
- if (resolveJsxElementType(node) !== "button") return;
5731
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "button") return;
5758
5732
  const typeAttr = hasJsxPropIgnoreCase(node.attributes, "type");
5759
5733
  if (!typeAttr) {
5760
5734
  if (hasJsxSpreadAttribute$1(node.attributes)) {
@@ -5944,7 +5918,7 @@ const checkedRequiresOnchangeOrReadonly = defineRule({
5944
5918
  };
5945
5919
  return {
5946
5920
  JSXOpeningElement(node) {
5947
- if (resolveJsxElementType(node) !== "input") return;
5921
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
5948
5922
  reportFromPresence(collectFromJsxAttributes(node.attributes));
5949
5923
  },
5950
5924
  CallExpression(node) {
@@ -6021,7 +5995,7 @@ const isPureEventBlockerHandler = (attribute) => {
6021
5995
  //#endregion
6022
5996
  //#region src/plugin/utils/resolve-const-identifier-alias.ts
6023
5997
  const resolveConstIdentifierAlias = (identifier, scopes) => {
6024
- if (!isNodeOfType(identifier, "Identifier") && !isNodeOfType(identifier, "JSXIdentifier")) return null;
5998
+ if (!isNodeOfType(identifier, "Identifier")) return null;
6025
5999
  const visitedSymbolIds = /* @__PURE__ */ new Set();
6026
6000
  let symbol = scopes.symbolFor(identifier);
6027
6001
  while (symbol?.kind === "const") {
@@ -6296,131 +6270,6 @@ const isGlobalMethodCall = (node, objectName, methodName) => {
6296
6270
  return isNodeOfType(receiver, "Identifier") && receiver.name === objectName && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName;
6297
6271
  };
6298
6272
  //#endregion
6299
- //#region src/plugin/utils/collect-safely-validated-local-storage-keys.ts
6300
- const isLocalStorageCall = (node, methodName) => {
6301
- if (!isNodeOfType(node, "CallExpression")) return false;
6302
- const callee = stripParenExpression(node.callee);
6303
- if (!isNodeOfType(callee, "MemberExpression")) return false;
6304
- const receiver = stripParenExpression(callee.object);
6305
- return isNodeOfType(receiver, "Identifier") && receiver.name === "localStorage" && isNodeOfType(callee.property, "Identifier") && callee.property.name === methodName;
6306
- };
6307
- const catchReturnsFallback = (tryStatement) => {
6308
- const handler = tryStatement.handler;
6309
- if (!handler) return false;
6310
- let hasReturn = false;
6311
- let hasThrow = false;
6312
- walkAst(handler.body, (child) => {
6313
- if (isFunctionLike$1(child)) return false;
6314
- if (isNodeOfType(child, "ReturnStatement")) hasReturn = true;
6315
- if (isNodeOfType(child, "ThrowStatement")) hasThrow = true;
6316
- });
6317
- return hasReturn && !hasThrow;
6318
- };
6319
- const resolveValidatorFunction = (callee, scopes) => {
6320
- const unwrappedCallee = stripParenExpression(callee);
6321
- if (!isNodeOfType(unwrappedCallee, "Identifier")) return null;
6322
- const symbol = scopes.symbolFor(unwrappedCallee);
6323
- if (!symbol) return null;
6324
- const candidate = symbol.kind === "function" ? symbol.declarationNode : symbol.initializer;
6325
- return isFunctionLike$1(candidate) ? candidate : null;
6326
- };
6327
- const validatorChecksPayloadProperties = (validatorFunction, scopes) => {
6328
- if (!isFunctionLike$1(validatorFunction)) return false;
6329
- const firstParameter = validatorFunction.params?.[0];
6330
- if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
6331
- const parameterSymbol = scopes.symbolFor(firstParameter);
6332
- if (!parameterSymbol) return false;
6333
- const payloadSymbolIds = new Set([parameterSymbol.id]);
6334
- let hasPropertyTypeCheck = false;
6335
- walkAst(validatorFunction.body, (child) => {
6336
- if (isFunctionLike$1(child)) return false;
6337
- if (isNodeOfType(child, "VariableDeclarator") && isNodeOfType(child.id, "Identifier") && child.init) {
6338
- const initializer = stripParenExpression(child.init);
6339
- if (isNodeOfType(initializer, "Identifier")) {
6340
- const initializerSymbol = scopes.symbolFor(initializer);
6341
- if (initializerSymbol && payloadSymbolIds.has(initializerSymbol.id)) {
6342
- const aliasSymbol = scopes.symbolFor(child.id);
6343
- if (aliasSymbol) payloadSymbolIds.add(aliasSymbol.id);
6344
- }
6345
- }
6346
- }
6347
- if (!isNodeOfType(child, "UnaryExpression") || child.operator !== "typeof") return;
6348
- const checkedValue = stripParenExpression(child.argument);
6349
- if (!isNodeOfType(checkedValue, "MemberExpression")) return;
6350
- const receiver = stripParenExpression(checkedValue.object);
6351
- if (!isNodeOfType(receiver, "Identifier")) return;
6352
- const receiverSymbol = scopes.symbolFor(receiver);
6353
- if (receiverSymbol && payloadSymbolIds.has(receiverSymbol.id)) hasPropertyTypeCheck = true;
6354
- });
6355
- return hasPropertyTypeCheck;
6356
- };
6357
- const incrementCount = (counts, keyValue) => {
6358
- counts.set(keyValue, (counts.get(keyValue) ?? 0) + 1);
6359
- };
6360
- const isReturnedExpression = (expression) => {
6361
- const parent = expression.parent;
6362
- return Boolean(parent && isNodeOfType(parent, "ReturnStatement") && parent.argument === expression);
6363
- };
6364
- const collectSafelyValidatedLocalStorageKeys = ({ programNode, scopes, resolveKey }) => {
6365
- const readCountsByKey = /* @__PURE__ */ new Map();
6366
- const safeReadCountsByKey = /* @__PURE__ */ new Map();
6367
- const safeReadSymbolIds = /* @__PURE__ */ new Set();
6368
- walkAst(programNode, (child) => {
6369
- if (!isNodeOfType(child, "CallExpression") || !isLocalStorageCall(child, "getItem")) return;
6370
- const keyArgument = child.arguments?.[0];
6371
- if (!keyArgument) return;
6372
- const keyValue = resolveKey(keyArgument);
6373
- if (keyValue !== null) incrementCount(readCountsByKey, keyValue);
6374
- });
6375
- walkAst(programNode, (child) => {
6376
- if (!isNodeOfType(child, "TryStatement") || !catchReturnsFallback(child)) return;
6377
- const rawValueKeys = /* @__PURE__ */ new Map();
6378
- const parsedValueSources = /* @__PURE__ */ new Map();
6379
- walkAst(child.block, (tryChild) => {
6380
- if (isFunctionLike$1(tryChild)) return false;
6381
- if (isNodeOfType(tryChild, "VariableDeclarator") && isNodeOfType(tryChild.id, "Identifier") && tryChild.init) {
6382
- const initializer = stripParenExpression(tryChild.init);
6383
- if (isNodeOfType(initializer, "CallExpression") && isLocalStorageCall(initializer, "getItem")) {
6384
- const keyArgument = initializer.arguments?.[0];
6385
- const bindingSymbol = scopes.symbolFor(tryChild.id);
6386
- if (keyArgument && bindingSymbol) {
6387
- const keyValue = resolveKey(keyArgument);
6388
- if (keyValue !== null) rawValueKeys.set(bindingSymbol.id, keyValue);
6389
- }
6390
- }
6391
- if (isNodeOfType(initializer, "CallExpression") && isGlobalMethodCall(initializer, "JSON", "parse")) {
6392
- const rawValueArgument = initializer.arguments?.[0];
6393
- const parsedValueSymbol = scopes.symbolFor(tryChild.id);
6394
- if (rawValueArgument && isNodeOfType(rawValueArgument, "Identifier") && parsedValueSymbol) {
6395
- const rawValueSymbol = scopes.symbolFor(rawValueArgument);
6396
- if (rawValueSymbol && rawValueKeys.has(rawValueSymbol.id)) parsedValueSources.set(parsedValueSymbol.id, rawValueSymbol.id);
6397
- }
6398
- }
6399
- }
6400
- if (!isNodeOfType(tryChild, "ConditionalExpression") || !isReturnedExpression(tryChild)) return;
6401
- const test = stripParenExpression(tryChild.test);
6402
- if (!isNodeOfType(test, "CallExpression")) return;
6403
- const testedValue = test.arguments?.[0];
6404
- if (!testedValue || !isNodeOfType(testedValue, "Identifier")) return;
6405
- const parsedValueSymbol = scopes.symbolFor(testedValue);
6406
- if (!parsedValueSymbol) return;
6407
- const rawValueSymbolId = parsedValueSources.get(parsedValueSymbol.id);
6408
- if (rawValueSymbolId === void 0) return;
6409
- const returnedValue = stripParenExpression(tryChild.consequent);
6410
- if (!isNodeOfType(returnedValue, "Identifier") || scopes.symbolFor(returnedValue)?.id !== parsedValueSymbol.id) return;
6411
- const validatorFunction = resolveValidatorFunction(test.callee, scopes);
6412
- if (!validatorFunction || !validatorChecksPayloadProperties(validatorFunction, scopes)) return;
6413
- const keyValue = rawValueKeys.get(rawValueSymbolId);
6414
- if (keyValue === void 0 || safeReadSymbolIds.has(rawValueSymbolId)) return;
6415
- safeReadSymbolIds.add(rawValueSymbolId);
6416
- incrementCount(safeReadCountsByKey, keyValue);
6417
- });
6418
- });
6419
- const safeKeys = /* @__PURE__ */ new Set();
6420
- for (const [keyValue, readCount] of readCountsByKey) if (safeReadCountsByKey.get(keyValue) === readCount) safeKeys.add(keyValue);
6421
- return safeKeys;
6422
- };
6423
- //#endregion
6424
6273
  //#region src/plugin/rules/client/client-localstorage-no-version.ts
6425
6274
  const VERSIONED_KEY_PATTERN = /(?:[._:-]v\d+|@\d+|\bv\d+\b)/i;
6426
6275
  const CAMEL_CASE_VERSIONED_KEY_PATTERN = /[a-z]V\d+/;
@@ -6442,39 +6291,26 @@ const clientLocalstorageNoVersion = defineRule({
6442
6291
  severity: "warn",
6443
6292
  category: "Correctness",
6444
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.",
6445
- create: (context) => {
6446
- let safelyValidatedStorageKeys = /* @__PURE__ */ new Set();
6447
- return {
6448
- Program(node) {
6449
- safelyValidatedStorageKeys = collectSafelyValidatedLocalStorageKeys({
6450
- programNode: node,
6451
- scopes: context.scopes,
6452
- resolveKey: (keyNode) => resolveStringKey(keyNode, context)
6453
- });
6454
- },
6455
- CallExpression(node) {
6456
- if (!isNodeOfType(node.callee, "MemberExpression")) return;
6457
- const receiver = stripParenExpression(node.callee.object);
6458
- if (!isNodeOfType(receiver, "Identifier")) return;
6459
- if (receiver.name !== "localStorage") return;
6460
- if (!isNodeOfType(node.callee.property, "Identifier")) return;
6461
- if (node.callee.property.name !== "setItem") return;
6462
- const keyArg = node.arguments?.[0];
6463
- if (!keyArg) return;
6464
- const keyValue = resolveStringKey(keyArg, context);
6465
- if (keyValue === null) return;
6466
- if (isVersionedKey(keyValue)) return;
6467
- if (safelyValidatedStorageKeys.has(keyValue)) return;
6468
- const valueArg = node.arguments?.[1];
6469
- if (!valueArg) return;
6470
- if (!isJsonStringifyCall(valueArg)) return;
6471
- context.report({
6472
- node: keyArg,
6473
- 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").`
6474
- });
6475
- }
6476
- };
6477
- }
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
+ } })
6478
6314
  });
6479
6315
  //#endregion
6480
6316
  //#region src/plugin/rules/client/client-passive-event-listeners.ts
@@ -7597,7 +7433,7 @@ const findJsxAttribute = (attributes, attributeName) => attributes?.find((attrib
7597
7433
  const getOpeningElementTagName = (openingElement) => {
7598
7434
  if (!openingElement) return null;
7599
7435
  if (!isNodeOfType(openingElement, "JSXOpeningElement")) return null;
7600
- if (isNodeOfType(openingElement.name, "JSXIdentifier")) return resolveJsxElementType(openingElement);
7436
+ if (isNodeOfType(openingElement.name, "JSXIdentifier")) return openingElement.name.name;
7601
7437
  if (isNodeOfType(openingElement.name, "JSXMemberExpression")) {
7602
7438
  let cursor = openingElement.name;
7603
7439
  while (isNodeOfType(cursor, "JSXMemberExpression")) cursor = cursor.property;
@@ -7849,7 +7685,7 @@ const dialogHasAccessibleName = defineRule({
7849
7685
  if (isTestlikeFilename(context.filename)) return {};
7850
7686
  return { JSXOpeningElement(node) {
7851
7687
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
7852
- const tagName = resolveJsxElementType(node);
7688
+ const tagName = node.name.name;
7853
7689
  if (tagName[0] !== tagName[0]?.toLowerCase()) return;
7854
7690
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
7855
7691
  const roleValue = roleAttribute ? getJsxPropStringValue(roleAttribute) : null;
@@ -12081,7 +11917,7 @@ const forbidDomProps = defineRule({
12081
11917
  return { JSXOpeningElement(node) {
12082
11918
  if (forbidMap.size === 0) return;
12083
11919
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12084
- const elementName = resolveJsxElementType(node);
11920
+ const elementName = node.name.name;
12085
11921
  if (isReactComponentName(elementName)) return;
12086
11922
  for (const attribute of node.attributes) {
12087
11923
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -12159,7 +11995,7 @@ const forbidElements = defineRule({
12159
11995
  return {
12160
11996
  JSXOpeningElement(node) {
12161
11997
  if (forbidMap.size === 0) return;
12162
- const fullName = resolveJsxElementType(node);
11998
+ const fullName = flattenJsxName$1(node.name);
12163
11999
  if (!fullName || !forbidMap.has(fullName)) return;
12164
12000
  context.report({
12165
12001
  node: node.name,
@@ -12521,7 +12357,8 @@ const buildMessage$22 = (childTagName) => `Your users get reshuffled HTML becaus
12521
12357
  const isParagraphElement = (candidate) => {
12522
12358
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12523
12359
  const opening = candidate.openingElement;
12524
- return resolveJsxElementType(opening) === "p";
12360
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12361
+ return opening.name.name === "p";
12525
12362
  };
12526
12363
  const findEnclosingParagraph = (openingElement) => {
12527
12364
  const owningElement = openingElement.parent;
@@ -12542,7 +12379,8 @@ const htmlNoInvalidParagraphChild = defineRule({
12542
12379
  severity: "warn",
12543
12380
  recommendation: "Swap the `<p>` for a `<div>`, or move the child outside the paragraph.",
12544
12381
  create: (context) => ({ JSXOpeningElement(node) {
12545
- const childTagName = resolveJsxElementType(node);
12382
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12383
+ const childTagName = node.name.name;
12546
12384
  if (!BLOCK_LEVEL_ELEMENTS.has(childTagName)) return;
12547
12385
  if (!findEnclosingParagraph(node)) return;
12548
12386
  context.report({
@@ -12573,7 +12411,7 @@ const getHostTagName = (jsxElement) => {
12573
12411
  if (!isNodeOfType(jsxElement, "JSXElement")) return null;
12574
12412
  const opening = jsxElement.openingElement;
12575
12413
  if (!isNodeOfType(opening.name, "JSXIdentifier")) return null;
12576
- const tagName = resolveJsxElementType(opening);
12414
+ const tagName = opening.name.name;
12577
12415
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return null;
12578
12416
  return tagName;
12579
12417
  };
@@ -12603,7 +12441,7 @@ const findClosestHostAncestor = (jsxElement) => {
12603
12441
  if (isNodeOfType(ancestor, "JSXElement")) {
12604
12442
  const opening = ancestor.openingElement;
12605
12443
  if (isNodeOfType(opening.name, "JSXIdentifier")) {
12606
- const ancestorTag = resolveJsxElementType(opening);
12444
+ const ancestorTag = opening.name.name;
12607
12445
  if (ancestorTag.length === 0) {
12608
12446
  previous = ancestor;
12609
12447
  ancestor = ancestor.parent ?? null;
@@ -12685,7 +12523,8 @@ const buildMessage$20 = (tagName) => `Your users get broken clicks, focus & scre
12685
12523
  const isJsxElementWithTagName = (candidate, tagName) => {
12686
12524
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12687
12525
  const opening = candidate.openingElement;
12688
- return resolveJsxElementType(opening) === tagName;
12526
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12527
+ return opening.name.name === tagName;
12689
12528
  };
12690
12529
  const findEnclosingSameTag = (openingElement, tagName) => {
12691
12530
  const owningElement = openingElement.parent;
@@ -12706,7 +12545,8 @@ const htmlNoNestedInteractive = defineRule({
12706
12545
  severity: "warn",
12707
12546
  recommendation: "Move the inner `<a>` or `<button>` so it's a sibling, or change the outer one to a plain `<div>` or `<span>`.",
12708
12547
  create: (context) => ({ JSXOpeningElement(node) {
12709
- const tagName = resolveJsxElementType(node);
12548
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12549
+ const tagName = node.name.name;
12710
12550
  if (tagName !== "a" && tagName !== "button") return;
12711
12551
  if (!findEnclosingSameTag(node, tagName)) return;
12712
12552
  context.report({
@@ -12821,7 +12661,7 @@ const iframeMissingSandbox = defineRule({
12821
12661
  matchByOccurrence: true,
12822
12662
  create: skipNonProductionFiles((context) => ({
12823
12663
  JSXOpeningElement(node) {
12824
- if (resolveJsxElementType(node) !== "iframe") return;
12664
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "iframe") return;
12825
12665
  const sandboxAttr = hasJsxPropIgnoreCase(node.attributes, "sandbox");
12826
12666
  if (!sandboxAttr) {
12827
12667
  const hasExplicitSrc = Boolean(hasJsxPropIgnoreCase(node.attributes, "src"));
@@ -13813,28 +13653,6 @@ const jsAsyncReduceWithoutAwaitedAcc = defineRule({
13813
13653
  } })
13814
13654
  });
13815
13655
  //#endregion
13816
- //#region src/plugin/utils/unwrap-discarded-expression.ts
13817
- const unwrapDiscardedExpression = (node) => {
13818
- let expression = isNodeOfType(node, "ExpressionStatement") ? node.expression : node;
13819
- for (;;) {
13820
- expression = stripParenExpression(expression);
13821
- if (isNodeOfType(expression, "UnaryExpression") && expression.operator === "void") {
13822
- expression = expression.argument;
13823
- continue;
13824
- }
13825
- if (isNodeOfType(expression, "SequenceExpression")) {
13826
- const expressions = expression.expressions ?? [];
13827
- const finalExpression = expressions.at(-1);
13828
- const prefixExpressions = expressions.slice(0, -1);
13829
- if (finalExpression && prefixExpressions.length > 0 && prefixExpressions.every((prefixExpression) => isNodeOfType(stripParenExpression(prefixExpression), "Literal"))) {
13830
- expression = finalExpression;
13831
- continue;
13832
- }
13833
- }
13834
- return expression;
13835
- }
13836
- };
13837
- //#endregion
13838
13656
  //#region src/plugin/rules/js-performance/js-batch-dom-css.ts
13839
13657
  const ITERATOR_METHOD_NAMES$2 = new Set([
13840
13658
  "forEach",
@@ -14019,19 +13837,9 @@ const hasAttachmentBefore = (scopeOwner, elementName, beforeStart) => {
14019
13837
  });
14020
13838
  return foundAttachment;
14021
13839
  };
14022
- const getStyleAssignment = (node) => {
14023
- if (!isNodeOfType(node, "ExpressionStatement")) return null;
14024
- const expression = unwrapDiscardedExpression(node);
14025
- if (!isNodeOfType(expression, "AssignmentExpression")) return null;
14026
- if (!isNodeOfType(expression.left, "MemberExpression")) return null;
14027
- if (!isNodeOfType(expression.left.object, "MemberExpression")) return null;
14028
- if (!isNodeOfType(expression.left.object.property, "Identifier")) return null;
14029
- return expression.left.object.property.name === "style" ? expression : null;
14030
- };
14031
13840
  const isProvablyDetachedAtWrite = (styleWriteStatement) => {
14032
- const assignment = getStyleAssignment(styleWriteStatement);
14033
- if (!assignment || !isNodeOfType(assignment.left, "MemberExpression") || !isNodeOfType(assignment.left.object, "MemberExpression")) return false;
14034
- const elementExpression = assignment.left.object.object;
13841
+ if (!isNodeOfType(styleWriteStatement, "ExpressionStatement") || !isNodeOfType(styleWriteStatement.expression, "AssignmentExpression") || !isNodeOfType(styleWriteStatement.expression.left, "MemberExpression") || !isNodeOfType(styleWriteStatement.expression.left.object, "MemberExpression")) return false;
13842
+ const elementExpression = styleWriteStatement.expression.left.object.object;
14035
13843
  const creationRoot = resolveDetachedCreationRoot(elementExpression, 0);
14036
13844
  if (!creationRoot) return false;
14037
13845
  return !hasAttachmentBefore(creationRoot.scopeOwner, creationRoot.rootName, getNodeStart$1(styleWriteStatement));
@@ -14043,17 +13851,15 @@ const jsBatchDomCss = defineRule({
14043
13851
  severity: "warn",
14044
13852
  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",
14045
13853
  create: (context) => {
14046
- const writesLayoutAffectingProperty = (node) => {
14047
- const assignment = getStyleAssignment(node);
14048
- return assignment !== null && isNodeOfType(assignment.left, "MemberExpression") && isNodeOfType(assignment.left.property, "Identifier") && !LAYOUT_NEUTRAL_STYLE_PROPERTY_NAMES.has(assignment.left.property.name);
14049
- };
13854
+ 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";
13855
+ 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);
14050
13856
  return { BlockStatement(node) {
14051
13857
  const perIterationBody = findEnclosingPerIterationBody(node);
14052
13858
  if (!perIterationBody) return;
14053
13859
  const statements = node.body ?? [];
14054
13860
  let layoutReads = null;
14055
13861
  for (let statementIndex = 1; statementIndex < statements.length; statementIndex++) {
14056
- if (getStyleAssignment(statements[statementIndex]) === null || getStyleAssignment(statements[statementIndex - 1]) === null) continue;
13862
+ if (!isStyleAssignment(statements[statementIndex]) || !isStyleAssignment(statements[statementIndex - 1])) continue;
14057
13863
  if (!writesLayoutAffectingProperty(statements[statementIndex]) && !writesLayoutAffectingProperty(statements[statementIndex - 1])) continue;
14058
13864
  layoutReads ??= scanPerIterationLayoutReads(perIterationBody);
14059
13865
  if (!layoutReads.hasUsedLayoutRead || layoutReads.hasDeliberateForcedReflow) return;
@@ -14749,18 +14555,6 @@ const jsHoistRegexp = defineRule({
14749
14555
  }, { treatIteratorCallbacksAsLoops: true })
14750
14556
  });
14751
14557
  //#endregion
14752
- //#region src/plugin/utils/resolve-first-argument-binding.ts
14753
- const resolveFirstArgumentBinding = (firstParameter) => {
14754
- if (!firstParameter) return null;
14755
- if (!isNodeOfType(firstParameter, "RestElement")) return firstParameter;
14756
- if (!isNodeOfType(firstParameter.argument, "ArrayPattern")) return null;
14757
- const elements = firstParameter.argument.elements ?? [];
14758
- if (elements.length !== 1) return null;
14759
- const firstBinding = elements[0];
14760
- if (!firstBinding || isNodeOfType(firstBinding, "RestElement")) return null;
14761
- return firstBinding;
14762
- };
14763
- //#endregion
14764
14558
  //#region src/plugin/rules/js-performance/js-index-maps.ts
14765
14559
  const referencesParameter = (expression, parameterName) => {
14766
14560
  if (!expression) return false;
@@ -14771,7 +14565,7 @@ const referencesParameter = (expression, parameterName) => {
14771
14565
  const isSingleFieldEqualityPredicate = (node) => {
14772
14566
  const callback = node.arguments?.[0];
14773
14567
  if (!isInlineFunctionExpression(callback)) return false;
14774
- const firstParameter = resolveFirstArgumentBinding(callback.params?.[0]);
14568
+ const firstParameter = callback.params?.[0];
14775
14569
  if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
14776
14570
  let predicate = null;
14777
14571
  const body = callback.body;
@@ -15463,21 +15257,9 @@ const isSmallFixedListMember = (receiver) => {
15463
15257
  };
15464
15258
  const getResolvedInitializer = (receiver) => {
15465
15259
  if (!isNodeOfType(receiver, "Identifier")) return null;
15466
- const binding = findVariableInitializer(receiver, receiver.name);
15467
- const initializer = binding?.initializer ?? null;
15468
- if (!binding || !initializer) return null;
15469
- const isDefault = isNodeOfType(binding.bindingIdentifier.parent, "AssignmentPattern");
15470
- if (isNodeOfType(initializer, "Identifier")) {
15471
- const aliased = findVariableInitializer(initializer, initializer.name);
15472
- if (aliased?.initializer) return {
15473
- initializer: aliased.initializer,
15474
- isDefault: isDefault || isNodeOfType(aliased.bindingIdentifier.parent, "AssignmentPattern")
15475
- };
15476
- }
15477
- return {
15478
- initializer,
15479
- isDefault
15480
- };
15260
+ const initializer = findVariableInitializer(receiver, receiver.name)?.initializer ?? null;
15261
+ if (initializer && isNodeOfType(initializer, "Identifier")) return findVariableInitializer(initializer, initializer.name)?.initializer ?? initializer;
15262
+ return initializer;
15481
15263
  };
15482
15264
  const isSplitCall = (expression) => {
15483
15265
  if (!expression) return false;
@@ -15643,8 +15425,8 @@ const isBoundedConstantCollection = (collection) => {
15643
15425
  if (isScreamingSnakeCaseConstantReceiver(stripped)) return true;
15644
15426
  if (isSmallInlineLiteralArray(stripped)) return true;
15645
15427
  if (isNodeOfType(stripped, "Identifier")) {
15646
- const resolved = getResolvedInitializer(stripped);
15647
- if (resolved && !resolved.isDefault && isSmallInlineLiteralArray(resolved.initializer)) return true;
15428
+ const initializer = getResolvedInitializer(stripped);
15429
+ if (initializer && isSmallInlineLiteralArray(initializer)) return true;
15648
15430
  }
15649
15431
  return false;
15650
15432
  };
@@ -15696,8 +15478,8 @@ const jsSetMapLookups = defineRule({
15696
15478
  if (isIndexedArrayElementWithStringArgument(receiver, node.arguments?.[0])) return;
15697
15479
  const resolvedInitializer = getResolvedInitializer(receiver);
15698
15480
  if (resolvedInitializer) {
15699
- if (isLikelyStringReceiver(resolvedInitializer.initializer)) return;
15700
- if (!resolvedInitializer.isDefault && isSmallInlineLiteralArray(resolvedInitializer.initializer)) return;
15481
+ if (isLikelyStringReceiver(resolvedInitializer)) return;
15482
+ if (isSmallInlineLiteralArray(resolvedInitializer)) return;
15701
15483
  }
15702
15484
  if (isStringElementOfSplitIteration(receiver)) return;
15703
15485
  if (isReceiverDeclaredInNearestLoop(receiver, node)) return;
@@ -16110,21 +15892,14 @@ const jsxFilenameExtension = defineRule({
16110
15892
  });
16111
15893
  //#endregion
16112
15894
  //#region src/plugin/utils/is-jsx-fragment-element.ts
16113
- const isJsxFragmentElement = (node, scopes) => {
15895
+ const isJsxFragmentElement = (node) => {
16114
15896
  if (!isNodeOfType(node, "JSXOpeningElement")) return false;
16115
15897
  const elementName = node.name;
16116
- if (isNodeOfType(elementName, "JSXIdentifier")) {
16117
- if (!scopes) return elementName.name === "Fragment";
16118
- const symbol = resolveConstIdentifierAlias(elementName, scopes);
16119
- if (!symbol) return elementName.name === "Fragment" && scopes.isGlobalReference(elementName);
16120
- return isImportedFromReact(symbol) && getImportedName(symbol.declarationNode) === "Fragment";
16121
- }
15898
+ if (isNodeOfType(elementName, "JSXIdentifier")) return elementName.name === "Fragment";
16122
15899
  if (isNodeOfType(elementName, "JSXMemberExpression")) {
16123
15900
  if (!isNodeOfType(elementName.object, "JSXIdentifier")) return false;
16124
- if (elementName.property.name !== "Fragment") return false;
16125
- if (!scopes) return elementName.object.name === "React";
16126
- if (isReactNamespaceImport(elementName.object, scopes)) return true;
16127
- return elementName.object.name === "React" && scopes.isGlobalReference(elementName.object);
15901
+ if (elementName.object.name !== "React") return false;
15902
+ return elementName.property.name === "Fragment";
16128
15903
  }
16129
15904
  return false;
16130
15905
  };
@@ -16150,7 +15925,7 @@ const jsxFragments = defineRule({
16150
15925
  if (mode !== "syntax") return;
16151
15926
  if (!node.closingElement) return;
16152
15927
  const openingElement = node.openingElement;
16153
- if (!isJsxFragmentElement(openingElement, context.scopes)) return;
15928
+ if (!isJsxFragmentElement(openingElement)) return;
16154
15929
  if (openingElement.attributes.length > 0) return;
16155
15930
  context.report({
16156
15931
  node: openingElement,
@@ -18831,6 +18606,10 @@ const resolveSettings$29 = (settings) => {
18831
18606
  if (typeof reactDoctor !== "object" || reactDoctor === null) return {};
18832
18607
  return reactDoctor.jsxNoScriptUrl ?? {};
18833
18608
  };
18609
+ const getElementName = (node) => {
18610
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
18611
+ return null;
18612
+ };
18834
18613
  const isLinkPropForElement = (elementName, attributeName, options) => {
18835
18614
  if (elementName === "a" && attributeName === "href") return true;
18836
18615
  const explicit = options.components?.[elementName];
@@ -18850,8 +18629,7 @@ const jsxNoScriptUrl = defineRule({
18850
18629
  create: (context) => {
18851
18630
  const options = resolveSettings$29(context.settings);
18852
18631
  return { JSXOpeningElement(node) {
18853
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
18854
- const elementName = resolveJsxElementType(node);
18632
+ const elementName = getElementName(node);
18855
18633
  if (!elementName) return;
18856
18634
  for (const attribute of node.attributes) {
18857
18635
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -19038,6 +18816,11 @@ const checkTarget = (attributeValue) => {
19038
18816
  alternate: false
19039
18817
  };
19040
18818
  };
18819
+ const getOpeningElementName = (node) => {
18820
+ const name = node.name;
18821
+ if (isNodeOfType(name, "JSXIdentifier")) return name.name;
18822
+ return null;
18823
+ };
19041
18824
  const jsxNoTargetBlank = defineRule({
19042
18825
  id: "jsx-no-target-blank",
19043
18826
  title: "Unsafe target=_blank link",
@@ -19057,8 +18840,7 @@ const jsxNoTargetBlank = defineRule({
19057
18840
  return settings.formComponents.has(tagName);
19058
18841
  };
19059
18842
  return { JSXOpeningElement(node) {
19060
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
19061
- const tagName = resolveJsxElementType(node);
18843
+ const tagName = getOpeningElementName(node);
19062
18844
  if (!tagName) return;
19063
18845
  if (!isLink(tagName) && !isForm(tagName)) return;
19064
18846
  const linkAttributeNames = settings.linkComponents.get(tagName) ?? ["href"];
@@ -19291,7 +19073,7 @@ const jsxNoUselessFragment = defineRule({
19291
19073
  return {
19292
19074
  JSXElement(node) {
19293
19075
  const openingElement = node.openingElement;
19294
- if (!isJsxFragmentElement(openingElement, context.scopes)) return;
19076
+ if (!isJsxFragmentElement(openingElement)) return;
19295
19077
  if (hasJsxKeyAttribute(openingElement)) return;
19296
19078
  if (checkChildren(node, openingElement, node.children)) return;
19297
19079
  if (isChildOfHtmlElement(node)) context.report({
@@ -20533,6 +20315,9 @@ const hasDirective = (programNode, directive) => {
20533
20315
  return Boolean(programNode.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === directive));
20534
20316
  };
20535
20317
  //#endregion
20318
+ //#region src/plugin/utils/is-uppercase-name.ts
20319
+ const isUppercaseName = (name) => UPPERCASE_PATTERN.test(name);
20320
+ //#endregion
20536
20321
  //#region src/plugin/utils/is-component-assignment.ts
20537
20322
  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"));
20538
20323
  //#endregion
@@ -20865,7 +20650,7 @@ const nextjsNoAElement = defineRule({
20865
20650
  severity: "warn",
20866
20651
  recommendation: "`import Link from 'next/link'` for client-side navigation, prefetching, and preserved scroll position",
20867
20652
  create: (context) => ({ JSXOpeningElement(node) {
20868
- if (resolveJsxElementType(node) !== "a") return;
20653
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
20869
20654
  const attributes = node.attributes ?? [];
20870
20655
  const downloadAttribute = findJsxAttribute(attributes, "download");
20871
20656
  if (downloadAttribute) {
@@ -21061,7 +20846,7 @@ const nextjsNoCssLink = defineRule({
21061
20846
  severity: "warn",
21062
20847
  recommendation: "Import CSS directly or use CSS Modules so Next.js can bundle, order, and optimize the stylesheet.",
21063
20848
  create: (context) => ({ JSXOpeningElement(node) {
21064
- if (resolveJsxElementType(node) !== "link") return;
20849
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
21065
20850
  const attributes = node.attributes ?? [];
21066
20851
  const relAttribute = findJsxAttribute(attributes, "rel");
21067
20852
  if (!relAttribute?.value) return;
@@ -21169,7 +20954,7 @@ const nextjsNoFontLink = defineRule({
21169
20954
  severity: "warn",
21170
20955
  recommendation: "`import { Inter } from \"next/font/google\"` for self-hosting, zero layout shift, and no render-blocking requests",
21171
20956
  create: (context) => ({ JSXOpeningElement(node) {
21172
- if (resolveJsxElementType(node) !== "link") return;
20957
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
21173
20958
  const attributes = node.attributes ?? [];
21174
20959
  const hrefAttribute = findJsxAttribute(attributes, "href");
21175
20960
  if (!hrefAttribute?.value) return;
@@ -21344,7 +21129,7 @@ const nextjsNoImgElement = defineRule({
21344
21129
  create: (context) => {
21345
21130
  if (isGeneratedImageRenderContext(context)) return {};
21346
21131
  return { JSXOpeningElement(node) {
21347
- if (resolveJsxElementType(node) !== "img") return;
21132
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
21348
21133
  if (isGeneratedImageRenderContext(context, node)) return;
21349
21134
  const programRoot = findProgramRoot(node);
21350
21135
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
@@ -21391,8 +21176,8 @@ const nextjsNoPolyfillScript = defineRule({
21391
21176
  severity: "warn",
21392
21177
  recommendation: "Next.js includes polyfills for fetch, Promise, Object.assign, Array.from, and 50+ others automatically",
21393
21178
  create: (context) => ({ JSXOpeningElement(node) {
21394
- const elementName = resolveJsxElementType(node);
21395
- if (elementName !== "script" && elementName !== "Script") return;
21179
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
21180
+ if (node.name.name !== "script" && node.name.name !== "Script") return;
21396
21181
  const srcAttribute = findJsxAttribute(node.attributes ?? [], "src");
21397
21182
  if (!srcAttribute?.value) return;
21398
21183
  const srcValue = isNodeOfType(srcAttribute.value, "Literal") ? srcAttribute.value.value : null;
@@ -23200,15 +22985,10 @@ const isProp = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
23200
22985
  if (!declaringNode) return false;
23201
22986
  return isReactFunctionalComponent(declaringNode) && !isReactFunctionalHOC(analysis, declaringNode) || isCustomHook(declaringNode);
23202
22987
  }));
23203
- const isWholePropsParameterBinding = (bindingNode) => {
23204
- if (isFunctionLike$1(bindingNode.parent)) return true;
23205
- let declaringFunction = bindingNode.parent;
23206
- while (declaringFunction && !isFunctionLike$1(declaringFunction)) declaringFunction = declaringFunction.parent;
23207
- return Boolean(declaringFunction && resolveFirstArgumentBinding(declaringFunction.params?.[0]) === bindingNode);
23208
- };
23209
22988
  const isWholePropsObjectReference = (analysis, ref) => isProp(analysis, ref) && Boolean(ref.resolved?.defs.some((def) => {
23210
22989
  if (def.type !== "Parameter") return false;
23211
- return isWholePropsParameterBinding(def.name);
22990
+ const bindingParent = def.name.parent;
22991
+ return isFunctionLike$1(bindingParent);
23212
22992
  }));
23213
22993
  const isIdentifierOrMemberExpression = (node) => isNodeOfType(node, "Identifier") || isNodeOfType(node, "MemberExpression");
23214
22994
  const isPropAlias = (analysis, ref) => {
@@ -26205,56 +25985,6 @@ const noBarrelImport = defineRule({
26205
25985
  }
26206
25986
  });
26207
25987
  //#endregion
26208
- //#region src/plugin/utils/function-returns-matching-expression.ts
26209
- const collectReturnedExpressions = (functionNode) => {
26210
- if (!isFunctionLike$1(functionNode)) return [];
26211
- const body = functionNode.body;
26212
- if (!body) return [];
26213
- if (!isNodeOfType(body, "BlockStatement")) return [body];
26214
- const returnedExpressions = [];
26215
- walkAst(body, (node) => {
26216
- if (node !== body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
26217
- if (isNodeOfType(node, "ReturnStatement") && node.argument) returnedExpressions.push(node.argument);
26218
- });
26219
- return returnedExpressions;
26220
- };
26221
- const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpression) => {
26222
- const visitedExpressions = /* @__PURE__ */ new Set();
26223
- const visitedFunctions = /* @__PURE__ */ new Set();
26224
- const functionMatches = (candidateFunction) => {
26225
- if (visitedFunctions.has(candidateFunction)) return false;
26226
- visitedFunctions.add(candidateFunction);
26227
- return collectReturnedExpressions(candidateFunction).some(expressionMatches);
26228
- };
26229
- const expressionMatches = (expression) => {
26230
- const unwrappedExpression = stripParenExpression(expression);
26231
- if (visitedExpressions.has(unwrappedExpression)) return false;
26232
- visitedExpressions.add(unwrappedExpression);
26233
- if (matchesExpression(unwrappedExpression)) return true;
26234
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
26235
- const symbol = scopes.symbolFor(unwrappedExpression);
26236
- if (!symbol || symbol.kind !== "const" || !symbol.initializer) return false;
26237
- const initializer = stripParenExpression(symbol.initializer);
26238
- if (isFunctionLike$1(initializer)) return false;
26239
- return expressionMatches(initializer);
26240
- }
26241
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
26242
- if (unwrappedExpression.arguments.length !== 0) return false;
26243
- if (!isNodeOfType(unwrappedExpression.callee, "Identifier")) return false;
26244
- const symbol = scopes.symbolFor(unwrappedExpression.callee);
26245
- if (!symbol || symbol.kind !== "const" && symbol.kind !== "function") return false;
26246
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
26247
- const candidateFunction = isFunctionLike$1(initializer) ? initializer : isFunctionLike$1(symbol.declarationNode) ? symbol.declarationNode : null;
26248
- if (!candidateFunction || candidateFunction.async || candidateFunction.generator || candidateFunction.params.length !== 0) return false;
26249
- return functionMatches(candidateFunction);
26250
- }
26251
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return expressionMatches(unwrappedExpression.consequent) || expressionMatches(unwrappedExpression.alternate);
26252
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) return expressionMatches(unwrappedExpression.left) || expressionMatches(unwrappedExpression.right);
26253
- return false;
26254
- };
26255
- return functionMatches(functionNode);
26256
- };
26257
- //#endregion
26258
25988
  //#region src/plugin/utils/function-contains-react-render-output.ts
26259
25989
  const NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES = new Set([
26260
25990
  "FunctionDeclaration",
@@ -26274,13 +26004,12 @@ const isCallArgumentFunctionExpression = (node) => {
26274
26004
  return parent.arguments.some((argumentNode) => argumentNode === node);
26275
26005
  };
26276
26006
  const isNestedRenderEvidenceBoundary = (node) => NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES.has(node.type) && !isCallArgumentFunctionExpression(node);
26277
- const isRenderOutputExpression = (node, scopes) => node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS);
26278
26007
  const containsRenderOutput$1 = (rootNode, scopes) => {
26279
26008
  let hasRenderOutput = false;
26280
26009
  walkAst(rootNode, (node) => {
26281
26010
  if (hasRenderOutput) return false;
26282
26011
  if (node !== rootNode && isNestedRenderEvidenceBoundary(node)) return false;
26283
- if (isRenderOutputExpression(node, scopes)) {
26012
+ if (node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS)) {
26284
26013
  hasRenderOutput = true;
26285
26014
  return false;
26286
26015
  }
@@ -26291,7 +26020,7 @@ const renderOutputCache = /* @__PURE__ */ new WeakMap();
26291
26020
  const functionContainsReactRenderOutput = (functionNode, scopes) => {
26292
26021
  const cachedEntry = renderOutputCache.get(functionNode);
26293
26022
  if (cachedEntry && cachedEntry.scopes === scopes) return cachedEntry.hasRenderOutput;
26294
- const hasRenderOutput = containsRenderOutput$1(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => isRenderOutputExpression(expression, scopes));
26023
+ const hasRenderOutput = containsRenderOutput$1(functionNode, scopes);
26295
26024
  renderOutputCache.set(functionNode, {
26296
26025
  scopes,
26297
26026
  hasRenderOutput
@@ -28756,7 +28485,7 @@ const noDisabledZoom = defineRule({
28756
28485
  category: "Accessibility",
28757
28486
  recommendation: "Remove `user-scalable=no` and `maximum-scale` from the viewport meta tag. If the layout breaks at 200% zoom, fix the layout instead.",
28758
28487
  create: (context) => ({ JSXOpeningElement(node) {
28759
- if (resolveJsxElementType(node) !== "meta") return;
28488
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "meta") return;
28760
28489
  const nameAttr = findJsxAttribute(node.attributes ?? [], "name");
28761
28490
  if (!nameAttr?.value) return;
28762
28491
  if ((isNodeOfType(nameAttr.value, "Literal") ? nameAttr.value.value : null) !== "viewport") return;
@@ -29146,7 +28875,7 @@ const findTopLevelEffectCalls = (componentBody) => {
29146
28875
  if (!isNodeOfType(componentBody, "BlockStatement")) return effectCalls;
29147
28876
  for (const statement of componentBody.body ?? []) {
29148
28877
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
29149
- const expression = unwrapDiscardedExpression(statement);
28878
+ const expression = statement.expression;
29150
28879
  if (!isNodeOfType(expression, "CallExpression")) continue;
29151
28880
  if (!isHookCall$2(expression, EFFECT_HOOK_NAMES$1)) continue;
29152
28881
  effectCalls.push(expression);
@@ -31928,7 +31657,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
31928
31657
  severity: "warn",
31929
31658
  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.",
31930
31659
  create: (context) => ({ JSXOpeningElement(node) {
31931
- if (resolveJsxElementType(node) !== "img") return;
31660
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
31932
31661
  const loadingAttribute = hasJsxPropIgnoreCase(node.attributes, "loading");
31933
31662
  if (!loadingAttribute || getJsxPropStringValue(loadingAttribute)?.toLowerCase() !== "lazy") return;
31934
31663
  const fetchPriorityAttribute = hasJsxPropIgnoreCase(node.attributes, "fetchPriority");
@@ -32169,7 +31898,7 @@ const noIndeterminateAttribute = defineRule({
32169
31898
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
32170
31899
  return {
32171
31900
  JSXOpeningElement(node) {
32172
- if (resolveJsxElementType(node) !== "input") return;
31901
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
32173
31902
  let typeAttribute = null;
32174
31903
  let typeAttributeIndex = null;
32175
31904
  let indeterminateAttribute = null;
@@ -33241,13 +32970,11 @@ const noManyBooleanProps = defineRule({
33241
32970
  };
33242
32971
  const checkComponent = (functionNode, param, body, componentName, reportNode) => {
33243
32972
  if (!param) return;
33244
- const propsBinding = resolveFirstArgumentBinding(param);
33245
- if (!propsBinding) return;
33246
32973
  if (!functionContainsReactRenderOutput(functionNode, context.scopes)) return;
33247
- if (isNodeOfType(propsBinding, "ObjectPattern")) {
32974
+ if (isNodeOfType(param, "ObjectPattern")) {
33248
32975
  const callbackUsedNames = collectCallbackUsedNames(body, param, context.scopes);
33249
32976
  const booleanLikePropNames = [];
33250
- for (const property of propsBinding.properties ?? []) {
32977
+ for (const property of param.properties ?? []) {
33251
32978
  if (!isNodeOfType(property, "Property")) continue;
33252
32979
  const keyName = isNodeOfType(property.key, "Identifier") ? property.key.name : null;
33253
32980
  if (!keyName) continue;
@@ -33258,7 +32985,7 @@ const noManyBooleanProps = defineRule({
33258
32985
  reportIfMany(booleanLikePropNames, componentName, reportNode);
33259
32986
  return;
33260
32987
  }
33261
- if (isNodeOfType(propsBinding, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, propsBinding.name)], componentName, reportNode);
32988
+ if (isNodeOfType(param, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, param.name)], componentName, reportNode);
33262
32989
  };
33263
32990
  return {
33264
32991
  FunctionDeclaration(node) {
@@ -33380,7 +33107,7 @@ const noMirrorPropEffect = defineRule({
33380
33107
  if (mirrorBindings.length === 0) return;
33381
33108
  for (const statement of componentBody.body ?? []) {
33382
33109
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
33383
- const effectCall = unwrapDiscardedExpression(statement);
33110
+ const effectCall = statement.expression;
33384
33111
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
33385
33112
  if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
33386
33113
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
@@ -33394,7 +33121,7 @@ const noMirrorPropEffect = defineRule({
33394
33121
  const bodyStatements = getCallbackStatements(callback);
33395
33122
  if (bodyStatements.length !== 1) continue;
33396
33123
  const onlyStatement = bodyStatements[0];
33397
- const expression = unwrapDiscardedExpression(onlyStatement);
33124
+ const expression = isNodeOfType(onlyStatement, "ExpressionStatement") ? onlyStatement.expression : onlyStatement;
33398
33125
  if (!isNodeOfType(expression, "CallExpression")) continue;
33399
33126
  if (!isNodeOfType(expression.callee, "Identifier")) continue;
33400
33127
  if (!isSetterIdentifier(expression.callee.name)) continue;
@@ -36010,7 +35737,7 @@ const noPreventDefault = defineRule({
36010
35737
  const isServerActionsFramework = hasCapability(context.settings, "server-actions");
36011
35738
  const formMessage = isServerActionsFramework ? FORM_MESSAGE_SERVER_CAPABLE : FORM_MESSAGE_GENERIC;
36012
35739
  return { JSXOpeningElement(node) {
36013
- const elementName = resolveJsxElementType(node);
35740
+ const elementName = isNodeOfType(node.name, "JSXIdentifier") ? node.name.name : null;
36014
35741
  if (!elementName) return;
36015
35742
  const targetEventProps = PREVENT_DEFAULT_ELEMENTS.get(elementName);
36016
35743
  if (!targetEventProps) return;
@@ -37495,7 +37222,7 @@ const isNonSettlingSetterArgument = (setterCall, stateName) => {
37495
37222
  return expressionReadsStateValue(argument, stateName);
37496
37223
  };
37497
37224
  const getUnconditionalSetterCall = (statement, setterNames) => {
37498
- const expression = unwrapDiscardedExpression(statement);
37225
+ const expression = stripParenExpression(isNodeOfType(statement, "ExpressionStatement") ? statement.expression : statement);
37499
37226
  if (!isNodeOfType(expression, "CallExpression")) return null;
37500
37227
  if (!isNodeOfType(expression.callee, "Identifier")) return null;
37501
37228
  if (!setterNames.has(expression.callee.name)) return null;
@@ -37844,7 +37571,7 @@ const noSelfUpdatingEffect = defineRule({
37844
37571
  const setterNames = new Set(setterNameToStateName.keys());
37845
37572
  for (const statement of functionBody.body ?? []) {
37846
37573
  if (!isNodeOfType(statement, "ExpressionStatement")) continue;
37847
- const effectCall = unwrapDiscardedExpression(statement);
37574
+ const effectCall = statement.expression;
37848
37575
  if (!isNodeOfType(effectCall, "CallExpression")) continue;
37849
37576
  if (!isHookCall$2(effectCall, EFFECT_HOOK_NAMES$1)) continue;
37850
37577
  if ((effectCall.arguments?.length ?? 0) < 2) continue;
@@ -38474,10 +38201,9 @@ const noStringFalseOnBooleanAttribute = defineRule({
38474
38201
  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.",
38475
38202
  create: (context) => ({ JSXOpeningElement(node) {
38476
38203
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
38477
- const elementName = resolveJsxElementType(node);
38478
- const firstCharacter = elementName.charCodeAt(0);
38204
+ const firstCharacter = node.name.name.charCodeAt(0);
38479
38205
  if (firstCharacter < 97 || firstCharacter > 122) return;
38480
- if (elementName.includes("-")) return;
38206
+ if (node.name.name.includes("-")) return;
38481
38207
  for (const attribute of node.attributes) {
38482
38208
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
38483
38209
  if (!isNodeOfType(attribute.name, "JSXIdentifier")) continue;
@@ -38865,7 +38591,8 @@ const noUncontrolledInput = defineRule({
38865
38591
  const undefinedInitialStateNames = isNodeOfType(componentBody, "BlockStatement") ? collectUndefinedInitialStateNames(componentBody) : /* @__PURE__ */ new Set();
38866
38592
  walkAst(componentBody, (child) => {
38867
38593
  if (!isNodeOfType(child, "JSXOpeningElement")) return;
38868
- const tagName = resolveJsxElementType(child);
38594
+ if (!isNodeOfType(child.name, "JSXIdentifier")) return;
38595
+ const tagName = child.name.name;
38869
38596
  if (!UNCONTROLLED_INPUT_TAGS.has(tagName)) return;
38870
38597
  const attributes = child.attributes ?? [];
38871
38598
  if (hasJsxSpreadAttribute(attributes)) return;
@@ -38926,7 +38653,7 @@ const noUndeferredThirdParty = defineRule({
38926
38653
  severity: "warn",
38927
38654
  recommendation: "Use `next/script` with `strategy=\"lazyOnload\"`, or add the `defer` attribute.",
38928
38655
  create: (context) => ({ JSXOpeningElement(node) {
38929
- if (resolveJsxElementType(node) !== "script") return;
38656
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "script") return;
38930
38657
  const attributes = node.attributes ?? [];
38931
38658
  const srcAttribute = findJsxAttribute(attributes, "src");
38932
38659
  if (!srcAttribute) return;
@@ -40141,7 +39868,7 @@ const noUnknownProperty = defineRule({
40141
39868
  }
40142
39869
  if (fileIsNonReactJsx) return;
40143
39870
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
40144
- const elementType = resolveJsxElementType(node);
39871
+ const elementType = node.name.name;
40145
39872
  const firstCharacter = elementType.charCodeAt(0);
40146
39873
  if (!(firstCharacter >= 97 && firstCharacter <= 122) || elementType === "fbt" || elementType === "fbs") return;
40147
39874
  let isValidHtmlTag = isKnownDomTag(elementType);
@@ -40319,21 +40046,20 @@ const expressionContainsJsxOrCreateElement = (root) => {
40319
40046
  });
40320
40047
  return found;
40321
40048
  };
40322
- const functionContainsJsxOrCreateElement = (functionNode, scopes) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement);
40323
40049
  const isReactClassComponent = (classNode) => {
40324
40050
  if (isEs6Component(classNode)) return true;
40325
40051
  return expressionContainsJsxOrCreateElement(classNode);
40326
40052
  };
40327
- const findEnclosingComponent = (node, scopes) => {
40053
+ const findEnclosingComponent = (node) => {
40328
40054
  let walker = node.parent;
40329
40055
  while (walker) {
40330
40056
  if (isFunctionLike$1(walker)) {
40331
40057
  const componentName = inferFunctionLikeName(walker);
40332
- if (componentName && isReactComponentName(componentName) && functionContainsJsxOrCreateElement(walker, scopes)) return {
40058
+ if (componentName && isReactComponentName(componentName) && expressionContainsJsxOrCreateElement(walker)) return {
40333
40059
  component: walker,
40334
40060
  name: componentName
40335
40061
  };
40336
- if (!componentName && functionContainsJsxOrCreateElement(walker, scopes) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40062
+ if (!componentName && expressionContainsJsxOrCreateElement(walker) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40337
40063
  component: walker,
40338
40064
  name: null
40339
40065
  };
@@ -40572,7 +40298,7 @@ const noUnstableNestedComponents = defineRule({
40572
40298
  if (renderPropRegex.test(propInfo.propName)) return;
40573
40299
  if (settings.allowAsProps) return;
40574
40300
  }
40575
- const enclosing = findEnclosingComponent(candidateNode, context.scopes);
40301
+ const enclosing = findEnclosingComponent(candidateNode);
40576
40302
  if (!enclosing) return;
40577
40303
  const gatedName = propInfo ? null : requiredInstantiationName;
40578
40304
  queuedReports.push({
@@ -40583,7 +40309,7 @@ const noUnstableNestedComponents = defineRule({
40583
40309
  });
40584
40310
  };
40585
40311
  const checkFunctionLike = (node) => {
40586
- if (!functionContainsJsxOrCreateElement(node, context.scopes)) return;
40312
+ if (!expressionContainsJsxOrCreateElement(node)) return;
40587
40313
  const inferredName = inferFunctionLikeName(node);
40588
40314
  const propInfo = isComponentDeclaredInProp(node);
40589
40315
  const isObjectCallback = isObjectCallbackCandidate(node);
@@ -41843,7 +41569,7 @@ const preactPreferOndblclick = defineRule({
41843
41569
  recommendation: "Rename `onDoubleClick` to `onDblClick` because Preact core listens for the DOM `dblclick` event name and `onDoubleClick` never fires.",
41844
41570
  create: (context) => ({ JSXOpeningElement(node) {
41845
41571
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
41846
- const tagName = resolveJsxElementType(node);
41572
+ const tagName = node.name.name;
41847
41573
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
41848
41574
  const onDoubleClickAttribute = findJsxAttribute(node.attributes, "onDoubleClick");
41849
41575
  if (!onDoubleClickAttribute) return;
@@ -41863,7 +41589,7 @@ const COMPAT_EXEMPT_INPUT_TYPES = new Set([
41863
41589
  ]);
41864
41590
  const isTextLikeInput = (openingElement) => {
41865
41591
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
41866
- const tagName = resolveJsxElementType(openingElement);
41592
+ const tagName = openingElement.name.name;
41867
41593
  if (tagName === "textarea") return true;
41868
41594
  if (tagName !== "input") return false;
41869
41595
  const typeAttribute = findJsxAttribute(openingElement.attributes, "type");
@@ -42020,9 +41746,8 @@ const CROSS_CUTTING_STATE_BOOLEAN_NAMES = new Set([
42020
41746
  ]);
42021
41747
  const collectBooleanPropBindings = (param) => {
42022
41748
  const bindings = /* @__PURE__ */ new Set();
42023
- const propsBinding = resolveFirstArgumentBinding(param);
42024
- if (!isNodeOfType(propsBinding, "ObjectPattern")) return bindings;
42025
- for (const property of propsBinding.properties ?? []) {
41749
+ if (!param || !isNodeOfType(param, "ObjectPattern")) return bindings;
41750
+ for (const property of param.properties ?? []) {
42026
41751
  if (!isNodeOfType(property, "Property")) continue;
42027
41752
  if (property.computed) continue;
42028
41753
  if (!isNodeOfType(property.key, "Identifier")) continue;
@@ -42283,7 +42008,7 @@ const preferHtmlDialog = defineRule({
42283
42008
  },
42284
42009
  JSXOpeningElement(node) {
42285
42010
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
42286
- const tagName = resolveJsxElementType(node);
42011
+ const tagName = node.name.name;
42287
42012
  if (tagName === "dialog") return;
42288
42013
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
42289
42014
  if (!HTML_TAGS.has(tagName)) return;
@@ -43830,13 +43555,13 @@ const resolveTanstackQueryHookName = (callExpression) => {
43830
43555
  }
43831
43556
  return null;
43832
43557
  };
43833
- const resolveHookNameFromInitializer = (initializer, scopes) => {
43558
+ const resolveHookNameFromInitializer = (initializer) => {
43834
43559
  if (isNodeOfType(initializer, "CallExpression")) return resolveTanstackQueryHookName(initializer);
43835
43560
  if (!isNodeOfType(initializer, "Identifier")) return null;
43836
- const resolvedSymbol = resolveConstIdentifierAlias(initializer, scopes);
43837
- if (resolvedSymbol?.kind !== "const" || !resolvedSymbol.initializer) return null;
43838
- if (!isNodeOfType(resolvedSymbol.initializer, "CallExpression")) return null;
43839
- return resolveTanstackQueryHookName(resolvedSymbol.initializer);
43561
+ const binding = findVariableInitializer(initializer, initializer.name);
43562
+ if (!binding?.initializer || !isConstDeclaredBinding(binding)) return null;
43563
+ if (!isNodeOfType(binding.initializer, "CallExpression")) return null;
43564
+ return resolveTanstackQueryHookName(binding.initializer);
43840
43565
  };
43841
43566
  const queryNoRestDestructuring = defineRule({
43842
43567
  id: "query-no-rest-destructuring",
@@ -43849,7 +43574,7 @@ const queryNoRestDestructuring = defineRule({
43849
43574
  if (!isNodeOfType(node.id, "ObjectPattern")) return;
43850
43575
  if (!node.init) return;
43851
43576
  if (!node.id.properties?.some((property) => isNodeOfType(property, "RestElement"))) return;
43852
- const hookName = resolveHookNameFromInitializer(node.init, context.scopes);
43577
+ const hookName = resolveHookNameFromInitializer(node.init);
43853
43578
  if (!hookName) return;
43854
43579
  context.report({
43855
43580
  node: node.id,
@@ -44320,7 +44045,7 @@ const renderingAnimateSvgWrapper = defineRule({
44320
44045
  severity: "warn",
44321
44046
  recommendation: "Wrap the SVG in a motion element so animation props apply to a stable wrapper instead of the SVG node itself.",
44322
44047
  create: (context) => ({ JSXOpeningElement(node) {
44323
- if (resolveJsxElementType(node) !== "svg") return;
44048
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "svg") return;
44324
44049
  if (node.attributes?.some((attribute) => isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && MOTION_ANIMATE_PROPS.has(attribute.name.name))) context.report({
44325
44050
  node,
44326
44051
  message: "This is slow to render because you animate <svg> directly, so wrap it in a <div> or <motion.div> & animate that instead"
@@ -45613,10 +45338,14 @@ const rerenderLazyStateInit = defineRule({
45613
45338
  //#endregion
45614
45339
  //#region src/plugin/rules/performance/rerender-memo-before-early-return.ts
45615
45340
  const isJsxExpression = (node) => Boolean(node && isJsxElementOrFragment(stripParenExpression(node)));
45616
- const callbackReturnsJsx = (callback, scopes) => {
45341
+ const callbackReturnsJsx = (callback) => {
45617
45342
  if (!callback) return false;
45618
45343
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
45619
- return functionReturnsMatchingExpression(callback, scopes, isJsxExpression);
45344
+ const body = callback.body;
45345
+ if (isJsxExpression(body)) return true;
45346
+ if (!isNodeOfType(body, "BlockStatement")) return false;
45347
+ for (const stmt of body.body ?? []) if (isNodeOfType(stmt, "ReturnStatement") && isJsxExpression(stmt.argument)) return true;
45348
+ return false;
45620
45349
  };
45621
45350
  const returnArgumentUsesAnyName = (returnStatement, names) => {
45622
45351
  if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
@@ -45694,7 +45423,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
45694
45423
  if (!isNodeOfType(stmt, "VariableDeclaration")) continue;
45695
45424
  for (const declarator of stmt.declarations ?? []) {
45696
45425
  const init = declarator.init;
45697
- if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0], context.scopes)) {
45426
+ if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0])) {
45698
45427
  memoNode = declarator;
45699
45428
  callbackGuardTests = collectLeadingCallbackGuardTests(init.arguments?.[0]);
45700
45429
  if (isNodeOfType(declarator.id, "Identifier")) memoConsumerNames.add(declarator.id.name);
@@ -45760,11 +45489,8 @@ const collectFromObjectPattern = (pattern, bindings) => {
45760
45489
  const collectDefaultedEmptyBindings = (functionNode) => {
45761
45490
  const bindings = /* @__PURE__ */ new Map();
45762
45491
  const params = functionNode.params ?? [];
45763
- for (const [parameterIndex, param] of params.entries()) {
45764
- const parameterBinding = parameterIndex === 0 ? resolveFirstArgumentBinding(param) : param;
45765
- if (parameterBinding) collectFromObjectPattern(parameterBinding, bindings);
45766
- }
45767
- const propsParam = resolveFirstArgumentBinding(params[0]);
45492
+ for (const param of params) collectFromObjectPattern(param, bindings);
45493
+ const propsParam = params[0];
45768
45494
  const body = functionNode.body;
45769
45495
  if (!propsParam || !isNodeOfType(propsParam, "Identifier") || !body) return bindings;
45770
45496
  if (!isNodeOfType(body, "BlockStatement")) return bindings;
@@ -46270,8 +45996,8 @@ const rnAnimationReactionAsDerived = defineRule({
46270
45996
  if (statements.length !== 1) return;
46271
45997
  const onlyStatement = statements[0];
46272
45998
  if (!isNodeOfType(onlyStatement, "ExpressionStatement")) return;
46273
- singleAssignment = unwrapDiscardedExpression(onlyStatement);
46274
- } else if (body) singleAssignment = unwrapDiscardedExpression(body);
45999
+ singleAssignment = onlyStatement.expression;
46000
+ } else if (body) singleAssignment = body;
46275
46001
  if (!singleAssignment) return;
46276
46002
  const isValueAssignment = isNodeOfType(singleAssignment, "AssignmentExpression") && isNodeOfType(singleAssignment.left, "MemberExpression") && isNodeOfType(singleAssignment.left.object, "Identifier") && isNodeOfType(singleAssignment.left.property, "Identifier") && singleAssignment.left.property.name === "value";
46277
46003
  const isSetCall = isNodeOfType(singleAssignment, "CallExpression") && isNodeOfType(singleAssignment.callee, "MemberExpression") && isNodeOfType(singleAssignment.callee.property, "Identifier") && singleAssignment.callee.property.name === "set" && (singleAssignment.arguments?.length ?? 0) === 1;
@@ -53878,6 +53604,10 @@ const isInvalidStyleExpression = (expression) => {
53878
53604
  }
53879
53605
  return false;
53880
53606
  };
53607
+ const getJsxOpeningElementName = (node) => {
53608
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
53609
+ return null;
53610
+ };
53881
53611
  const stylePropObject = defineRule({
53882
53612
  id: "style-prop-object",
53883
53613
  title: "Style prop is not an object",
@@ -53889,8 +53619,7 @@ const stylePropObject = defineRule({
53889
53619
  const allowSet = new Set(allow);
53890
53620
  return {
53891
53621
  JSXOpeningElement(node) {
53892
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
53893
- const elementName = resolveJsxElementType(node);
53622
+ const elementName = getJsxOpeningElementName(node);
53894
53623
  if (elementName && allowSet.has(elementName)) return;
53895
53624
  if (elementName) {
53896
53625
  const firstCharCode = elementName.charCodeAt(0);
@@ -54595,7 +54324,7 @@ const tanstackStartNoAnchorElement = defineRule({
54595
54324
  create: (context) => {
54596
54325
  if (!isInProjectDirectory(context, "routes")) return {};
54597
54326
  return { JSXOpeningElement(node) {
54598
- if (resolveJsxElementType(node) !== "a") return;
54327
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
54599
54328
  const attributes = node.attributes ?? [];
54600
54329
  const hrefAttribute = findJsxAttribute(attributes, "href");
54601
54330
  if (!hrefAttribute?.value) return;
@@ -55216,7 +54945,8 @@ const voidDomElementsNoChildren = defineRule({
55216
54945
  create: (context) => ({
55217
54946
  JSXElement(node) {
55218
54947
  const openingElement = node.openingElement;
55219
- const tagName = resolveJsxElementType(openingElement);
54948
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return;
54949
+ const tagName = openingElement.name.name;
55220
54950
  if (!VOID_DOM_ELEMENTS.has(tagName)) return;
55221
54951
  const hasChildrenContent = node.children.some(isMeaningfulJsxChild);
55222
54952
  const hasChildrenLikeProp = findChildrenLikePropName(openingElement.attributes);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oxlint-plugin-react-doctor",
3
- "version": "0.7.6-dev.c6f996e",
3
+ "version": "0.7.6-dev.dfd47a7",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",