mirror of
https://github.com/facebook/react.git
synced 2026-02-22 03:42:05 +00:00
Add a fast heuristic to detect whether a file may contain React components or hooks before running the full compiler. This avoids the overhead of Babel AST parsing and compilation for utility files, config files, and other non-React code. The heuristic uses ESLint's already-parsed AST to check for functions with React-like names at module scope: - Capitalized functions: MyComponent, Button, App - Hook pattern functions: useEffect, useState, useMyCustomHook Files without matching function names are skipped and return an empty result, which is cached to avoid re-checking for subsequent rules. Also adds test coverage for the heuristic edge cases.
148 lines
4.2 KiB
TypeScript
148 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 {RuleTester} from 'eslint';
|
|
import {allRules} from '../src/shared/ReactCompiler';
|
|
|
|
const ESLintTesterV8 = require('eslint-v8').RuleTester;
|
|
|
|
/**
|
|
* A string template tag that removes padding from the left side of multi-line strings
|
|
* @param {Array} strings array of code strings (only one expected)
|
|
*/
|
|
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: RuleTester.ValidTestCase[];
|
|
invalid: RuleTester.InvalidTestCase[];
|
|
};
|
|
|
|
const tests: CompilerTestCases = {
|
|
valid: [
|
|
// ===========================================
|
|
// Tests for mayContainReactCode heuristic with Flow syntax
|
|
// Files that should be SKIPPED (no React-like function names)
|
|
// These contain code that WOULD trigger errors if compiled,
|
|
// but since the heuristic skips them, no errors are reported.
|
|
// ===========================================
|
|
{
|
|
name: '[Heuristic/Flow] Skips files with only lowercase utility functions',
|
|
filename: 'utils.js',
|
|
code: normalizeIndent`
|
|
function helper(obj) {
|
|
obj.key = 'value';
|
|
return obj;
|
|
}
|
|
`,
|
|
},
|
|
{
|
|
name: '[Heuristic/Flow] Skips lowercase arrow functions even with mutations',
|
|
filename: 'helpers.js',
|
|
code: normalizeIndent`
|
|
const processData = (input) => {
|
|
input.modified = true;
|
|
return input;
|
|
};
|
|
`,
|
|
},
|
|
],
|
|
invalid: [
|
|
// ===========================================
|
|
// Tests for mayContainReactCode heuristic with Flow component/hook syntax
|
|
// These use Flow's component/hook declarations which should be detected
|
|
// ===========================================
|
|
{
|
|
name: '[Heuristic/Flow] Compiles Flow component declaration - detects prop mutation',
|
|
filename: 'component.js',
|
|
code: normalizeIndent`
|
|
component MyComponent(a: {key: string}) {
|
|
a.key = 'value';
|
|
return <div />;
|
|
}
|
|
`,
|
|
errors: [
|
|
{
|
|
message: /Modifying component props/,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: '[Heuristic/Flow] Compiles exported Flow component declaration - detects prop mutation',
|
|
filename: 'component.js',
|
|
code: normalizeIndent`
|
|
export component MyComponent(a: {key: string}) {
|
|
a.key = 'value';
|
|
return <div />;
|
|
}
|
|
`,
|
|
errors: [
|
|
{
|
|
message: /Modifying component props/,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: '[Heuristic/Flow] Compiles default exported Flow component declaration - detects prop mutation',
|
|
filename: 'component.js',
|
|
code: normalizeIndent`
|
|
export default component MyComponent(a: {key: string}) {
|
|
a.key = 'value';
|
|
return <div />;
|
|
}
|
|
`,
|
|
errors: [
|
|
{
|
|
message: /Modifying component props/,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: '[Heuristic/Flow] Compiles Flow hook declaration - detects argument mutation',
|
|
filename: 'hooks.js',
|
|
code: normalizeIndent`
|
|
hook useMyHook(a: {key: string}) {
|
|
a.key = 'value';
|
|
return a;
|
|
}
|
|
`,
|
|
errors: [
|
|
{
|
|
message: /Modifying component props or hook arguments/,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: '[Heuristic/Flow] Compiles exported Flow hook declaration - detects argument mutation',
|
|
filename: 'hooks.js',
|
|
code: normalizeIndent`
|
|
export hook useMyHook(a: {key: string}) {
|
|
a.key = 'value';
|
|
return a;
|
|
}
|
|
`,
|
|
errors: [
|
|
{
|
|
message: /Modifying component props or hook arguments/,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
const eslintTester = new ESLintTesterV8({
|
|
parser: require.resolve('hermes-eslint'),
|
|
parserOptions: {
|
|
sourceType: 'module',
|
|
enableExperimentalComponentSyntax: true,
|
|
},
|
|
});
|
|
eslintTester.run('react-compiler', allRules['immutability'].rule, tests);
|