mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Now that the official way to use React Compiler's linting is via `eslint-plugin-react-hooks` v7, eslint-plugin-react-compiler isn't strictly necessary anymore. However, at Meta our linting setup makes it a bit tedious to use the current eprh setup with lots of rules. Here I'm experimenting with making eslint-plugin-react-compiler just report all issues under one rule, to make it a bit easier to sync internally. Unclear if we even need to land this or just use it to help figure out the migration.
158 lines
4.2 KiB
TypeScript
158 lines
4.2 KiB
TypeScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
import {normalizeIndent, testRule, makeTestCaseError} from './shared-utils';
|
|
import {AllRules} from '../src/rules/ReactCompilerRule';
|
|
|
|
testRule('plugin-recommended', AllRules, {
|
|
valid: [
|
|
{
|
|
name: 'Basic example with component syntax',
|
|
code: normalizeIndent`
|
|
export default component HelloWorld(
|
|
text: string = 'Hello!',
|
|
onClick: () => void,
|
|
) {
|
|
return <div onClick={onClick}>{text}</div>;
|
|
}
|
|
`,
|
|
},
|
|
|
|
{
|
|
// OK because invariants are only meant for the compiler team's consumption
|
|
name: '[Invariant] Defined after use',
|
|
code: normalizeIndent`
|
|
function Component(props) {
|
|
let y = function () {
|
|
m(x);
|
|
};
|
|
|
|
let x = { a };
|
|
m(x);
|
|
return y;
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: "Classes don't throw",
|
|
code: normalizeIndent`
|
|
class Foo {
|
|
#bar() {}
|
|
}
|
|
`,
|
|
},
|
|
],
|
|
invalid: [
|
|
{
|
|
// TODO: actually return multiple diagnostics in this case
|
|
name: 'Multiple diagnostic kinds from the same function are surfaced',
|
|
code: normalizeIndent`
|
|
import Child from './Child';
|
|
function Component() {
|
|
const result = cond ?? useConditionalHook();
|
|
return <>
|
|
{Child(result)}
|
|
</>;
|
|
}
|
|
`,
|
|
errors: [
|
|
makeTestCaseError('Hooks must always be called in a consistent order'),
|
|
],
|
|
},
|
|
{
|
|
name: 'Multiple diagnostics within the same file are surfaced',
|
|
code: normalizeIndent`
|
|
function useConditional1() {
|
|
'use memo';
|
|
return cond ?? useConditionalHook();
|
|
}
|
|
function useConditional2(props) {
|
|
'use memo';
|
|
return props.cond && useConditionalHook();
|
|
}`,
|
|
errors: [
|
|
makeTestCaseError('Hooks must always be called in a consistent order'),
|
|
makeTestCaseError('Hooks must always be called in a consistent order'),
|
|
],
|
|
},
|
|
{
|
|
name: "'use no forget' does not disable eslint rule",
|
|
code: normalizeIndent`
|
|
let count = 0;
|
|
function Component() {
|
|
'use no forget';
|
|
return cond ?? useConditionalHook();
|
|
|
|
}
|
|
`,
|
|
errors: [
|
|
makeTestCaseError('Hooks must always be called in a consistent order'),
|
|
],
|
|
},
|
|
{
|
|
name: 'Multiple non-fatal useMemo diagnostics are surfaced',
|
|
code: normalizeIndent`
|
|
import {useMemo, useState} from 'react';
|
|
|
|
function Component({item, cond}) {
|
|
const [prevItem, setPrevItem] = useState(item);
|
|
const [state, setState] = useState(0);
|
|
|
|
useMemo(() => {
|
|
if (cond) {
|
|
setPrevItem(item);
|
|
setState(0);
|
|
}
|
|
}, [cond, item, init]);
|
|
|
|
return <Child x={state} />;
|
|
}`,
|
|
errors: [
|
|
makeTestCaseError('useMemo() callbacks must return a value'),
|
|
makeTestCaseError(
|
|
'Calling setState from useMemo may trigger an infinite loop',
|
|
),
|
|
makeTestCaseError(
|
|
'Calling setState from useMemo may trigger an infinite loop',
|
|
),
|
|
],
|
|
},
|
|
{
|
|
name: 'Pipeline errors are reported',
|
|
code: normalizeIndent`
|
|
import useMyEffect from 'useMyEffect';
|
|
import {AUTODEPS} from 'react';
|
|
function Component({a}) {
|
|
'use no memo';
|
|
useMyEffect(() => console.log(a.b), AUTODEPS);
|
|
return <div>Hello world</div>;
|
|
}
|
|
`,
|
|
options: [
|
|
{
|
|
environment: {
|
|
inferEffectDependencies: [
|
|
{
|
|
function: {
|
|
source: 'useMyEffect',
|
|
importSpecifierName: 'default',
|
|
},
|
|
autodepsIndex: 1,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
errors: [
|
|
{
|
|
message: /Cannot infer dependencies of this effect/,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|