Skip to content

Instantly share code, notes, and snippets.

@sikanhe
Last active November 17, 2024 20:14
Show Gist options
  • Save sikanhe/f9ac68dd4c78c914c29cc98e7b875466 to your computer and use it in GitHub Desktop.
Save sikanhe/f9ac68dd4c78c914c29cc98e7b875466 to your computer and use it in GitHub Desktop.
React Compiler plugin for ESBuild
import { readFileSync } from "node:fs"
import * as babel from "@babel/core"
import BabelPluginReactCompiler from "babel-plugin-react-compiler"
import type { Plugin } from "esbuild"
import QuickLRU from "quick-lru"
export function ReactCompilerEsbuildPlugin({
filter,
sourceMaps,
runtimeModulePath,
}: { filter: RegExp; sourceMaps: boolean; runtimeModulePath: string }): Plugin {
return {
name: "esbuild-react-compiler-plugin",
setup(build) {
// Cache previous outputs for incremental rebuilds
const buildCache = new QuickLRU<string, string>({ maxSize: 1000 })
let timings: number[] = []
build.onEnd(() => {
if (timings.length < 1) return
const totalTime = timings.reduce((sum, x) => sum + x, 0).toFixed(0)
console.log(`[⚛️ React Compiler] ${timings.length} files changed`)
console.log(`[⚛️ React Compiler] Used ${totalTime} ms`)
timings = []
})
build.onLoad({ filter, namespace: "" }, (args) => {
const contents = readFileSync(args.path, "utf8")
const t0 = performance.now()
if (buildCache.has(contents)) {
return {
contents: buildCache.get(contents),
loader: "js",
}
}
const output = build.esbuild.transformSync(contents, {
loader: "tsx",
jsx: "automatic",
define: build.initialOptions.define,
target: build.initialOptions.target,
})
const transformResult = babel.transformSync(output.code, {
plugins: [
// Warning: using string config here (ie 'babel-plugin-react-compiler') instead of the directly
// imported object is much slower than directly passing the plugin object because
// Babel has to resolve the plugin file from node_modules
[
BabelPluginReactCompiler,
{
runtimeModule: runtimeModulePath,
},
],
],
filename: args.path,
caller: {
name: "esbuild-react-compiler-plugin",
supportsStaticESM: true,
},
// TODO: figure out sourcemap setting and chaining
sourceMaps,
})
timings.push(performance.now() - t0)
if (transformResult?.code) {
buildCache.set(contents, transformResult?.code)
}
return {
contents: transformResult?.code ?? undefined,
loader: "js",
}
})
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment