FAQ

これはesbuildに関するよくある質問を集めたものです。 GitHubのissue trackerでも質問できます。

なぜesbuildは速いのか?

いくつかの理由があります

これらの要因のそれぞれは、ある程度の速度向上にすぎませんが、これらを組み合わせることで、現在一般的に使用されている他のバンドラよりも桁違いに高速なバンドラを実現できます。

ベンチマークの詳細

各ベンチマークの詳細を以下に示します。

JavaScriptベンチマーク
esbuild
0.39秒
parcel 2
14.91秒
rollup 4 + terser
34.10秒
webpack 5
41.21秒
0秒
10秒
20秒
30秒
40秒

このベンチマークは、three.jsライブラリを10回複製し、キャッシュを使用せずにゼロから単一のバンドルをビルドすることにより、大規模なJavaScriptコードベースを近似しています。このベンチマークは、esbuildリポジトリで`make bench-three`を使用して実行できます。

バンドラ 時間 相対的な遅延 絶対速度 出力サイズ
esbuild 0.39秒 1倍 1403.7 kloc/s 5.80MB
parcel 2 14.91秒 38倍 36.7 kloc/s 5.78MB
rollup 4 + terser 34.10秒 87倍 16.1 kloc/s 5.82MB
webpack 5 41.21秒 106倍 13.3 kloc/s 5.84MB

報告された時間はすべて3回の実行のうち最良の結果です。esbuildは`--bundle --minify --sourcemap`で実行しています。`@rollup/plugin-terser`プラグインを使用したのは、Rollup自体がミニファイをサポートしていないためです。Webpack 5は`--mode=production --devtool=sourcemap`を使用します。Parcel 2はデフォルトのオプションを使用します。絶対速度は、コメントと空白行を含む合計行数に基づいており、現在547,441行です。テストは、16GBのRAMを搭載した6コアの2019 MacBook Proで、macOS Spotlightを無効にして行われました。

TypeScriptベンチマーク
esbuild
0.10秒
parcel 2
6.91秒
webpack 5
16.69秒
0秒
5秒
10秒
15秒

このベンチマークは、古いRomeコードベース(Rustによる書き換え前)を使用して、大規模なTypeScriptコードベースを近似しています。すべてのコードを単一のミニファイされたバンドルにソースマップ付きで結合する必要があり、結果のバンドルは正しく機能する必要があります。このベンチマークは、esbuildリポジトリで`make bench-rome`を使用して実行できます。

バンドラ 時間 相対的な遅延 絶対速度 出力サイズ
esbuild 0.10秒 1倍 1318.4 kloc/s 0.97MB
parcel 2 6.91秒 69倍 16.1 kloc/s 0.96MB
webpack 5 16.69秒 167倍 8.3 kloc/s 1.27MB

報告された時間はすべて3回の実行のうち最良の結果です。esbuildは`--bundle --minify --sourcemap --platform=node`で実行しています。Webpack 5は`ts-loader`を`transpileOnly: true`および`--mode=production --devtool=sourcemap`で使用しています。Parcel 2は`package.json`で`"engines": "node"`を使用します。絶対速度は、コメントと空白行を含む合計行数に基づいており、現在131,836行です。テストは、16GBのRAMを搭載した6コアの2019 MacBook Proで、macOS Spotlightを無効にして行われました。

TypeScriptコンパイルに関連する理由で動作させることができなかったため、結果はRollupを含んでいません。`@rollup/plugin-typescript`を試しましたが、型チェックを無効にすることができず、`@rollup/plugin-sucrase`を試しましたが、`tsconfig.json`ファイル(正しいパス解決に必要)を提供する方法がありません。

今後のロードマップ

これらの機能はすでに進行中で、最優先事項です。

これらは将来の潜在的な機能ですが、実現しない場合や、より限定的な範囲で実現する場合があります。

その時点以降、esbuildは比較的完成していると見なされます。esbuildがほぼ安定した状態に達し、それ以上の機能の追加を停止することを計画しています。これには、esbuild自体に主要な機能を追加する要求を「拒否する」ことが含まれます。esbuildはすべてのフロントエンドニーズに対するオールインワンのソリューションになるべきではないと考えています。特に、「webpack config」モデルの問題点と苦痛を避けたいと考えています。このモデルでは、基盤となるツールが柔軟すぎるため、使いやすさが損なわれます。

たとえば、esbuildのコア自体には次の機能を含める予定はありません

esbuild に追加している拡張ポイント(プラグインAPI)により、よりカスタマイズされたビルドワークフローの一部として esbuild を使用できるようになることを願っていますが、これらの拡張ポイントですべてのユースケースを網羅することを意図したり、期待したりしていません。非常に特殊な要件がある場合は、他のツールを使用する必要があります。また、esbuild が他のビルドツールの劇的なパフォーマンス向上を促し、実装を見直すことで、esbuild を使用する人だけでなく、すべての人が恩恵を受けられるようになると期待しています。

esbuild が安定版に到達した後も、esbuild の既存の範囲内のすべてのものを維持し続ける予定です。たとえば、新しくリリースされた JavaScript と TypeScript の構文機能のサポートを実装することを意味します。

本番環境への対応

このプロジェクトはまだバージョン 1.0.0 に到達しておらず、開発中です。とはいえ、アルファ段階をはるかに超えており、かなり安定しています。後期ベータ版と考えています。一部の先進的な利用者にとっては、実運用に十分なレベルです。一方、まだ準備が整っていないと考える人もいます。このセクションでは、どちらの意見にも説得しようとはしません。esbuild をバンドラとして使用するかどうかの判断に必要な情報を提供することを目的としています。

いくつかのデータポイント

ウイルス対策ソフトウェア

esbuild はネイティブコードで記述されているため、ウイルス対策ソフトウェアが誤ってウイルスとしてフラグを立てることがあります。これは esbuild がウイルスであることを意味するものではありません。私は悪意のあるコードを公開しておらず、サプライチェーンのセキュリティを非常に真剣に受け止めています。

esbuild のコードの大部分は、Google の追加 Go パッケージへの1 つの依存関係を除いて、ファーストパーティコードです。私の開発作業は、ビルドを公開するために使用するマシンとは別の、隔離されたマシンで行われています。esbuild の公開ビルドが完全に再現可能であることを確認するための追加作業を行い、リリース後には、公開されたビルドが自動的に比較され、関連のない環境でローカルに構築されたビルドとビット単位で同一であることが確認されます(つまり、Go コンパイラ自体が侵害されていないことを意味します)。ソースから自分で esbuild をビルドし、ビルド成果物を公開されたものと比較して、これを独立して検証することもできます。

誤検知に対処しなければならないのは、ウイルス対策ソフトウェアを使用する際の不幸な現実です。ウイルス対策ソフトが esbuild を使用できない場合の回避策をいくつか示します。

古いバージョンの Go

自動化された依存関係の脆弱性スキャナーを使用している場合、esbuild が使用する Go コンパイラのバージョンや golang.org/x/sys(esbuild の唯一の依存関係)のバージョンが古いというレポートを受け取る可能性があります。これらのレポートは良性であり、無視する必要があります。

これは、esbuild のコードが Go 1.13 でコンパイルできるように意図的に設計されているためです。後のバージョンの Go では、esbuild を実行できる古いプラットフォーム(たとえば、古いバージョンの macOS)のサポートが削除されています。esbuild の公開バイナリは新しいバージョンの Go コンパイラを使用してコンパイルされているため(そのため、古いバージョンの macOS では動作しません)、Go 1.13 で最新の esbuild を自分でコンパイルして古いバージョンの macOS で使用できます(esbuild のコードは Go 1.13 までコンパイルできます)。

人々や自動化ツールは、go.modgo 1.13 行を見て、esbuild の公開バイナリが非常に古いバージョンの Go である Go 1.13 を使用してビルドされていると不満を言うことがあります。しかし、それは正しくありません。go.mod のその行は、最小のコンパイラバージョンのみを指定します。esbuild の公開バイナリがビルドされている Go のバージョンとは関係ありません。これは、はるかに新しいバージョンの Go です。ドキュメントをお読みください。

