Skip to content

Commit 02a5276

Browse files
authored
refactor: simplify JSX element type resolution for React DOM rules (#1059)
1 parent d1f4707 commit 02a5276

File tree

7 files changed

+67
-98
lines changed

7 files changed

+67
-98
lines changed

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-button-type.ts

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit";
22
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
33
import type { CamelCase } from "string-ts";
44
import * as JSX from "@eslint-react/jsx";
5-
import { getSettingsFromContext } from "@eslint-react/shared";
65

7-
import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils";
6+
import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils";
87

98
export const RULE_NAME = "no-missing-button-type";
109

@@ -30,24 +29,13 @@ export default createRule<[], MessageID>({
3029
});
3130

3231
export function create(context: RuleContext<MessageID, []>): RuleListener {
33-
const settings = getSettingsFromContext(context);
34-
const polymorphicPropName = settings.polymorphicPropName;
35-
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "button");
36-
32+
const resolver = createJsxElementResolver(context);
3733
return {
3834
JSXElement(node) {
39-
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
40-
context,
41-
node,
42-
polymorphicPropName,
43-
additionalComponents,
44-
);
45-
46-
if (elementNameOnDom !== "button") return;
47-
35+
const { attributes, domElementType } = resolver.resolve(node);
36+
if (domElementType !== "button") return;
4837
const elementScope = context.sourceCode.getScope(node);
49-
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
50-
const customComponentProp = findCustomComponentProp("type", customComponent?.attributes ?? []);
38+
const customComponentProp = findCustomComponentProp("type", attributes);
5139
const propNameOnJsx = customComponentProp?.name ?? "type";
5240
const attributeNode = JSX.getAttribute(
5341
propNameOnJsx,

packages/plugins/eslint-plugin-react-dom/src/rules/no-missing-iframe-sandbox.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit";
22
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
33
import type { CamelCase } from "string-ts";
44
import * as JSX from "@eslint-react/jsx";
5-
import { getSettingsFromContext } from "@eslint-react/shared";
65

7-
import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils";
6+
import { createJsxElementResolver, createRule, findCustomComponent, findCustomComponentProp } from "../utils";
87

98
export const RULE_NAME = "no-missing-iframe-sandbox";
109

@@ -56,23 +55,13 @@ export default createRule<[], MessageID>({
5655
});
5756

5857
export function create(context: RuleContext<MessageID, []>): RuleListener {
59-
const settings = getSettingsFromContext(context);
60-
const polymorphicPropName = settings.polymorphicPropName;
61-
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
58+
const resolver = createJsxElementResolver(context);
6259
return {
6360
JSXElement(node) {
64-
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
65-
context,
66-
node,
67-
polymorphicPropName,
68-
additionalComponents,
69-
);
70-
71-
if (elementNameOnDom !== "iframe") return;
72-
61+
const { attributes, domElementType } = resolver.resolve(node);
62+
if (domElementType !== "iframe") return;
7363
const elementScope = context.sourceCode.getScope(node);
74-
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
75-
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
64+
const customComponentProp = findCustomComponentProp("sandbox", attributes);
7665
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
7766
const attributeNode = JSX.getAttribute(
7867
propNameOnJsx,

packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-iframe-sandbox.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ import type { RuleContext, RuleFeature } from "@eslint-react/kit";
22
import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
33
import type { CamelCase } from "string-ts";
44
import * as JSX from "@eslint-react/jsx";
5-
import { getSettingsFromContext } from "@eslint-react/shared";
65

7-
import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils";
6+
import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils";
87

98
export const RULE_NAME = "no-unsafe-iframe-sandbox";
109

@@ -41,23 +40,13 @@ export default createRule<[], MessageID>({
4140
});
4241

4342
export function create(context: RuleContext<MessageID, []>): RuleListener {
44-
const settings = getSettingsFromContext(context);
45-
const polymorphicPropName = settings.polymorphicPropName;
46-
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "iframe");
43+
const resolver = createJsxElementResolver(context);
4744
return {
4845
JSXElement(node) {
49-
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
50-
context,
51-
node,
52-
polymorphicPropName,
53-
additionalComponents,
54-
);
55-
56-
if (elementNameOnDom !== "iframe") return;
57-
46+
const { attributes, domElementType } = resolver.resolve(node);
47+
if (domElementType !== "iframe") return;
5848
const elementScope = context.sourceCode.getScope(node);
59-
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
60-
const customComponentProp = findCustomComponentProp("sandbox", customComponent?.attributes ?? []);
49+
const customComponentProp = findCustomComponentProp("sandbox", attributes);
6150
const propNameOnJsx = customComponentProp?.name ?? "sandbox";
6251
const attributeNode = JSX.getAttribute(
6352
propNameOnJsx,

packages/plugins/eslint-plugin-react-dom/src/rules/no-unsafe-target-blank.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import type { RuleListener } from "@typescript-eslint/utils/ts-eslint";
44
import type { CamelCase } from "string-ts";
55
import { _ } from "@eslint-react/eff";
66
import * as JSX from "@eslint-react/jsx";
7-
import { getSettingsFromContext } from "@eslint-react/shared";
87

9-
import { createRule, findCustomComponent, findCustomComponentProp, getElementTypeOnJsxAndDom } from "../utils";
8+
import { createJsxElementResolver, createRule, findCustomComponentProp } from "../utils";
109

1110
export const RULE_NAME = "no-unsafe-target-blank";
1211

@@ -45,24 +44,15 @@ export default createRule<[], MessageID>({
4544
});
4645

4746
export function create(context: RuleContext<MessageID, []>): RuleListener {
48-
const settings = getSettingsFromContext(context);
49-
const polymorphicPropName = settings.polymorphicPropName;
50-
const additionalComponents = settings.additionalComponents.filter((c) => c.as === "a");
51-
47+
const resolver = createJsxElementResolver(context);
5248
return {
5349
JSXElement(node: TSESTree.JSXElement) {
54-
const [elementNameOnJsx, elementNameOnDom] = getElementTypeOnJsxAndDom(
55-
context,
56-
node,
57-
polymorphicPropName,
58-
additionalComponents,
59-
);
60-
if (elementNameOnDom !== "a") return;
50+
const { attributes, domElementType } = resolver.resolve(node);
51+
if (domElementType !== "a") return;
6152
const elementScope = context.sourceCode.getScope(node);
62-
const customComponent = findCustomComponent(elementNameOnJsx, additionalComponents);
6353

6454
const getAttributeStringValue = (name: string) => {
65-
const customComponentProp = findCustomComponentProp(name, customComponent?.attributes ?? []);
55+
const customComponentProp = findCustomComponentProp(name, attributes);
6656
const propNameOnJsx = customComponentProp?.name ?? name;
6757
const attributeNode = JSX.getAttribute(
6858
propNameOnJsx,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { RuleContext } from "@eslint-react/kit";
2+
import type { TSESTree } from "@typescript-eslint/types";
3+
import * as JSX from "@eslint-react/jsx";
4+
import { getSettingsFromContext } from "@eslint-react/shared";
5+
6+
export function createJsxElementResolver(context: RuleContext) {
7+
const {
8+
additionalComponents,
9+
polymorphicPropName,
10+
} = getSettingsFromContext(context);
11+
return {
12+
resolve(node: TSESTree.JSXElement) {
13+
const name = JSX.getElementType(node);
14+
const component = additionalComponents
15+
.findLast((c) => c.name === name || c.re.test(name));
16+
const result = {
17+
attributes: component?.attributes ?? [],
18+
domElementType: component?.as ?? name,
19+
jsxElementType: name,
20+
};
21+
if (name === name.toLowerCase() || component != null || polymorphicPropName == null) {
22+
return result;
23+
}
24+
const initialScope = context.sourceCode.getScope(node);
25+
const polymorphicPropAttr = JSX.getAttribute(
26+
polymorphicPropName,
27+
node.openingElement.attributes,
28+
initialScope,
29+
);
30+
if (polymorphicPropAttr != null) {
31+
const polymorphicPropValue = JSX.getAttributeValue(
32+
polymorphicPropAttr,
33+
polymorphicPropName,
34+
initialScope,
35+
);
36+
if (polymorphicPropValue.kind === "some" && typeof polymorphicPropValue.value === "string") {
37+
return {
38+
...result,
39+
domElementType: polymorphicPropValue.value,
40+
};
41+
}
42+
}
43+
return result;
44+
},
45+
} as const;
46+
}

packages/plugins/eslint-plugin-react-dom/src/utils/get-element-type-on-jsx-and-dom.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1+
export * from "./create-jsx-element-resolver";
12
export * from "./create-rule";
23
export * from "./find-custom-component";
3-
export * from "./get-element-type-on-jsx-and-dom";

0 commit comments

Comments
 (0)