esbuild の使い方 — 超高速JavaScriptバンドラー&ミニファイアー完全ガイド
一言でいうと
esbuildは、Go言語で実装された極めて高速なJavaScript/CSSバンドラー兼ミニファイアーです。従来のバンドラー(webpack等)と比較して10〜100倍の速度でビルドを完了し、開発体験を劇的に改善します。
どんな時に使う?
- 高速なプロダクションビルドが必要な時 — 大規模プロジェクトでwebpackのビルド時間がボトルネックになっている場合の代替・補完として
- TypeScript/JSXのトランスパイル — tscやBabelを使わず、TypeScriptやJSXを高速にJavaScriptへ変換したい場合
- ライブラリのバンドル&配布 — npmパッケージとして配布するライブラリをESM/CJS両形式でバンドルしたい場合
- CSSのバンドル&ミニファイ — CSS Modulesやcssファイルの結合・圧縮を高速に行いたい場合
インストール
# npm
npm install --save-dev esbuild
# yarn
yarn add --dev esbuild
# pnpm
pnpm add -D esbuild
注意: esbuildはプラットフォーム固有のネイティブバイナリを含むため、インストール時にOS/アーキテクチャに対応したバイナリが自動的にダウンロードされます。
基本的な使い方
CLIでの使い方
# 単一ファイルのバンドル
npx esbuild src/index.ts --bundle --outfile=dist/index.js
# プロダクションビルド(ミニファイ付き)
npx esbuild src/index.ts --bundle --minify --sourcemap --target=es2020 --outfile=dist/index.js
JavaScript APIでの使い方(TypeScript)
import * as esbuild from 'esbuild';
// 基本的なビルド
const result = await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
minify: true,
sourcemap: true,
target: ['es2020'],
outfile: 'dist/index.js',
});
console.log('Build completed:', result);
package.json でのスクリプト設定
{
"scripts": {
"build": "esbuild src/index.ts --bundle --minify --sourcemap --outfile=dist/index.js",
"dev": "esbuild src/index.ts --bundle --sourcemap --watch --outfile=dist/index.js"
}
}
よく使うAPI
1. build — ファイルシステムへのビルド出力
最も基本的なAPIです。エントリーポイントからバンドルし、ファイルに出力します。
import * as esbuild from 'esbuild';
const result = await esbuild.build({
entryPoints: ['src/index.ts', 'src/worker.ts'],
bundle: true,
minify: true,
splitting: true,
format: 'esm',
target: ['es2020'],
outdir: 'dist',
sourcemap: true,
// 外部依存として除外
external: ['express', 'pg'],
// Node.js向け
platform: 'node',
});
// ビルドの警告・エラーを確認
if (result.warnings.length > 0) {
console.warn('Warnings:', result.warnings);
}
2. transform — 文字列のインメモリ変換
ファイルを読み書きせず、文字列を直接変換します。ミニファイやトランスパイルに便利です。
import * as esbuild from 'esbuild';
const tsCode = `
interface User {
name: string;
age: number;
}
const greet = (user: User): string => {
return \`Hello, \${user.name}!\`;
};
export { greet };
`;
const result = await esbuild.transform(tsCode, {
loader: 'ts',
minify: true,
target: 'es2020',
});
console.log(result.code);
// => "const greet=e=>`Hello, ${e.name}!`;export{greet};"
3. context — インクリメンタルビルド&ウォッチモード
開発時に変更を監視して自動リビルドを行います。buildと異なり、ビルドコンテキストを保持するため再ビルドが高速です。
import * as esbuild from 'esbuild';
const ctx = await esbuild.context({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
sourcemap: true,
});
// ファイル変更を監視して自動リビルド
await ctx.watch();
console.log('Watching for changes...');
// 開発サーバーも起動可能
const { host, port } = await ctx.serve({
servedir: 'dist',
port: 3000,
});
console.log(`Serving on http://${host}:${port}`);
// 終了時にリソースを解放
// await ctx.dispose();
4. buildSync / transformSync — 同期版API
非同期処理が使えない環境向けの同期版です。パフォーマンス上は非同期版が推奨されます。
import * as esbuild from 'esbuild';
// 同期ビルド
const buildResult = esbuild.buildSync({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
write: false, // ファイルに書き込まず結果を返す
});
// 出力ファイルの内容をメモリ上で取得
for (const file of buildResult.outputFiles!) {
console.log(`${file.path}: ${file.text.length} chars`);
}
// 同期トランスフォーム
const transformResult = esbuild.transformSync('const x: number = 1;', {
loader: 'ts',
});
console.log(transformResult.code); // => "const x = 1;\n"
5. プラグインAPI — カスタムビルドロジックの追加
esbuildの処理パイプラインに独自のロジックを差し込めます。
import * as esbuild from 'esbuild';
import fs from 'node:fs';
// 環境変数を注入するプラグインの例
const envPlugin: esbuild.Plugin = {
name: 'env-plugin',
setup(build) {
// .env ファイルの読み込みをインターセプト
build.onResolve({ filter: /^env$/ }, (args) => ({
path: args.path,
namespace: 'env-ns',
}));
build.onLoad({ filter: /.*/, namespace: 'env-ns' }, () => ({
contents: JSON.stringify(process.env),
loader: 'json',
}));
},
};
// SVGをコンポーネントとして読み込むプラグインの例
const svgPlugin: esbuild.Plugin = {
name: 'svg-plugin',
setup(build) {
build.onLoad({ filter: /\.svg$/ }, async (args) => {
const svg = await fs.promises.readFile(args.path, 'utf8');
return {
contents: `export default ${JSON.stringify(svg)}`,
loader: 'js',
};
});
},
};
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
plugins: [envPlugin, svgPlugin],
});
類似パッケージとの比較
| 特徴 | esbuild | webpack | Rollup | swc | Vite |
|---|---|---|---|---|---|
| 言語 | Go | JavaScript | JavaScript | Rust | JavaScript (内部でesbuild/Rollup使用) |
| ビルド速度 | ◎ 極めて高速 | △ 遅い | ○ 普通 | ◎ 極めて高速 | ◎ 高速(dev時) |
| プラグインエコシステム | △ 限定的 | ◎ 非常に豊富 | ◎ 豊富 | ○ 成長中 | ◎ 豊富 |
| コード分割 | ○ ESMのみ | ◎ 柔軟 | ◎ 柔軟 | △ バンドラーではない | ◎ 柔軟 |
| HMR | △ 基本的 | ◎ 成熟 | ○ プラグイン依存 | — | ◎ 高速 |
| 設定の複雑さ | ◎ シンプル | △ 複雑 | ○ 普通 | ◎ シンプル | ○ 普通 |
| 本番ビルド向き | ○ | ◎ | ◎ | — | ◎(内部でRollup) |
| 主な用途 | ライブラリ、高速ビルド | 大規模アプリ | ライブラリ | トランスパイル | フロントエンドアプリ |
補足: Viteは開発時にesbuildをトランスパイルに使用し、本番ビルドにはRollupを使用しています。esbuildを直接使うか、Viteのようなesbuild統合ツールを使うかはプロジェクトの要件次第です。
注意点・Tips
1. Tree Shakingの挙動に注意
esbuildのTree Shakingは高速化のためにやや保守的です。副作用のあるコードは除去されない場合があります。
// package.json で sideEffects を明示すると効果的
// { "sideEffects": false }
// または、ビルド設定で pure アノテーションを活用
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
treeShaking: true,
// 副作用がないと明示する関数
pure: ['console.log', 'console.debug'],
drop: ['debugger'],
outfile: 'dist/index.js',
});
2. TypeScriptの型チェックは行わない
esbuildはTypeScriptのトランスパイルのみを行い、型チェックは一切行いません。型チェックは別途tsc --noEmitで実行してください。
{
"scripts": {
"typecheck": "tsc --noEmit",
"build": "npm run typecheck && esbuild src/index.ts --bundle --outfile=dist/index.js"
}
}
3. defineでコンパイル時定数を注入
await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
outfile: 'dist/index.js',
define: {
'process.env.NODE_ENV': '"production"',
'__APP_VERSION__': '"1.2.3"',
'DEBUG': 'false',
},
});
4. CJS/ESMデュアルパッケージの出力
ライブラリ作者向けに、両形式を同時に出力できます。
import * as esbuild from 'esbuild';
const sharedConfig: esbuild.BuildOptions = {
entryPoints: ['src/index.ts'],
bundle: true,
sourcemap: true,
external: ['react', 'react-dom'], // peerDepsは外部化
};
// ESM
await esbuild.build({
...sharedConfig,
format: 'esm',
outfile: 'dist/index.mjs',
});
// CJS
await esbuild.build({
...sharedConfig,
format: 'cjs',
outfile: 'dist/index.cjs',
});
5. --analyzeでバンドルサイズを可視化
npx esbuild src/index.ts --bundle --minify --analyze --outfile=dist/index.js
metafile: trueオプションを使えば、プログラムからも解析できます。
const result = await esbuild.build({
entryPoints: ['src/index.ts'],
bundle: true,
metafile: true,
outfile: 'dist/index.js',
});
// 人間が読みやすい形式で出力
const analysis = await esbuild.analyzeMetafile(result.metafile, {
verbose: true,
});
console.log(analysis);
6. CSS Modulesとの連携
await esbuild.build({
entryPoints: ['src/app.css'],
bundle: true,
outdir: 'dist',
// CSSのミニファイ
minify: true,
// ローダーの設定
loader: {
'.png': 'file',
'.woff2': 'file',
'.svg': 'dataurl',
},
});
まとめ
esbuildは、ビルド速度に特化した実用的なバンドラーです。Go言語による実装がもたらす圧倒的な速度は、開発体験とCI/CDパイプラインの両方を大幅に改善します。webpackほどの柔軟なプラグインエコシステムは持ちませんが、ライブラリのバンドルや高速トランスパイルといった用途では最適な選択肢です。
大規模なフロントエンドアプリケーションでは、esbuildを内部で活用するViteやtsupなどのツールと組み合わせることで、esbuildの速度とリッ