Skip to content

Commit f009d42

Browse files
committed
wip: add 'react-dom/no-render'
1 parent 3826794 commit f009d42

File tree

2 files changed

+66
-6
lines changed

2 files changed

+66
-6
lines changed
Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,61 @@
1+
import tsx from "dedent";
2+
13
import { ruleTester } from "../../../../../test";
24
import rule, { RULE_NAME } from "./no-render";
35

46
ruleTester.run(RULE_NAME, rule, {
57
invalid: [
68
{
7-
code: /* tsx */ `
9+
code: tsx`
10+
import React from "react";
11+
import { render } from "react-dom";
12+
import Component from "Component";
13+
14+
render(<Component />, document.getElementById("app"));
15+
`,
16+
errors: [{ messageId: "noRender" }],
17+
output: tsx`
18+
import { createRoot } from "react-dom";
19+
import React from "react";
20+
import { render } from "react-dom";
21+
import Component from "Component";
22+
23+
createRoot(document.getElementById("app")).render(<Component />);
24+
`,
25+
},
26+
{
27+
code: tsx`
28+
import React from "react";
829
import ReactDom from "react-dom";
930
import Component from "Component";
1031
1132
ReactDom.render(<Component />, document.getElementById("app"));
1233
`,
1334
errors: [{ messageId: "noRender" }],
35+
output: tsx`
36+
import { createRoot } from "react-dom";
37+
import React from "react";
38+
import ReactDom from "react-dom";
39+
import Component from "Component";
40+
41+
createRoot(document.getElementById("app")).render(<Component />);
42+
`,
1443
},
1544
],
1645
valid: [
17-
/* tsx */ `
18-
import { createRoot } from "react-dom/client";
46+
tsx`
47+
import React from "react";
48+
import { render } from "react-dom";
49+
import Component from "Component";
50+
51+
createRoot(document.getElementById("app")).render(<Component />);
52+
`,
53+
tsx`
54+
import React from "react";
1955
import ReactDom from "react-dom";
2056
import Component from "Component";
2157
22-
const root = createRoot(document.getElementById("app"));
23-
root.render(<Component />);
58+
createRoot(document.getElementById("app")).render(<Component />);
2459
`,
2560
],
2661
});

packages/plugins/eslint-plugin-react-dom/src/rules/no-render.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import type { RuleFeature } from "@eslint-react/shared";
22
import { getSettingsFromContext } from "@eslint-react/shared";
3+
import type { TSESTree } from "@typescript-eslint/types";
34
import { AST_NODE_TYPES as T } from "@typescript-eslint/types";
5+
import type { RuleFixer } from "@typescript-eslint/utils/ts-eslint";
46
import { compare } from "compare-versions";
57
import type { CamelCase } from "string-ts";
68

9+
import type { RuleContext } from "../../../../shared/src/types";
710
import { createRule } from "../utils";
811

912
export const RULE_NAME = "no-render";
@@ -37,13 +40,16 @@ export default createRule<[], MessageID>({
3740
}
3841
const reactDomNames = new Set<string>();
3942
const renderNames = new Set<string>();
43+
4044
return {
4145
CallExpression(node) {
4246
switch (true) {
43-
case node.callee.type === T.Identifier && renderNames.has(node.callee.name):
47+
case node.callee.type === T.Identifier
48+
&& renderNames.has(node.callee.name):
4449
context.report({
4550
messageId: "noRender",
4651
node,
52+
fix: getFix(context, node),
4753
});
4854
return;
4955
case node.callee.type === T.MemberExpression
@@ -54,6 +60,7 @@ export default createRule<[], MessageID>({
5460
context.report({
5561
messageId: "noRender",
5662
node,
63+
fix: getFix(context, node),
5764
});
5865
return;
5966
}
@@ -80,3 +87,21 @@ export default createRule<[], MessageID>({
8087
},
8188
defaultOptions: [],
8289
});
90+
91+
function getFix(context: RuleContext, node: TSESTree.CallExpression) {
92+
const getText = (n: TSESTree.Node) => context.sourceCode.getText(n);
93+
return (fixer: RuleFixer) => {
94+
const [arg0, arg1] = node.arguments;
95+
if (arg0 == null || arg1 == null) return null;
96+
const fixedCallExpressionText = [
97+
"createRoot",
98+
"(" + getText(arg1) + ")" + ".",
99+
"render",
100+
"(" + getText(arg0) + ")",
101+
].join("");
102+
return [
103+
fixer.insertTextBefore(context.sourceCode.ast, 'import { createRoot } from "react-dom";\n'),
104+
fixer.replaceText(node, fixedCallExpressionText),
105+
];
106+
};
107+
}

0 commit comments

Comments
 (0)