diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts index 755b1eef1a..659339501a 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts @@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as JSX from "@eslint-react/jsx"; -import { getSettingsFromContext } from "@eslint-react/shared"; -import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils"; +import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-missing-button-type"; @@ -30,24 +29,13 @@ export default createRule<[], MessageID>({ }); export function create(context: RuleContext): RuleListener { - const settings = getSettingsFromContext(context); - const polymorphicPropName = settings.polymorphicPropName; - const additionalComponents = settings.additionalComponents.filter((c) => c.as === "button"); - + const resolver = createJsxElementResolver(context); return { JSXElement(node) { - const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom( - context, - node, - polymorphicPropName, - additionalComponents, - ); - - if (elementNameOnDom !== "button") return; - + const { attributes, domElementType } = resolver.resolve(node); + if (domElementType !== "button") return; const elementScope = context.sourceCode.getScope(node); - const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents); - const customComponentProp = findCustomComponentProp("type", customComponent?.attributes ?? []); + const customComponentProp = findCustomComponentProp("type", attributes); const propNameOnJsx = customComponentProp?.name ?? "type"; const attributeNode = JSX.getAttribute( propNameOnJsx, diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts index 39d4c3deba..7b876b9622 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts @@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as JSX from "@eslint-react/jsx"; -import { getSettingsFromContext } from "@eslint-react/shared"; -import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils"; +import { createJsxElementResolver, createRule, findCustomComponent, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-missing-iframe-sandbox"; @@ -56,23 +55,13 @@ export default createRule<[], MessageID>({ }); export function create(context: RuleContext): RuleListener { - const settings = getSettingsFromContext(context); - const polymorphicPropName = settings.polymorphicPropName; - const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe"); + const resolver = createJsxElementResolver(context); return { JSXElement(node) { - const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom( - context, - node, - polymorphicPropName, - additionalComponents, - ); - - if (elementNameOnDom !== "iframe") return; - + const { attributes, domElementType } = resolver.resolve(node); + if (domElementType !== "iframe") return; const elementScope = context.sourceCode.getScope(node); - const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents); - const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []); + const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; const attributeNode = JSX.getAttribute( propNameOnJsx, diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts index 5699f52748..1c4c4e34c9 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts @@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit"; import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import * as JSX from "@eslint-react/jsx"; -import { getSettingsFromContext } from "@eslint-react/shared"; -import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils"; +import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-unsafe-iframe-sandbox"; @@ -41,23 +40,13 @@ export default createRule<[], MessageID>({ }); export function create(context: RuleContext): RuleListener { - const settings = getSettingsFromContext(context); - const polymorphicPropName = settings.polymorphicPropName; - const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe"); + const resolver = createJsxElementResolver(context); return { JSXElement(node) { - const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom( - context, - node, - polymorphicPropName, - additionalComponents, - ); - - if (elementNameOnDom !== "iframe") return; - + const { attributes, domElementType } = resolver.resolve(node); + if (domElementType !== "iframe") return; const elementScope = context.sourceCode.getScope(node); - const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents); - const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []); + const customComponentProp = findCustomComponentProp("sandbox", attributes); const propNameOnJsx = customComponentProp?.name ?? "sandbox"; const attributeNode = JSX.getAttribute( propNameOnJsx, diff --git a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts index 92094e75be..d37228dcde 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts @@ -4,9 +4,8 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint"; import type { CamelCase } from "string-ts"; import { _ } from "@eslint-react/eff"; import * as JSX from "@eslint-react/jsx"; -import { getSettingsFromContext } from "@eslint-react/shared"; -import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils"; +import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils"; export const RULE_NAME = "no-unsafe-target-blank"; @@ -45,24 +44,15 @@ export default createRule<[], MessageID>({ }); export function create(context: RuleContext): RuleListener { - const settings = getSettingsFromContext(context); - const polymorphicPropName = settings.polymorphicPropName; - const additionalComponents = settings.additionalComponents.filter((c) => c.as === "a"); - + const resolver = createJsxElementResolver(context); return { JSXElement(node: TSESTree.JSXElement) { - const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom( - context, - node, - polymorphicPropName, - additionalComponents, - ); - if (elementNameOnDom !== "a") return; + const { attributes, domElementType } = resolver.resolve(node); + if (domElementType !== "a") return; const elementScope = context.sourceCode.getScope(node); - const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents); const getAttributeStringValue = (name: string) => { - const customComponentProp = findCustomComponentProp(name, customComponent?.attributes ?? []); + const customComponentProp = findCustomComponentProp(name, attributes); const propNameOnJsx = customComponentProp?.name ?? name; const attributeNode = JSX.getAttribute( propNameOnJsx, diff --git a/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts b/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts new file mode 100644 index 0000000000..400aa18ae1 --- /dev/null +++ b/packages/plugins/eslint-plugin-react-dom/src/utils/create-jsx-element-resolver.ts @@ -0,0 +1,46 @@ +import type { RuleContext } from "@eslint-react/kit"; +import type { TSESTree } from "@typescript-eslint/types"; +import * as JSX from "@eslint-react/jsx"; +import { getSettingsFromContext } from "@eslint-react/shared"; + +export function createJsxElementResolver(context: RuleContext) { + const { + additionalComponents, + polymorphicPropName, + } = getSettingsFromContext(context); + return { + resolve(node: TSESTree.JSXElement) { + const name = JSX.getElementType(node); + const component = additionalComponents + .findLast((c) => c.name === name || c.re.test(name)); + const result = { + attributes: component?.attributes ?? [], + domElementType: component?.as ?? name, + jsxElementType: name, + }; + if (name === name.toLowerCase() || component != null || polymorphicPropName == null) { + return result; + } + const initialScope = context.sourceCode.getScope(node); + const polymorphicPropAttr = JSX.getAttribute( + polymorphicPropName, + node.openingElement.attributes, + initialScope, + ); + if (polymorphicPropAttr != null) { + const polymorphicPropValue = JSX.getAttributeValue( + polymorphicPropAttr, + polymorphicPropName, + initialScope, + ); + if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") { + return { + ...result, + domElementType: polymorphicPropValue.value, + }; + } + } + return result; + }, + } as const; +} diff --git a/packages/plugins/eslint-plugin-react-dom/src/utils/get-element-type-on-jsx-and-dom.ts b/packages/plugins/eslint-plugin-react-dom/src/utils/get-element-type-on-jsx-and-dom.ts deleted file mode 100644 index 463d48b454..0000000000 --- a/packages/plugins/eslint-plugin-react-dom/src/utils/get-element-type-on-jsx-and-dom.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { RuleContext } from "@eslint-react/kit"; -import type { CustomComponentNormalized } from "@eslint-react/shared"; -import type { TSESTree } from "@typescript-eslint/types"; -import * as JSX from "@eslint-react/jsx"; - -export function getElementTypeOnJsxAndDom( - context: RuleContext, - node: TSESTree.JSXElement, - polymorphicPropName?: string, - additionalComponents: CustomComponentNormalized[] = [], -): [string, string] { - const name = JSX.getElementType(node); - // Skip JsxIntrinsicElements - if (name === name.toLowerCase()) return [name, name]; - // Get the component name using the `settings["react-x"].additionalComponents` setting - const component = additionalComponents - .findLast((c) => c.name === name || c.re.test(name)); - if (component != null) return [name, component.as]; - if (polymorphicPropName == null) return [name, name]; - // Get the component name using the `settings["react-x"].polymorphicPropName` setting - const initialScope = context.sourceCode.getScope(node); - const attributeNode = JSX.getAttribute( - polymorphicPropName, - node.openingElement.attributes, - initialScope, - ); - if (attributeNode == null) return [name, name]; - const polymorphicPropValue = JSX.getAttributeValue(attributeNode, polymorphicPropName, initialScope); - if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") { - return [name, polymorphicPropValue.value]; - } - return [name, name]; -} diff --git a/packages/plugins/eslint-plugin-react-dom/src/utils/index.ts b/packages/plugins/eslint-plugin-react-dom/src/utils/index.ts index 0f22b88b90..38afa0ffa5 100644 --- a/packages/plugins/eslint-plugin-react-dom/src/utils/index.ts +++ b/packages/plugins/eslint-plugin-react-dom/src/utils/index.ts @@ -1,3 +1,3 @@ +export * from "./create-jsx-element-resolver"; export * from "./create-rule"; export * from "./find-custom-component"; -export * from "./get-element-type-on-jsx-and-dom";