Skip to content

Instantly share code, notes, and snippets.

@nfarina
Created October 28, 2024 20:23
Show Gist options
  • Save nfarina/3e4fbecac32053c4b31053fbccc37b15 to your computer and use it in GitHub Desktop.
Save nfarina/3e4fbecac32053c4b31053fbccc37b15 to your computer and use it in GitHub Desktop.
React Compiler ESLint Plugin with all errors surfaced
import * as BabelParser from "@babel/parser";
import BabelTraverse from "@babel/traverse";
import {
compileProgram,
parsePluginOptions,
} from "babel-plugin-react-compiler";
const traverse = BabelTraverse["default"];
const parse = BabelParser.parse;
/**
* An ESLint plugin that attempts to actually run the React Compiler the way it
* does in the Playground. This will tell us if the component or hook has any
* issues that prevent it from being compiled. The eslint-plugin-react-compiler
* plugin doesn't do this, and instead just checks for a small variety of
* "Rules of React" problems.
*
* I wrote this before I knew about the `__unstable_donotuse_reportAllBailouts`
* option in the official plugin. But it turns out to be useful because we
* report the "compiler bailouts" with the correct location information,
* compared to the official plugin. Also the speed is identical.
*/
export default {
rules: {
"react-compiler": {
meta: {
type: "problem",
docs: {
description: "Detect React Compiler issues",
category: "Possible Errors",
},
},
create(context) {
return {
Program(node) {
const sourceCode = context.sourceCode.text;
const filename = context.filename;
// Parse the source code to get a Babel AST
const ast = parse(sourceCode, {
sourceType: "module",
plugins: ["jsx", "typescript"],
});
// Create a NodePath for the Program
let programPath;
traverse(ast, {
Program(path) {
programPath = path;
},
});
if (!programPath) {
context.report({
node,
message: "Failed to create Babel NodePath for Program",
});
return;
}
// Use default plugin options, but override panicThreshold to
// "all_errors" so all errors are thrown.
const opts = {
...parsePluginOptions(),
panicThreshold: "all_errors",
};
try {
compileProgram(programPath, {
opts,
filename: filename ?? null,
comments: ast.comments ?? [],
code: sourceCode,
});
} catch (error) {
for (const detail of error.details ?? []) {
context.report({
node: detail.node,
message: error.message,
loc: detail.options.loc,
});
}
}
},
};
},
},
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment