はじめに
「コードを1行変更しただけなのに、ビルドが終わるまで数秒〜数十秒待たされる」——フロントエンド開発をしていると、こうしたビルド待ちのストレスに心当たりがある方は多いのではないでしょうか。webpackやRollupといった従来のバンドラーは高機能な反面、プロジェクトが大きくなるほどビルド時間も伸びていきがちです。
esbuildは、この「ビルドが遅い」という課題に真正面から挑んだJavaScript/TypeScript向けのバンドラーです。公式サイトでは既存のバンドラーと比べて「10〜100倍高速」を謳っており、にわかには信じがたい数字ですが、実際に使ってみるとその速さは体感できるレベルで違います。本記事では、esbuildの特徴からインストール方法、実践的な使い方までを解説します。
esbuildとは
esbuildは、Go言語で実装されたJavaScript/TypeScript/CSS向けの高速バンドラー兼ミニファイアです。Evan Wallace氏によって開発され、単体でのバンドル・トランスパイル・ミニファイに加え、Vite・Next.jsなどの主要フレームワークやツールチェーンの内部でも広く採用されています。
多くのバンドラーがJavaScriptで実装されているのに対し、esbuildはGoで書かれておりネイティブコードとしてコンパイルされます。さらに並列処理を前提に設計されているため、キャッシュなしでも高速な処理を実現できる点が最大の強みです。
主な特徴
- 圧倒的なビルド速度 - Goによるネイティブ実装と並列処理設計により、キャッシュなしでもwebpackやRollupの10〜100倍のビルド速度を実現します
- TypeScript/JSXのネイティブサポート - 追加のトランスパイラを挟まずに
.tsや.tsxファイルをそのまま処理でき、型チェックを除いたコンパイルだけであれば非常に高速です - 豊富な出力形式 - ESM、CommonJS、IIFEなど複数の出力形式に対応し、ツリーシェイキングやミニフィケーション、ソースマップ生成も標準機能として搭載しています
- 柔軟な利用方法 - CLI、JavaScript API、Goパッケージのいずれからも利用でき、既存のビルドパイプラインへの組み込みも容易です
インストール
esbuildはnpm・yarn・pnpmのいずれからもインストールできます。
# npmの場合
npm install --save-dev esbuild
# yarnの場合
yarn add --dev esbuild
# pnpmの場合
pnpm add --save-dev esbuild
CLIのみを素早く試したい場合は、npx経由での実行も可能です。
npx esbuild --version
基本的な使い方
まずはCLIからシンプルなバンドルを行ってみましょう。エントリーポイントとなるsrc/app.tsを指定し、バンドル・ミニファイした結果をdist/app.jsに出力します。
npx esbuild src/app.ts --bundle --minify --outfile=dist/app.js
JavaScript APIを使う場合は、Node.jsのスクリプトから直接ビルドを実行できます。以下はbuild.mjsとしてビルドスクリプトを定義する例です。
// build.mjs
import * as esbuild from "esbuild";
await esbuild.build({
entryPoints: ["src/app.ts"],
bundle: true,
minify: true,
sourcemap: true,
outfile: "dist/app.js",
target: ["es2020"],
});
console.log("ビルドが完了しました");
node build.mjs
開発時にファイル変更を検知して自動ビルドしたい場合は、watchオプションではなく、esbuildが提供するコンテキストAPIを使います。
// dev.mjs
import * as esbuild from "esbuild";
const ctx = await esbuild.context({
entryPoints: ["src/app.ts"],
bundle: true,
outfile: "dist/app.js",
sourcemap: true,
});
await ctx.watch();
console.log("ファイルの変更を監視しています...");
実践的なユースケース
ローカル開発サーバーとの組み合わせ
esbuildにはビルド成果物を配信する簡易サーバー機能が組み込まれており、開発中のプレビューにそのまま使えます。
// serve.mjs
import * as esbuild from "esbuild";
const ctx = await esbuild.context({
entryPoints: ["src/app.ts"],
bundle: true,
outfile: "dist/app.js",
sourcemap: true,
});
const { host, port } = await ctx.serve({
servedir: "dist",
port: 8000,
});
console.log(`開発サーバーを起動しました: http://${host}:${port}`);
複数エントリーポイントの一括ビルド
ライブラリやモノレポで複数のエントリーポイントを扱う場合も、配列で指定するだけで対応できます。
// build-multi.mjs
import * as esbuild from "esbuild";
await esbuild.build({
entryPoints: ["src/index.ts", "src/cli.ts", "src/worker.ts"],
bundle: true,
outdir: "dist",
format: "esm",
target: ["node18"],
platform: "node",
});
既存ツールとの共存
esbuildは型チェックを行わないため、TypeScriptの型安全性を保ちたい場合はtsc --noEmitと組み合わせるのが定石です。ビルドは高速なesbuildに任せ、型チェックはCIやpre-commitフックで別途実行することで、開発体験を損なわずに安全性も確保できます。
{
"scripts": {
"build": "node build.mjs",
"typecheck": "tsc --noEmit"
}
}
まとめ
esbuildは、Goによるネイティブ実装と並列処理設計によって、従来のJavaScriptバンドラーとは一線を画す速度を実現するツールです。CLI・JavaScript API・Goパッケージという複数のインターフェースを備え、単体でのビルドはもちろん、Viteをはじめとする多くのツールの内部エンジンとしても採用されている実績が、その信頼性を裏付けています。
型チェックのような一部の責務は他のツールに任せる必要があるものの、その割り切りこそがesbuildの速さの源泉です。ビルド待ちのストレスに悩んでいるプロジェクトがあれば、まずは小さなスクリプトからesbuild.build()を試してみてはいかがでしょうか。