また、esbuild が使用するバージョン(特に GO-2022-0493Faccessat 関数に関するもの)に既知の脆弱性があるため、golang.org/x/sys の依存関係を更新することを求める人もいます。esbuild が新しいバージョンの golang.org/x/sys 依存関係に更新できない理由は、新しいバージョンが unsafe.Slice 関数を使用し始めたことであり、これは Go 1.17 で初めて導入されたため(そのため、古いバージョンの Go ではコンパイルされません)。ただし、この脆弱性レポートは、a) esbuild はそもそもその関数を呼び出さないこと、b) esbuild はビルドツールでありサンドボックスではなく、esbuild のファイルシステムアクセスはセキュリティに敏感ではないため、無関係です。

古いプラットフォームとの互換性を維持せず、無関係な脆弱性レポートを回避するために一部の人が esbuild を使用できなくすることはありません。上記の記述されている問題に関するレポートは無視してください。

圧縮された改行

esbuild のミニファイアが通常、JavaScript 文字列内の文字エスケープシーケンス \n をテンプレートリテラルの改行文字に変更することに驚く人がいます。しかし、これは意図的なものです。**これは esbuild のバグではありません。**ミニファイアの役割は、入力と同等の可能な限りコンパクトな出力を生成することです。文字エスケープシーケンス \n は 2 バイト長ですが、改行文字は 1 バイト長です。

たとえば、このコードは 21 バイト長です。

var text="a\nb\nc\n";

一方、このコードは 18 バイト長です。

var text=`a
b
c
`;

したがって、2 番目のコードは完全にミニファイされているのに対し、最初のコードはそうではありません。コードのミニファイは、すべてを 1 行にすることを意味するものではありません。代わりに、可能な限り少ないバイト数を使用する同等のコードを生成することを意味します。JavaScript では、タグなしテンプレートリテラルは文字列リテラルと同等であるため、esbuild はここで正しいことを行っています。

名前の衝突の回避

エントリポイントモジュールの最上位レベルの変数は、esbuild の出力をブラウザで実行する場合、グローバルスコープに決して存在してはなりません。それが発生する場合は、esbuild の出力形式に関するドキュメントに従っておらず、esbuild を正しく使用していないことを意味します。**これは esbuild のバグではありません。**

具体的には、esbuild の出力をブラウザで実行する場合は、次のいずれかを行う必要があります。

  1. --format=iife<script src="...">

    コードをグローバルスコープで実行する場合は、--format=iife を使用する必要があります。これにより、esbuild の出力によってコードがラップされ、最上位レベルの変数がネストされたスコープで宣言されます。

  2. --format=esm<script src="..." type="module">

    --format=esm を使用する場合は、コードをモジュールとして実行する必要があります。これにより、ブラウザによってコードがラップされ、最上位レベルの変数がネストされたスコープで宣言されます。

<script src="...">--format=esm を使用すると、コードが微妙で分かりにくい方法で壊れます(type="module" を省略すると、最上位レベルの変数はすべてグローバルスコープに存在することになり、他の JavaScript ファイルの同じ名前の最上位レベルの変数と衝突します)。

最上位レベルの var

esbuild が最上位レベルの letconstclass 宣言を var 宣言として書き換えることがあることに驚く人がいます。これはいくつかの理由で行われます。

esbuildは、モジュールが遅延初期化される必要がある可能性があるため(上記の説明を参照)、宣言と初期化を分離する必要があるため、最上位のTDZ副作用を保持しません。最上位のシンボルに対するTDZチェックは、仮に、最上位のシンボルを使用する前にチェックを行い、初期化されていない場合は例外をスローする追加コードを生成することで(事実上、実際のJavaScript VMが行うことを手動で実装することで)サポートできる可能性があります。しかし、これはコードサイズと実行時間の両方のオーバーヘッドが過大であり、本番環境向けのバンドラが実行すべきことではないと思われます。