esbuild-register
esbuild-register is a utility that enables on-the-fly transpilation of JSX, TypeScript, and modern JavaScript (esnext) features in Node.js environments by leveraging the highly performant esbuild bundler. It functions as a Node.js `require` hook or an experimental `--loader`, allowing developers to execute TypeScript or JSX files directly without a prior build step. The current stable version is 3.6.0. While widely used, the package has a slow release cadence, with the last update two years ago, leading to it being classified as having 'not healthy' project activity by some analyses. Its primary differentiator compared to alternatives like `ts-node` or `babel-register` is its speed, attributed to `esbuild` being written in Go and optimized for fast compilation. It automatically respects `jsxFactory`, `jsxFragmentFactory`, and `target` options from `tsconfig.json`.
Common errors
-
SyntaxError: Cannot use import statement outside a module
cause Using `node -r esbuild-register file.ts` in an ESM project (`"type": "module"` in package.json) or when Node.js's experimental loader behavior changed in 20.6.0.fixFor ESM projects, use `node --loader esbuild-register/loader -r esbuild-register ./file.ts`. If on Node.js 20.6.0+, this error might indicate incompatibility due to Node.js's loader changes; consider a Node.js version downgrade or an alternative loader. -
Error: Cannot find module 'esbuild'
cause The peer dependency `esbuild` was not installed alongside `esbuild-register`.fixInstall `esbuild` explicitly: `npm install esbuild esbuild-register -D` (or `yarn add esbuild esbuild-register --dev`, `pnpm add esbuild esbuild-register -D`). -
TypeError: (0 , esbuild_register_dist_node_1.register) is not a function
cause Attempting to `import { register } from 'esbuild-register/dist/node'` in an ESM context when the module is CJS-only or doesn't provide a named ESM export for `register`.fixUse CommonJS `require` for programmatic access: `const { register } = require('esbuild-register/dist/node')`.
Warnings
- breaking The experimental ESM loader (esbuild-register/loader) stopped working in Node.js 20.6.0 due to internal changes in Node.js's ESM loader implementation. This often manifested as 'SyntaxError: Cannot use import statement outside a module'. While potential fixes or workarounds might exist, relying on experimental Node.js features carries inherent risks of breakage in minor updates.
- gotcha esbuild-register's peer dependency `esbuild` (current range `^0.12.0 <1`) is subject to frequent breaking changes in its own minor versions (e.g., esbuild v0.17 introduced breaking changes to `watch()`, `rebuild()`, and `serve()` APIs). While `esbuild-register` might generally work with newer `esbuild` versions within its peer dependency range, specific features or underlying API calls might break if esbuild-register has not been updated to accommodate `esbuild`'s breaking changes.
- gotcha esbuild-register is a runtime transpiler, meaning it transpiles code on demand when Node.js encounters it. While significantly faster than other runtime transpilers, it still incurs overhead compared to a dedicated build step using `esbuild` directly as a bundler. For production deployments or large-scale applications, pre-compilation is generally recommended for optimal performance.
- deprecated The Node.js `--loader` flag used by `esbuild-register/loader` is still considered experimental by Node.js, even if `esbuild-register` exposes it. This means its behavior can change in future Node.js versions without adhering to semantic versioning, potentially leading to unexpected breakages.
Install
-
npm install esbuild-register -
yarn add esbuild-register -
pnpm add esbuild-register
Imports
- CLI Runtime Hook
node -r esbuild-register file.ts
- ESM Loader
node -r esbuild-register ./file.ts (when 'type': 'module')
node --loader esbuild-register/loader -r esbuild-register ./file.ts
- Programmatic Register
import { register } from 'esbuild-register/dist/node'const { register } = require('esbuild-register/dist/node')
Quickstart
/* file.ts */
import { createServer } from 'http';
interface Greeter {
greet(name: string): string;
}
const myGreeter: Greeter = {
greet(name: string): string {
return `Hello, ${name}! This is esbuild-register running TypeScript.`;
},
};
const server = createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(myGreeter.greet('World') + '\n');
});
const PORT = process.env.PORT ?? '3000';
server.listen(Number(PORT), () => {
console.log(`Server running at http://localhost:${PORT}/`);
console.log('Try running with: node -r esbuild-register file.ts');
console.log('Or for ESM projects: node --loader esbuild-register/loader -r esbuild-register file.ts');
});