[compiler:eslint] Fix false positive with TS type param syntax

Previously we would attempt to parse code in the eslint plugin with the
HermesParser first as it can handle some TS syntax. However, this was
leading to a mis-parse of React hook calls with type params (eg,
`useRef<null>()` as a BinaryExpression rather than a CallExpression with
a type param. This triggered our validation that Hooks should not be
used as normal values.

To fix this, we now try to parse with the babel parser (with TS support)
for filenames that end with ts/tsx, and fallback to HermesParser for
regular JS files.

ghstack-source-id: 5b7231031c
Pull Request resolved: https://github.com/facebook/react/pull/29081
This commit is contained in:
Lauren Tan
2024-05-15 15:44:21 -07:00
parent 3adca7a477
commit cf7d895db6
2 changed files with 16 additions and 26 deletions
@@ -34,10 +34,8 @@ const tests: CompilerTestCases = {
}
`,
},
],
invalid: [
{
name: "[FALSE POSITIVE] Repro for hooks as normal values",
name: "Repro for hooks as normal values",
filename: "test.tsx",
code: normalizeIndent`
function Button(props) {
@@ -45,13 +43,9 @@ const tests: CompilerTestCases = {
return <Button thing={scrollview} />;
}
`,
errors: [
{
message:
"Hooks may not be referenced as normal values, they must be called. See https://react.dev/reference/rules/react-calls-components-and-hooks#never-pass-around-hooks-as-regular-values",
},
],
},
],
invalid: [
{
name: "Mutating useState value",
filename: "test.tsx",
@@ -114,29 +114,25 @@ const rule: Rule.RuleModule = {
}
let babelAST;
try {
// first try parsing with the faster Hermes that also supports JS, Flow
// and most TS syntax
if (
context.filename.endsWith(".tsx") ||
context.filename.endsWith(".ts")
) {
try {
const { parse: babelParse } = require("@babel/parser");
babelAST = babelParse(sourceCode, {
filename,
sourceType: "unambiguous",
plugins: ["typescript", "jsx"],
});
} catch {}
} else {
babelAST = HermesParser.parse(sourceCode, {
babel: true,
enableExperimentalComponentSyntax: true,
sourceFilename: filename,
sourceType: "module",
});
} catch {
// If Hermes fails, try Babel for advanced TS syntax.
if (
context.filename.endsWith(".tsx") ||
context.filename.endsWith(".ts")
) {
try {
const { parse: babelParse } = require("@babel/parser");
babelAST = babelParse(sourceCode, {
sourceType: "unambiguous",
plugins: ["typescript", "jsx"],
});
} catch {}
}
}
if (babelAST != null) {