Skip to content

Commit b6041a7

Browse files
committed
Add local eslint rule to validate markdown codeblocks with React Compiler
In facebook/react#34462 for example, we found an issue where the compiler was incorrectly validating an example straight from the docs. In order to find more issues like this + also provide more feedback to doc authors on valid/invalid patterns, this PR adds a new local eslint rule which validates all markdown codeblocks containing components/hooks with React Compiler. An autofixer is also provided. To express that a codeblock has an expected error, we can use the following metadata: ```ts // pseudo type def type MarkdownCodeBlockMetadata = { expectedErrors?: { 'react-compiler'?: number[]; }; }; ``` and can be used like so: ```` ```js {expectedErrors: {'react-compiler': [4]}} // ❌ setState directly in render function Component({value}) { const [count, setCount] = useState(0); setCount(value); // error on L4 return <div>{count}</div>; } ``` ```` Because this is defined as a local rule, we don't have the same granular reporting that `eslint-plugin-react-hooks` yet. I can look into that later but for now this first PR just sets us up with something basic.
1 parent 5b9a2ce commit b6041a7

File tree

6 files changed

+759
-5
lines changed

6 files changed

+759
-5
lines changed

.eslintrc

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,38 @@
22
"root": true,
33
"extends": "next/core-web-vitals",
44
"parser": "@typescript-eslint/parser",
5-
"plugins": ["@typescript-eslint", "eslint-plugin-react-compiler"],
5+
"plugins": ["@typescript-eslint", "eslint-plugin-react-compiler", "local-rules"],
66
"rules": {
77
"no-unused-vars": "off",
88
"@typescript-eslint/no-unused-vars": ["error", {"varsIgnorePattern": "^_"}],
99
"react-hooks/exhaustive-deps": "error",
1010
"react/no-unknown-property": ["error", {"ignore": ["meta"]}],
11-
"react-compiler/react-compiler": "error"
11+
"react-compiler/react-compiler": "error",
12+
"local-rules/lint-markdown-code-blocks": "error"
1213
},
1314
"env": {
1415
"node": true,
1516
"commonjs": true,
1617
"browser": true,
1718
"es6": true
18-
}
19+
},
20+
"overrides": [
21+
{
22+
"files": ["src/content/**/*.md"],
23+
"processor": "local-rules/markdown",
24+
"parser": "espree",
25+
"parserOptions": {
26+
"ecmaVersion": "latest",
27+
"sourceType": "script"
28+
},
29+
"rules": {
30+
"no-unused-vars": "off",
31+
"@typescript-eslint/no-unused-vars": "off",
32+
"react-hooks/exhaustive-deps": "off",
33+
"react/no-unknown-property": "off",
34+
"react-compiler/react-compiler": "off",
35+
"local-rules/lint-markdown-code-blocks": "error"
36+
}
37+
}
38+
]
1939
}

eslint-local-rules/index.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const lintMarkdownCodeBlocks = require('./rules/lint-markdown-code-blocks');
2+
3+
module.exports = {
4+
rules: {
5+
'lint-markdown-code-blocks': lintMarkdownCodeBlocks,
6+
},
7+
processors: {
8+
markdown: {
9+
preprocess(text, filename) {
10+
return [
11+
`// Markdown content for processing\n/* ${text.replace(/\*\//g, '*\\/')} */`
12+
];
13+
},
14+
postprocess(messages) {
15+
const processedMessages = messages.flat().map(message => {
16+
if (message.suggestions) {
17+
const processedSuggestions = message.suggestions.map(suggestion => {
18+
if (suggestion.fix) {
19+
// Unescape */ in fix text
20+
const fixedText = suggestion.fix.text.replace(/\*\\\//g, '*/');
21+
return { ...suggestion, fix: { ...suggestion.fix, text: fixedText } };
22+
}
23+
return suggestion;
24+
});
25+
return { ...message, suggestions: processedSuggestions };
26+
}
27+
return message;
28+
});
29+
return processedMessages;
30+
},
31+
supportsAutofix: true
32+
}
33+
}
34+
};

eslint-local-rules/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "eslint-plugin-local-rules",
3+
"version": "0.0.0",
4+
"main": "index.js",
5+
"private": "true"
6+
}

0 commit comments

Comments
 (0)