[compiler] Script to produce markdown of lint rule docs (#34260)
The docs site is in a separate repo, but this gives us a semi-automated way to update the docs about our lint rules. The script generates markdown files from the rule definitions which we can then manually copy/paste into the docs site somewhere. In the future we can automate this fully.
Joseph Savona committed
Aug 22, 2025 at 09:59 UTC
425ba0ad6d3ebd1779f6a658dcbf5c666d054948
3 files changed
+68
-16
compiler/package.json
+2
-1
@@ -19,7 +19,8 @@
19
"test": "yarn workspaces run test",
20
"snap": "yarn workspace babel-plugin-react-compiler run snap",
21
"snap:build": "yarn workspace snap run build",
22
- "npm:publish": "node scripts/release/publish"
22
+ "npm:publish": "node scripts/release/publish",
23
+ "eslint-docs": "yarn workspace babel-plugin-react-compiler build && node scripts/build-eslint-docs.js"
24
},
25
"dependencies": {
26
"fs-extra": "^4.0.2",
compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts
+34
-15
@@ -7,9 +7,10 @@
7
8
import * as t from '@babel/types';
9
import {codeFrameColumns} from '@babel/code-frame';
10
-import type {SourceLocation} from './HIR';
10
+import {type SourceLocation} from './HIR';
11
import {Err, Ok, Result} from './Utils/Result';
12
import {assertExhaustive} from './Utils/utils';
13
+import invariant from 'invariant';
14
15
export enum ErrorSeverity {
16
/**
@@ -628,7 +629,18 @@ export type LintRule = {
629
recommended: boolean;
630
};
631
632
+const RULE_NAME_PATTERN = /^[a-z]+(-[a-z]+)*$/;
633
+
634
export function getRuleForCategory(category: ErrorCategory): LintRule {
635
+ const rule = getRuleForCategoryImpl(category);
636
+ invariant(
637
+ RULE_NAME_PATTERN.test(rule.name),
638
+ `Invalid rule name, got '${rule.name}' but rules must match ${RULE_NAME_PATTERN.toString()}`,
639
+ );
640
+ return rule;
641
+}
642
+
643
+function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
644
switch (category) {
645
case ErrorCategory.AutomaticEffectDependencies: {
646
return {
@@ -636,7 +648,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
648
name: 'automatic-effect-dependencies',
649
description:
650
'Verifies that automatic effect dependencies are compiled if opted-in',
639
- recommended: true,
651
+ recommended: false,
652
};
653
}
654
case ErrorCategory.CapitalizedCalls: {
@@ -652,7 +664,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
664
return {
665
category,
666
name: 'config',
655
- description: 'Validates the configuration',
667
+ description: 'Validates the compiler configuration options',
668
recommended: true,
669
};
670
}
@@ -678,7 +690,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
690
category,
691
name: 'set-state-in-effect',
692
description:
681
- 'Validates against calling setState synchronously in an effect',
693
+ 'Validates against calling setState synchronously in an effect, which can lead to re-renders that degrade performance',
694
recommended: true,
695
};
696
}
@@ -687,7 +699,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
699
category,
700
name: 'error-boundaries',
701
description:
690
- 'Validates usage of error boundaries instead of try/catch for errors in JSX',
702
+ 'Validates usage of error boundaries instead of try/catch for errors in child components',
703
recommended: true,
704
};
705
}
@@ -711,7 +723,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
723
return {
724
category,
725
name: 'gating',
714
- description: 'Validates configuration of gating mode',
726
+ description:
727
+ 'Validates configuration of [gating mode](https://react.dev/reference/react-compiler/gating)',
728
recommended: true,
729
};
730
}
@@ -720,7 +733,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
733
category,
734
name: 'globals',
735
description:
723
- 'Validates against assignment/mutation of globals during render',
736
+ 'Validates against assignment/mutation of globals during render, part of ensuring that ' +
737
+ '[side effects must render outside of render](https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render)',
738
recommended: true,
739
};
740
}
@@ -742,7 +756,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
756
category,
757
name: 'immutability',
758
description:
745
- 'Validates that immutable values (props, state, etc) are not mutated',
759
+ 'Validates against mutating props, state, and other values that [are immutable](https://react.dev/reference/rules/components-and-hooks-must-be-pure#props-and-state-are-immutable)',
760
recommended: true,
761
};
762
}
@@ -759,7 +773,9 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
773
category,
774
name: 'preserve-manual-memoization',
775
description:
762
- 'Validates that existing manual memoized is preserved by the compiler',
776
+ 'Validates that existing manual memoized is preserved by the compiler. ' +
777
+ 'React Compiler will only compile components and hooks if its inference ' +
778
+ '[matches or exceeds the existing manual memoization](https://react.dev/learn/react-compiler/introduction#what-should-i-do-about-usememo-usecallback-and-reactmemo)',
779
recommended: true,
780
};
781
}
@@ -768,7 +784,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
784
category,
785
name: 'purity',
786
description:
771
- 'Validates that the component/hook is pure, and does not call known-impure functions',
787
+ 'Validates that [components/hooks are pure](https://react.dev/reference/rules/components-and-hooks-must-be-pure) by checking that they do not call known-impure functions',
788
recommended: true,
789
};
790
}
@@ -777,7 +793,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
793
category,
794
name: 'refs',
795
description:
780
- 'Validates correct usage of refs, not reading/writing during render',
796
+ 'Validates correct usage of refs, not reading/writing during render. See the "pitfalls" section in [`useRef()` usage](https://react.dev/reference/react/useRef#usage)',
797
recommended: true,
798
};
799
}
@@ -785,7 +801,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
801
return {
802
category,
803
name: 'set-state-in-render',
788
- description: 'Validates against setting state during render',
804
+ description:
805
+ 'Validates against setting state during render, which can trigger additional renders and potential infinite render loops',
806
recommended: true,
807
};
808
}
@@ -794,7 +811,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
811
category,
812
name: 'static-components',
813
description:
797
- 'Validates that components are static, not recreated every render',
814
+ 'Validates that components are static, not recreated every render. Components that are recreated dynamically can reset state and trigger excessive re-rendering',
815
recommended: true,
816
};
817
}
@@ -826,7 +843,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
843
return {
844
category,
845
name: 'unsupported-syntax',
829
- description: 'Validates against syntax that we do not plan to support',
846
+ description:
847
+ 'Validates against syntax that we do not plan to support in React Compiler',
848
recommended: true,
849
};
850
}
@@ -834,7 +852,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
852
return {
853
category,
854
name: 'use-memo',
837
- description: 'Validates usage of the useMemo() hook',
855
+ description:
856
+ 'Validates usage of the useMemo() hook against common mistakes. See [`useMemo()` docs](https://react.dev/reference/react/useMemo) for more information.',
857
recommended: true,
858
};
859
}
compiler/scripts/build-eslint-docs.js
new
+32
@@ -0,0 +1,32 @@
1
+const ReactCompiler = require('../packages/babel-plugin-react-compiler/dist');
2
+
3
+const combinedRules = [
4
+ {
5
+ name: 'rules-of-hooks',
6
+ recommended: true,
7
+ description:
8
+ 'Validates that components and hooks follow the [Rules of Hooks](https://react.dev/reference/rules/rules-of-hooks)',
9
+ },
10
+ {
11
+ name: 'exhaustive-deps',
12
+ recommended: true,
13
+ description:
14
+ 'Validates that hooks which accept dependency arrays (`useMemo()`, `useCallback()`, `useEffect()`, etc) ' +
15
+ 'list all referenced variables in their dependency array. Referencing a value without including it in the ' +
16
+ 'dependency array can lead to stale UI or callbacks.',
17
+ },
18
+ ...ReactCompiler.LintRules,
19
+];
20
+
21
+const printed = combinedRules
22
+ .filter(rule => rule.recommended)
23
+ .map(rule => {
24
+ return `
25
+## \`react-hooks/${rule.name}\`
26
+
27
+${rule.description}
28
+ `.trim();
29
+ })
30
+ .join('\n\n');
31
+
32
+console.log(printed);