import { ErrorSeverity } from "babel-plugin-react-compiler/src";
import { RuleTester as ESLintTester } from "eslint";
import ReactCompilerRule from "../src/rules/ReactCompilerRule";
function normalizeIndent(strings: TemplateStringsArray): string {
const codeLines = strings[0].split("\n");
const leftPadding = codeLines[1].match(/\s+/)[0];
return codeLines.map((line) => line.slice(leftPadding.length)).join("\n");
}
type CompilerTestCases = {
valid: ESLintTester.ValidTestCase[];
invalid: ESLintTester.InvalidTestCase[];
};
const tests: CompilerTestCases = {
valid: [
{
name: "Basic example",
code: normalizeIndent`
function foo(x, y) {
if (x) {
return foo(false, y);
}
return [y * 10];
}
`,
},
{
name: "Violation with Flow suppression",
code: `
// Valid since error already suppressed with flow.
function useHookWithHook() {
if (cond) {
// $FlowFixMe[react-rule-hook]
useConditionalHook();
}
}
`,
},
{
name: "Basic example with component syntax",
code: normalizeIndent`
export default component HelloWorld(
text: string = 'Hello!',
onClick: () => void,
) {
return <div onClick={onClick}>{text}</div>;
}
`,
},
{
name: "Unsupported syntax",
code: normalizeIndent`
function foo(x) {
var y = 1;
return y * x;
}
`,
},
{
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() {}
}
`,
},
{
name: "[InvalidInput] Ref access during render",
code: normalizeIndent`
function Component(props) {
const ref = useRef(null);
const value = ref.current;
return value;
}
`,
},
],
invalid: [
{
name: "Reportable levels can be configured",
options: [{ reportableLevels: new Set([ErrorSeverity.Todo]) }],
code: normalizeIndent`
function Foo(x) {
var y = 1;
return <div>{y * x}</div>;
}`,
errors: [
{
message:
"(BuildHIR::lowerStatement) Handle var kinds in VariableDeclaration",
},
],
},
{
name: "[InvalidReact] ESlint suppression",
code: normalizeIndent`
function Component(props) {
// eslint-disable-next-line react-hooks/rules-of-hooks
return <div>{props.foo}</div>;
}`,
errors: [
{
message:
"React Compiler has skipped optimizing this component because one or more React ESLint rules were disabled. React Compiler only works when your components follow all the rules of React, disabling them may result in unexpected or incorrect behavior",
suggestions: [
{
output: normalizeIndent`
function Component(props) {
return <div>{props.foo}</div>;
}`,
},
],
},
{
message:
"Definition for rule 'react-hooks/rules-of-hooks' was not found.",
},
],
},
],
};
const eslintTester = new ESLintTester({
parser: require.resolve("hermes-eslint"),
parserOptions: {
ecmaVersion: 2015,
sourceType: "module",
enableExperimentalComponentSyntax: true,
},
});
eslintTester.run("react-compiler", ReactCompilerRule, tests);