Skip to content

Commit 425ba0a

Browse files
authored
[compiler] Script to produce markdown of lint rule docs (facebook#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.
1 parent 6de32a5 commit 425ba0a

File tree

3 files changed

+68
-16
lines changed

3 files changed

+68
-16
lines changed

compiler/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"test": "yarn workspaces run test",
2020
"snap": "yarn workspace babel-plugin-react-compiler run snap",
2121
"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"
2324
},
2425
"dependencies": {
2526
"fs-extra": "^4.0.2",

compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@
77

88
import * as t from '@babel/types';
99
import {codeFrameColumns} from '@babel/code-frame';
10-
import type {SourceLocation} from './HIR';
10+
import {type SourceLocation} from './HIR';
1111
import {Err, Ok, Result} from './Utils/Result';
1212
import {assertExhaustive} from './Utils/utils';
13+
import invariant from 'invariant';
1314

1415
export enum ErrorSeverity {
1516
/**
@@ -628,15 +629,26 @@ export type LintRule = {
628629
recommended: boolean;
629630
};
630631

632+
const RULE_NAME_PATTERN = /^[a-z]+(-[a-z]+)*$/;
633+
631634
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 {
632644
switch (category) {
633645
case ErrorCategory.AutomaticEffectDependencies: {
634646
return {
635647
category,
636648
name: 'automatic-effect-dependencies',
637649
description:
638650
'Verifies that automatic effect dependencies are compiled if opted-in',
639-
recommended: true,
651+
recommended: false,
640652
};
641653
}
642654
case ErrorCategory.CapitalizedCalls: {
@@ -652,7 +664,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
652664
return {
653665
category,
654666
name: 'config',
655-
description: 'Validates the configuration',
667+
description: 'Validates the compiler configuration options',
656668
recommended: true,
657669
};
658670
}
@@ -678,7 +690,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
678690
category,
679691
name: 'set-state-in-effect',
680692
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',
682694
recommended: true,
683695
};
684696
}
@@ -687,7 +699,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
687699
category,
688700
name: 'error-boundaries',
689701
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',
691703
recommended: true,
692704
};
693705
}
@@ -711,7 +723,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
711723
return {
712724
category,
713725
name: 'gating',
714-
description: 'Validates configuration of gating mode',
726+
description:
727+
'Validates configuration of [gating mode](https://react.dev/reference/react-compiler/gating)',
715728
recommended: true,
716729
};
717730
}
@@ -720,7 +733,8 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
720733
category,
721734
name: 'globals',
722735
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)',
724738
recommended: true,
725739
};
726740
}
@@ -742,7 +756,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
742756
category,
743757
name: 'immutability',
744758
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)',
746760
recommended: true,
747761
};
748762
}
@@ -759,7 +773,9 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
759773
category,
760774
name: 'preserve-manual-memoization',
761775
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)',
763779
recommended: true,
764780
};
765781
}
@@ -768,7 +784,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
768784
category,
769785
name: 'purity',
770786
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',
772788
recommended: true,
773789
};
774790
}
@@ -777,15 +793,16 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
777793
category,
778794
name: 'refs',
779795
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)',
781797
recommended: true,
782798
};
783799
}
784800
case ErrorCategory.RenderSetState: {
785801
return {
786802
category,
787803
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',
789806
recommended: true,
790807
};
791808
}
@@ -794,7 +811,7 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
794811
category,
795812
name: 'static-components',
796813
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',
798815
recommended: true,
799816
};
800817
}
@@ -826,15 +843,17 @@ export function getRuleForCategory(category: ErrorCategory): LintRule {
826843
return {
827844
category,
828845
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',
830848
recommended: true,
831849
};
832850
}
833851
case ErrorCategory.UseMemo: {
834852
return {
835853
category,
836854
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.',
838857
recommended: true,
839858
};
840859
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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);

0 commit comments

Comments
 (0)