oxlint-plugin-react-doctor 0.7.6-dev.f215ace → 0.7.6-dev.fbd85e3

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 +99 -190
  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) {
@@ -7459,7 +7433,7 @@ const findJsxAttribute = (attributes, attributeName) => attributes?.find((attrib
7459
7433
  const getOpeningElementTagName = (openingElement) => {
7460
7434
  if (!openingElement) return null;
7461
7435
  if (!isNodeOfType(openingElement, "JSXOpeningElement")) return null;
7462
- if (isNodeOfType(openingElement.name, "JSXIdentifier")) return resolveJsxElementType(openingElement);
7436
+ if (isNodeOfType(openingElement.name, "JSXIdentifier")) return openingElement.name.name;
7463
7437
  if (isNodeOfType(openingElement.name, "JSXMemberExpression")) {
7464
7438
  let cursor = openingElement.name;
7465
7439
  while (isNodeOfType(cursor, "JSXMemberExpression")) cursor = cursor.property;
@@ -7711,7 +7685,7 @@ const dialogHasAccessibleName = defineRule({
7711
7685
  if (isTestlikeFilename(context.filename)) return {};
7712
7686
  return { JSXOpeningElement(node) {
7713
7687
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
7714
- const tagName = resolveJsxElementType(node);
7688
+ const tagName = node.name.name;
7715
7689
  if (tagName[0] !== tagName[0]?.toLowerCase()) return;
7716
7690
  const roleAttribute = hasJsxPropIgnoreCase(node.attributes, "role");
7717
7691
  const roleValue = roleAttribute ? getJsxPropStringValue(roleAttribute) : null;
@@ -11943,7 +11917,7 @@ const forbidDomProps = defineRule({
11943
11917
  return { JSXOpeningElement(node) {
11944
11918
  if (forbidMap.size === 0) return;
11945
11919
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
11946
- const elementName = resolveJsxElementType(node);
11920
+ const elementName = node.name.name;
11947
11921
  if (isReactComponentName(elementName)) return;
11948
11922
  for (const attribute of node.attributes) {
11949
11923
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -12021,7 +11995,7 @@ const forbidElements = defineRule({
12021
11995
  return {
12022
11996
  JSXOpeningElement(node) {
12023
11997
  if (forbidMap.size === 0) return;
12024
- const fullName = resolveJsxElementType(node);
11998
+ const fullName = flattenJsxName$1(node.name);
12025
11999
  if (!fullName || !forbidMap.has(fullName)) return;
12026
12000
  context.report({
12027
12001
  node: node.name,
@@ -12383,7 +12357,8 @@ const buildMessage$22 = (childTagName) => `Your users get reshuffled HTML becaus
12383
12357
  const isParagraphElement = (candidate) => {
12384
12358
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12385
12359
  const opening = candidate.openingElement;
12386
- return resolveJsxElementType(opening) === "p";
12360
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12361
+ return opening.name.name === "p";
12387
12362
  };
12388
12363
  const findEnclosingParagraph = (openingElement) => {
12389
12364
  const owningElement = openingElement.parent;
@@ -12404,7 +12379,8 @@ const htmlNoInvalidParagraphChild = defineRule({
12404
12379
  severity: "warn",
12405
12380
  recommendation: "Swap the `<p>` for a `<div>`, or move the child outside the paragraph.",
12406
12381
  create: (context) => ({ JSXOpeningElement(node) {
12407
- const childTagName = resolveJsxElementType(node);
12382
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12383
+ const childTagName = node.name.name;
12408
12384
  if (!BLOCK_LEVEL_ELEMENTS.has(childTagName)) return;
12409
12385
  if (!findEnclosingParagraph(node)) return;
12410
12386
  context.report({
@@ -12435,7 +12411,7 @@ const getHostTagName = (jsxElement) => {
12435
12411
  if (!isNodeOfType(jsxElement, "JSXElement")) return null;
12436
12412
  const opening = jsxElement.openingElement;
12437
12413
  if (!isNodeOfType(opening.name, "JSXIdentifier")) return null;
12438
- const tagName = resolveJsxElementType(opening);
12414
+ const tagName = opening.name.name;
12439
12415
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return null;
12440
12416
  return tagName;
12441
12417
  };
@@ -12465,7 +12441,7 @@ const findClosestHostAncestor = (jsxElement) => {
12465
12441
  if (isNodeOfType(ancestor, "JSXElement")) {
12466
12442
  const opening = ancestor.openingElement;
12467
12443
  if (isNodeOfType(opening.name, "JSXIdentifier")) {
12468
- const ancestorTag = resolveJsxElementType(opening);
12444
+ const ancestorTag = opening.name.name;
12469
12445
  if (ancestorTag.length === 0) {
12470
12446
  previous = ancestor;
12471
12447
  ancestor = ancestor.parent ?? null;
@@ -12547,7 +12523,8 @@ const buildMessage$20 = (tagName) => `Your users get broken clicks, focus & scre
12547
12523
  const isJsxElementWithTagName = (candidate, tagName) => {
12548
12524
  if (!isNodeOfType(candidate, "JSXElement")) return false;
12549
12525
  const opening = candidate.openingElement;
12550
- return resolveJsxElementType(opening) === tagName;
12526
+ if (!isNodeOfType(opening.name, "JSXIdentifier")) return false;
12527
+ return opening.name.name === tagName;
12551
12528
  };
12552
12529
  const findEnclosingSameTag = (openingElement, tagName) => {
12553
12530
  const owningElement = openingElement.parent;
@@ -12568,7 +12545,8 @@ const htmlNoNestedInteractive = defineRule({
12568
12545
  severity: "warn",
12569
12546
  recommendation: "Move the inner `<a>` or `<button>` so it's a sibling, or change the outer one to a plain `<div>` or `<span>`.",
12570
12547
  create: (context) => ({ JSXOpeningElement(node) {
12571
- const tagName = resolveJsxElementType(node);
12548
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
12549
+ const tagName = node.name.name;
12572
12550
  if (tagName !== "a" && tagName !== "button") return;
12573
12551
  if (!findEnclosingSameTag(node, tagName)) return;
12574
12552
  context.report({
@@ -12683,7 +12661,7 @@ const iframeMissingSandbox = defineRule({
12683
12661
  matchByOccurrence: true,
12684
12662
  create: skipNonProductionFiles((context) => ({
12685
12663
  JSXOpeningElement(node) {
12686
- if (resolveJsxElementType(node) !== "iframe") return;
12664
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "iframe") return;
12687
12665
  const sandboxAttr = hasJsxPropIgnoreCase(node.attributes, "sandbox");
12688
12666
  if (!sandboxAttr) {
12689
12667
  const hasExplicitSrc = Boolean(hasJsxPropIgnoreCase(node.attributes, "src"));
@@ -14611,18 +14589,6 @@ const jsHoistRegexp = defineRule({
14611
14589
  }, { treatIteratorCallbacksAsLoops: true })
14612
14590
  });
14613
14591
  //#endregion
14614
- //#region src/plugin/utils/resolve-first-argument-binding.ts
14615
- const resolveFirstArgumentBinding = (firstParameter) => {
14616
- if (!firstParameter) return null;
14617
- if (!isNodeOfType(firstParameter, "RestElement")) return firstParameter;
14618
- if (!isNodeOfType(firstParameter.argument, "ArrayPattern")) return null;
14619
- const elements = firstParameter.argument.elements ?? [];
14620
- if (elements.length !== 1) return null;
14621
- const firstBinding = elements[0];
14622
- if (!firstBinding || isNodeOfType(firstBinding, "RestElement")) return null;
14623
- return firstBinding;
14624
- };
14625
- //#endregion
14626
14592
  //#region src/plugin/rules/js-performance/js-index-maps.ts
14627
14593
  const referencesParameter = (expression, parameterName) => {
14628
14594
  if (!expression) return false;
@@ -14633,7 +14599,7 @@ const referencesParameter = (expression, parameterName) => {
14633
14599
  const isSingleFieldEqualityPredicate = (node) => {
14634
14600
  const callback = node.arguments?.[0];
14635
14601
  if (!isInlineFunctionExpression(callback)) return false;
14636
- const firstParameter = resolveFirstArgumentBinding(callback.params?.[0]);
14602
+ const firstParameter = callback.params?.[0];
14637
14603
  if (!firstParameter || !isNodeOfType(firstParameter, "Identifier")) return false;
14638
14604
  let predicate = null;
14639
14605
  const body = callback.body;
@@ -15325,21 +15291,9 @@ const isSmallFixedListMember = (receiver) => {
15325
15291
  };
15326
15292
  const getResolvedInitializer = (receiver) => {
15327
15293
  if (!isNodeOfType(receiver, "Identifier")) return null;
15328
- const binding = findVariableInitializer(receiver, receiver.name);
15329
- const initializer = binding?.initializer ?? null;
15330
- if (!binding || !initializer) return null;
15331
- const isDefault = isNodeOfType(binding.bindingIdentifier.parent, "AssignmentPattern");
15332
- if (isNodeOfType(initializer, "Identifier")) {
15333
- const aliased = findVariableInitializer(initializer, initializer.name);
15334
- if (aliased?.initializer) return {
15335
- initializer: aliased.initializer,
15336
- isDefault: isDefault || isNodeOfType(aliased.bindingIdentifier.parent, "AssignmentPattern")
15337
- };
15338
- }
15339
- return {
15340
- initializer,
15341
- isDefault
15342
- };
15294
+ const initializer = findVariableInitializer(receiver, receiver.name)?.initializer ?? null;
15295
+ if (initializer && isNodeOfType(initializer, "Identifier")) return findVariableInitializer(initializer, initializer.name)?.initializer ?? initializer;
15296
+ return initializer;
15343
15297
  };
15344
15298
  const isSplitCall = (expression) => {
15345
15299
  if (!expression) return false;
@@ -15505,8 +15459,8 @@ const isBoundedConstantCollection = (collection) => {
15505
15459
  if (isScreamingSnakeCaseConstantReceiver(stripped)) return true;
15506
15460
  if (isSmallInlineLiteralArray(stripped)) return true;
15507
15461
  if (isNodeOfType(stripped, "Identifier")) {
15508
- const resolved = getResolvedInitializer(stripped);
15509
- if (resolved && !resolved.isDefault && isSmallInlineLiteralArray(resolved.initializer)) return true;
15462
+ const initializer = getResolvedInitializer(stripped);
15463
+ if (initializer && isSmallInlineLiteralArray(initializer)) return true;
15510
15464
  }
15511
15465
  return false;
15512
15466
  };
@@ -15558,8 +15512,8 @@ const jsSetMapLookups = defineRule({
15558
15512
  if (isIndexedArrayElementWithStringArgument(receiver, node.arguments?.[0])) return;
15559
15513
  const resolvedInitializer = getResolvedInitializer(receiver);
15560
15514
  if (resolvedInitializer) {
15561
- if (isLikelyStringReceiver(resolvedInitializer.initializer)) return;
15562
- if (!resolvedInitializer.isDefault && isSmallInlineLiteralArray(resolvedInitializer.initializer)) return;
15515
+ if (isLikelyStringReceiver(resolvedInitializer)) return;
15516
+ if (isSmallInlineLiteralArray(resolvedInitializer)) return;
15563
15517
  }
15564
15518
  if (isStringElementOfSplitIteration(receiver)) return;
15565
15519
  if (isReceiverDeclaredInNearestLoop(receiver, node)) return;
@@ -18686,6 +18640,10 @@ const resolveSettings$29 = (settings) => {
18686
18640
  if (typeof reactDoctor !== "object" || reactDoctor === null) return {};
18687
18641
  return reactDoctor.jsxNoScriptUrl ?? {};
18688
18642
  };
18643
+ const getElementName = (node) => {
18644
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
18645
+ return null;
18646
+ };
18689
18647
  const isLinkPropForElement = (elementName, attributeName, options) => {
18690
18648
  if (elementName === "a" && attributeName === "href") return true;
18691
18649
  const explicit = options.components?.[elementName];
@@ -18705,8 +18663,7 @@ const jsxNoScriptUrl = defineRule({
18705
18663
  create: (context) => {
18706
18664
  const options = resolveSettings$29(context.settings);
18707
18665
  return { JSXOpeningElement(node) {
18708
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
18709
- const elementName = resolveJsxElementType(node);
18666
+ const elementName = getElementName(node);
18710
18667
  if (!elementName) return;
18711
18668
  for (const attribute of node.attributes) {
18712
18669
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
@@ -18893,6 +18850,11 @@ const checkTarget = (attributeValue) => {
18893
18850
  alternate: false
18894
18851
  };
18895
18852
  };
18853
+ const getOpeningElementName = (node) => {
18854
+ const name = node.name;
18855
+ if (isNodeOfType(name, "JSXIdentifier")) return name.name;
18856
+ return null;
18857
+ };
18896
18858
  const jsxNoTargetBlank = defineRule({
18897
18859
  id: "jsx-no-target-blank",
18898
18860
  title: "Unsafe target=_blank link",
@@ -18912,8 +18874,7 @@ const jsxNoTargetBlank = defineRule({
18912
18874
  return settings.formComponents.has(tagName);
18913
18875
  };
18914
18876
  return { JSXOpeningElement(node) {
18915
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
18916
- const tagName = resolveJsxElementType(node);
18877
+ const tagName = getOpeningElementName(node);
18917
18878
  if (!tagName) return;
18918
18879
  if (!isLink(tagName) && !isForm(tagName)) return;
18919
18880
  const linkAttributeNames = settings.linkComponents.get(tagName) ?? ["href"];
@@ -20388,6 +20349,9 @@ const hasDirective = (programNode, directive) => {
20388
20349
  return Boolean(programNode.body?.some((statement) => isNodeOfType(statement, "ExpressionStatement") && isNodeOfType(statement.expression, "Literal") && statement.expression.value === directive));
20389
20350
  };
20390
20351
  //#endregion
20352
+ //#region src/plugin/utils/is-uppercase-name.ts
20353
+ const isUppercaseName = (name) => UPPERCASE_PATTERN.test(name);
20354
+ //#endregion
20391
20355
  //#region src/plugin/utils/is-component-assignment.ts
20392
20356
  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"));
20393
20357
  //#endregion
@@ -20720,7 +20684,7 @@ const nextjsNoAElement = defineRule({
20720
20684
  severity: "warn",
20721
20685
  recommendation: "`import Link from 'next/link'` for client-side navigation, prefetching, and preserved scroll position",
20722
20686
  create: (context) => ({ JSXOpeningElement(node) {
20723
- if (resolveJsxElementType(node) !== "a") return;
20687
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
20724
20688
  const attributes = node.attributes ?? [];
20725
20689
  const downloadAttribute = findJsxAttribute(attributes, "download");
20726
20690
  if (downloadAttribute) {
@@ -20916,7 +20880,7 @@ const nextjsNoCssLink = defineRule({
20916
20880
  severity: "warn",
20917
20881
  recommendation: "Import CSS directly or use CSS Modules so Next.js can bundle, order, and optimize the stylesheet.",
20918
20882
  create: (context) => ({ JSXOpeningElement(node) {
20919
- if (resolveJsxElementType(node) !== "link") return;
20883
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
20920
20884
  const attributes = node.attributes ?? [];
20921
20885
  const relAttribute = findJsxAttribute(attributes, "rel");
20922
20886
  if (!relAttribute?.value) return;
@@ -21024,7 +20988,7 @@ const nextjsNoFontLink = defineRule({
21024
20988
  severity: "warn",
21025
20989
  recommendation: "`import { Inter } from \"next/font/google\"` for self-hosting, zero layout shift, and no render-blocking requests",
21026
20990
  create: (context) => ({ JSXOpeningElement(node) {
21027
- if (resolveJsxElementType(node) !== "link") return;
20991
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "link") return;
21028
20992
  const attributes = node.attributes ?? [];
21029
20993
  const hrefAttribute = findJsxAttribute(attributes, "href");
21030
20994
  if (!hrefAttribute?.value) return;
@@ -21199,7 +21163,7 @@ const nextjsNoImgElement = defineRule({
21199
21163
  create: (context) => {
21200
21164
  if (isGeneratedImageRenderContext(context)) return {};
21201
21165
  return { JSXOpeningElement(node) {
21202
- if (resolveJsxElementType(node) !== "img") return;
21166
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
21203
21167
  if (isGeneratedImageRenderContext(context, node)) return;
21204
21168
  const programRoot = findProgramRoot(node);
21205
21169
  if (programRoot && hasEmailTemplateImport(programRoot)) return;
@@ -21246,8 +21210,8 @@ const nextjsNoPolyfillScript = defineRule({
21246
21210
  severity: "warn",
21247
21211
  recommendation: "Next.js includes polyfills for fetch, Promise, Object.assign, Array.from, and 50+ others automatically",
21248
21212
  create: (context) => ({ JSXOpeningElement(node) {
21249
- const elementName = resolveJsxElementType(node);
21250
- if (elementName !== "script" && elementName !== "Script") return;
21213
+ if (!isNodeOfType(node.name, "JSXIdentifier")) return;
21214
+ if (node.name.name !== "script" && node.name.name !== "Script") return;
21251
21215
  const srcAttribute = findJsxAttribute(node.attributes ?? [], "src");
21252
21216
  if (!srcAttribute?.value) return;
21253
21217
  const srcValue = isNodeOfType(srcAttribute.value, "Literal") ? srcAttribute.value.value : null;
@@ -23055,15 +23019,10 @@ const isProp = (analysis, ref) => Boolean(ref.resolved?.defs.some((def) => {
23055
23019
  if (!declaringNode) return false;
23056
23020
  return isReactFunctionalComponent(declaringNode) && !isReactFunctionalHOC(analysis, declaringNode) || isCustomHook(declaringNode);
23057
23021
  }));
23058
- const isWholePropsParameterBinding = (bindingNode) => {
23059
- if (isFunctionLike$1(bindingNode.parent)) return true;
23060
- let declaringFunction = bindingNode.parent;
23061
- while (declaringFunction && !isFunctionLike$1(declaringFunction)) declaringFunction = declaringFunction.parent;
23062
- return Boolean(declaringFunction && resolveFirstArgumentBinding(declaringFunction.params?.[0]) === bindingNode);
23063
- };
23064
23022
  const isWholePropsObjectReference = (analysis, ref) => isProp(analysis, ref) && Boolean(ref.resolved?.defs.some((def) => {
23065
23023
  if (def.type !== "Parameter") return false;
23066
- return isWholePropsParameterBinding(def.name);
23024
+ const bindingParent = def.name.parent;
23025
+ return isFunctionLike$1(bindingParent);
23067
23026
  }));
23068
23027
  const isIdentifierOrMemberExpression = (node) => isNodeOfType(node, "Identifier") || isNodeOfType(node, "MemberExpression");
23069
23028
  const isPropAlias = (analysis, ref) => {
@@ -26060,56 +26019,6 @@ const noBarrelImport = defineRule({
26060
26019
  }
26061
26020
  });
26062
26021
  //#endregion
26063
- //#region src/plugin/utils/function-returns-matching-expression.ts
26064
- const collectReturnedExpressions = (functionNode) => {
26065
- if (!isFunctionLike$1(functionNode)) return [];
26066
- const body = functionNode.body;
26067
- if (!body) return [];
26068
- if (!isNodeOfType(body, "BlockStatement")) return [body];
26069
- const returnedExpressions = [];
26070
- walkAst(body, (node) => {
26071
- if (node !== body && (isFunctionLike$1(node) || isNodeOfType(node, "ClassDeclaration") || isNodeOfType(node, "ClassExpression"))) return false;
26072
- if (isNodeOfType(node, "ReturnStatement") && node.argument) returnedExpressions.push(node.argument);
26073
- });
26074
- return returnedExpressions;
26075
- };
26076
- const functionReturnsMatchingExpression = (functionNode, scopes, matchesExpression) => {
26077
- const visitedExpressions = /* @__PURE__ */ new Set();
26078
- const visitedFunctions = /* @__PURE__ */ new Set();
26079
- const functionMatches = (candidateFunction) => {
26080
- if (visitedFunctions.has(candidateFunction)) return false;
26081
- visitedFunctions.add(candidateFunction);
26082
- return collectReturnedExpressions(candidateFunction).some(expressionMatches);
26083
- };
26084
- const expressionMatches = (expression) => {
26085
- const unwrappedExpression = stripParenExpression(expression);
26086
- if (visitedExpressions.has(unwrappedExpression)) return false;
26087
- visitedExpressions.add(unwrappedExpression);
26088
- if (matchesExpression(unwrappedExpression)) return true;
26089
- if (isNodeOfType(unwrappedExpression, "Identifier")) {
26090
- const symbol = scopes.symbolFor(unwrappedExpression);
26091
- if (!symbol || symbol.kind !== "const" || !symbol.initializer) return false;
26092
- const initializer = stripParenExpression(symbol.initializer);
26093
- if (isFunctionLike$1(initializer)) return false;
26094
- return expressionMatches(initializer);
26095
- }
26096
- if (isNodeOfType(unwrappedExpression, "CallExpression")) {
26097
- if (unwrappedExpression.arguments.length !== 0) return false;
26098
- if (!isNodeOfType(unwrappedExpression.callee, "Identifier")) return false;
26099
- const symbol = scopes.symbolFor(unwrappedExpression.callee);
26100
- if (!symbol || symbol.kind !== "const" && symbol.kind !== "function") return false;
26101
- const initializer = symbol.initializer ? stripParenExpression(symbol.initializer) : null;
26102
- const candidateFunction = isFunctionLike$1(initializer) ? initializer : isFunctionLike$1(symbol.declarationNode) ? symbol.declarationNode : null;
26103
- if (!candidateFunction || candidateFunction.async || candidateFunction.generator || candidateFunction.params.length !== 0) return false;
26104
- return functionMatches(candidateFunction);
26105
- }
26106
- if (isNodeOfType(unwrappedExpression, "ConditionalExpression")) return expressionMatches(unwrappedExpression.consequent) || expressionMatches(unwrappedExpression.alternate);
26107
- if (isNodeOfType(unwrappedExpression, "LogicalExpression")) return expressionMatches(unwrappedExpression.left) || expressionMatches(unwrappedExpression.right);
26108
- return false;
26109
- };
26110
- return functionMatches(functionNode);
26111
- };
26112
- //#endregion
26113
26022
  //#region src/plugin/utils/function-contains-react-render-output.ts
26114
26023
  const NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES = new Set([
26115
26024
  "FunctionDeclaration",
@@ -26129,13 +26038,12 @@ const isCallArgumentFunctionExpression = (node) => {
26129
26038
  return parent.arguments.some((argumentNode) => argumentNode === node);
26130
26039
  };
26131
26040
  const isNestedRenderEvidenceBoundary = (node) => NESTED_RENDER_EVIDENCE_BOUNDARY_TYPES.has(node.type) && !isCallArgumentFunctionExpression(node);
26132
- const isRenderOutputExpression = (node, scopes) => node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS);
26133
26041
  const containsRenderOutput$1 = (rootNode, scopes) => {
26134
26042
  let hasRenderOutput = false;
26135
26043
  walkAst(rootNode, (node) => {
26136
26044
  if (hasRenderOutput) return false;
26137
26045
  if (node !== rootNode && isNestedRenderEvidenceBoundary(node)) return false;
26138
- if (isRenderOutputExpression(node, scopes)) {
26046
+ if (node.type === "JSXElement" || node.type === "JSXFragment" || isReactApiCall(node, "createElement", scopes, REACT_CREATE_ELEMENT_OPTIONS)) {
26139
26047
  hasRenderOutput = true;
26140
26048
  return false;
26141
26049
  }
@@ -26146,7 +26054,7 @@ const renderOutputCache = /* @__PURE__ */ new WeakMap();
26146
26054
  const functionContainsReactRenderOutput = (functionNode, scopes) => {
26147
26055
  const cachedEntry = renderOutputCache.get(functionNode);
26148
26056
  if (cachedEntry && cachedEntry.scopes === scopes) return cachedEntry.hasRenderOutput;
26149
- const hasRenderOutput = containsRenderOutput$1(functionNode, scopes) || functionReturnsMatchingExpression(functionNode, scopes, (expression) => isRenderOutputExpression(expression, scopes));
26057
+ const hasRenderOutput = containsRenderOutput$1(functionNode, scopes);
26150
26058
  renderOutputCache.set(functionNode, {
26151
26059
  scopes,
26152
26060
  hasRenderOutput
@@ -28611,7 +28519,7 @@ const noDisabledZoom = defineRule({
28611
28519
  category: "Accessibility",
28612
28520
  recommendation: "Remove `user-scalable=no` and `maximum-scale` from the viewport meta tag. If the layout breaks at 200% zoom, fix the layout instead.",
28613
28521
  create: (context) => ({ JSXOpeningElement(node) {
28614
- if (resolveJsxElementType(node) !== "meta") return;
28522
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "meta") return;
28615
28523
  const nameAttr = findJsxAttribute(node.attributes ?? [], "name");
28616
28524
  if (!nameAttr?.value) return;
28617
28525
  if ((isNodeOfType(nameAttr.value, "Literal") ? nameAttr.value.value : null) !== "viewport") return;
@@ -31783,7 +31691,7 @@ const noImgLazyWithHighFetchpriority = defineRule({
31783
31691
  severity: "warn",
31784
31692
  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.",
31785
31693
  create: (context) => ({ JSXOpeningElement(node) {
31786
- if (resolveJsxElementType(node) !== "img") return;
31694
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "img") return;
31787
31695
  const loadingAttribute = hasJsxPropIgnoreCase(node.attributes, "loading");
31788
31696
  if (!loadingAttribute || getJsxPropStringValue(loadingAttribute)?.toLowerCase() !== "lazy") return;
31789
31697
  const fetchPriorityAttribute = hasJsxPropIgnoreCase(node.attributes, "fetchPriority");
@@ -32024,7 +31932,7 @@ const noIndeterminateAttribute = defineRule({
32024
31932
  if (classifyReactNativeFileTarget(context) === "react-native") return {};
32025
31933
  return {
32026
31934
  JSXOpeningElement(node) {
32027
- if (resolveJsxElementType(node) !== "input") return;
31935
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "input") return;
32028
31936
  let typeAttribute = null;
32029
31937
  let typeAttributeIndex = null;
32030
31938
  let indeterminateAttribute = null;
@@ -33096,13 +33004,11 @@ const noManyBooleanProps = defineRule({
33096
33004
  };
33097
33005
  const checkComponent = (functionNode, param, body, componentName, reportNode) => {
33098
33006
  if (!param) return;
33099
- const propsBinding = resolveFirstArgumentBinding(param);
33100
- if (!propsBinding) return;
33101
33007
  if (!functionContainsReactRenderOutput(functionNode, context.scopes)) return;
33102
- if (isNodeOfType(propsBinding, "ObjectPattern")) {
33008
+ if (isNodeOfType(param, "ObjectPattern")) {
33103
33009
  const callbackUsedNames = collectCallbackUsedNames(body, param, context.scopes);
33104
33010
  const booleanLikePropNames = [];
33105
- for (const property of propsBinding.properties ?? []) {
33011
+ for (const property of param.properties ?? []) {
33106
33012
  if (!isNodeOfType(property, "Property")) continue;
33107
33013
  const keyName = isNodeOfType(property.key, "Identifier") ? property.key.name : null;
33108
33014
  if (!keyName) continue;
@@ -33113,7 +33019,7 @@ const noManyBooleanProps = defineRule({
33113
33019
  reportIfMany(booleanLikePropNames, componentName, reportNode);
33114
33020
  return;
33115
33021
  }
33116
- if (isNodeOfType(propsBinding, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, propsBinding.name)], componentName, reportNode);
33022
+ if (isNodeOfType(param, "Identifier")) reportIfMany([...collectBooleanLikePropsFromBody(body, param.name)], componentName, reportNode);
33117
33023
  };
33118
33024
  return {
33119
33025
  FunctionDeclaration(node) {
@@ -35865,7 +35771,7 @@ const noPreventDefault = defineRule({
35865
35771
  const isServerActionsFramework = hasCapability(context.settings, "server-actions");
35866
35772
  const formMessage = isServerActionsFramework ? FORM_MESSAGE_SERVER_CAPABLE : FORM_MESSAGE_GENERIC;
35867
35773
  return { JSXOpeningElement(node) {
35868
- const elementName = resolveJsxElementType(node);
35774
+ const elementName = isNodeOfType(node.name, "JSXIdentifier") ? node.name.name : null;
35869
35775
  if (!elementName) return;
35870
35776
  const targetEventProps = PREVENT_DEFAULT_ELEMENTS.get(elementName);
35871
35777
  if (!targetEventProps) return;
@@ -38329,10 +38235,9 @@ const noStringFalseOnBooleanAttribute = defineRule({
38329
38235
  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.",
38330
38236
  create: (context) => ({ JSXOpeningElement(node) {
38331
38237
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
38332
- const elementName = resolveJsxElementType(node);
38333
- const firstCharacter = elementName.charCodeAt(0);
38238
+ const firstCharacter = node.name.name.charCodeAt(0);
38334
38239
  if (firstCharacter < 97 || firstCharacter > 122) return;
38335
- if (elementName.includes("-")) return;
38240
+ if (node.name.name.includes("-")) return;
38336
38241
  for (const attribute of node.attributes) {
38337
38242
  if (!isNodeOfType(attribute, "JSXAttribute")) continue;
38338
38243
  if (!isNodeOfType(attribute.name, "JSXIdentifier")) continue;
@@ -38720,7 +38625,8 @@ const noUncontrolledInput = defineRule({
38720
38625
  const undefinedInitialStateNames = isNodeOfType(componentBody, "BlockStatement") ? collectUndefinedInitialStateNames(componentBody) : /* @__PURE__ */ new Set();
38721
38626
  walkAst(componentBody, (child) => {
38722
38627
  if (!isNodeOfType(child, "JSXOpeningElement")) return;
38723
- const tagName = resolveJsxElementType(child);
38628
+ if (!isNodeOfType(child.name, "JSXIdentifier")) return;
38629
+ const tagName = child.name.name;
38724
38630
  if (!UNCONTROLLED_INPUT_TAGS.has(tagName)) return;
38725
38631
  const attributes = child.attributes ?? [];
38726
38632
  if (hasJsxSpreadAttribute(attributes)) return;
@@ -38781,7 +38687,7 @@ const noUndeferredThirdParty = defineRule({
38781
38687
  severity: "warn",
38782
38688
  recommendation: "Use `next/script` with `strategy=\"lazyOnload\"`, or add the `defer` attribute.",
38783
38689
  create: (context) => ({ JSXOpeningElement(node) {
38784
- if (resolveJsxElementType(node) !== "script") return;
38690
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "script") return;
38785
38691
  const attributes = node.attributes ?? [];
38786
38692
  const srcAttribute = findJsxAttribute(attributes, "src");
38787
38693
  if (!srcAttribute) return;
@@ -39996,7 +39902,7 @@ const noUnknownProperty = defineRule({
39996
39902
  }
39997
39903
  if (fileIsNonReactJsx) return;
39998
39904
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
39999
- const elementType = resolveJsxElementType(node);
39905
+ const elementType = node.name.name;
40000
39906
  const firstCharacter = elementType.charCodeAt(0);
40001
39907
  if (!(firstCharacter >= 97 && firstCharacter <= 122) || elementType === "fbt" || elementType === "fbs") return;
40002
39908
  let isValidHtmlTag = isKnownDomTag(elementType);
@@ -40174,21 +40080,20 @@ const expressionContainsJsxOrCreateElement = (root) => {
40174
40080
  });
40175
40081
  return found;
40176
40082
  };
40177
- const functionContainsJsxOrCreateElement = (functionNode, scopes) => expressionContainsJsxOrCreateElement(functionNode) || functionReturnsMatchingExpression(functionNode, scopes, expressionContainsJsxOrCreateElement);
40178
40083
  const isReactClassComponent = (classNode) => {
40179
40084
  if (isEs6Component(classNode)) return true;
40180
40085
  return expressionContainsJsxOrCreateElement(classNode);
40181
40086
  };
40182
- const findEnclosingComponent = (node, scopes) => {
40087
+ const findEnclosingComponent = (node) => {
40183
40088
  let walker = node.parent;
40184
40089
  while (walker) {
40185
40090
  if (isFunctionLike$1(walker)) {
40186
40091
  const componentName = inferFunctionLikeName(walker);
40187
- if (componentName && isReactComponentName(componentName) && functionContainsJsxOrCreateElement(walker, scopes)) return {
40092
+ if (componentName && isReactComponentName(componentName) && expressionContainsJsxOrCreateElement(walker)) return {
40188
40093
  component: walker,
40189
40094
  name: componentName
40190
40095
  };
40191
- if (!componentName && functionContainsJsxOrCreateElement(walker, scopes) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40096
+ if (!componentName && expressionContainsJsxOrCreateElement(walker) && walker.parent && isNodeOfType(walker.parent, "ExportDefaultDeclaration")) return {
40192
40097
  component: walker,
40193
40098
  name: null
40194
40099
  };
@@ -40427,7 +40332,7 @@ const noUnstableNestedComponents = defineRule({
40427
40332
  if (renderPropRegex.test(propInfo.propName)) return;
40428
40333
  if (settings.allowAsProps) return;
40429
40334
  }
40430
- const enclosing = findEnclosingComponent(candidateNode, context.scopes);
40335
+ const enclosing = findEnclosingComponent(candidateNode);
40431
40336
  if (!enclosing) return;
40432
40337
  const gatedName = propInfo ? null : requiredInstantiationName;
40433
40338
  queuedReports.push({
@@ -40438,7 +40343,7 @@ const noUnstableNestedComponents = defineRule({
40438
40343
  });
40439
40344
  };
40440
40345
  const checkFunctionLike = (node) => {
40441
- if (!functionContainsJsxOrCreateElement(node, context.scopes)) return;
40346
+ if (!expressionContainsJsxOrCreateElement(node)) return;
40442
40347
  const inferredName = inferFunctionLikeName(node);
40443
40348
  const propInfo = isComponentDeclaredInProp(node);
40444
40349
  const isObjectCallback = isObjectCallbackCandidate(node);
@@ -41698,7 +41603,7 @@ const preactPreferOndblclick = defineRule({
41698
41603
  recommendation: "Rename `onDoubleClick` to `onDblClick` because Preact core listens for the DOM `dblclick` event name and `onDoubleClick` never fires.",
41699
41604
  create: (context) => ({ JSXOpeningElement(node) {
41700
41605
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
41701
- const tagName = resolveJsxElementType(node);
41606
+ const tagName = node.name.name;
41702
41607
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
41703
41608
  const onDoubleClickAttribute = findJsxAttribute(node.attributes, "onDoubleClick");
41704
41609
  if (!onDoubleClickAttribute) return;
@@ -41718,7 +41623,7 @@ const COMPAT_EXEMPT_INPUT_TYPES = new Set([
41718
41623
  ]);
41719
41624
  const isTextLikeInput = (openingElement) => {
41720
41625
  if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return false;
41721
- const tagName = resolveJsxElementType(openingElement);
41626
+ const tagName = openingElement.name.name;
41722
41627
  if (tagName === "textarea") return true;
41723
41628
  if (tagName !== "input") return false;
41724
41629
  const typeAttribute = findJsxAttribute(openingElement.attributes, "type");
@@ -41875,9 +41780,8 @@ const CROSS_CUTTING_STATE_BOOLEAN_NAMES = new Set([
41875
41780
  ]);
41876
41781
  const collectBooleanPropBindings = (param) => {
41877
41782
  const bindings = /* @__PURE__ */ new Set();
41878
- const propsBinding = resolveFirstArgumentBinding(param);
41879
- if (!isNodeOfType(propsBinding, "ObjectPattern")) return bindings;
41880
- for (const property of propsBinding.properties ?? []) {
41783
+ if (!param || !isNodeOfType(param, "ObjectPattern")) return bindings;
41784
+ for (const property of param.properties ?? []) {
41881
41785
  if (!isNodeOfType(property, "Property")) continue;
41882
41786
  if (property.computed) continue;
41883
41787
  if (!isNodeOfType(property.key, "Identifier")) continue;
@@ -42138,7 +42042,7 @@ const preferHtmlDialog = defineRule({
42138
42042
  },
42139
42043
  JSXOpeningElement(node) {
42140
42044
  if (!isNodeOfType(node.name, "JSXIdentifier")) return;
42141
- const tagName = resolveJsxElementType(node);
42045
+ const tagName = node.name.name;
42142
42046
  if (tagName === "dialog") return;
42143
42047
  if (tagName.length === 0 || tagName[0] !== tagName[0].toLowerCase()) return;
42144
42048
  if (!HTML_TAGS.has(tagName)) return;
@@ -44175,7 +44079,7 @@ const renderingAnimateSvgWrapper = defineRule({
44175
44079
  severity: "warn",
44176
44080
  recommendation: "Wrap the SVG in a motion element so animation props apply to a stable wrapper instead of the SVG node itself.",
44177
44081
  create: (context) => ({ JSXOpeningElement(node) {
44178
- if (resolveJsxElementType(node) !== "svg") return;
44082
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "svg") return;
44179
44083
  if (node.attributes?.some((attribute) => isNodeOfType(attribute, "JSXAttribute") && isNodeOfType(attribute.name, "JSXIdentifier") && MOTION_ANIMATE_PROPS.has(attribute.name.name))) context.report({
44180
44084
  node,
44181
44085
  message: "This is slow to render because you animate <svg> directly, so wrap it in a <div> or <motion.div> & animate that instead"
@@ -45468,10 +45372,14 @@ const rerenderLazyStateInit = defineRule({
45468
45372
  //#endregion
45469
45373
  //#region src/plugin/rules/performance/rerender-memo-before-early-return.ts
45470
45374
  const isJsxExpression = (node) => Boolean(node && isJsxElementOrFragment(stripParenExpression(node)));
45471
- const callbackReturnsJsx = (callback, scopes) => {
45375
+ const callbackReturnsJsx = (callback) => {
45472
45376
  if (!callback) return false;
45473
45377
  if (!isNodeOfType(callback, "ArrowFunctionExpression") && !isNodeOfType(callback, "FunctionExpression")) return false;
45474
- return functionReturnsMatchingExpression(callback, scopes, isJsxExpression);
45378
+ const body = callback.body;
45379
+ if (isJsxExpression(body)) return true;
45380
+ if (!isNodeOfType(body, "BlockStatement")) return false;
45381
+ for (const stmt of body.body ?? []) if (isNodeOfType(stmt, "ReturnStatement") && isJsxExpression(stmt.argument)) return true;
45382
+ return false;
45475
45383
  };
45476
45384
  const returnArgumentUsesAnyName = (returnStatement, names) => {
45477
45385
  if (!isNodeOfType(returnStatement, "ReturnStatement") || !returnStatement.argument) return false;
@@ -45549,7 +45457,7 @@ const rerenderMemoBeforeEarlyReturn = defineRule({
45549
45457
  if (!isNodeOfType(stmt, "VariableDeclaration")) continue;
45550
45458
  for (const declarator of stmt.declarations ?? []) {
45551
45459
  const init = declarator.init;
45552
- if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0], context.scopes)) {
45460
+ if (isNodeOfType(init, "CallExpression") && isHookCall$2(init, "useMemo") && callbackReturnsJsx(init.arguments?.[0])) {
45553
45461
  memoNode = declarator;
45554
45462
  callbackGuardTests = collectLeadingCallbackGuardTests(init.arguments?.[0]);
45555
45463
  if (isNodeOfType(declarator.id, "Identifier")) memoConsumerNames.add(declarator.id.name);
@@ -45615,11 +45523,8 @@ const collectFromObjectPattern = (pattern, bindings) => {
45615
45523
  const collectDefaultedEmptyBindings = (functionNode) => {
45616
45524
  const bindings = /* @__PURE__ */ new Map();
45617
45525
  const params = functionNode.params ?? [];
45618
- for (const [parameterIndex, param] of params.entries()) {
45619
- const parameterBinding = parameterIndex === 0 ? resolveFirstArgumentBinding(param) : param;
45620
- if (parameterBinding) collectFromObjectPattern(parameterBinding, bindings);
45621
- }
45622
- const propsParam = resolveFirstArgumentBinding(params[0]);
45526
+ for (const param of params) collectFromObjectPattern(param, bindings);
45527
+ const propsParam = params[0];
45623
45528
  const body = functionNode.body;
45624
45529
  if (!propsParam || !isNodeOfType(propsParam, "Identifier") || !body) return bindings;
45625
45530
  if (!isNodeOfType(body, "BlockStatement")) return bindings;
@@ -53733,6 +53638,10 @@ const isInvalidStyleExpression = (expression) => {
53733
53638
  }
53734
53639
  return false;
53735
53640
  };
53641
+ const getJsxOpeningElementName = (node) => {
53642
+ if (isNodeOfType(node.name, "JSXIdentifier")) return node.name.name;
53643
+ return null;
53644
+ };
53736
53645
  const stylePropObject = defineRule({
53737
53646
  id: "style-prop-object",
53738
53647
  title: "Style prop is not an object",
@@ -53744,8 +53653,7 @@ const stylePropObject = defineRule({
53744
53653
  const allowSet = new Set(allow);
53745
53654
  return {
53746
53655
  JSXOpeningElement(node) {
53747
- if (!isNodeOfType(node.name, "JSXIdentifier")) return;
53748
- const elementName = resolveJsxElementType(node);
53656
+ const elementName = getJsxOpeningElementName(node);
53749
53657
  if (elementName && allowSet.has(elementName)) return;
53750
53658
  if (elementName) {
53751
53659
  const firstCharCode = elementName.charCodeAt(0);
@@ -54450,7 +54358,7 @@ const tanstackStartNoAnchorElement = defineRule({
54450
54358
  create: (context) => {
54451
54359
  if (!isInProjectDirectory(context, "routes")) return {};
54452
54360
  return { JSXOpeningElement(node) {
54453
- if (resolveJsxElementType(node) !== "a") return;
54361
+ if (!isNodeOfType(node.name, "JSXIdentifier") || node.name.name !== "a") return;
54454
54362
  const attributes = node.attributes ?? [];
54455
54363
  const hrefAttribute = findJsxAttribute(attributes, "href");
54456
54364
  if (!hrefAttribute?.value) return;
@@ -55071,7 +54979,8 @@ const voidDomElementsNoChildren = defineRule({
55071
54979
  create: (context) => ({
55072
54980
  JSXElement(node) {
55073
54981
  const openingElement = node.openingElement;
55074
- const tagName = resolveJsxElementType(openingElement);
54982
+ if (!isNodeOfType(openingElement.name, "JSXIdentifier")) return;
54983
+ const tagName = openingElement.name.name;
55075
54984
  if (!VOID_DOM_ELEMENTS.has(tagName)) return;
55076
54985
  const hasChildrenContent = node.children.some(isMeaningfulJsxChild);
55077
54986
  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.f215ace",
3
+ "version": "0.7.6-dev.fbd85e3",
4
4
  "description": "React Doctor rules for oxlint.",
5
5
  "keywords": [
6
6
  "accessibility",