API
API は、コマンドライン、JavaScript、Go の 3 つの言語のいずれかでアクセスできます。コンセプトとパラメータは 3 つの言語間でほぼ同じなので、言語ごとに個別のドキュメントを用意する代わりに、ここでまとめて説明します。コード例の右上隅にある CLI
、JS
、Go
のタブを使用して言語を切り替えることができます。各言語の具体的な内容
CLI: コマンドライン API を使用している場合は、フラグが
--foo
、--foo=bar
、または--foo:bar
の 3 つの形式のいずれかであることを知っておくと役立つでしょう。--foo
の形式は--minify
のようなブールフラグを有効にするために使用され、--foo=bar
の形式は--platform=
のように単一の値を持つフラグで、一度だけ指定される場合に使用され、--foo:bar
の形式は--external:
のように複数の値を持ち、複数回再指定できるフラグに使用されます。JavaScript: JavaScript を使用している場合は、以下の JS 固有の詳細と ブラウザセクションを確認してください。また、esbuild の TypeScript 型定義もリファレンスとして役立つかもしれません。
Go: Go を使用している場合は、esbuild 用に自動生成された Go ドキュメントがリファレンスとして役立つでしょう。パブリックな Go パッケージには、
pkg/api
とpkg/cli
の 2 つがあり、それぞれに個別のドキュメントがあります。
#概要
最も一般的に使用される esbuild API は、build と transform の 2 つです。それぞれについて、以下に概要を説明し、次に個々の API オプションのドキュメントを示します。
#ビルド
これは esbuild の主要なインターフェースです。通常、処理する 1 つ以上のエントリーポイントファイルをさまざまなオプションとともに渡すと、esbuild は結果をファイルシステムに書き戻します。以下は、バンドルを出力ディレクトリで有効にする簡単な例です。
esbuild app.ts --bundle --outdir=dist
import * as esbuild from 'esbuild'
let result = await esbuild.build({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'dist',
})
console.log(result)
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "dist",
})
if len(result.Errors) != 0 {
os.Exit(1)
}
}
ビルド API の高度な使用法には、長時間実行されるビルドコンテキストの設定が含まれます。このコンテキストは、JS および Go では明示的なオブジェクトですが、CLI では暗黙的です。特定のコンテキストで行われたすべてのビルドは、同じビルドオプションを共有し、後続のビルドはインクリメンタルに行われます (つまり、以前のビルドからのいくつかの作業を再利用してパフォーマンスを向上させます)。これは開発に役立ちます。esbuild は、作業中にバックグラウンドでアプリを再構築できるためです。
3 つの異なるインクリメンタルビルド API があります。
- 監視モード は、esbuild にファイルシステムを監視し、ビルドを無効にする可能性のあるファイルを編集して保存するたびに、自動的に再構築するように指示します。以下に例を示します。
esbuild app.ts --bundle --outdir=dist --watch [watch] build finished, watching for changes...
let ctx = await esbuild.context({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'dist',
})
await ctx.watch()
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "dist",
})
err2 := ctx.Watch(api.WatchOptions{})
- サーバーモード は、最新のビルドの結果を提供するローカル開発サーバーを開始します。受信リクエストは自動的に新しいビルドを開始するため、ブラウザでページをリロードすると、Web アプリは常に最新の状態になります。以下に例を示します。
esbuild app.ts --bundle --outdir=dist --serve > Local: http://127.0.0.1:8000/ > Network: http://192.168.0.1:8000/ 127.0.0.1:61302 - "GET /" 200 [1ms]
let ctx = await esbuild.context({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'dist',
})
let { host, port } = await ctx.serve()
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "dist",
})
server, err2 := ctx.Serve(api.ServeOptions{})
- リビルドモード を使用すると、手動でビルドを呼び出すことができます。これは、esbuild を他のツールと統合する場合に役立ちます (たとえば、esbuild の組み込みのもの以外のカスタムファイルウォッチャーまたは開発サーバーを使用する場合)。以下に例を示します。
# The CLI does not have an API for "rebuild"
let ctx = await esbuild.context({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'dist',
})
for (let i = 0; i < 5; i++) {
let result = await ctx.rebuild()
}
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "dist",
})
for i := 0; i < 5; i++ {
result := ctx.Rebuild()
}
これら 3 つのインクリメンタルビルド API は組み合わせることができます。ライブリロード (ファイルを編集して保存したときに自動的にページをリロードする) を有効にするには、同じコンテキストで 監視と サーバーを一緒に有効にする必要があります。
コンテキストオブジェクトが完了したら、コンテキストで dispose()
を呼び出して、既存のビルドが完了するのを待ち、監視モードおよび/またはサーバーモードを停止し、リソースを解放できます。
ビルド API とコンテキスト API は、両方とも次のオプションを使用します。
#変換
これは、ビルドの限定的な特殊ケースであり、他のファイルから完全に切り離された分離された環境にあるインメモリファイルを表現するコード文字列を変換します。一般的な用途には、コードの最小化と TypeScript から JavaScript への変換が含まれます。以下に例を示します。
echo 'let x: number = 1' | esbuild --loader=ts
let x = 1;
import * as esbuild from 'esbuild'
let ts = 'let x: number = 1'
let result = await esbuild.transform(ts, {
loader: 'ts',
})
console.log(result)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
ts := "let x: number = 1"
result := api.Transform(ts, api.TransformOptions{
Loader: api.LoaderTS,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
ファイルではなく文字列を入力として取得すると、特定のユースケースでより人間工学的になります。ファイルシステムの分離には特定の利点 (たとえば、ブラウザで動作する、近くの package.json
ファイルの影響を受けない) と特定の欠点 (たとえば、バンドルまたはプラグインで使用できない) があります。ユースケースが transform API に適合しない場合は、より一般的な ビルド API を使用する必要があります。
transform API は、次のオプションを使用します。
#JS固有の詳細
esbuild の JS API には、非同期と同期の 2 つのフレーバーがあります。非同期 API は、すべての環境で動作し、より高速で強力なため、推奨されます。同期 API は node でのみ動作し、特定の操作しか実行できませんが、node 固有の特定の状況では必要な場合があります。詳細
#非同期 API
非同期 API 呼び出しは、Promise を使用して結果を返します。import
キーワードとトップレベルの await
キーワードを使用するため、node で .mjs
ファイル拡張子を使用する必要がある可能性が高いことに注意してください。
import * as esbuild from 'esbuild'
let result1 = await esbuild.transform(code, options)
let result2 = await esbuild.build(options)
長所
- 非同期 API でプラグインを使用できます
- 現在のスレッドはブロックされないため、その間に他の作業を実行できます
- 多くの同時 esbuild API 呼び出しを同時に実行でき、それらは利用可能なすべての CPU に分散されてパフォーマンスを最大化します
短所
- Promise を使用すると、特に トップレベル await が使用できない CommonJS では、コードが複雑になる可能性があります
require
内のように同期する必要がある状況では動作しません.extensions
#同期 API
同期 API 呼び出しは、結果をインラインで返します
let esbuild = require('esbuild')
let result1 = esbuild.transformSync(code, options)
let result2 = esbuild.buildSync(options)
長所
- Promise を回避すると、特に トップレベル await が使用できない場合に、コードがより簡潔になります
require
内のように同期する必要がある状況で動作します.extensions
短所
- プラグインは非同期であるため、同期 API でプラグインを使用することはできません
- 現在のスレッドがブロックされるため、その間に他の作業を実行できません
- 同期 API を使用すると、esbuild が esbuild API 呼び出しを並列化するのを防ぎます
#ブラウザ内
esbuild API は、Web Worker で WebAssembly を使用してブラウザでも実行できます。これを利用するには、esbuild
パッケージの代わりに esbuild-wasm
パッケージをインストールする必要があります。
npm install esbuild-wasm
ブラウザ用の API は、最初に initialize()
を呼び出す必要があることと、WebAssembly バイナリの URL を渡す必要があることを除いて、node 用の API と似ています。また、API の同期バージョンは利用できません。バンドラーを使用していると仮定すると、次のようになります
import * as esbuild from 'esbuild-wasm'
await esbuild.initialize({
wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
})
let result1 = await esbuild.transform(code, options)
let result2 = esbuild.build(options)
ワーカーからこのコードを既に実行していて、initialize
が別のワーカーを作成したくない場合は、worker:
を渡すことができます。これにより、initialize
を呼び出すスレッドと同じスレッドで WebAssembly モジュールが作成されます。
また、<script>
タグで lib/browser.min.js
ファイルを読み込むことで、バンドラーを使用せずに HTML ファイル内のスクリプトタグとして esbuild の API を使用することもできます。この場合、API は API オブジェクトを保持する esbuild
というグローバルを作成します
<script src="./node_modules/esbuild-wasm/lib/browser.min.js"></script>
<script>
esbuild.initialize({
wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
}).then(() => {
...
})
</script>
この API を ECMAScript モジュールで使用する場合は、代わりに esm/browser.min.js
ファイルをインポートする必要があります
<script type="module">
import * as esbuild from './node_modules/esbuild-wasm/esm/browser.min.js'
await esbuild.initialize({
wasmURL: './node_modules/esbuild-wasm/esbuild.wasm',
})
...
</script>
#全般オプション
#バンドル
サポート対象: ビルド
ファイルをバンドルするとは、インポートされた依存関係をファイル自体にインライン化することを意味します。このプロセスは再帰的であるため、依存関係の依存関係 (など) もインライン化されます。デフォルトでは、esbuild は入力ファイルをバンドルしません。バンドルは次のように明示的に有効にする必要があります
esbuild in.js --bundle
import * as esbuild from 'esbuild'
console.log(await esbuild.build({
entryPoints: ['in.js'],
bundle: true,
outfile: 'out.js',
}))
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"in.js"},
Bundle: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
実際のコードを使用したバンドルの例については、スタートガイドを参照してください。
バンドルはファイルの連結とは異なることに注意してください。バンドルを有効にして esbuild に複数の入力ファイルを渡すと、入力ファイルが結合されるのではなく、複数の別々のバンドルが作成されます。esbuild でファイルセットを結合するには、すべてを単一のエントリーポイントファイルにインポートし、esbuild でその 1 つのファイルのみをバンドルします。
#分析できないインポート
インポートパスは、現在、文字列リテラルまたはglob パターンの場合のみバンドルされます。その他の形式のインポートパスはバンドルされず、代わりに生成された出力で verbatim で保持されます。これは、バンドルがコンパイル時の操作であり、esbuild が実行時のパス解決のすべての形式をサポートしていないためです。次に例を示します。
// Analyzable imports (will be bundled by esbuild)
import 'pkg';
import('pkg');
require('pkg');
import(`./locale-${foo}.json`);
require(`./locale-${foo}.json`);
// Non-analyzable imports (will not be bundled by esbuild)
import(`pkg/${foo}`);
require(`pkg/${foo}`);
['pkg'].map(require);
分析できないインポートを回避する方法は、この問題のあるコードを含むパッケージを外部としてマークし、バンドルに含まれないようにすることです。次に、バンドルされたコードが実行時に外部パッケージのコピーを利用できることを確認する必要があります。
一部のバンドラー(Webpack など)は、実行時パス解決のすべての形式をサポートするために、到達可能なすべてのファイルをバンドルに含め、実行時にファイルシステムをエミュレートしようとします。しかし、実行時のファイルシステムエミュレーションは範囲外であり、esbuild では実装されません。どうしてもこれを行うコードをバンドルする必要がある場合は、esbuild ではなく別のバンドラーを使用する必要があるでしょう。
#グロブスタイルのインポート
実行時に評価されるインポートパスは、特定の制限された状況下でバンドルできるようになりました。インポートパスの式は、文字列連結の形式である必要があり、./
または ../
のいずれかで始まる必要があります。文字列連結チェーン内の文字列以外の各式は、グロブパターンにおけるワイルドカードになります。いくつかの例を挙げます。
// These two forms are equivalent
const json1 = require('./data/' + kind + '.json')
const json2 = require(`./data/${kind}.json`)
これを行うと、esbuild はパターンに一致するすべてのファイルをファイルシステム内で検索し、それらすべてを、一致するインポートパスをバンドルされたモジュールにマッピングするマップとともにバンドルに含めます。インポート式は、そのマップへのルックアップに置き換えられます。インポートパスがマップに存在しない場合は、実行時にエラーがスローされます。生成されるコードは、次のようになります(簡潔にするために重要でない部分は省略されています)。
// data/bar.json
var require_bar = ...;
// data/foo.json
var require_foo = ...;
// require("./data/**/*.json") in example.js
var globRequire_data_json = __glob({
"./data/bar.json": () => require_bar(),
"./data/foo.json": () => require_foo()
});
// example.js
var json1 = globRequire_data_json("./data/" + kind + ".json");
var json2 = globRequire_data_json(`./data/${kind}.json`);
この機能は、require(...)
および import(...)
で動作します。これらはすべて実行時式を受け入れることができるためです。import
および export
ステートメントでは、実行時式を受け入れることができないため、機能しません。esbuild がこれらのインポートをバンドルしようとするのを防ぎたい場合は、文字列連結式を require(...)
または import(...)
の外に移動する必要があります。例:
// This will be bundled
const json1 = require('./data/' + kind + '.json')
// This will not be bundled
const path = './data/' + kind + '.json'
const json2 = require(path)
この機能を使用すると、esbuild はパターンに一致する可能性のあるすべてのファイルを検索するために、大量のファイルシステム I/O を実行する可能性があることに注意してください。これは仕様であり、バグではありません。これが懸念される場合は、esbuild が実行するファイルシステム I/O の量を減らす方法が2つあります。
最も簡単な方法は、特定の実行時インポート式でインポートしたいすべてのファイルをサブディレクトリに入れ、そのサブディレクトリをパターンに含めることです。これにより、esbuild はそのサブディレクトリ内のみを検索するように制限されます。esbuild はパターンマッチング中に
..
パス要素を考慮しないためです。もう1つの方法は、esbuild がどのサブディレクトリも検索しないようにすることです。esbuild が使用するパターンマッチングアルゴリズムでは、ワイルドカードがパターン内で前に
/
を持つ場合にのみ、/
パス区切り記号を含むものと一致できます。たとえば、'./data/' +
は、x + '.json' x
を任意のサブディレクトリ内の任意のものと一致させますが、'./data-' +
は、x + '.json' x
をトップレベルディレクトリ(サブディレクトリ内ではない)内の任意のものとのみ一致させます。
#キャンセル
サポート対象: ビルド
リビルドを使用してインクリメンタルビルドを手動で呼び出す場合、このキャンセル API を使用して現在のビルドを早期に終了し、新しいビルドを開始できるようにすることができます。次のように行うことができます。
# The CLI does not have an API for "cancel"
import * as esbuild from 'esbuild'
import process from 'node:process'
let ctx = await esbuild.context({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'www',
logLevel: 'info',
})
// Whenever we get some data over stdin
process.stdin.on('data', async () => {
try {
// Cancel the already-running build
await ctx.cancel()
// Then start a new build
console.log('build:', await ctx.rebuild())
} catch (err) {
console.error(err)
}
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "www",
LogLevel: api.LogLevelInfo,
})
if err != nil {
os.Exit(1)
}
// Whenever we get some data over stdin
buf := make([]byte, 100)
for {
if n, err := os.Stdin.Read(buf); err != nil || n == 0 {
break
}
go func() {
// Cancel the already-running build
ctx.Cancel()
// Then start a new build
result := ctx.Rebuild()
fmt.Fprintf(os.Stderr, "build: %v\n", result)
}()
}
}
新しいビルドを開始する前に、キャンセル操作が完了するまで待機してください(つまり、JavaScript を使用する場合は返された promise を await
してください)。そうしないと、次の リビルドは、まだ終了していないキャンセルされたビルドを返します。プラグインのon-end コールバックは、ビルドがキャンセルされたかどうかにかかわらず実行されることに注意してください。
#ライブリロード
サポート対象: ビルド
ライブリロードは、コードエディターと同時にブラウザーを開いて表示する開発手法です。ソースコードを編集して保存すると、ブラウザーが自動的にリロードされ、リロードされたアプリのバージョンには変更が含まれます。つまり、変更ごとに手動でブラウザーに切り替えてリロードし、コードエディターに戻る必要がないため、より迅速に反復処理を行うことができます。これは、たとえば CSS を変更する場合に非常に役立ちます。
ライブリロード用の esbuild API はありません。代わりに、ウォッチモード(ファイルを編集して保存したときに自動的にビルドを開始する)とサービスモード(最新のビルドを提供するが、完了するまでブロックする)を組み合わせ、開発中にのみアプリに追加する小さなクライアント側の JavaScript コードを使用して、ライブリロードを構築できます。
最初の手順は、ウォッチとサービスを一緒に有効にすることです。
esbuild app.ts --bundle --outdir=www --watch --servedir=www
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['app.ts'],
bundle: true,
outdir: 'www',
})
await ctx.watch()
let { host, port } = await ctx.serve({
servedir: 'www',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Outdir: "www",
})
if err != nil {
os.Exit(1)
}
err2 := ctx.Watch(api.WatchOptions{})
if err2 != nil {
os.Exit(1)
}
result, err3 := ctx.Serve(api.ServeOptions{
Servedir: "www",
})
if err3 != nil {
os.Exit(1)
}
}
2番目のステップは、/esbuild
サーバー送信イベントソースをサブスクライブするコードをJavaScriptに追加することです。change
イベントを受信すると、ページをリロードしてアプリの最新バージョンを取得できます。これは、1行のコードで実行できます。
new EventSource('/esbuild').addEventListener('change', () => location.reload())
それだけです! ブラウザでアプリをロードすると、ファイル編集して保存すると、ページが自動的にリロードされるはずです(ビルドエラーがない場合)。
これは開発中にのみ含める必要があり、本番環境には含めないでください。本番環境でこのコードを削除する方法の1つは、if (!window.IS_PRODUCTION)
のようなifステートメントで保護し、defineを使用して、本番環境で window.IS_PRODUCTION
を true
に設定することです。
#ライブリロードの注意点
このようにライブリロードを実装するには、いくつかの既知の注意点があります。
これらのイベントは、esbuild の出力が変更された場合にのみトリガーされます。監視対象のビルドに関係のないファイルが変更された場合はトリガーされません。HTML ファイルが esbuild が認識していない他のファイルを参照しており、それらのファイルが変更された場合、ページを手動でリロードするか、esbuild の組み込み動作を使用する代わりに、独自のライブリロードインフラストラクチャを実装することができます。
EventSource
API は自動的に再接続するはずです。ただし、サーバーが一時的に到達不能になると、このAPIを破壊するFirefox のバグがあります。回避策は、他のブラウザを使用するか、この現象が発生した場合はページを手動でリロードするか、接続エラーが発生した場合にEventSource
オブジェクトを手動で閉じて再作成する、より複雑なコードを作成することです。ブラウザベンダーは、TLSなしでHTTP/2を実装しないことを決定しました。これは、
http://
プロトコルを使用する場合、各/esbuild
イベントソースが、ドメインごとに貴重な6つの同時HTTP/1.1接続の1つを占有することを意味します。したがって、このライブリロード手法を使用するHTTPタブを6つ以上開くと、それらのタブの一部ではライブリロードを使用できなくなります(他のものも壊れる可能性があります)。回避策は、https://
プロトコルを有効にすることです。
#CSSのホットリロード
change
イベントには、より高度なユースケースを可能にする追加情報も含まれています。現在、前のビルドから変更されたファイルのパスを含む added
、removed
、および updated
配列が含まれており、次の TypeScript インターフェイスで記述できます。
interface ChangeEvent {
added: string[]
removed: string[]
updated: string[]
}
以下のコードサンプルでは、CSS の「ホットリロード」を有効にしています。これは、CSS がページをリロードせずにその場で自動的に更新される場合です。CSS に関連しないイベントが発生した場合は、フォールバックとしてページ全体がリロードされます。
new EventSource('/esbuild').addEventListener('change', e => {
const { added, removed, updated } = JSON.parse(e.data)
if (!added.length && !removed.length && updated.length === 1) {
for (const link of document.getElementsByTagName("link")) {
const url = new URL(link.href)
if (url.host === location.host && url.pathname === updated[0]) {
const next = link.cloneNode()
next.href = updated[0] + '?' + Math.random().toString(36).slice(2)
next.onload = () => link.remove()
link.parentNode.insertBefore(next, link.nextSibling)
return
}
}
}
location.reload()
})
#JavaScriptのホットリロード
JavaScriptのホットリロードは現在esbuildでは実装されていません。CSSはステートレスであるため、CSSのホットリロードを透過的に実装することは可能ですが、JavaScriptはステートフルであるため、CSSのようにJavaScriptのホットリロードを透過的に実装することはできません。
他の開発サーバーでは、いずれにしても JavaScript のホットリロードを実装していますが、追加の API が必要になり、フレームワーク固有のハックが必要になる場合があり、編集セッション中に一時的な状態関連のバグが発生する場合があります。これを行うことは、esbuild の範囲外です。JavaScript のホットリロードが要件の1つである場合は、esbuild の代わりに他のツールを使用してもかまいません。
ただし、esbuildのライブリロードを使用すると、sessionStorage
にアプリの現在のJavaScriptの状態を永続化して、ページのリロード後にアプリのJavaScriptの状態をより簡単に復元できます。アプリの読み込みが速い場合(ユーザーのためにも高速である必要があります)、JavaScriptを使用したライブリロードは、JavaScriptを使用したホットリロードとほぼ同じくらい高速になる可能性があります。
#プラットフォーム
デフォルトでは、esbuild のバンドラーは、ブラウザで実行するように設計されたコードを生成するように構成されています。バンドルされたコードが代わりに node で実行されるようにする場合は、プラットフォームを node
に設定する必要があります。
esbuild app.js --bundle --platform=node
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
platform: 'node',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Platform: api.PlatformNode,
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
プラットフォームが browser
(デフォルト値)に設定されている場合
バンドルが有効になっている場合、デフォルトの出力フォーマットは
iife
に設定されます。これは、生成された JavaScript コードを即時呼び出し関数式でラップして、変数がグローバルスコープに漏洩するのを防ぎます。パッケージが
package.json
ファイルのbrowser
フィールドにマップを指定した場合、esbuild はそのマップを使用して、特定のファイルまたはモジュールをブラウザフレンドリーなバージョンに置き換えます。たとえば、パッケージには、path
をpath-browserify
に置換するものが含まれている場合があります。メインフィールド設定は
browser,
に設定されますが、いくつかの追加の特殊な動作があります。パッケージがmodule, main module
およびmain
エントリポイントを提供するが、browser
エントリポイントを提供しない場合、そのパッケージがrequire()
を使用してインポートされる場合は、module
の代わりにmain
が使用されます。この動作により、関数をmodule.exports
に割り当てることでエクスポートする CommonJS モジュールとの互換性が向上します。この追加の特別な動作を無効にする場合は、メインフィールド設定を明示的にbrowser,
に設定できます。module, main 条件設定には、自動的に
browser
条件が含まれます。これにより、package.json
ファイルのexports
フィールドが、ブラウザ固有のコードを優先するように解釈される方法が変わります。カスタム条件が構成されていない場合、Webpack 固有の
module
条件も含まれます。module
条件は、パッケージの作成者が、デュアルパッケージハザードを作成せずに、CommonJSファイルへのツリーシェーカブルな ESM 代替を提供するために使用されます。カスタム条件(空のリストでも)を明示的に構成することで、module
条件が含まれないようにすることができます。ビルド API を使用する場合、すべての
process.
式は、すべての最小化オプションが有効になっている場合は自動的にenv. NODE_ENV "production"
に定義され、それ以外の場合は"development"
に定義されます。これは、process
、process.env
、およびprocess.env.NODE_ENV
がまだ定義されていない場合にのみ発生します。この置換は、React ベースのコードが即座にクラッシュするのを防ぐために必要です(process
は node API であり、Web API ではないため)。文字シーケンス
</script>
は JavaScript コードでエスケープされ、文字シーケンス</style>
は CSS コードでエスケープされます。これは、esbuild の出力を HTML ファイルに直接インライン化する場合に実行されます。これは、esbuild のサポート機能で、inline-script
(JavaScriptの場合)および/またはinline-style
(CSSの場合)をfalse
に設定することで無効にできます。
プラットフォームが node
に設定されている場合
バンドルが有効になっている場合、デフォルトの出力フォーマットは
cjs
に設定されます。これは CommonJS (nodeで使用されるモジュール形式)の略です。export
ステートメントを使用するES6スタイルのエクスポートは、CommonJSexports
オブジェクトのゲッターに変換されます。fs
などのすべての組み込みの node モジュールは、バンドラーがバンドルしようとしたときにエラーが発生しないように、自動的に外部としてマークされます。メインフィールド設定は
main,
に設定されます。これは、ツリーシェイキングが ECMAScript モジュールでは機能するが CommonJS モジュールでは機能しないため、module module
とmain
の両方を提供するパッケージでは、ツリーシェイキングが実行されない可能性が高いことを意味します。残念ながら、一部のパッケージでは
module
を「ECMAScriptモジュールコード」ではなく「ブラウザコード」と誤って解釈するため、このデフォルトの動作は互換性のために必要です。ツリーシェイキングを有効にしたい場合や、それが安全であることがわかっている場合は、main fields設定をmodule,
に手動で構成できます。main conditions設定には、自動的に
node
条件が含まれます。これにより、package.json
ファイルのexports
フィールドが、Node.js固有のコードを優先するように解釈される方法が変わります。カスタム条件が構成されていない場合、Webpack 固有の
module
条件も含まれます。module
条件は、パッケージの作成者が、デュアルパッケージハザードを作成せずに、CommonJSファイルへのツリーシェーカブルな ESM 代替を提供するために使用されます。カスタム条件(空のリストでも)を明示的に構成することで、module
条件が含まれないようにすることができます。formatが
cjs
に設定されているが、エントリポイントがESMの場合、esbuildは、結果のCommonJSファイルからESM構文を使用して名前付きエクスポートをインポートできるように、名前付きエクスポートに特別なアノテーションを追加します。Node.jsのドキュメントに、Node.jsによるCommonJSの名前付きエクスポートの検出に関する詳細情報があります。binary
ローダーは、バンドルに埋め込まれたbase64データをBuffer.from
APIを使用してUint8Array
にデコードします。これは、Node.jsによってネイティブコードで実装されているため、esbuildがそれ以外で行うよりも高速です。
プラットフォームがneutral
に設定されている場合
バンドルが有効になっている場合、デフォルトの出力形式は
esm
に設定されます。これは、ECMAScript 2015(つまりES6)で導入されたexport
構文を使用します。このデフォルトが適切でない場合は、出力形式を変更できます。main fields設定は、デフォルトでは空です。npmスタイルのパッケージを使用する場合は、Node.jsで使用される標準のmainフィールドである
main
など、別の値に設定する必要があるでしょう。conditions設定には、プラットフォーム固有の値は自動的に含まれません。
ブラウザ用のバンドルとNode.js用のバンドルも参照してください。
#再構築
サポート対象: ビルド
同じオプションでesbuildのbuild APIを繰り返し呼び出すユースケースがある場合は、このAPIを使用できます。たとえば、独自のファイル監視サービスを実装している場合に役立ちます。再構築は、前のビルドからのデータの一部がキャッシュされ、前のビルドから元のファイルが変更されていない場合は再利用できるため、再度ビルドするよりも効率的です。再構築APIで使用されるキャッシュには、現在2つの形式があります。
ファイルはメモリに保存され、最後のビルドからファイルメタデータが変更されていない場合は、ファイルシステムから再度読み込まれることはありません。この最適化は、ファイルシステムパスにのみ適用されます。プラグインによって作成された仮想モジュールには適用されません。
パースされたASTはメモリに保存され、最後のビルドからファイルの内容が変更されていない場合は、ASTの再パースは回避されます。この最適化は、仮想モジュールパスが同じままであれば、ファイルシステムモジュールに加えて、プラグインによって作成された仮想モジュールにも適用されます。
再構築の方法は次のとおりです。
# The CLI does not have an API for "rebuild"
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
})
// Call "rebuild" as many times as you want
for (let i = 0; i < 5; i++) {
let result = await ctx.rebuild()
}
// Call "dispose" when you're done to free up resources
ctx.dispose()
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Outfile: "out.js",
})
if err != nil {
os.Exit(1)
}
// Call "Rebuild" as many times as you want
for i := 0; i < 5; i++ {
result := ctx.Rebuild()
if len(result.Errors) > 0 {
os.Exit(1)
}
}
// Call "Dispose" when you're done to free up resources
ctx.Dispose()
}
#Serve
サポート対象: ビルド
Serveモードは、デバイス上のブラウザにコードを提供するWebサーバーを起動します。src/app.ts
をwww/js/app.js
にバンドルし、www
ディレクトリをhttp://localhost:8000/
で提供する例を次に示します。
esbuild src/app.ts --outdir=www/js --bundle --servedir=www
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['src/app.ts'],
outdir: 'www/js',
bundle: true,
})
let { host, port } = await ctx.serve({
servedir: 'www',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"src/app.ts"},
Outdir: "www/js",
Bundle: true,
})
if err != nil {
os.Exit(1)
}
server, err2 := ctx.Serve(api.ServeOptions{
Servedir: "www",
})
if err2 != nil {
os.Exit(1)
}
// Returning from main() exits immediately in Go.
// Block forever so we keep serving and don't exit.
<-make(chan struct{})
}
次の内容でファイルwww/index.html
を作成すると、http://localhost:8000/
にアクセスすると、src/app.ts
に含まれるコードがロードされます。
<script src="js/app.js"></script>
esbuildの組み込みWebサーバーを別のWebサーバーの代わりに使用する利点の1つは、リロードするたびに、esbuildが提供するファイルが常に最新であることです。これは、他の開発環境では必ずしも当てはまりません。一般的な設定の1つは、入力ファイルが変更されたときにいつでも出力ファイルを再構築するローカルファイルウォッチャーを実行し、それらの出力ファイルを提供するためにローカルファイルサーバーを個別に実行することです。しかし、これは、編集後のリロードで、再構築がまだ完了していない場合は古い出力ファイルがリロードされる可能性があることを意味します。esbuildのWebサーバーでは、着信リクエストごとに、まだ進行中でない場合は再構築が開始され、現在の再構築が完了するまで待機してからファイルが提供されます。これは、esbuildが古いビルド結果を提供しないことを意味します。
このWebサーバーは、開発でのみ使用することを目的としていることに注意してください。本番環境では使用しないでください。
#引数
serve APIへの引数は次のとおりです。
# Enable serve mode
--serve
# Set the port
--serve=9000
# Set the host and port (IPv4)
--serve=127.0.0.1:9000
# Set the host and port (IPv6)
--serve=[::1]:9000
# Set the directory to serve
--servedir=www
# Enable HTTPS
--keyfile=your.key --certfile=your.cert
# Specify a fallback HTML file
--serve-fallback=some-file.html
interface ServeOptions {
port?: number
host?: string
servedir?: string
keyfile?: string
certfile?: string
fallback?: string
onRequest?: (args: ServeOnRequestArgs) => void
}
interface ServeOnRequestArgs {
remoteAddress: string
method: string
path: string
status: number
timeInMS: number
}
type ServeOptions struct {
Port uint16
Host string
Servedir string
Keyfile string
Certfile string
Fallback string
OnRequest func(ServeOnRequestArgs)
}
type ServeOnRequestArgs struct {
RemoteAddress string
Method string
Path string
Status int
TimeInMS int
}
host
デフォルトでは、esbuildはすべてのIPv4ネットワークインターフェースでWebサーバーを利用可能にします。これは、ホストアドレス
0.0.0.0
に対応します。別のホスト(たとえば、ネットワークに何も公開せずに127.0.0.1
ループバックインターフェースでのみサービスを提供する場合)を構成する場合は、この引数を使用してホストを指定できます。IPv4ではなくIPv6を使用する必要がある場合は、IPv6ホストアドレスを指定する必要があります。IPv6での
127.0.0.1
ループバックインターフェースに相当するものは::1
であり、IPv6での0.0.0.0
ユニバーサルインターフェースに相当するものは::
です。port
HTTPポートは、ここでオプションで構成できます。省略した場合、8000〜8009の範囲のポートを優先して、オープンのポートがデフォルトになります。
servedir
これは、着信リクエストが生成された出力ファイルのパスに一致しない場合に、esbuildのHTTPサーバーが404の代わりに提供する追加コンテンツのディレクトリです。これにより、esbuildを汎用のローカルWebサーバーとして使用できます。
たとえば、
index.html
ファイルを作成し、servedir
を"."
に設定して、現在のディレクトリ(index.html
ファイルを含む)を提供することができます。servedir
を設定しない場合、esbuildはビルド結果のみを提供し、他のファイルは提供しません。keyfile
とcertfile
keyfile
とcertfile
を使用してesbuildに秘密鍵と証明書を渡すと、esbuildのWebサーバーはhttp://
プロトコルの代わりにhttps://
プロトコルを使用します。詳細については、HTTPSの有効化を参照してください。fallback
これは、着信リクエストが生成された出力ファイルのパスに一致しない場合に、esbuildのHTTPサーバーが404の代わりに提供するHTMLファイルです。これは、カスタムの「見つかりません」ページに使用できます。また、現在のURLを変更し、したがって多くの異なるURLから同時に提供する必要があるシングルページアプリケーションのエントリポイントとして使用することもできます。
onRequest
これは、各着信リクエストに対して1回、リクエストに関するいくつかの情報とともに呼び出されます。このコールバックは、CLIが各リクエストのログメッセージを出力するために使用されます。timeフィールドはリクエストのデータを生成する時間ですが、リクエストをクライアントにストリームする時間は含まれていません。
これは、リクエストが完了した後に呼び出されることに注意してください。このコールバックを使用してリクエストを変更することはできません。これを行う場合は、代わりにesbuildの前にプロキシを配置する必要があります。
#戻り値
# The CLI will print the host and port like this:
> Local: http://127.0.0.1:8000/
interface ServeResult {
host: string
port: number
}
type ServeResult struct {
Host string
Port uint16
}
host
これは、Webサーバーによって最終的に使用されたホストです。カスタムホストが構成されていない限り、
0.0.0.0
(つまり、利用可能なすべてのネットワークインターフェースでサービスを提供)になります。CLIを使用していて、ホストが0.0.0.0
の場合、利用可能なすべてのネットワークインターフェースが代わりにホストとして出力されます。port
これは、Webサーバーによって最終的に使用されたポートです。ポートを指定しない場合はこれを使用する必要があります。esbuildは最終的に任意のオープンポートを選択するため、接続するには選択したポートを知っておく必要があるためです。
#HTTPSの有効化
デフォルトでは、esbuildのWebサーバーはhttp://
プロトコルを使用します。ただし、特定の最新のWeb機能はHTTP Webサイトでは利用できません。これらの機能を使用する場合は、代わりにhttps://
プロトコルを使用するようにesbuildに指示する必要があります。
esbuildでHTTPSを有効にするには
自己署名証明書を生成します。これを行うには多くの方法があります。
openssl
コマンドがインストールされていると仮定して、その方法の1つを次に示します。openssl req -x509 -newkey rsa:4096 -keyout your.key -out your.cert -days 9999 -nodes -subj /CN=127.0.0.1
keyfile
とcertfile
serve引数を使用して、your.key
とyour.cert
をesbuildに渡します。ページをロードするときにブラウザの怖い警告をクリックして進みます(自己署名証明書は安全ではありませんが、ローカル開発を行っているだけなので問題ありません)。
これよりも複雑なニーズがある場合は、esbuildの前にプロキシを配置して、代わりにHTTPSに使用できます。ページをロードしたときにClient
というメッセージが表示された場合は、誤ったプロトコルを使用しています。ブラウザのアドレスバーでhttp://
をhttps://
に置き換えます。
esbuildのHTTPSサポートはセキュリティとは関係がないことに注意してください。esbuildでHTTPSを有効にする唯一の理由は、ブラウザが、これらの追加の面倒な作業なしでは、特定の最新のWeb機能を使用してローカル開発を行うことを不可能にしたためです。安全である必要のあるものには、esbuildの開発サーバーを使用しないでください。ローカル開発専用であり、本番環境は一切考慮されていません。
#サーバーの動作のカスタマイズ
esbuildのローカルサーバーにフックして、サーバー自体の動作をカスタマイズすることはできません。代わりに、esbuildの前にプロキシを配置して動作をカスタマイズする必要があります。
次に、Node.jsの組み込みのhttp
モジュールを使用した、プロキシサーバーの簡単な例を示します。esbuildのデフォルトの404ページの代わりに、カスタム404ページを追加します。
import * as esbuild from 'esbuild'
import http from 'node:http'
// Start esbuild's server on a random local port
let ctx = await esbuild.context({
// ... your build options go here ...
})
// The return value tells us where esbuild's local server is
let { host, port } = await ctx.serve({ servedir: '.' })
// Then start a proxy server on port 3000
http.createServer((req, res) => {
const options = {
hostname: host,
port: port,
path: req.url,
method: req.method,
headers: req.headers,
}
// Forward each incoming request to esbuild
const proxyReq = http.request(options, proxyRes => {
// If esbuild returns "not found", send a custom 404 page
if (proxyRes.statusCode === 404) {
res.writeHead(404, { 'Content-Type': 'text/html' })
res.end('<h1>A custom 404 page</h1>')
return
}
// Otherwise, forward the response from esbuild to the client
res.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.pipe(res, { end: true })
})
// Forward the body of the request to esbuild
req.pipe(proxyReq, { end: true })
}).listen(3000)
このコードは、ランダムなローカルポートで esbuild のサーバーを起動し、ポート 3000 でプロキシサーバーを起動します。開発中は、ブラウザで http://localhost:3000 をロードし、プロキシと通信します。この例では、esbuild がリクエストを処理した後にレスポンスを変更する方法を示していますが、esbuild が処理する前にリクエストを変更または置き換えることもできます。
このようなプロキシを使用すると、さまざまなことができます。
- 独自の 404 ページを挿入する(上記の例)
- ファイルシステム上のファイルへのルートのマッピングをカスタマイズする
- 一部のルートを esbuild の代わりに API サーバーにリダイレクトする
より高度なニーズがある場合は、nginx のような実際のプロキシを使用することもできます。
#Tsconfig
サポート対象: ビルド
通常、build API は、ビルド中に tsconfig.json
ファイルを自動的に検出してその内容を読み取ります。ただし、代わりにカスタムの tsconfig.json
ファイルを使用するように設定することもできます。これは、異なる設定で同じコードを複数回ビルドする必要がある場合に役立ちます。
esbuild app.ts --bundle --tsconfig=custom-tsconfig.json
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
bundle: true,
tsconfig: 'custom-tsconfig.json',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Bundle: true,
Tsconfig: "custom-tsconfig.json",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#Tsconfig raw
このオプションは、ファイルシステムにアクセスしない transform API に tsconfig.json
ファイルを渡すために使用できます。また、tsconfig.json
ファイルの内容をファイルに書き込むことなく、インラインで build API に渡すためにも使用できます。使用方法は次のとおりです。
echo 'class Foo { foo }' | esbuild --loader=ts --tsconfig-raw='{"compilerOptions":{"useDefineForClassFields":false}}'
import * as esbuild from 'esbuild'
let ts = 'class Foo { foo }'
let result = await esbuild.transform(ts, {
loader: 'ts',
tsconfigRaw: `{
"compilerOptions": {
"useDefineForClassFields": false,
},
}`,
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
ts := "class Foo { foo }"
result := api.Transform(ts, api.TransformOptions{
Loader: api.LoaderTS,
TsconfigRaw: `{
"compilerOptions": {
"useDefineForClassFields": false,
},
}`,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#Watch
サポート対象: ビルド
ウォッチモードを有効にすると、esbuild はファイルシステムの変更をリッスンし、ビルドを無効にする可能性のあるファイルが変更されるたびに自動的に再ビルドします。使用方法は次のとおりです。
esbuild app.js --outfile=out.js --bundle --watch [watch] build finished, watching for changes...
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['app.js'],
outfile: 'out.js',
bundle: true,
})
await ctx.watch()
console.log('watching...')
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.js"},
Outfile: "out.js",
Bundle: true,
Write: true,
})
if err != nil {
os.Exit(1)
}
err2 := ctx.Watch(api.WatchOptions{})
if err2 != nil {
os.Exit(1)
}
fmt.Printf("watching...\n")
// Returning from main() exits immediately in Go.
// Block forever so we keep watching and don't exit.
<-make(chan struct{})
}
将来のある時点でウォッチモードを停止する場合は、コンテキストオブジェクトで dispose
を呼び出してファイルウォッチャーを終了できます。
# Use Ctrl+C to stop the CLI in watch mode
import * as esbuild from 'esbuild'
let ctx = await esbuild.context({
entryPoints: ['app.js'],
outfile: 'out.js',
bundle: true,
})
await ctx.watch()
console.log('watching...')
await new Promise(r => setTimeout(r, 10 * 1000))
await ctx.dispose()
console.log('stopped watching')
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"
import "time"
func main() {
ctx, err := api.Context(api.BuildOptions{
EntryPoints: []string{"app.js"},
Outfile: "out.js",
Bundle: true,
Write: true,
})
if err != nil {
os.Exit(1)
}
err2 := ctx.Watch(api.WatchOptions{})
if err2 != nil {
os.Exit(1)
}
fmt.Printf("watching...\n")
time.Sleep(10 * time.Second)
ctx.Dispose()
fmt.Printf("stopped watching\n")
}
esbuild のウォッチモードは、移植性のために OS 固有のファイルシステム API ではなくポーリングを使用して実装されています。ポーリングシステムは、ディレクトリツリー全体を一度にスキャンする従来のポーリングシステムと比較して、比較的少ない CPU を使用するように設計されています。ファイルシステムは定期的にスキャンされますが、各スキャンではファイルのランダムなサブセットのみがチェックされるため、ファイルの変更は変更が行われた直後に検出されますが、必ずしも瞬時には検出されません。
現在のヒューリスティックでは、大規模なプロジェクトは約 2 秒ごとに完全にスキャンされるため、最悪の場合、変更が検出されるまでに最大 2 秒かかる可能性があります。ただし、変更が検出された後、変更のパスは最近変更されたパスの短いリストに追加され、スキャンごとにチェックされるため、最近変更されたファイルへのさらなる変更はほぼ瞬時に検出されます。
ポーリングベースのアプローチを使用しない場合は、esbuild の rebuild API と選択したファイルウォッチャーライブラリを使用して、自分でウォッチモードを実装することもできます。
CLI を使用している場合は、esbuild の stdin が閉じられるとウォッチモードが終了することに注意してください。これにより、esbuild が誤って親プロセスよりも長く存続し、システムのリソースを予期せず消費し続けることを防ぎます。親プロセスが終了した場合でも esbuild が永遠に監視し続ける必要があるユースケースがある場合は、--watch
の代わりに --watch=
を使用できます。
#入力
#エントリーポイント
サポート対象: ビルド
これは、それぞれがバンドルアルゴリズムへの入力として機能するファイルの配列です。それらは「エントリーポイント」と呼ばれます。なぜなら、それぞれが評価される最初のスクリプトであり、そのスクリプトが表すコードの他のすべての側面をロードすることを目的としているからです。ページに多くのライブラリを <script>
タグでロードする代わりに、import
ステートメントを使用してそれらをエントリーポイントにインポートします(またはエントリーポイントにインポートされる別のファイルにインポートします)。
単純なアプリにはエントリーポイントが 1 つだけ必要ですが、メインスレッドとワーカースレッド、またはランディングページ、エディターページ、設定ページなどの比較的無関係な領域が分かれているアプリなど、複数の論理的に独立したコードグループがある場合は、追加のエントリーポイントが役立ちます。別々のエントリーポイントは、関心の分離を導入するのに役立ち、ブラウザがダウンロードする必要がある不要なコードの量を減らすのに役立ちます。該当する場合は、コード分割を有効にすると、最初に見られたページとすでにダウンロードされたコードの一部を共有する 2 番目のページを閲覧する際のダウンロードサイズをさらに削減できます。
エントリーポイントを指定する簡単な方法は、ファイルパスの配列を渡すだけです。
esbuild home.ts settings.ts --bundle --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['home.ts', 'settings.ts'],
bundle: true,
write: true,
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"home.ts", "settings.ts"},
Bundle: true,
Write: true,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これにより、2 つのエントリーポイント home.ts
と settings.ts
に対応する 2 つの出力ファイル out/home.js
と out/settings.js
が生成されます。
対応する入力エントリーポイントから出力ファイルのパスを導出する方法をさらに制御するには、次のオプションを検討する必要があります。
さらに、代替のエントリーポイント構文を使用して、個々のエントリーポイントごとに完全にカスタムの出力パスを指定することもできます。
esbuild out1=home.ts out2=settings.ts --bundle --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: [
{ out: 'out1', in: 'home.ts'},
{ out: 'out2', in: 'settings.ts'},
],
bundle: true,
write: true,
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPointsAdvanced: []api.EntryPoint{{
OutputPath: "out1",
InputPath: "home.ts",
}, {
OutputPath: "out2",
InputPath: "settings.ts",
}},
Bundle: true,
Write: true,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これにより、2 つのエントリーポイント home.ts
と settings.ts
に対応する 2 つの出力ファイル out/out1.js
と out/out2.js
が生成されます。
#ローダー
このオプションは、特定の入力ファイルの解釈方法を変更します。たとえば、js
ローダーはファイルを JavaScript として解釈し、css
ローダーはファイルを CSS として解釈します。組み込みローダーの完全なリストについては、コンテンツタイプのページを参照してください。
特定のファイルタイプのローダーを設定すると、import
ステートメントまたは require
呼び出しでそのファイルタイプをロードできます。たとえば、.png
ファイル拡張子が データ URL ローダーを使用するように設定した場合、.png
ファイルをインポートすると、そのイメージの内容を含むデータ URL が提供されます。
import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)
import svg from './example.svg'
let doc = new DOMParser().parseFromString(svg, 'application/xml')
let node = document.importNode(doc.documentElement, true)
document.body.appendChild(node)
上記のコードは、次のように build API 呼び出しを使用してバンドルできます。
esbuild app.js --bundle --loader:.png=dataurl --loader:.svg=text
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
loader: {
'.png': 'dataurl',
'.svg': 'text',
},
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Loader: map[string]api.Loader{
".png": api.LoaderDataURL,
".svg": api.LoaderText,
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
このオプションは、stdin からの入力で build API を使用している場合は、stdin にファイル拡張子がないため、異なる方法で指定されます。build API で stdin のローダーを設定すると、次のようになります。
echo 'import pkg = require("./pkg")' | esbuild --loader=ts --bundle
import * as esbuild from 'esbuild'
await esbuild.build({
stdin: {
contents: 'import pkg = require("./pkg")',
loader: 'ts',
resolveDir: '.',
},
bundle: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
Stdin: &api.StdinOptions{
Contents: "import pkg = require('./pkg')",
Loader: api.LoaderTS,
ResolveDir: ".",
},
Bundle: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
transform API 呼び出しは、ファイルシステムとの対話を含まないため、単一のローダーのみを受け取ります。したがって、ファイル拡張子を扱いません。transform API のローダー(この場合は ts
ローダー)を設定すると、次のようになります。
echo 'let x: number = 1' | esbuild --loader=ts
let x = 1;
import * as esbuild from 'esbuild'
let ts = 'let x: number = 1'
let result = await esbuild.transform(ts, {
loader: 'ts',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
ts := "let x: number = 1"
result := api.Transform(ts, api.TransformOptions{
Loader: api.LoaderTS,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#Stdin
サポート対象: ビルド
通常、build API 呼び出しは、入力として 1 つ以上のファイル名を受け取ります。ただし、このオプションを使用すると、ファイルシステムにモジュールが存在しない状態でビルドを実行できます。これは、コマンドラインでファイルを stdin にパイプすることに対応するため、「stdin」と呼ばれます。
stdin ファイルの内容を指定するだけでなく、オプションで解決ディレクトリ(相対インポートの場所を決定するために使用)、sourcefile(エラーメッセージとソースマップで使用するファイル名)、および ローダー(ファイル内容の解釈方法を決定)を指定することもできます。CLI には解決ディレクトリを指定する方法はありません。代わりに、現在の作業ディレクトリに自動的に設定されます。
この機能の使用方法は次のとおりです。
echo 'export * from "./another-file"' | esbuild --bundle --sourcefile=imaginary-file.js --loader=ts --format=cjs
import * as esbuild from 'esbuild'
let result = await esbuild.build({
stdin: {
contents: `export * from "./another-file"`,
// These are all optional:
resolveDir: './src',
sourcefile: 'imaginary-file.js',
loader: 'ts',
},
format: 'cjs',
write: false,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
Stdin: &api.StdinOptions{
Contents: "export * from './another-file'",
// These are all optional:
ResolveDir: "./src",
Sourcefile: "imaginary-file.js",
Loader: api.LoaderTS,
},
Format: api.FormatCommonJS,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#出力内容
#バナー
生成された JavaScript ファイルおよび CSS ファイルの先頭に任意の文字列を挿入するには、これを使用します。これは通常、コメントを挿入するために使用されます。
esbuild app.js --banner:js=//comment --banner:css=/*comment*/
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
banner: {
js: '//comment',
css: '/*comment*/',
},
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Banner: map[string]string{
"js": "//comment",
"css": "/*comment*/",
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これは、先頭ではなく末尾に挿入する footer と似ています。
コメント以外のコードを CSS ファイルに挿入する場合は、CSS が (@charset
ルール以外の) @import
ルール以外のルールに続くすべての @import
ルールを無視することに注意してください。したがって、バナーを使用して CSS ルールを挿入すると、外部スタイルシートのインポートが誤って無効になる可能性があります。
#文字セット
デフォルトでは、esbuild の出力は ASCII のみです。ASCII 以外の文字は、バックスラッシュエスケープシーケンスを使用してエスケープされます。その理由の 1 つは、ASCII 以外の文字がデフォルトでブラウザによって誤って解釈され、混乱を引き起こすためです。ブラウザがコードを改ざんしないようにするには、HTML に <meta
を明示的に追加するか、正しい Content-
ヘッダーを付けて提供する必要があります。別の理由として、ASCII 以外の文字はブラウザのパーサーを大幅に遅くする可能性があります。ただし、エスケープシーケンスを使用すると、生成される出力がわずかに大きくなり、読み取りも困難になります。
esbuild にエスケープシーケンスを使用せずに元の文字を印刷させ、ブラウザがコードを UTF-8 として解釈することを保証した場合は、文字セットを設定して文字のエスケープを無効にできます。
echo 'let π = Math.PI' | esbuild
let \u03C0 = Math.PI;
echo 'let π = Math.PI' | esbuild --charset=utf8
let π = Math.PI;
import * as esbuild from 'esbuild'
let js = 'let π = Math.PI'
(await esbuild.transform(js)).code
'let \\u03C0 = Math.PI;\n'
(await esbuild.transform(js, {
charset: 'utf8',
})).code
'let π = Math.PI;\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "let π = Math.PI"
result1 := api.Transform(js, api.TransformOptions{})
if len(result1.Errors) == 0 {
fmt.Printf("%s", result1.Code)
}
result2 := api.Transform(js, api.TransformOptions{
Charset: api.CharsetUTF8,
})
if len(result2.Errors) == 0 {
fmt.Printf("%s", result2.Code)
}
}
いくつかの注意点
これは、正規表現に埋め込まれた ASCII 以外の文字をまだエスケープしません。これは、esbuild が現在正規表現の内容をまったく解析しないためです。この制限にもかかわらず、このフラグが追加されたのは、このようなケースを含まないコードにも役立つためです。
このフラグはコメントには適用されません。コメントのエンコードが間違っていても、実行時環境はすべてのコメントの内容を完全に無視するため、コメントに ASCII 以外のデータを保持することは問題ないと考えています。たとえば、V8 ブログ記事では、コメントの内容のデコードを完全に回避する最適化について言及しています。また、ライセンス関連のコメント以外のすべてのコメントは、いずれにしても esbuild によって削除されます。
このオプションは、すべての出力ファイルタイプ(JavaScript、CSS、および JSON)に同時に適用されます。したがって、Web サーバーが正しい
Content-
ヘッダーを送信するように設定し、UTF-8 文字セットを使用する場合は、Web サーバーがType .js
ファイルと.css
ファイルの両方を UTF-8 として扱うように設定されていることを確認してください。
#フッター
生成された JavaScript ファイルおよび CSS ファイルの末尾に任意の文字列を挿入するには、これを使用します。これは通常、コメントを挿入するために使用されます。
esbuild app.js --footer:js=//comment --footer:css=/*comment*/
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
footer: {
js: '//comment',
css: '/*comment*/',
},
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Footer: map[string]string{
"js": "//comment",
"css": "/*comment*/",
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これは、末尾ではなく先頭に挿入する banner と似ています。
#形式
これは、生成された JavaScript ファイルの出力形式を設定します。現在、設定可能な 3 つの値 iife
、cjs
、および esm
があります。出力形式が指定されていない場合、esbuild は バンドルが有効な場合は(以下で説明するように)出力形式を選択し、バンドルが無効な場合は形式変換を行いません。
#IIFE
iife
形式は「即時実行関数式」の略で、ブラウザで実行することを目的としています。コードを関数式でラップすると、コード内の変数がグローバルスコープの変数と誤って競合しないようになります。エントリーポイントにブラウザでグローバルとして公開するエクスポートがある場合は、グローバル名設定を使用してそのグローバルの名前を設定できます。出力形式が指定されておらず、バンドルが有効で、プラットフォームが browser
(デフォルト)に設定されている場合、iife
形式は自動的に有効になります。iife
形式の指定は次のようになります。
echo 'alert("test")' | esbuild --format=iife
(() => {
alert("test");
})();
import * as esbuild from 'esbuild'
let js = 'alert("test")'
let result = await esbuild.transform(js, {
format: 'iife',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "alert(\"test\")"
result := api.Transform(js, api.TransformOptions{
Format: api.FormatIIFE,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#CommonJS
cjs
形式は「CommonJS」の略で、Node での実行を想定しています。この形式は、環境に exports
、require
、および module
が存在することを前提としています。ECMAScript モジュール構文でエクスポートを含むエントリポイントは、各エクスポート名に対して exports
にゲッターを持つモジュールに変換されます。cjs
形式は、出力形式が指定されておらず、バンドルが有効で、プラットフォームが node
に設定されている場合、自動的に有効になります。cjs
形式の指定は次のようになります。
echo 'export default "test"' | esbuild --format=cjs
...
var stdin_exports = {};
__export(stdin_exports, {
default: () => stdin_default
});
module.exports = __toCommonJS(stdin_exports);
var stdin_default = "test";
import * as esbuild from 'esbuild'
let js = 'export default "test"'
let result = await esbuild.transform(js, {
format: 'cjs',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "export default 'test'"
result := api.Transform(js, api.TransformOptions{
Format: api.FormatCommonJS,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#ESM
esm
形式は「ECMAScript モジュール」の略です。この形式は、環境が import
および export
構文をサポートしていることを前提としています。CommonJS モジュール構文でエクスポートを含むエントリポイントは、module.exports
の値の単一の default
エクスポートに変換されます。esm
形式は、出力形式が指定されておらず、バンドルが有効で、プラットフォームが neutral
に設定されている場合、自動的に有効になります。esm
形式の指定は次のようになります。
echo 'module.exports = "test"' | esbuild --format=esm
...
var require_stdin = __commonJS({
"<stdin>"(exports, module) {
module.exports = "test";
}
});
export default require_stdin();
import * as esbuild from 'esbuild'
let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
format: 'esm',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "module.exports = 'test'"
result := api.Transform(js, api.TransformOptions{
Format: api.FormatESModule,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
esm
形式はブラウザでも Node でも使用できますが、明示的にモジュールとしてロードする必要があります。これは、別のモジュールから import
すると自動的に行われます。それ以外の場合は
- ブラウザでは、
<script
を使用してモジュールをロードできます。src=" file.js" type=" module"> </script> type="
を忘れないでください。これを忘れると、コードが微妙で混乱を招くような形で壊れてしまいます(module" type="
を省略すると、すべてのトップレベル変数がグローバルスコープに入り、他の JavaScript ファイルで同じ名前のトップレベル変数と衝突します)。module"
- Node では、
node
を使用してモジュールをロードできます。file.mjs package.json
ファイルで"type":
を構成していない限り、Node では"module" .mjs
拡張子が必要であることに注意してください。esbuild で 出力拡張子設定を使用して、esbuild が生成するファイルの出力拡張子をカスタマイズできます。Node での ECMAScript モジュールの使用については、こちらをご覧ください。
#グローバル名
このオプションは、形式設定が iife
(即時実行関数式)の場合にのみ意味があります。エントリポイントからのエクスポートを格納するために使用されるグローバル変数の名前を設定します。
echo 'module.exports = "test"' | esbuild --format=iife --global-name=xyz
import * as esbuild from 'esbuild'
let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
format: 'iife',
globalName: 'xyz',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "module.exports = 'test'"
result := api.Transform(js, api.TransformOptions{
Format: api.FormatIIFE,
GlobalName: "xyz",
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
iife
形式でグローバル名を指定すると、次のようなコードが生成されます。
var xyz = (() => {
...
var require_stdin = __commonJS((exports, module) => {
module.exports = "test";
});
return require_stdin();
})();
グローバル名は複合プロパティ式にすることもでき、その場合、esbuild はそのプロパティを持つグローバル変数を生成します。競合する既存のグローバル変数は上書きされません。これは、複数の独立したスクリプトが同じグローバルオブジェクトにエクスポートを追加する「ネームスペース」を実装するために使用できます。例えば
echo 'module.exports = "test"' | esbuild --format=iife --global-name='example.versions["1.0"]'
import * as esbuild from 'esbuild'
let js = 'module.exports = "test"'
let result = await esbuild.transform(js, {
format: 'iife',
globalName: 'example.versions["1.0"]',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "module.exports = 'test'"
result := api.Transform(js, api.TransformOptions{
Format: api.FormatIIFE,
GlobalName: `example.versions["1.0"]`,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
上記で使用されている複合グローバル名は、次のようなコードを生成します。
var example = example || {};
example.versions = example.versions || {};
example.versions["1.0"] = (() => {
...
var require_stdin = __commonJS((exports, module) => {
module.exports = "test";
});
return require_stdin();
})();
#法的コメント
「法的コメント」は、@license
または @preserve
を含む、または //!
または /*!
で始まる JS のステートメントレベルのコメントまたは CSS のルールレベルのコメントであると見なされます。これらのコメントは、コードの元の作者の意図に従っているため、デフォルトで出力ファイルに保持されます。ただし、この動作は、次のいずれかのオプションを使用して構成できます。
none
法的コメントを保持しません。inline
すべての法的コメントを保持します。eof
すべての法的コメントをファイルの末尾に移動します。linked
すべての法的コメントを.LEGAL.txt
ファイルに移動し、コメントでそれらにリンクします。external
すべての法的コメントを.LEGAL.txt
ファイルに移動しますが、それらにリンクしません。
デフォルトの動作は、バンドルが有効な場合は eof
で、それ以外の場合は inline
です。法的コメントモードの設定は次のようになります。
esbuild app.js --legal-comments=eof
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
legalComments: 'eof',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
LegalComments: api.LegalCommentsEndOfFile,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
JS の「ステートメントレベル」および CSS の「ルールレベル」とは、トップレベルスコープまたはステートメントまたはルールブロックなど、複数のステートメントまたはルールが許可されているコンテキストにコメントが表示される必要があることを意味します。したがって、式内または宣言レベルのコメントは法的コメントとは見なされません。
#行制限
この設定は、esbuild が非常に長い行を含む出力ファイルを生成するのを防ぐ方法であり、実装の不十分なテキストエディタでの編集パフォーマンスを向上させるのに役立ちます。この値を正の整数に設定して、指定されたバイト数を超えた直後に特定の行を終了するように esbuild に指示します。たとえば、これは、約 80 文字を超えた直後に長い行を折り返します。
esbuild app.ts --line-limit=80
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
lineLimit: 80,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
LineLimit: 80,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
行は、制限を通過する直前ではなく、通過した後に切り捨てられます。これは、制限をいつ通過したかをチェックする方が、制限をいつ通過しそうかを予測するよりも簡単であり、出力ファイルを生成するときにバックアップして書き直すことを避ける方が高速であるためです。したがって、制限は概算のみです。
この設定は JavaScript と CSS の両方に適用され、最小化が無効になっている場合でも機能します。この設定をオンにすると、ファイルが大きくなることに注意してください。追加の改行はファイル内で追加のスペースを占有するためです(gzip 圧縮後でも)。
#分割
サポート対象: ビルド
esm
出力形式でのみ機能します。また、コード分割チャンクをまたがる import
ステートメントに、既知の順序付けの問題があります。この機能の更新については、追跡中の問題を参照してください。これにより、「コード分割」が有効になり、次の 2 つの目的を果たします。
複数のエントリポイント間で共有されているコードは、両方のエントリポイントがインポートする別の共有ファイルに分割されます。これにより、ユーザーが最初に 1 つのページを閲覧し、次に別のページを閲覧した場合、共有部分がすでにブラウザーによってダウンロードおよびキャッシュされている場合は、2 番目のページのすべての JavaScript を最初からダウンロードする必要がなくなります。
非同期の
import()
式を通じて参照されるコードは、別のファイルに分割され、その式が評価されたときにのみロードされます。これにより、起動時に必要なコードのみをダウンロードし、後で必要に応じて追加のコードを遅延ロードすることで、アプリの初期ダウンロード時間を改善できます。コード分割が有効になっていない場合、
import()
式は代わりにPromise
になります。これにより、式の非同期セマンティクスは維持されますが、インポートされたコードは、別のファイルに分割されるのではなく、同じバンドルに含まれることを意味します。.resolve() .then(() => require())
コード分割を有効にする場合は、outdir 設定を使用して出力ディレクトリも構成する必要があります。
esbuild home.ts about.ts --bundle --splitting --outdir=out --format=esm
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['home.ts', 'about.ts'],
bundle: true,
splitting: true,
outdir: 'out',
format: 'esm',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"home.ts", "about.ts"},
Bundle: true,
Splitting: true,
Outdir: "out",
Format: api.FormatESModule,
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#出力場所
#上書きを許可
サポート対象: ビルド
この設定を有効にすると、出力ファイルが入力ファイルを上書きできるようになります。デフォルトでは有効になっていません。これは、ソースコードを上書きすることを意味し、コードがチェックインされていない場合はデータの損失につながる可能性があるためです。しかし、これをサポートすることで、一時ディレクトリの必要性を回避し、特定のワークフローが容易になります。したがって、ソースコードを意図的に上書きする場合は、これを有効にできます。
esbuild app.js --outdir=. --allow-overwrite
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
outdir: '.',
allowOverwrite: true,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Outdir: ".",
AllowOverwrite: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#アセット名
サポート対象: ビルド
このオプションは、ローダーが file
に設定されている場合に追加で生成される出力ファイルの名前を制御します。出力パスが生成されるときにファイルに固有の値で置き換えられるプレースホルダーを使用して、テンプレートで出力パスを構成します。たとえば、assets/
のアセット名テンプレートを指定すると、すべてのアセットが出力ディレクトリ内の assets
というサブディレクトリに配置され、ファイル名にアセットのコンテンツハッシュが含まれます。それを行うと、次のようになります。
esbuild app.js --asset-names=assets/[name]-[hash] --loader:.png=file --bundle --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
assetNames: 'assets/[name]-[hash]',
loader: { '.png': 'file' },
bundle: true,
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
AssetNames: "assets/[name]-[hash]",
Loader: map[string]api.Loader{
".png": api.LoaderFile,
},
Bundle: true,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
アセットパスのテンプレートで使用できるプレースホルダーは 4 つあります。
[dir]
これは、アセットファイルを含むディレクトリから outbase ディレクトリへの相対パスです。その目的は、出力ディレクトリ内の入力ディレクトリ構造をミラーリングすることにより、アセット出力パスをより美しく見せることです。
[name]
これは、拡張子なしのアセットの元のファイル名です。たとえば、アセットの元の名前が
image.png
の場合、[name]
はテンプレート内でimage
で置き換えられます。このプレースホルダーを使用する必要はありません。デバッグを容易にするための人間にとってわかりやすいアセット名を提供する目的でのみ存在します。[hash]
これは、名前の衝突を回避するのに役立つ、アセットのコンテンツハッシュです。たとえば、コードで
components/
とbutton/ icon.png components/
をインポートする場合、両方ともselect/ icon.png icon
という名前のアセットを区別するためにハッシュが必要になります。[ext]
これは、アセットのファイル拡張子(つまり、最後の
.
文字の後のすべて)です。異なるタイプのアセットを異なるディレクトリに入れるために使用できます。たとえば、--asset-names=
は、assets/ [ext]/ [name]-[hash] image.png
という名前のアセットをassets/
として書き出す可能性があります。png/ image-CQFGD2NG.png
アセットパステンプレートにファイル拡張子を含める必要はありません。テンプレート置換後、アセットの元のファイル拡張子は出力パスの末尾に自動的に追加されます。
このオプションは、チャンク名および エントリ名オプションに似ています。
#チャンク名
サポート対象: ビルド
このオプションは、コード分割が有効な場合に自動的に生成される共有コードのチャンクのファイル名を制御します。出力パスが生成されるときにチャンクに固有の値で置き換えられるプレースホルダーを使用して、テンプレートで出力パスを構成します。たとえば、chunks/
のチャンク名テンプレートを指定すると、生成されたすべてのチャンクが出力ディレクトリ内の chunks
というサブディレクトリに配置され、ファイル名にチャンクのコンテンツハッシュが含まれます。それを行うと、次のようになります。
esbuild app.js --chunk-names=chunks/[name]-[hash] --bundle --outdir=out --splitting --format=esm
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
chunkNames: 'chunks/[name]-[hash]',
bundle: true,
outdir: 'out',
splitting: true,
format: 'esm',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
ChunkNames: "chunks/[name]-[hash]",
Bundle: true,
Outdir: "out",
Splitting: true,
Format: api.FormatESModule,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
チャンクパステンプレートで使用できるプレースホルダーは 3 つあります。
[name]
これは現在、常にテキスト
chunk
になりますが、このプレースホルダーは将来のリリースで追加の値を取る可能性があります。[hash]
これは、チャンクのコンテンツハッシュです。これを含めることは、複数の共有コードのチャンクが生成される場合に、異なるチャンクを互いに区別するために必要です。
[ext]
これは、チャンクのファイル拡張子(つまり、最後の
.
文字の後のすべて)です。異なるタイプのチャンクを異なるディレクトリに入れるために使用できます。たとえば、--chunk-names=
は、チャンクをchunks/ [ext]/ [name]-[hash] chunks/
として書き出す可能性があります。css/ chunk-DEFJT7KY.css
チャンクパステンプレートにファイル拡張子を含める必要はありません。適切なコンテンツタイプ用に構成された 出力拡張子は、テンプレート置換後、出力パスの末尾に自動的に追加されます。
このオプションは、自動的に生成される共有コードのチャンクの名前のみを制御することに注意してください。エントリポイントに関連する出力ファイルの名前は制御しません。これらの名前は現在、outbase ディレクトリに対する元のエントリポイントファイルのパスから決定され、この動作は変更できません。エントリポイント出力ファイルのファイル名を変更できるようにする追加の API オプションが将来追加されます。
このオプションは、アセット名および エントリ名オプションに似ています。
#エントリ名
サポート対象: ビルド
このオプションは、各入力エントリポイントファイルに対応する出力ファイルの名前を制御します。出力パスを生成する際に、ファイルに固有の値で置き換えられるプレースホルダーを使用したテンプレートを使用して出力パスを設定します。たとえば、エントリ名テンプレートを[dir]/
と指定すると、ファイル名に出力ファイルのハッシュが含まれ、ファイルは出力ディレクトリ(場合によってはサブディレクトリ)に配置されます([dir]
の詳細については下記を参照)。この設定は次のようになります。
esbuild src/main-app/app.js --entry-names=[dir]/[name]-[hash] --outbase=src --bundle --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/main-app/app.js'],
entryNames: '[dir]/[name]-[hash]',
outbase: 'src',
bundle: true,
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"src/main-app/app.js"},
EntryNames: "[dir]/[name]-[hash]",
Outbase: "src",
Bundle: true,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
エントリパステンプレートで使用できるプレースホルダーは4つあります。
[dir]
これは、入力エントリポイントファイルを含むディレクトリからoutbaseディレクトリへの相対パスです。その目的は、異なるサブディレクトリにある同じ名前のエントリポイント間の衝突を避けるのに役立つことです。
たとえば、2つのエントリポイント
src/
とpages/ home/ index.ts src/
があり、outbaseディレクトリがpages/ about/ index.ts src
で、エントリ名テンプレートが[dir]/[name]
の場合、出力ディレクトリにはpages/
とhome/ index.js pages/
が含まれます。エントリ名テンプレートがabout/ index.js [name]
のみだった場合、出力ディレクトリ内に同じ出力パスindex.js
を持つ2つの出力ファイルが存在するため、バンドルは失敗したでしょう。[name]
これは、拡張子を除いたエントリポイントの元のファイル名です。たとえば、入力エントリポイントファイルの名前が
app.js
の場合、[name]
はテンプレート内でapp
に置き換えられます。[hash]
これは、ブラウザのキャッシュを最大限に活用するために使用できる出力ファイルのコンテンツハッシュです。エントリポイント名に
[hash]
を追加すると、esbuildは対応する出力ファイル(およびコード分割がアクティブな場合は、インポートするすべての出力ファイル)のすべてのコンテンツに関連するハッシュを計算します。ハッシュは、その出力ファイルに関連する入力ファイルのいずれかが変更された場合にのみ変更されるように設計されています。その後、Webサーバーにこれらのファイルを永久にキャッシュするようにブラウザに指示できます(実際には、1年後など、非常に長い時間が経過してから期限切れになると言えます)。次に、メタファイルの情報を使用して、どの出力ファイルパスがどの入力エントリポイントに対応するかを判断し、
<script>
タグに含めるパスを知ることができます。[ext]
これは、エントリポイントファイルが書き出されるファイル拡張子です(つまり、元のファイル拡張子ではなく、出力拡張子の設定)。さまざまなタイプのエントリポイントを異なるディレクトリに配置するために使用できます。たとえば、
--entry-names=
は、entries/ [ext]/ [name] app.ts
の出力ファイルをentries/
に書き込む場合があります。js/ app.js
エントリパステンプレートにファイル拡張子を含める必要はありません。ファイルタイプに基づいた適切な出力拡張子が、テンプレート置換後に出力パスの末尾に自動的に追加されます。
このオプションは、アセット名およびチャンク名オプションに似ています。
#出力拡張子
サポート対象: ビルド
このオプションを使用すると、esbuildが生成するファイルの拡張子を.js
または.css
以外のものにカスタマイズできます。特に、.mjs
および.cjs
のファイル拡張子は、nodeで特別な意味を持ちます(それぞれ、ESM形式とCommonJS形式のファイルを示します)。このオプションは、esbuildを使用して複数のファイルを生成する場合で、outfileオプションの代わりにoutdirオプションを使用する必要がある場合に役立ちます。次のように使用できます。
esbuild app.js --bundle --outdir=dist --out-extension:.js=.mjs
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outdir: 'dist',
outExtension: { '.js': '.mjs' },
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Outdir: "dist",
OutExtension: map[string]string{
".js": ".mjs",
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#アウトベース
サポート対象: ビルド
ビルドに別々のディレクトリに複数のエントリポイントが含まれている場合、ディレクトリ構造はアウトベースディレクトリを基準にして出力ディレクトリに複製されます。たとえば、2つのエントリポイントsrc/
とsrc/
があり、アウトベースディレクトリがsrc
の場合、出力ディレクトリにはpages/
とpages/
が含まれます。使用方法は次のとおりです。
esbuild src/pages/home/index.ts src/pages/about/index.ts --bundle --outdir=out --outbase=src
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: [
'src/pages/home/index.ts',
'src/pages/about/index.ts',
],
bundle: true,
outdir: 'out',
outbase: 'src',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{
"src/pages/home/index.ts",
"src/pages/about/index.ts",
},
Bundle: true,
Outdir: "out",
Outbase: "src",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
アウトベースディレクトリが指定されていない場合、すべての入力エントリポイントパスの中で共通の祖先ディレクトリがデフォルトになります。上記の例ではsrc/
であり、デフォルトでは出力ディレクトリには代わりにhome/
とabout/
が含まれることを意味します。
#出力ディレクトリ
サポート対象: ビルド
このオプションは、ビルド操作の出力ディレクトリを設定します。たとえば、このコマンドはout
というディレクトリを生成します。
esbuild app.js --bundle --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
出力ディレクトリが存在しない場合は生成されますが、すでにいくつかのファイルが含まれている場合はクリアされません。生成されたファイルは、同じ名前の既存のファイルを上書きします。出力ディレクトリにesbuildの現在の実行からのファイルのみが含まれるようにする場合は、esbuildを実行する前に自分で出力ディレクトリをクリアする必要があります。
ビルドに別々のディレクトリに複数のエントリポイントが含まれている場合、ディレクトリ構造は、すべての入力エントリポイントパスの中で共通の祖先ディレクトリから始まる出力ディレクトリに複製されます。たとえば、2つのエントリポイントsrc/
とsrc/
がある場合、出力ディレクトリにはhome/
とabout/
が含まれます。この動作をカスタマイズする場合は、アウトベースディレクトリを変更する必要があります。
#出力ファイル
サポート対象: ビルド
このオプションは、ビルド操作の出力ファイル名を設定します。これは、エントリポイントが1つしかない場合にのみ適用されます。複数のエントリポイントがある場合は、代わりにoutdirオプションを使用して出力ディレクトリを指定する必要があります。outfileの使用例は次のとおりです。
esbuild app.js --bundle --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Outdir: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#パブリックパス
サポート対象: ビルド
これは、外部ファイルローダーと組み合わせて使用すると便利です。デフォルトでは、そのローダーは、インポートされたファイルの名前をdefault
エクスポートを使用して文字列としてエクスポートします。パブリックパスオプションを使用すると、このローダーによってロードされた各ファイルのエクスポートされた文字列にベースパスを付加できます。
esbuild app.js --bundle --loader:.png=file --public-path=https://www.example.com/v1 --outdir=out
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
loader: { '.png': 'file' },
publicPath: 'https://www.example.com/v1',
outdir: 'out',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Loader: map[string]api.Loader{
".png": api.LoaderFile,
},
Outdir: "out",
PublicPath: "https://www.example.com/v1",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#書き込み
サポート対象: ビルド
ビルドAPI呼び出しは、ファイルシステムに直接書き込むか、書き込まれるはずだったファイルをインメモリバッファーとして返すことができます。デフォルトでは、CLIおよびJavaScript APIはファイルシステムに書き込み、Go APIは書き込みません。インメモリバッファーを使用するには、次のようにします。
import * as esbuild from 'esbuild'
let result = await esbuild.build({
entryPoints: ['app.js'],
sourcemap: 'external',
write: false,
outdir: 'out',
})
for (let out of result.outputFiles) {
console.log(out.path, out.contents, out.hash, out.text)
}
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Sourcemap: api.SourceMapExternal,
Write: false,
Outdir: "out",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
for _, out := range result.OutputFiles {
fmt.Printf("%v %v %s\n", out.Path, out.Contents, out.Hash)
}
}
hash
プロパティはcontents
フィールドのハッシュであり、便宜上提供されています。ハッシュアルゴリズム(現在はXXH64)は実装に依存しており、esbuildのバージョン間でいつでも変更される可能性があります。
#パス解決
#エイリアス
サポート対象: ビルド
この機能を使用すると、バンドル時にあるパッケージを別のパッケージに置き換えることができます。以下の例では、パッケージoldpkg
をパッケージnewpkg
に置き換えています。
esbuild app.js --bundle --alias:oldpkg=newpkg
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
write: true,
alias: {
'oldpkg': 'newpkg',
},
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Write: true,
Alias: map[string]string{
"oldpkg": "newpkg",
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これらの新しい置換は、esbuildの他のすべてのパス解決ロジックよりも先に実行されます。この機能のユースケースの1つは、制御できないサードパーティコードで、nodeのみのパッケージをブラウザフレンドリーなパッケージに置き換えることです。
エイリアスを使用してインポートパスが置換されると、結果のインポートパスは、インポートパスを含むソースファイル内のディレクトリではなく、作業ディレクトリで解決されることに注意してください。必要に応じて、esbuildが使用する作業ディレクトリは、作業ディレクトリ機能で設定できます。
#条件
サポート対象: ビルド
この機能は、package.json
のexports
フィールドがどのように解釈されるかを制御します。カスタム条件は、条件設定を使用して追加できます。これらは必要なだけ指定でき、これらの意味はパッケージ作成者に完全に委ねられます。Nodeは現在、推奨される使用のためにdevelopment
およびproduction
のカスタム条件のみを承認しています。カスタム条件custom1
およびcustom2
を追加する例を以下に示します。
esbuild src/app.js --bundle --conditions=custom1,custom2
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['src/app.js'],
bundle: true,
conditions: ['custom1', 'custom2'],
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"src/app.js"},
Bundle: true,
Conditions: []string{"custom1", "custom2"},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#条件の仕組み
条件を使用すると、さまざまな状況で同じインポートパスを異なるファイルの場所にリダイレクトできます。条件とパスを含むリダイレクトマップは、パッケージのpackage.json
ファイルのexports
フィールドに保存されます。たとえば、これはrequire('pkg/foo')
をpkg/required.cjs
にリマップし、import 'pkg/foo'
をimport
およびrequire
条件を使用してpkg/imported.mjs
にリマップします。
{
"name": "pkg",
"exports": {
"./foo": {
"import": "./imported.mjs",
"require": "./required.cjs",
"default": "./fallback.js"
}
}
}
条件は、JSONファイル内に表示される順序でチェックされます。したがって、上記の例は、次のように動作します。
if (importPath === './foo') {
if (conditions.has('import')) return './imported.mjs'
if (conditions.has('require')) return './required.cjs'
return './fallback.js'
}
デフォルトでは、esbuildに組み込まれており、無効にできない特別な動作を持つ5つの条件があります。
default
この条件は常にアクティブです。これは最後に配置されることを目的としており、他の条件が適用されない場合のフォールバックを提供します。この条件は、nodeでネイティブにコードを実行する場合にもアクティブです。
import
この条件は、インポートパスがESMの
import
ステートメントまたはimport()
式からの場合にのみアクティブです。ESM固有のコードを提供するために使用できます。この条件は、nodeでネイティブにコードを実行する場合にもアクティブです(ただし、ESMコンテキストでのみ)。require
この条件は、インポートパスがCommonJSの
require()
呼び出しからの場合にのみアクティブです。CommonJS固有のコードを提供するために使用できます。この条件は、nodeでネイティブにコードを実行する場合にもアクティブです(ただし、CommonJSコンテキストでのみ)。browser
この条件は、esbuildのplatform設定が
browser
に設定されている場合にのみアクティブです。ブラウザ固有のコードを提供するために使用できます。この条件は、nodeでネイティブにコードを実行する場合はアクティブではありません。node
この条件は、esbuildのplatform設定が
node
に設定されている場合にのみアクティブです。node固有のコードを提供するために使用できます。この条件は、nodeでネイティブにコードを実行する場合にもアクティブです。
platformがbrowser
またはnode
のいずれかに設定されており、カスタム条件が構成されていない場合、次の条件も自動的に含まれます。カスタム条件が(空のリストであっても)構成されている場合、この条件は自動的に含まれなくなります。
module
この条件を使用すると、バンドル時にツリーシェイキングを向上させるために、特定のインポートパスのESMバリアントを選択するようにesbuildに指示できます。この条件は、nodeでネイティブにコードを実行する場合はアクティブではありません。これはバンドラーに固有のものであり、Webpackから生まれました。
require
およびimport
条件を使用する場合、パッケージがバンドルに複数回含まれる可能性があります! これは、結果のバンドルを肥大化させるだけでなく、コードの状態の重複コピーによるバグを引き起こす可能性のある微妙な問題です。これは一般にデュアルパッケージハザードとして知られています。
バンドラーとnodeでネイティブに実行する場合の両方で機能するデュアルパッケージハザードを回避する方法の1つは、すべてのコードをCommonJSとしてrequire
条件に配置し、import
条件がパッケージでrequire
を呼び出してESM構文を使用してパッケージを再エクスポートする軽量のESMラッパーになるようにすることです。ただし、esbuildはCommonJSモジュールをツリーシェイクしないため、このアプローチは適切なツリーシェイキングを提供しません。
デュアルパッケージの危険性を回避するもう1つの方法は、バンドラー固有のmodule
条件を使用して、バンドラーが常にパッケージのESMバージョンをロードするように指示し、nodeが常にパッケージのCommonJSバージョンにフォールバックするようにすることです。import
とmodule
はどちらもESMで使用することを目的としていますが、import
とは異なり、module
条件は、インポートパスがrequire
呼び出しを使用してロードされた場合でも常にアクティブです。これは、バンドラーがrequire
を使用してESMをロードすることをサポートしているため、バンドラーでうまく機能しますが、nodeは意図的にrequire
を使用したESMのロードを実装していないため、nodeでは機能しません。
#外部
サポート対象: ビルド
ファイルまたはパッケージを外部としてマークして、ビルドから除外できます。バンドルされる代わりに、インポートは保持され(iife
およびcjs
形式ではrequire
を使用し、esm
形式ではimport
を使用)、代わりに実行時に評価されます。
これにはいくつかの用途があります。まず、実行されないことがわかっているコードパスについて、バンドルから不要なコードを削除するために使用できます。たとえば、パッケージにはnodeでのみ実行されるコードが含まれている場合がありますが、そのパッケージはブラウザでのみ使用することになります。また、バンドルできないパッケージから、実行時にnodeでコードをインポートするためにも使用できます。たとえば、fsevents
パッケージには、esbuildがサポートしていないネイティブ拡張機能が含まれています。何かを外部としてマークする方法は次のとおりです。
echo 'require("fsevents")' > app.js
esbuild app.js --bundle --external:fsevents --platform=node
// app.js
require("fsevents");
import * as esbuild from 'esbuild'
import fs from 'node:fs'
fs.writeFileSync('app.js', 'require("fsevents")')
await esbuild.build({
entryPoints: ['app.js'],
outfile: 'out.js',
bundle: true,
platform: 'node',
external: ['fsevents'],
})
package main
import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
ioutil.WriteFile("app.js", []byte("require(\"fsevents\")"), 0644)
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Outfile: "out.js",
Bundle: true,
Write: true,
Platform: api.PlatformNode,
External: []string{"fsevents"},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
外部パスで*
ワイルドカード文字を使用して、そのパターンに一致するすべてのファイルを外部としてマークすることもできます。たとえば、すべての.png
ファイルを削除するには*.png
を使用し、/images/
で始まるすべてのパスを削除するには/images/*
を使用できます。
esbuild app.js --bundle "--external:*.png" "--external:/images/*"
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
outfile: 'out.js',
bundle: true,
external: ['*.png', '/images/*'],
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Outfile: "out.js",
Bundle: true,
Write: true,
External: []string{"*.png", "/images/*"},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
外部パスは、パス解決の前と後の両方に適用されるため、ソースコード内のインポートパスと絶対ファイルシステムパスの両方に一致させることができます。外部パスがどちらの場合でも一致した場合、パスは外部と見なされます。具体的な動作は次のとおりです。
パス解決が開始される前に、インポートパスはすべての外部パスと照合されます。さらに、外部パスがパッケージパスのように見える場合(つまり、
/
、./
、または../
で始まらない場合)、インポートパスは、そのパッケージパスをパスプレフィックスとして持つかどうかを確認するためにチェックされます。つまり、
--external:
は暗黙的に@foo/ bar --external:
も意味し、これはインポートパス@foo/ bar/* @foo/
に一致します。したがって、bar/ baz @foo/bar
パッケージ内のすべてのパスも外部としてマークします。パス解決が終了した後、解決された絶対パスは、パッケージパスのように見えない(つまり、
/
、./
、または../
で始まる)すべての外部パスと照合されます。ただし、チェックする前に、外部パスは現在の作業ディレクトリと結合され、正規化されて絶対パスになります(*
ワイルドカード文字が含まれている場合でも)。つまり、
--external:
を使用して、ディレクトリ./dir/* dir
内のすべてを外部としてマークできます。先頭の./
が重要であることに注意してください。代わりに--external:
を使用すると、パッケージパスとして扱われ、パス解決の完了後にチェックされません。dir/*
#メインフィールド
サポート対象: ビルド
nodeでパッケージをインポートすると、そのパッケージのpackage.json
ファイルのmain
フィールドによって、どのファイルがインポートされるかが決定されます(その他多くのルールとともに)。esbuildを含む主要なJavaScriptバンドラーを使用すると、パッケージの解決時に試す追加のpackage.json
フィールドを指定できます。少なくとも3つのそのようなフィールドが一般的に使用されています。
main
これは、nodeで使用することを目的としたすべてのパッケージの標準フィールドです。名前
main
は、nodeのモジュール解決ロジック自体にハードコードされています。nodeで使用することを目的としているため、このフィールドのファイルパスがCommonJSスタイルのモジュールであることが期待できます。module
このフィールドは、ECMAScriptモジュールをnodeに統合する方法に関する提案から生まれました。このため、このフィールドのファイルパスがECMAScriptスタイルのモジュールであることが期待できます。この提案はnodeでは採用されませんでしたが(nodeは代わりに
"type":
を使用します)、ECMAScriptスタイルのモジュールの方がツリーシェイキング、つまりデッドコードの削除が優れているため、主要なバンドラーで採用されました。"module" パッケージ作成者向け:一部のパッケージでは、ブラウザ固有のコードに
module
フィールドを誤って使用し、node固有のコードをmain
フィールドに残しています。これは、おそらくnodeがmodule
フィールドを無視し、人々が通常ブラウザ固有のコードにのみバンドラーを使用するためです。ただし、node固有のコードをバンドルすることも価値があり(たとえば、ダウンロード時間と起動時間を短縮します)、ブラウザ固有のコードをmodule
に入れるパッケージは、バンドラーが効果的にツリーシェイキングを実行することを妨げます。パッケージでブラウザ固有のコードを公開しようとする場合は、代わりにbrowser
フィールドを使用してください。browser
このフィールドは、バンドラーがnode固有のファイルまたはモジュールをブラウザフレンドリーなバージョンに置き換えることができる提案から生まれました。これにより、代替のブラウザ固有のエントリポイントを指定できます。パッケージが
browser
フィールドとmodule
フィールドの両方を一緒に使用できることに注意してください(以下の注を参照)。
デフォルトのメインフィールドは、現在のプラットフォーム設定によって異なります。これらのデフォルトは、既存のパッケージエコシステムと最も広く互換性があるはずです。ただし、必要に応じて次のようにカスタマイズできます。
esbuild app.js --bundle --main-fields=module,main
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
mainFields: ['module', 'main'],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
MainFields: []string{"module", "main"},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#パッケージ作成者向け
module
フィールドと組み合わせてbrowser
フィールドを使用するパッケージを作成する場合は、おそらく、完全なCommonJS対ESMおよびブラウザ対nodeの互換性マトリックスの4つのエントリすべてを入力する必要があります。そのためには、単なる文字列ではなくマップであるbrowser
フィールドの拡張形式を使用する必要があります。
{
"main": "./node-cjs.js",
"module": "./node-esm.js",
"browser": {
"./node-cjs.js": "./browser-cjs.js",
"./node-esm.js": "./browser-esm.js"
}
}
main
フィールドはCommonJSであることが期待され、module
フィールドはESMであることが期待されます。使用するモジュール形式に関する決定は、ブラウザ固有またはnode固有のバリアントを使用するかどうかに関する決定とは独立しています。これらの4つのエントリのいずれかを省略すると、間違ったバリアントが選択されるリスクがあります。たとえば、CommonJSブラウザビルドのエントリを省略すると、代わりにCommonJS nodeビルドが選択される可能性があります。
main
、module
、およびbrowser
を使用するのは古い方法であることに注意してください。代わりに、より新しい方法を使用することもできます。package.json
のexports
フィールドです。これは、異なるトレードオフを提供します。たとえば、パッケージ内のすべてのサブパスのインポートをより正確に制御できます(main
フィールドはエントリポイントのみを制御できます)が、構成方法によっては、パッケージが複数回インポートされる可能性があります。
#Nodeパス
サポート対象: ビルド
nodeのモジュール解決アルゴリズムは、インポートパスの解決時に使用するグローバルディレクトリのリストを含むNODE_PATH
という環境変数をサポートしています。これらのパスは、すべての親ディレクトリにあるnode_modules
ディレクトリに加えて、パッケージを検索するために使用されます。CLIで環境変数を使用し、JSおよびGo APIで配列を使用して、このディレクトリのリストをesbuildに渡すことができます。
NODE_PATH=someDir esbuild app.js --bundle --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
nodePaths: ['someDir'],
entryPoints: ['app.js'],
bundle: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
NodePaths: []string{"someDir"},
EntryPoints: []string{"app.js"},
Bundle: true,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
CLIを使用しており、NODE_PATH
を使用して複数のディレクトリを渡す場合は、Unixでは:
、Windowsでは;
で区切る必要があります。これは、Node自体が使用するのと同じ形式です。
#パッケージ
サポート対象: ビルド
この設定を使用して、パッケージのすべての依存関係をバンドルから除外します。これは、node用にバンドルする場合に役立ちます。これは、多くのnpmパッケージが、バンドル中にesbuildがサポートしないnode固有の機能(__dirname
、import.meta.url
、fs.readFileSync
、および*.node
ネイティブバイナリモジュールなど)を使用するためです。使用方法は次のとおりです。
esbuild app.js --bundle --packages=external
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
packages: 'external',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Packages: api.PackagesExternal,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これを有効にすると、npmパッケージのように見える(つまり、.
または..
パスコンポーネントで始まらず、絶対パスではない)すべてのインポートパスが自動的に外部としてマークされます。これは、各依存関係を外部に手動で渡すのと同じ効果がありますが、より簡潔です。どの依存関係を外部にし、どれを外部にしないかをカスタマイズする場合は、この設定ではなく外部を使用する必要があります。
この設定は、バンドルが有効になっている場合にのみ効果があることに注意してください。また、インポートパスを外部としてマークすることは、構成されたエイリアスによってインポートパスが書き換えられた後に行われるため、この設定を使用した場合でも、エイリアス機能は引き続き有効であることに注意してください。
#シンボリックリンクの保持
サポート対象: ビルド
この設定は、nodeの--preserve-symlinks
設定をミラーリングします。その設定(またはWebpackの同様のresolve.symlinks
設定)を使用する場合は、esbuildでもこの設定を有効にする必要がある可能性があります。次のように有効にできます。
esbuild app.js --bundle --preserve-symlinks --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
preserveSymlinks: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
PreserveSymlinks: true,
Outfile: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
この設定を有効にすると、esbuildは、実際のファイルパス(つまり、シンボリックリンクをたどった後のパス)ではなく、元のファイルパス(つまり、シンボリックリンクをたどらないパス)によってファイルの同一性を判断します。これは、特定のディレクトリ構造で有益になる可能性があります。これにより、ファイルに複数のシンボリックリンクがポイントしている場合は、複数の同一性が与えられる可能性があり、生成された出力ファイルに複数回表示される可能性があることに注意してください。
注:「シンボリックリンク」という用語はシンボリックリンクを意味し、パスが別のパスにリダイレクトできるファイルシステム機能を指します。
#拡張子の解決
サポート対象: ビルド
Node が使用するモジュール解決アルゴリズムは、暗黙的なファイル拡張子をサポートしています。require('./file')
と記述すると、./file
、./file.js
、./file.json
、./file.node
の順でファイルがチェックされます。esbuild を含む最新のバンドラーは、この概念を他のファイルタイプにも拡張しています。esbuild における暗黙的なファイル拡張子の完全な順序は、resolve extensions 設定を使用してカスタマイズできます。デフォルトは .tsx,
です。
esbuild app.js --bundle --resolve-extensions=.ts,.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
resolveExtensions: ['.ts', '.js'],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
ResolveExtensions: []string{".ts", ".js"},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
注意点として、esbuild は意図的に .mjs
および .cjs
という新しい拡張子をこのリストに含めていません。Node の解決アルゴリズムはこれらを暗黙的なファイル拡張子として扱わないため、esbuild も同様です。これらの拡張子を持つファイルをインポートしたい場合は、インポートパスに明示的に拡張子を追加するか、この設定を変更して暗黙的に扱いたい拡張子を追加する必要があります。
#ワーキングディレクトリ
サポート対象: ビルド
この API オプションを使用すると、ビルドに使用するワーキングディレクトリを指定できます。通常、これは esbuild の API を呼び出すために使用しているプロセスの現在のワーキングディレクトリにデフォルト設定されます。ワーキングディレクトリは、API オプションとして与えられた相対パスを絶対パスに解決したり、ログメッセージで絶対パスを相対パスとして見やすく表示するなど、いくつかの異なる目的で esbuild によって使用されます。esbuild のワーキングディレクトリをカスタマイズする方法は次のとおりです。
cd "/var/tmp/custom/working/directory"
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['file.js'],
absWorkingDir: '/var/tmp/custom/working/directory',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"file.js"},
AbsWorkingDir: "/var/tmp/custom/working/directory",
Outfile: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
注意: Yarn Plug'n'Play を使用している場合は、このワーキングディレクトリが Yarn のマニフェストファイルを検索するために使用されることに注意してください。esbuild を無関係なディレクトリから実行している場合は、esbuild がマニフェストファイルを見つけるために、このワーキングディレクトリをマニフェストファイルを含むディレクトリ(またはその子ディレクトリのいずれか)に設定する必要があります。
#変換
#JSX
このオプションは、JSX 構文をどのように処理するかを esbuild に指示します。利用可能なオプションは以下のとおりです。
transform
これは、JSX 構文を使用する多くのライブラリ間で共有される汎用的な変換を使用して、JSX を JS に変換するように esbuild に指示します。各 JSX 要素は、要素のコンポーネント(またはフラグメントの場合はJSX フラグメント)を最初の引数として、JSX ファクトリ関数への呼び出しに変換されます。2 番目の引数は props の配列(または props がない場合は
null
)です。子要素が存在する場合は、2 番目の引数の後に追加の引数になります。ファイルごとにこの設定を構成したい場合は、
// @jsxRuntime
コメントを使用することで設定できます。これは、esbuild が従うBabel の JSX プラグインの規約です。classic preserve
これは、JSX 構文を関数呼び出しに変換するのではなく、出力に保持します。JSX 要素は、第一級構文として扱われ、minifyやプロパティマングリングなどの他の設定の影響を受けます。
これは、出力ファイルが有効な JavaScript コードではなくなることを意味します。この機能は、バンドル後に esbuild の出力ファイル内の JSX 構文を別のツールで変換する場合に使用することを目的としています。
automatic
この変換は、React 17+ で導入され、React に非常に固有のものです。これは、JSX インポートソースから自動的に
import
ステートメントを生成し、構文の処理方法に関して多くの特殊なケースを導入します。詳細をここで説明するには複雑すぎます。詳細については、新しい JSX 変換に関する React のドキュメントをお読みください。この変換の開発モードバージョンを有効にする場合は、さらにJSX dev設定を有効にする必要があります。ファイルごとにこの設定を構成したい場合は、
// @jsxRuntime
コメントを使用することで設定できます。これは、esbuild が従うBabel の JSX プラグインの規約です。automatic
JSX 変換を preserve
に設定する例を次に示します。
echo '<div/>' | esbuild --jsx=preserve --loader=jsx
<div />;
import * as esbuild from 'esbuild'
let result = await esbuild.transform('<div/>', {
jsx: 'preserve',
loader: 'jsx',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
result := api.Transform("<div/>", api.TransformOptions{
JSX: api.JSXPreserve,
Loader: api.LoaderJSX,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#JSX dev
JSX 変換が automatic
に設定されている場合、この設定を有効にすると、esbuild はファイル名とソースの場所を各 JSX 要素に自動的に挿入します。JSX ライブラリは、この情報を使用してデバッグを支援できます。JSX 変換が automatic
以外のものに設定されている場合、この設定は何も行いません。この設定を有効にする例を次に示します。
echo '<a/>' | esbuild --loader=jsx --jsx=automatic
import { jsx } from "react/jsx-runtime";
/* @__PURE__ */ jsx("a", {});
echo '<a/>' | esbuild --loader=jsx --jsx=automatic --jsx-dev
import { jsxDEV } from "react/jsx-dev-runtime";
/* @__PURE__ */ jsxDEV("a", {}, void 0, false, {
fileName: "<stdin>",
lineNumber: 1,
columnNumber: 1
}, this);
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
jsxDev: true,
jsx: 'automatic',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.jsx"},
JSXDev: true,
JSX: api.JSXAutomatic,
Outfile: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#JSX ファクトリ
これは、各 JSX 要素に対して呼び出される関数を設定します。通常、このような JSX 式は
<div>Example text</div>
次のように、React.createElement
への関数呼び出しにコンパイルされます。
React.createElement("div", null, "Example text");
JSX ファクトリを変更することで、React.createElement
以外のものを呼び出すことができます。たとえば、h
関数(Preactなどの他のライブラリで使用されます)を代わりに呼び出すには、次のようにします。
echo '<div/>' | esbuild --jsx-factory=h --loader=jsx
/* @__PURE__ */ h("div", null);
import * as esbuild from 'esbuild'
let result = await esbuild.transform('<div/>', {
jsxFactory: 'h',
loader: 'jsx',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
result := api.Transform("<div/>", api.TransformOptions{
JSXFactory: "h",
Loader: api.LoaderJSX,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
または、TypeScript を使用している場合は、tsconfig.json
ファイルに以下を追加することで、TypeScript 用の JSX を構成するだけで、esbuild は構成しなくても自動的にそれを認識するはずです。
{
"compilerOptions": {
"jsxFactory": "h"
}
}
ファイルごとにこれを構成したい場合は、// @jsx
コメントを使用することで設定できます。この設定は、JSX 変換が automatic
に設定されている場合は適用されないことに注意してください。
#JSX フラグメント
これは、各 JSX フラグメントに対して呼び出される関数を設定します。通常、このような JSX フラグメント式は
<>Stuff</>
次のように、React.Fragment
コンポーネントの使用にコンパイルされます。
React.createElement(React.Fragment, null, "Stuff");
JSX フラグメントを変更することで、React.Fragment
以外のコンポーネントを使用できます。たとえば、代わりに Fragment
コンポーネント(Preactなどの他のライブラリで使用されます)を使用するには、次のようにします。
echo '<>x</>' | esbuild --jsx-fragment=Fragment --loader=jsx
/* @__PURE__ */ React.createElement(Fragment, null, "x");
import * as esbuild from 'esbuild'
let result = await esbuild.transform('<>x</>', {
jsxFragment: 'Fragment',
loader: 'jsx',
})
console.log(result.code)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
result := api.Transform("<>x</>", api.TransformOptions{
JSXFragment: "Fragment",
Loader: api.LoaderJSX,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
または、TypeScript を使用している場合は、tsconfig.json
ファイルに以下を追加することで、TypeScript 用の JSX を構成するだけで、esbuild は構成しなくても自動的にそれを認識するはずです。
{
"compilerOptions": {
"jsxFragmentFactory": "Fragment"
}
}
ファイルごとにこれを構成したい場合は、// @jsxFrag
コメントを使用することで設定できます。この設定は、JSX 変換が automatic
に設定されている場合は適用されないことに注意してください。
#JSX インポートソース
JSX 変換が automatic
に設定されている場合、これを設定することで、esbuild が JSX ヘルパー関数を自動的にインポートするために使用するライブラリを変更できます。これは、React 17+ に固有の JSX 変換でのみ機能することに注意してください。JSX インポートソースを your-pkg
に設定する場合、そのパッケージは少なくとも次のエクスポートを公開する必要があります。
import { createElement } from "your-pkg"
import { Fragment, jsx, jsxs } from "your-pkg/jsx-runtime"
import { Fragment, jsxDEV } from "your-pkg/jsx-dev-runtime"
/jsx-runtime
および /jsx-dev-runtime
サブパスは、設計上ハードコードされており、変更することはできません。jsx
および jsxs
インポートは、JSX 開発モードがオフの場合に使用され、jsxDEV
インポートは JSX 開発モードがオンの場合に使用されます。これらの意味は、新しい JSX 変換に関する React のドキュメントで説明されています。createElement
インポートは、要素にプロップスプレッドが続き、その後ろに key
プロップがある場合に、JSX 開発モードに関係なく使用されます。これは次のように見えます。
return <div {...props} key={key} />
JSX インポートソースを preact
に設定する例を次に示します。
esbuild app.jsx --jsx-import-source=preact --jsx=automatic
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
jsxImportSource: 'preact',
jsx: 'automatic',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.jsx"},
JSXImportSource: "preact",
JSX: api.JSXAutomatic,
Outfile: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
または、TypeScript を使用している場合は、tsconfig.json
ファイルに以下を追加することで、TypeScript の JSX インポートソースを構成するだけで、esbuild は構成しなくても自動的にそれを認識するはずです。
{
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "preact"
}
}
また、この設定をファイルごとに制御したい場合は、各ファイルで // @jsxImportSource
コメントを使用できます。JSX 変換が他の方法で設定されていない場合、またはファイルごとに設定したい場合は、// @jsxRuntime
コメントも追加する必要があるかもしれません。
#JSX の副作用
デフォルトでは、esbuild は JSX 式に副作用がないと想定しています。つまり、/* @__PURE__ */
コメントで注釈が付けられ、未使用の場合はバンドル中に削除されます。これは仮想 DOM での JSX の一般的な使用法に従っており、JSX ライブラリの大部分に適用されます。ただし、一部の人はこのプロパティを持たない JSX ライブラリを記述しています(具体的には、JSX 式は任意の副作用を持つことができ、未使用の場合は削除できません)。このようなライブラリを使用している場合は、この設定を使用して、JSX 式に副作用があることを esbuild に伝えることができます。
esbuild app.jsx --jsx-side-effects
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.jsx'],
outfile: 'out.js',
jsxSideEffects: true,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.jsx"},
Outfile: "out.js",
JSXSideEffects: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#サポート対象
この設定を使用すると、個々の構文機能レベルで、esbuild でサポートされていない構文機能のセットをカスタマイズできます。たとえば、BigIntsがサポートされていないことを esbuild に指示して、それを使用しようとしたときに esbuild がエラーを生成するようにすることができます。通常、これはtarget
設定を使用するときに構成されます。これは、通常はこの設定の代わりに利用すべきものです。ターゲットがこの設定に加えて指定されている場合、この設定はターゲットによって指定されたものを上書きします。
ターゲットの設定に加えて、またはその代わりにこの設定を使用する必要がある理由の例を次に示します。
JavaScript ランタイムは、新しい構文機能をより高速に実装することがよくありますが、これは同等の古い JavaScript よりも遅くなるため、esbuild にこの構文機能がサポートされていないふりをさせることで、速度を上げることができます。たとえば、V8 には、オブジェクトスプレッドに関する長年のパフォーマンスバグがあり、オブジェクトスプレッド構文を使用する代わりにプロパティを手動でコピーすることで回避できます。
esbuild の
target
設定が認識するものの他に、多くの JavaScript 実装があり、それらは特定の機能をサポートしていない可能性があります。このような実装をターゲットにしている場合は、esbuild 自体を変更することなく、この設定を使用して、カスタム構文機能互換性セットで esbuild を構成できます。たとえば、TypeScript の JavaScript パーサーは、任意のモジュール名前空間識別子名をサポートしていない可能性があるため、TypeScript の JavaScript パーサーをターゲットにするときにそれらをオフにしたい場合があります。esbuild の出力を別のツールで処理する場合、esbuild で特定の機能を変換し、別のツールで別の機能を変換したい場合があります。たとえば、esbuild を使用して個々のファイルを ES5 に変換し、その出力をバンドルするために Webpack に渡す場合、ES5 では構文エラーであっても
import()
式を保持したいかもしれません。
esbuild に特定の構文機能をサポートされていないとみなさせたい場合は、次のように指定できます。
esbuild app.js --supported:bigint=false
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
supported: {
'bigint': false,
},
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Supported: map[string]bool{
"bigint": false,
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
構文機能は、esbuild 固有の機能名を使用して指定します。機能名の完全なセットは次のとおりです。
JavaScript
arbitrary-module-namespace-names
array-spread
arrow
async-await
async-generator
bigint
class
class-field
class-private-accessor
class-private-brand-check
class-private-field
class-private-method
class-private-static-accessor
class-private-static-field
class-private-static-method
class-static-blocks
class-static-field
const-and-let
decorators
default-argument
destructuring
dynamic-import
exponent-operator
export-star-as
for-await
for-of
function-or-class-property-access
generator
hashbang
import-assertions
import-meta
inline-script
logical-assignment
nested-rest-binding
new-target
node-colon-prefix-import
node-colon-prefix-require
nullish-coalescing
object-accessors
object-extensions
object-rest-spread
optional-catch-binding
optional-chain
regexp-dot-all-flag
regexp-lookbehind-assertions
regexp-match-indices
regexp-named-capture-groups
regexp-set-notation
regexp-sticky-and-unicode-flags
regexp-unicode-property-escapes
rest-argument
template-literal
top-level-await
typeof-exotic-object-is-object
unicode-escapes
using
CSS
color-functions
gradient-double-position
gradient-interpolation
gradient-midpoints
hwb
hex-rgba
inline-style
inset-property
is-pseudo-class
modern-rgb-hsl
nesting
rebecca-purple
#ターゲット
これは、生成された JavaScript および/または CSS コードのターゲット環境を設定します。esbuild に、これらの環境には新しすぎる JavaScript 構文を、これらの環境で動作する古い JavaScript 構文に変換するように指示します。たとえば、??
演算子は Chrome 80 で導入されたため、esbuild は Chrome 79 以前をターゲットにする場合、それを同等の(ただし冗長な)条件式に変換します。
これは構文機能のみに関係しており、API には関係していないことに注意してください。これらの環境で使用されない新しい API 用の ポリフィル を自動的に追加するわけではありません。必要な API のポリフィルを明示的にインポートする必要があります(例:core-js
をインポートするなど)。自動ポリフィル注入は esbuild の範囲外です。
各ターゲット環境は、環境名にバージョン番号が続きます。現在、次の環境名がサポートされています。
chrome
deno
edge
firefox
hermes
ie
ios
node
opera
rhino
safari
さらに、es2020
などの JavaScript 言語バージョンを指定することもできます。デフォルトのターゲットは esnext
であり、これは、デフォルトでは、esbuild が最新の JavaScript および CSS 機能がすべてサポートされていると想定することを意味します。以下に、複数のターゲット環境を構成する例を示します。すべてを指定する必要はなく、プロジェクトに関係するターゲット環境のサブセットのみを指定できます。必要に応じて、バージョン番号をより正確に指定することもできます(例:単に node12
ではなく node12.19.0
)。
esbuild app.js --target=es2020,chrome58,edge16,firefox57,node12,safari11
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
target: [
'es2020',
'chrome58',
'edge16',
'firefox57',
'node12',
'safari11',
],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Target: api.ES2020,
Engines: []api.Engine{
{Name: api.EngineChrome, Version: "58"},
{Name: api.EngineEdge, Version: "16"},
{Name: api.EngineFirefox, Version: "57"},
{Name: api.EngineNode, Version: "12"},
{Name: api.EngineSafari, Version: "11"},
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
どの構文機能がどの言語バージョンで導入されたかについての詳細は、JavaScript ローダーを参照してください。es2020
などの JavaScript 言語バージョンは年で識別されますが、それは仕様が承認された年であることに留意してください。それは、すべての主要なブラウザーがその仕様を実装する年とは関係がなく、その年より早くまたは遅く実装されることがよくあります。
esbuild が現在の言語ターゲットに変換するためのサポートをまだ持っていない構文機能を使用すると、esbuild は、サポートされていない構文が使用されている場所でエラーを生成します。たとえば、esbuild はほとんどの新しい JavaScript 構文機能を es6
に変換することのみをサポートしているため、es5
言語バージョンをターゲットにすると、多くの場合、このようになります。
target
が提供するものに加えて、またはその代わりに、個々の機能レベルでサポートされている構文機能のセットをカスタマイズする必要がある場合は、supported
設定でそれを行うことができます。
#最適化
#定義
この機能は、グローバル識別子を定数式で置き換える方法を提供します。これにより、コード自体を変更せずに、ビルド間で一部のコードの動作を変更することができます。
echo 'hooks = DEBUG && require("hooks")' | esbuild --define:DEBUG=true
hooks = require("hooks");
echo 'hooks = DEBUG && require("hooks")' | esbuild --define:DEBUG=false
hooks = false;
import * as esbuild from 'esbuild'let js = 'hooks = DEBUG && require("hooks")'(await esbuild.transform(js, {
define: { DEBUG: 'true' },
})).code
'hooks = require("hooks");\n'
(await esbuild.transform(js, {
define: { DEBUG: 'false' },
})).code
'hooks = false;\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "hooks = DEBUG && require('hooks')"
result1 := api.Transform(js, api.TransformOptions{
Define: map[string]string{"DEBUG": "true"},
})
if len(result1.Errors) == 0 {
fmt.Printf("%s", result1.Code)
}
result2 := api.Transform(js, api.TransformOptions{
Define: map[string]string{"DEBUG": "false"},
})
if len(result2.Errors) == 0 {
fmt.Printf("%s", result2.Code)
}
}
各 define
エントリは、識別子を式を含むコードの文字列にマッピングします。文字列内の式は、JSON オブジェクト(null、ブール値、数値、文字列、配列、またはオブジェクト)または単一の識別子のいずれかである必要があります。配列およびオブジェクト以外の置換式はインラインで置換されます。これは、定数畳み込みに参加できることを意味します。配列およびオブジェクトの置換式は変数に格納され、インラインで置換される代わりに識別子を使用して参照されます。これにより、値の繰り返しコピーを置換することを回避できますが、値が定数畳み込みに参加しないことを意味します。
文字列リテラルで何かを置き換えたい場合は、esbuild に渡される置換値自体に引用符を含める必要があることに注意してください。これは、各 define
エントリがコードを含む文字列にマッピングされるためです。引用符を省略すると、置換値は代わりに識別子になります。これは以下の例で示されています。
echo 'id, str' | esbuild --define:id=text --define:str=\"text\"
text, "text";
import * as esbuild from 'esbuild'(await esbuild.transform('id, str', {
define: { id: 'text', str: '"text"' },
})).code
'text, "text";\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
result := api.Transform("id, text", api.TransformOptions{
Define: map[string]string{
"id": "text",
"str": "\"text\"",
},
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
CLI を使用している場合、置換値が文字列の場合に必要となる二重引用符文字をエスケープする方法は、シェルによって異なることに注意してください。bash と Windows コマンドプロンプトの両方で機能するため、\"
バックスラッシュエスケープを使用してください。単一引用符で囲むなど、bash で機能する二重引用符をエスケープする他の方法は、Windows コマンドプロンプトでは単一引用符が削除されないため、Windows では機能しません。これは、package.json
ファイルの npm スクリプトから CLI を使用する場合に関連しており、すべてのプラットフォームで機能することが期待されます。
{
"scripts": {
"build": "esbuild --define:process.env.NODE_ENV=\\\"production\\\" app.js"
}
}
異なるシェルでプラットフォームをまたいだ引用符のエスケープの問題が発生する場合は、代わりに JavaScript API を使用するように切り替えることをお勧めします。ここでは、通常の JavaScript 構文を使用して、プラットフォームをまたいだ違いを解消できます。
式を定数以外のもの(例:グローバル変数をシムで置き換えるなど)で置き換えることができる、より高度な定義機能をお探しの場合は、同様の inject 機能を使用してそれを行うことができる場合があります。
#ドロップ
これは、esbuild に、ビルドする前に特定の構造をドロップするためにソースコードを編集するように指示します。現在、ドロップできるものは 2 つあります。
debugger
このフラグを渡すと、すべての
debugger
ステートメント が出力から削除されます。これは、人気の UglifyJS および Terser JavaScript ミニファイアで利用可能なdrop_debugger: true
フラグと同様です。JavaScript の
debugger
ステートメントにより、アクティブなデバッガーは、そのステートメントを自動的に構成されたブレークポイントとして扱います。このステートメントを含むコードは、デバッガーが開いていると自動的に一時停止されます。デバッガーが開いていない場合、ステートメントは何も行いません。これらのステートメントをコードからドロップすると、コードの実行時にデバッガーが自動的に停止するのを防ぐだけです。次のように
debugger
ステートメントをドロップできます。
esbuild app.js --drop:debugger
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
drop: ['debugger'],
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Drop: api.DropDebugger,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
console
このフラグを渡すと、すべての
console
API 呼び出しが出力から削除されます。これは、人気の UglifyJS および Terser JavaScript ミニファイアで利用可能なdrop_console: true
フラグと同様です。警告:このフラグを使用すると、コードにバグが発生する可能性があります!このフラグは、すべての呼び出し引数を含む呼び出し式全体を削除します。これらの引数に重要な副作用がある場合、このフラグを使用すると、コードの動作が変更されます。このフラグを使用する場合は、十分注意してください。
副作用のある引数を削除せずにコンソール API 呼び出しを削除したい場合(バグが発生しないように)、関連する API 呼び出しを 純粋 としてマークする必要があります。たとえば、
--pure:
を使用してconsole.log console.log
を純粋としてマークできます。これにより、ミニファイが有効になっている場合、これらの API 呼び出しは安全に削除されます。次のように
console
API 呼び出しをドロップできます。
esbuild app.js --drop:console
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
drop: ['console'],
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Drop: api.DropConsole,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#ラベルのドロップ
これは、esbuild に、ビルドする前に特定のラベル名を持つ ラベル付きステートメント をドロップするためにソースコードを編集するように指示します。たとえば、次のコードについて考えてみましょう。
function example() {
DEV: doAnExpensiveCheck()
return normalCodePath()
}
このオプションを使用して DEV
という名前のすべてのラベルをドロップすると、esbuild は次のようになります。
function example() {
return normalCodePath();
}
この機能を次のように構成できます(DEV
と TEST
の両方のラベルをドロップします)。
esbuild app.js --drop-labels=DEV,TEST
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
dropLabels: ['DEV', 'TEST'],
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
DropLabels: []string{"DEV", "TEST"},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これは、コードを条件付きで削除する唯一の方法ではないことに注意してください。もう 1 つの一般的な方法は、定義機能を使用して、特定のグローバル変数をブール値で置き換えることです。たとえば、次のコードについて考えてみましょう。
function example() {
DEV && doAnExpensiveCheck()
return normalCodePath()
}
DEV
を false
と定義すると、esbuild は次のようになります。
function example() {
return normalCodePath();
}
これは、ラベルを使用するのとほぼ同じです。ただし、グローバル変数ではなくラベルを使用してコードを条件付きで削除する利点は、誰かが esbuild を構成して何かで置き換えるのを忘れたために、グローバル変数が定義されていないことを心配する必要がないことです。ラベルアプローチを使用することのいくつかの欠点は、ラベルがドロップされないときにコードを条件付きで削除することをわずかに読みにくくすることと、ネストされた式に埋め込まれたコードでは機能しないことです。特定のプロジェクトでどのアプローチを使用するかは、個人の好みに帰着します。
#注釈の無視
JavaScript は動的な言語であるため、コンパイラーが未使用のコードを特定することは非常に困難な場合があるため、コミュニティは、どのコードを副作用がなく削除可能とみなすべきかをコンパイラーに伝えるのに役立つ特定の注釈を開発しました。現在、esbuild がサポートする副作用注釈には 2 つの形式があります。
関数呼び出しの前のインライン
/* @__PURE__ */
コメントは、結果の値が使用されていない場合、関数呼び出しを削除できることを esbuild に伝えます。詳細については、pure API オプションを参照してください。package.json
のsideEffects
フィールドは、そのファイルからのすべてのインポートが未使用になった場合に、パッケージ内のどのファイルを削除できるかを esbuild に伝えるために使用できます。これは Webpack の規約であり、npm に公開されている多くのライブラリには、すでにパッケージ定義にこのフィールドがあります。このフィールドの詳細については、Webpack のドキュメントを参照してください。
これらの注釈は、コンパイラーが完全に開発者の正確さに依存しているため、開発者が誤った注釈でパッケージを公開することがあるため、問題になる可能性があります。sideEffects
フィールドは、インポートが使用されていない場合、パッケージ内のすべてのファイルがデッドコードと見なされるため、開発者にとって特にエラーが発生しやすいです。副作用を含む新しいファイルを追加し、そのフィールドを更新するのを忘れると、パッケージをバンドルしようとしたときにパッケージが壊れる可能性があります。
これが、esbuild に副作用注釈を無視する方法が含まれている理由です。バンドルから必要なコードが予期せず削除されたためにバンドルが壊れているという問題が発生した場合にのみ、これを有効にする必要があります。
esbuild app.js --bundle --ignore-annotations
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
ignoreAnnotations: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
IgnoreAnnotations: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これを有効にすると、esbuildは/* @__PURE__ */
コメントやsideEffects
フィールドを尊重しなくなります。ただし、開発者からのアノテーションに依存しないため、未使用のインポートの自動ツリーシェイキングは引き続き行われます。理想的には、このフラグは一時的な回避策にすぎません。これらの問題はパッケージのメンテナーに報告して修正してもらうべきです。なぜなら、それらはパッケージの問題を示しており、他の人も同様の問題に遭遇する可能性があるからです。
#インジェクト
サポート対象: ビルド
このオプションを使用すると、グローバル変数を別のファイルからのインポートで自動的に置き換えることができます。これは、制御できないコードを新しい環境に適応させるのに役立つツールです。たとえば、process-cwd-shim.js
という名前のファイルがあり、エクスポート名process.cwd
を使用してシムをエクスポートすると仮定します。
// process-cwd-shim.js
let processCwdShim = () => ''
export { processCwdShim as 'process.cwd' }
// entry.js
console.log(process.cwd())
これは、nodeのprocess.cwd()
関数の使用を置き換えて、ブラウザで実行されたときにそれを呼び出すパッケージがクラッシュするのを防ぐことを目的としています。インジェクト機能を使用すると、グローバルプロパティprocess.cwd
へのすべての参照をそのファイルからのインポートに置き換えることができます。
esbuild entry.js --inject:./process-cwd-shim.js --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['entry.js'],
inject: ['./process-cwd-shim.js'],
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"entry.js"},
Inject: []string{"./process-cwd-shim.js"},
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
その結果、次のようになります。
// out.js
var processCwdShim = () => "";
console.log(processCwdShim());
インジェクト機能は、define機能と似ていると考えることができます。ただし、式を定数で置き換える代わりに、ファイルへのインポートで式を置き換え、置き換える式は、esbuildのAPIでインライン文字列を使用する代わりに、ファイル内のエクスポート名を使用して指定されます。
#JSXの自動インポート
React(JSX構文が最初に作成されたライブラリ)には、JSX構文を使用するために何もimport
する必要がないautomatic
と呼ばれるモードがあります。代わりに、JSXからJSへのトランスフォーマーは、適切なJSXファクトリ関数を自動的にインポートします。esbuildのjsx
設定でautomatic
JSXモードを有効にできます。JSXの自動インポートが必要で、十分に新しいバージョンのReactを使用している場合は、automatic
JSXモードを使用する必要があります。
ただし、jsx
をautomatic
に設定すると、残念ながら、デフォルトの汎用JSXトランスフォームではなく、Reactに特化したJSXトランスフォームを使用することにもなります。これは、JSXファクトリ関数を記述するのがより複雑になることを意味し、また、automatic
モードが標準のJSXトランスフォームで使用されることを期待するライブラリ(古いバージョンのReactを含む)では機能しないことを意味します。
esbuildのインジェクト機能を使用すると、JSXトランスフォームがautomatic
に設定されていない場合に、JSX式のファクトリとフラグメントを自動的にインポートできます。これを実行するためにインジェクトできるファイルの例を次に示します。
const { createElement, Fragment } = require('react')
export {
createElement as 'React.createElement',
Fragment as 'React.Fragment',
}
このコードではReactライブラリを例として使用していますが、適切な変更を加えることで、このアプローチを他のJSXライブラリでも使用できます。
#インポートなしでファイルをインジェクトする
この機能を、エクスポートがないファイルでも使用できます。その場合、インジェクトされたファイルは、すべての入力ファイルにimport
が含まれているかのように、出力の残りの部分の前に最初に配置されます。ECMAScriptモジュールの仕組みにより、このインジェクションは、異なるファイルで同じ名前のシンボルが互いに衝突しないように名前が変更される点で、依然として「衛生的」です。
#条件付きでファイルをインジェクトする
エクスポートが実際に使用されている場合にのみ、ファイルを条件付きでインポートする場合は、パッケージに入れて、そのパッケージのpackage.json
ファイルに"sideEffects":
を追加することにより、インジェクトされたファイルに副作用がないとマークする必要があります。この設定は、Webpackからの慣習であり、esbuildはインジェクトで使用されるファイルだけでなく、インポートされたすべてのファイルに対して尊重します。
#名前を保持する
JavaScriptでは、関数とクラスのname
プロパティは、ソースコード内の近くの識別子にデフォルトで設定されます。これらの構文形式はすべて、関数のname
プロパティを"fn"
に設定します。
function fn() {}
let fn = function() {};
fn = function() {};
let [fn = function() {}] = [];
let {fn = function() {}} = {};
[fn = function() {}] = [];
({fn = function() {}} = {});
ただし、minifyはコードサイズを小さくするためにシンボルを名前変更し、バンドルは衝突を避けるためにシンボルを名前変更する必要がある場合があります。これにより、これらの多くの場合にname
プロパティの値が変更されます。通常、name
プロパティは通常デバッグにのみ使用されるため、これは問題ありません。ただし、一部のフレームワークは、登録およびバインドの目的でname
プロパティに依存しています。この場合は、このオプションを有効にして、minifyされたコードでも元のname
値を保持できます。
esbuild app.js --minify --keep-names
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
minify: true,
keepNames: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
KeepNames: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
この機能は、ターゲットが、関数とクラスのname
プロパティをesbuildが変更することを許可しない古い環境に設定されている場合は利用できないことに注意してください。これは、ES6をサポートしていない環境の場合です。
#プロパティをマングルする
この設定では、正規表現をesbuildに渡して、この正規表現に一致するすべてのプロパティを自動的に名前変更するように指示できます。これは、生成されたコードを小さくしたり、コードの意図をある程度難読化したりするために、コード内の一部のプロパティ名をminifyしたい場合に役立ちます。
以下に、foo_
のようにアンダースコアで終わるすべてのプロパティをマングルするために、正規表現_$
を使用する例を示します。これにより、print({
がprint({
にマングルされます。
esbuild app.js --mangle-props=_$
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
mangleProps: /_$/,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
MangleProps: "_$",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
アンダースコアで終わるプロパティのみをマングルすることは、通常のJSコードにはそのような識別子が通常含まれていないため、妥当なヒューリスティックです。ブラウザAPIもこの命名規則を使用していないため、これはブラウザAPIとの競合も回避します。__defineGetter__
などの名前のマングルを回避したい場合は、より複雑な正規表現([^_]_$
など、つまり、アンダースコア以外の文字に続いてアンダースコアで終わる必要がある)を使用することを検討できます。
これは、minify設定の一部ではなく、別の設定です。なぜなら、これは任意のJavaScriptコードでは機能しない安全でない変換だからです。これは、指定された正規表現がマングルしたいすべてのプロパティと一致し、マングルしたくないプロパティと一致しない場合にのみ機能します。また、マングルされたプロパティを間接的に参照しない場合にのみ機能します。たとえば、prop
がプロパティ名を含む文字列である場合に、obj[prop]
を使用してプロパティを参照できないことを意味します。具体的には、次の構文構成のみがプロパティのマングリングの対象となります。
構文 | 例 |
---|---|
ドットプロパティアクセス | x.foo_ |
ドットオプショナルチェーン | x?.foo_ |
オブジェクトプロパティ | x = { foo_: y } |
オブジェクトメソッド | x = { foo_() {} } |
クラスフィールド | class x { foo_ = y } |
クラスメソッド | class x { foo_() {} } |
オブジェクトの分割代入バインディング | let { foo_: x } = y |
オブジェクトの分割代入割り当て | ({ foo_: x } = y) |
JSX要素メンバ式 | <X.foo_></X.foo_> |
JSX属性名 | <X foo_={y} /> |
TypeScript名前空間のエクスポート | namespace x { export let foo_ = y } |
TypeScriptパラメータプロパティ | class x { constructor(public foo_) {} } |
この機能を使用する場合は、プロパティ名は単一のesbuild API呼び出し内でのみ一貫してマングルされますが、esbuild API呼び出し間ではマングルされないことに注意してください。各esbuild API呼び出しは、独立したプロパティマングリング操作を実行するため、2つの異なるAPI呼び出しによって生成された出力ファイルは、同じプロパティを2つの異なる名前にマングルする可能性があり、結果としてコードが正しく動作しない可能性があります。
#引用符付きプロパティ
デフォルトでは、esbuildは文字列リテラルの内容を変更しません。つまり、文字列として引用することにより、個々のプロパティのプロパティマングルを回避できます。ただし、これが機能するには、特定のプロパティに対して、常に引用符を使用するか、引用符を使用しない必要があります。たとえば、print({
はprint({
にマングルされますが、print({
はマングルされません。
esbuildにも文字列リテラルの内容をマングルさせたい場合は、次のように明示的にその動作を有効にすることができます。
esbuild app.js --mangle-props=_$ --mangle-quoted
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
mangleProps: /_$/,
mangleQuoted: true,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
MangleProps: "_$",
MangleQuoted: api.MangleQuotedTrue,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これを有効にすると、次の構文構成もプロパティマングリングの対象になります。
構文 | 例 |
---|---|
引用符付きプロパティアクセス | x['foo_'] |
引用符付きオプショナルチェーン | x?.['foo_'] |
引用符付きオブジェクトプロパティ | x = { 'foo_': y } |
引用符付きオブジェクトメソッド | x = { 'foo_'() {} } |
引用符付きクラスフィールド | class x { 'foo_' = y } |
引用符付きクラスメソッド | class x { 'foo_'() {} } |
引用符付きオブジェクト分割代入バインディング | let { 'foo_': x } = y |
引用符付きオブジェクト分割代入割り当て | ({ 'foo_': x } = y) |
in の左側の文字列リテラル |
'foo_' in x |
#他の文字列のマングル
引用符付きプロパティのマングルは、依然としてプロパティ名位置にある文字列のみをマングルします。コード内の任意の他の場所にある文字列内のプロパティ名をマングルする必要がある場合もあります。これを行うには、文字列の先頭に/* @__KEY__ */
コメントを付けて、文字列の内容をマングルできるプロパティ名として扱うようにesbuildに指示できます。たとえば、
let obj = {}
Object.defineProperty(
obj,
/* @__KEY__ */ 'foo_',
{ get: () => 123 },
)
console.log(obj.foo_)
これにより、文字列'foo_'
の内容がプロパティ名としてマングルされます(プロパティマングルが有効になっており、foo_
が名前変更の対象であると仮定します)。/* @__KEY__ */
コメントは、同様のプロパティマングル機能を備えた人気のあるJavaScript minifyツールであるTerserからの慣習です。
#名前変更の防止
特定のプロパティをマングルから除外する場合は、追加の設定でそれらを予約できます。たとえば、これは、__foo__
のように、2つのアンダースコアで始まり、2つのアンダースコアで終わるすべてのプロパティを予約するために、正規表現^__.*__$
を使用します。
esbuild app.js --mangle-props=_$ "--reserve-props=^__.*__$"
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
mangleProps: /_$/,
reserveProps: /^__.*__$/,
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
MangleProps: "_$",
ReserveProps: "^__.*__$",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#名前変更の決定の永続化
プロパティマングリング機能の高度な使用法には、元の名前からマングルされた名前へのマッピングを永続的なキャッシュに格納することが含まれます。有効にすると、初期ビルド中にすべてのマングルされたプロパティの名前変更がキャッシュに記録されます。後続のビルドでは、キャッシュに格納された名前変更が再利用され、新しく追加されたプロパティの名前変更が追加されます。これにはいくつかの結果があります。
esbuildに渡す前にキャッシュを編集することで、マングルされたプロパティが名前変更される方法をカスタマイズできます。
キャッシュは、マングルされたすべてのプロパティのリストとして機能します。予期しないプロパティの名前変更があるかどうかを簡単にスキャンして確認できます。
名前変更された値を文字列の代わりに
false
に設定することにより、個々のプロパティのマングルを無効にできます。これは、プロパティの予約設定と似ていますが、プロパティごとに行われます。ビルド間(例:メインスレッドのファイルとウェブワーカー、またはライブラリとプラグイン)で一貫したリネームを保証できます。この機能がない場合、各ビルドは独立したリネーム操作を行い、マングルされたプロパティ名の一貫性がなくなる可能性があります。
例えば、次の入力ファイルを考えてみましょう。
console.log({
someProp_: 1,
customRenaming_: 2,
disabledRenaming_: 3
});
customRenaming_
を cR_
にリネームし、disabledRenaming_
をリネームしないようにしたい場合、次のマングルキャッシュJSONをesbuildに渡すことができます。
{
"customRenaming_": "cR_",
"disabledRenaming_": false
}
マングルキャッシュJSONは、以下のようにesbuildに渡すことができます。
esbuild app.js --mangle-props=_$ --mangle-cache=cache.json
import * as esbuild from 'esbuild'
let result = await esbuild.build({
entryPoints: ['app.js'],
mangleProps: /_$/,
mangleCache: {
customRenaming_: "cR_",
disabledRenaming_: false
},
})
console.log('updated mangle cache:', result.mangleCache)
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
MangleProps: "_$",
MangleCache: map[string]interface{}{
"customRenaming_": "cR_",
"disabledRenaming_": false,
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
fmt.Println("updated mangle cache:", result.MangleCache)
}
プロパティ名のリネームが有効な場合、結果として次の出力ファイルが得られます。
console.log({
a: 1,
cR_: 2,
disabledRenaming_: 3
});
そして、次の更新されたマングルキャッシュが得られます。
{
"customRenaming_": "cR_",
"disabledRenaming_": false,
"someProp_": "a"
}
#minify
有効にすると、生成されたコードはプリティプリントではなく、minifyされます。minifyされたコードは、一般的にminifyされていないコードと同等ですが、サイズが小さいため、ダウンロードは速くなりますが、デバッグは難しくなります。通常、本番環境ではコードをminifyしますが、開発環境ではminifyしません。
esbuildでminifyを有効にするには、次のようになります。
echo 'fn = obj => { return obj.x }' | esbuild --minify
fn=n=>n.x;
import * as esbuild from 'esbuild'var js = 'fn = obj => { return obj.x }'
(await esbuild.transform(js, {
minify: true,
})).code
'fn=n=>n.x;\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "fn = obj => { return obj.x }"
result := api.Transform(js, api.TransformOptions{
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
このオプションは、空白の削除、構文をよりコンパクトに書き換える、ローカル変数をより短い名前にリネームするという3つの異なることを組み合わせて実行します。通常、これらすべてを実行したいのですが、必要に応じてこれらのオプションを個別に有効にすることもできます。
echo 'fn = obj => { return obj.x }' | esbuild --minify-whitespace
fn=obj=>{return obj.x};
echo 'fn = obj => { return obj.x }' | esbuild --minify-identifiers
fn = (n) => {
return n.x;
};
echo 'fn = obj => { return obj.x }' | esbuild --minify-syntax
fn = (obj) => obj.x;
import * as esbuild from 'esbuild'var js = 'fn = obj => { return obj.x }'
(await esbuild.transform(js, {
minifyWhitespace: true,
})).code
'fn=obj=>{return obj.x};\n'
(await esbuild.transform(js, {
minifyIdentifiers: true,
})).code
'fn = (n) => {\n return n.x;\n};\n'
(await esbuild.transform(js, {
minifySyntax: true,
})).code
'fn = (obj) => obj.x;\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
css := "div { color: yellow }"
result1 := api.Transform(css, api.TransformOptions{
Loader: api.LoaderCSS,
MinifyWhitespace: true,
})
if len(result1.Errors) == 0 {
fmt.Printf("%s", result1.Code)
}
result2 := api.Transform(css, api.TransformOptions{
Loader: api.LoaderCSS,
MinifyIdentifiers: true,
})
if len(result2.Errors) == 0 {
fmt.Printf("%s", result2.Code)
}
result3 := api.Transform(css, api.TransformOptions{
Loader: api.LoaderCSS,
MinifySyntax: true,
})
if len(result3.Errors) == 0 {
fmt.Printf("%s", result3.Code)
}
}
これらの同じ概念は、JavaScriptだけでなく、CSSにも適用されます。
echo 'div { color: yellow }' | esbuild --loader=css --minify
div{color:#ff0}
import * as esbuild from 'esbuild'var css = 'div { color: yellow }'
(await esbuild.transform(css, {
loader: 'css',
minify: true,
})).code
'div{color:#ff0}\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
css := "div { color: yellow }"
result := api.Transform(css, api.TransformOptions{
Loader: api.LoaderCSS,
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
esbuildのJavaScript minifyアルゴリズムは通常、業界標準のJavaScript minifyツールのminifyされた出力サイズに非常に近い出力を生成します。このベンチマークには、異なるminifyツール間の出力サイズの比較例があります。esbuildは、すべてのケースで最適なJavaScript minifyツールではありません(また、そうしようともしていません)が、ほとんどのコードで専用のminifyツールのサイズから数パーセント以内のminifyされた出力を生成し、もちろん他のツールよりもはるかに高速に実行することを目指しています。
#考慮事項
esbuildをminifyツールとして使用する際に留意すべき点を以下に示します。
minifyを有効にする場合は、targetオプションも設定することをお勧めします。デフォルトでは、esbuildはコードを小さくするために最新のJavaScript機能を利用します。例えば、
a ===
はundefined || a === null ? 1 : a a ?? 1
にminifyされる可能性があります。minify時にesbuildに最新のJavaScript機能を利用させたくない場合は、--target=es6
などの古い言語ターゲットを使用する必要があります。文字エスケープシーケンス
\n
は、JavaScriptテンプレートリテラルでは改行文字に置き換えられます。文字列リテラルは、target がテンプレートリテラルをサポートしており、それによって出力が小さくなる場合にも、テンプレートリテラルに変換されます。これはバグではありません。minifyとは、より小さな出力を求めることであり、エスケープシーケンス\n
は2バイトを消費しますが、改行文字は1バイトを消費します。デフォルトでは、esbuildはトップレベルの宣言の名前をminifyしません。これは、esbuildが出力で何をするかを知らないためです。minifyされたコードを他のコードの途中に挿入する可能性があり、その場合、トップレベルの宣言名をminifyすることは安全ではありません。出力formatを設定する(またはバンドルを有効にする。バンドルは、設定していない場合に、出力フォーマットを自動的に選択する)と、esbuildは出力がそれ自身のスコープ内で実行されることを認識します。つまり、トップレベルの宣言名をminifyすることが安全になります。
minifyは、すべてのJavaScriptコードに対して100%安全ではありません。これは、esbuildだけでなく、terserなどの他の一般的なJavaScript minifyツールにも当てはまります。特に、esbuildは関数で
.toString()
を呼び出した値を保持するように設計されていません。その理由は、すべての関数内のすべてのコードを逐語的に保持する必要がある場合、minifyはほとんど何もせず、事実上役に立たなくなるためです。ただし、これは、.toString()
の戻り値に依存するJavaScriptコードがminify時に壊れる可能性が高いことを意味します。例えば、AngularJSフレームワークの一部のパターンは、AngularJSが関数の引数名を読み取るために.toString()
を使用するため、コードがminifyされると壊れます。回避策として、明示的な注釈を使用することがあります。デフォルトでは、esbuildは関数とクラスオブジェクトの
.name
の値を保持しません。これは、ほとんどのコードがこのプロパティに依存しておらず、より短い名前を使用することが重要なサイズ最適化であるためです。ただし、一部のコードは登録とバインディングの目的で.name
プロパティに依存しています。これに依存する必要がある場合は、名前を保持するオプションを有効にする必要があります。特定のJavaScript機能を使用すると、minifyを含むesbuildの多くの最適化が無効になる可能性があります。具体的には、直接
eval
やwith
ステートメントを使用すると、これらの機能によって識別子のバインディングがコンパイル時ではなく実行時に発生するため、esbuildが識別子をより短い名前にリネームすることができなくなります。これはほとんどの場合、意図しないことであり、直接eval
とは何か、なぜそれが悪いのかを認識していないために発生します。次のようなコードを作成することを考えている場合は、
// Direct eval (will disable minification for the whole file) let result = eval(something)
代わりに、次のようにコードを作成して、コードをminifyできるようにする必要があります。
// Indirect eval (has no effect on the surrounding code) let result = (0, eval)(something)
直接
eval
の影響と利用可能な代替手段の詳細については、こちらを参照してください。esbuildのminifyアルゴリズムは、まだ高度なコード最適化を行っていません。特に、次のコード最適化はJavaScriptコードでは可能ですが、esbuildでは実行されません(すべてを網羅したリストではありません)。
- 関数本体内のデッドコード削除
- 関数のインライン化
- 文をまたぐ定数伝播
- オブジェクト形状モデリング
- 割り当てシンキング
- メソッドの非仮想化
- シンボリック実行
- JSX式のホイスティング
- TypeScript列挙型の検出とインライン化
コードが、これらの形式のコード最適化の一部を必要とするパターンを使用している場合、またはユースケースに最適なJavaScript minifyアルゴリズムを探している場合は、他のツールを使用することを検討する必要があります。これらの高度なコード最適化の一部を実装しているツールの例としては、TerserとGoogle Closure Compilerなどがあります。
#pure
さまざまなJavaScriptツールで使用されている慣例として、新規または呼び出し式の前に /* @__PURE__ */
または /* #__PURE__ */
を含む特別なコメントは、結果の値が未使用の場合、その式を削除できることを意味します。それはこのような感じです。
let button = /* @__PURE__ */ React.createElement(Button, null);
この情報は、ツリーシェイキング(別名、デッドコード削除)中にesbuildなどのバンドラーによって使用され、JavaScriptコードの動的な性質のために、バンドラー自身が削除が安全であることを証明できない状況で、モジュール境界を越えて未使用のインポートを詳細に削除するために使用されます。
コメントは「pure」と表示されていますが、混乱を招くことに、呼び出されている関数がpureであることを示すものではありません。例えば、その関数への繰り返し呼び出しをキャッシュしても良いとは示していません。この名前は、本質的に「未使用の場合、削除しても良い」の抽象的な略語にすぎません。
JSXや特定の組み込みグローバルなどの一部の式は、esbuildで自動的に /* @__PURE__ */
として注釈が付けられます。追加のグローバルを /* @__PURE__ */
としてマークすることもできます。例えば、グローバルな document.
関数をそのようにマークすると、結果が使用されない限り、バンドルがminifyされたときに自動的にバンドルから削除されます。
注釈の効果は呼び出し自体にのみ拡張され、引数には拡張されないことは注目に値します。副作用のある引数は、minifyが有効になっている場合でも保持されます。
echo 'document.createElement(elemName())' | esbuild --pure:document.createElement
/* @__PURE__ */ document.createElement(elemName());
echo 'document.createElement(elemName())' | esbuild --pure:document.createElement --minify
elemName();
import * as esbuild from 'esbuild'let js = 'document.createElement(elemName())'
(await esbuild.transform(js, {
pure: ['document.createElement'],
})).code
'/* @__PURE__ */ document.createElement(elemName());\n'
(await esbuild.transform(js, {
pure: ['document.createElement'],
minify: true,
})).code
'elemName();\n'
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "document.createElement(elemName())"
result1 := api.Transform(js, api.TransformOptions{
Pure: []string{"document.createElement"},
})
if len(result1.Errors) == 0 {
fmt.Printf("%s", result1.Code)
}
result2 := api.Transform(js, api.TransformOptions{
Pure: []string{"document.createElement"},
MinifySyntax: true,
})
if len(result2.Errors) == 0 {
fmt.Printf("%s", result2.Code)
}
}
例えば、console.log
などのconsole
APIメソッドへのすべての呼び出しを削除しようとしており、副作用のある引数の評価も削除したい場合、これには特別なケースがあります。console
API呼び出しをpureとしてマークする代わりに、drop機能を使用できます。ただし、このメカニズムはconsole
APIに固有のものであり、他の呼び出し式では機能しません。
#ツリーシェイキング
ツリーシェイキングは、JavaScriptコミュニティがデッドコード削除に使用する用語であり、到達不能なコードを自動的に削除する一般的なコンパイラー最適化です。esbuild内では、この用語は特に宣言レベルのデッドコード削除を指します。
ツリーシェイキングは、例で説明するのが最も簡単です。次のファイルを考えてみましょう。1つの使用された関数と1つの未使用の関数があります。
// input.js
function one() {
console.log('one')
}
function two() {
console.log('two')
}
one()
このファイルを esbuild
でバンドルすると、未使用の関数は自動的に破棄され、次の出力が残ります。
// input.js
function one() {
console.log("one");
}
one();
これは、関数を別のライブラリファイルに分割し、import
ステートメントを使用してインポートする場合でも機能します。
// lib.js
export function one() {
console.log('one')
}
export function two() {
console.log('two')
}
// input.js
import * as lib from './lib.js'
lib.one()
このファイルを esbuild
でバンドルすると、未使用の関数と未使用のインポートは自動的に破棄され、次の出力が残ります。
// lib.js
function one() {
console.log("one");
}
// input.js
one();
このようにして、esbuildは実際に使用するパッケージの部分のみをバンドルするため、大幅なサイズ削減につながる場合があります。esbuildのツリーシェイキングの実装は、ECMAScriptモジュールのimport
ステートメントとexport
ステートメントの使用に依存していることに注意してください。これはCommonJSモジュールでは機能しません。npmの多くのパッケージには両方の形式が含まれており、esbuildはデフォルトでツリーシェイキングで機能する形式を選択しようとします。パッケージに応じて、main fieldsやconditionsオプションを使用して、esbuildが選択する形式をカスタマイズできます。
デフォルトでは、ツリーシェイキングは、バンドルが有効になっているか、出力formatがiife
に設定されている場合にのみ有効になり、それ以外の場合はツリーシェイキングは無効になります。true
に設定することで、ツリーシェイキングを強制的に有効にすることができます。
esbuild app.js --tree-shaking=true
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
treeShaking: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
TreeShaking: api.TreeShakingTrue,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
また、false
に設定することで、ツリーシェイキングを強制的に無効にすることもできます。
esbuild app.js --tree-shaking=false
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
treeShaking: false,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
TreeShaking: api.TreeShakingFalse,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#ツリーシェイキングと副作用
ツリーシェイキングに使用される副作用検出は保守的です。つまり、esbuildは、隠れた副作用がないと確信できる場合にのみ、コードをデッドコードとして削除可能とみなします。例えば、12.34
や"abcd"
などのプリミティブなリテラルは副作用がなく、削除できますが、"ab" + cd
やfoo.bar
などの式は副作用がないとは言えません(文字列の結合はtoString()
を呼び出し、副作用を持つ可能性があり、メンバーアクセスはgetterを呼び出して副作用を持つ可能性があります)。グローバル識別子を参照することさえ、その名前のグローバルが存在しない場合はReferenceError
をスローするため、副作用とみなされます。以下に例を示します。
// These are considered side-effect free
let a = 12.34;
let b = "abcd";
let c = { a: a };
// These are not considered side-effect free
// since they could cause some code to run
let x = "ab" + cd;
let y = foo.bar;
let z = { [x]: x };
コードが自動的に副作用がないと判断できない場合でも、ツリーシェイキングで削除できるようにすることが望ましい場合があります。これは、純粋アノテーションコメントを使用して行うことができます。このコメントは、esbuildに、注釈されたコード内に副作用がないことをコードの作成者が信頼するように指示します。アノテーションコメントは/* @__PURE__ */
であり、newまたはcall式の前でのみ使用できます。即時実行関数式に注釈を付け、関数本体内に任意の副作用を置くことができます。
// This is considered side-effect free due to
// the annotation, and will be removed if unused
let gammaTable = /* @__PURE__ */ (() => {
// Side-effect detection is skipped in here
let table = new Uint8Array(256);
for (let i = 0; i < 256; i++)
table[i] = Math.pow(i / 255, 2.2) * 255;
return table;
})();
/* @__PURE__ */
がcall式でのみ機能するという事実は、コードを冗長にする場合がありますが、この構文の大きな利点は、人気のあるUglifyJSやTerser JavaScriptミニファイア(WebpackやParcelなどの他の主要ツールで使用されています)を含む、JavaScriptエコシステムの他の多くのツールで移植可能であることです。
アノテーションにより、esbuildは、注釈されたコードが副作用がないと仮定します。アノテーションが間違っていて、コードに実際には重要な副作用がある場合、これらのアノテーションはコードの破損につながる可能性があります。誤って作成されたアノテーションを含むサードパーティのコードをバンドルしている場合は、バンドルされたコードが正しいことを確認するためにアノテーションの無視を有効にする必要があるかもしれません。
#ソースマップ
#ソースルート
この機能は、ソースマップが有効になっている場合にのみ関連します。これにより、ソースマップ内のすべてのパスが相対パスになるパスを指定する、ソースマップ内のsourceRoot
フィールドの値を設定できます。このフィールドが存在しない場合、ソースマップ内のすべてのパスは、ソースマップを含むディレクトリに対する相対パスとして解釈されます。
sourceRoot
は次のように構成できます。
esbuild app.js --sourcemap --source-root=https://raw.githubusercontent.com/some/repo/v1.2.3/
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
sourcemap: true,
sourceRoot: 'https://raw.githubusercontent.com/some/repo/v1.2.3/',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Sourcemap: api.SourceMapInline,
SourceRoot: "https://raw.githubusercontent.com/some/repo/v1.2.3/",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#ソースファイル
このオプションは、ファイル名がない入力を使用する場合にファイル名を設定します。これは、transform APIを使用する場合や、stdinでbuild APIを使用する場合に発生します。設定されたファイル名は、エラーメッセージとソースマップに反映されます。構成されていない場合、ファイル名はデフォルトで<stdin>
になります。これは次のように構成できます。
cat app.js | esbuild --sourcefile=example.js --sourcemap
import * as esbuild from 'esbuild'
import fs from 'node:fs'
let js = fs.readFileSync('app.js', 'utf8')
let result = await esbuild.transform(js, {
sourcefile: 'example.js',
sourcemap: 'inline',
})
console.log(result.code)
package main
import "fmt"
import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js, err := ioutil.ReadFile("app.js")
if err != nil {
panic(err)
}
result := api.Transform(string(js),
api.TransformOptions{
Sourcefile: "example.js",
Sourcemap: api.SourceMapInline,
})
if len(result.Errors) == 0 {
fmt.Printf("%s %s", result.Code)
}
}
#ソースマップ
ソースマップを使用すると、コードのデバッグが簡単になります。生成された出力ファイル内の行/列オフセットから、対応する元の入力ファイル内の行/列オフセットに変換するために必要な情報をエンコードします。これは、生成されたコードが元のコードと十分に異なる場合(例えば、元のコードがTypeScriptであるか、minifyを有効にした場合)に役立ちます。また、1つの大きなバンドルされたファイルではなく、ブラウザの開発者ツールで個々のファイルを見たい場合にも役立ちます。
ソースマップの出力はJavaScriptとCSSの両方でサポートされており、同じオプションが両方に適用されることに注意してください。以下で.js
ファイルについて説明することは、同様に.css
ファイルにも適用されます。
ソースマップの生成には、4つの異なるモードがあります。
-
linked
このモードでは、ソースマップは
.js
出力ファイルとともに別の.js.map
出力ファイルに生成され、.js
出力ファイルには、.js.map
出力ファイルを指す特別な//# sourceMappingURL=
コメントが含まれます。これにより、ブラウザはデバッガーを開いたときに、特定のファイルのソースマップをどこで検索すればよいかを知ることができます。次のようにlinked
ソースマップモードを使用します。
esbuild app.ts --sourcemap --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
sourcemap: true,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Sourcemap: api.SourceMapLinked,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
-
external
このモードでは、ソースマップは
.js
出力ファイルとともに別の.js.map
出力ファイルに生成されますが、linked
モードとは異なり、.js
出力ファイルには//# sourceMappingURL=
コメントが含まれません。次のようにexternal
ソースマップモードを使用します。
esbuild app.ts --sourcemap=external --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
sourcemap: 'external',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Sourcemap: api.SourceMapExternal,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
-
inline
このモードでは、ソースマップは
//# sourceMappingURL=
コメント内のbase64ペイロードとして.js
出力ファイルの末尾に追加されます。追加の.js.map
出力ファイルは生成されません。ソースマップには通常、元のソースコードがすべて含まれているため、非常に大きくなることに注意してください。そのため、通常はinline
ソースマップを含むコードを出荷したくありません。ソースマップからソースコードを削除する(ファイル名と行/列のマッピングのみを保持する)には、ソースコンテンツオプションを使用します。次のようにinline
ソースマップモードを使用します。
esbuild app.ts --sourcemap=inline --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
sourcemap: 'inline',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Sourcemap: api.SourceMapInline,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
-
both
このモードは、
inline
とexternal
の組み合わせです。ソースマップは、.js
出力ファイルの末尾にインラインで追加され、同じソースマップの別のコピーが、.js
出力ファイルとともに別の.js.map
出力ファイルに書き込まれます。次のようにboth
ソースマップモードを使用します。
esbuild app.ts --sourcemap=both --outfile=out.js
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.ts'],
sourcemap: 'both',
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.ts"},
Sourcemap: api.SourceMapInlineAndExternal,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
build APIは、上記でリストされた4つのソースマップモードをすべてサポートしていますが、transform APIはlinked
モードをサポートしていません。これは、transform APIから返される出力には関連付けられたファイル名がないためです。transform APIの出力にソースマップコメントを付けたい場合は、自分で追加できます。さらに、transform APIのCLI形式は、出力がstdoutに書き込まれるため、複数の出力ファイルを生成することができないため、inline
モードのみをサポートしています。
ソースマップが何をするのかを「詳しく見て」みたい場合(または、ソースマップに関する問題をデバッグする場合)は、関連する出力ファイルと関連付けられたソースマップをここにアップロードできます:ソースマップの可視化。
#ソースマップの使用
ブラウザでは、ソースマップ設定が有効になっていれば、ブラウザの開発者ツールによってソースマップが自動的に選択されるはずです。ブラウザは、ソースマップをコンソールにログされたスタックトレースの表示を変更するためにのみ使用することに注意してください。スタックトレース自体は変更されないため、コード内のerror.
を調べると、コンパイルされたコードを含むマップされていないスタックトレースが引き続き表示されます。ブラウザの開発者ツールでこの設定を有効にする方法は次のとおりです。
- Chrome: ⚙ → JavaScriptソースマップを有効にする
- Safari: ⚙ → ソース → ソースマップを有効にする
- Firefox: ··· → ソースマップを有効にする
nodeでは、ソースマップはバージョンv12.12.0以降からネイティブでサポートされています。この機能はデフォルトで無効になっていますが、フラグで有効にできます。ブラウザとは異なり、実際のスタックトレースもnodeで変更されるため、コード内のerror.
を調べると、元のソースコードを含むマップされたスタックトレースが表示されます。nodeでこの設定を有効にする方法は次のとおりです(--enable-
フラグは、スクリプトファイル名の前に来る必要があります)。
node --enable-source-maps app.js
#ソースコンテンツ
ソースマップは、最も広くサポートされているバリアントであるソースマップ形式のバージョン3を使用して生成されます。各ソースマップは、次のようになります。
{
"version": 3,
"sources": ["bar.js", "foo.js"],
"sourcesContent": ["bar()", "foo()\nimport './bar'"],
"mappings": ";AAAA;;;ACAA;",
"names": []
}
sourcesContent
フィールドは、元のソースコードをすべて含むオプションのフィールドです。これにより、デバッガーで元のソースコードが利用できるようになるため、デバッグに役立ちます。
ただし、一部のシナリオでは必要ありません。たとえば、本番環境でソースマップを使用して、元のファイル名を含むスタックトレースを生成するだけであれば、デバッガーが関与しないため、元のソースコードは必要ありません。その場合、ソースマップを小さくするためにsourcesContent
フィールドを省略することが望ましい場合があります。
esbuild --bundle app.js --sourcemap --sources-content=false
import * as esbuild from 'esbuild'
await esbuild.build({
bundle: true,
entryPoints: ['app.js'],
sourcemap: true,
sourcesContent: false,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
Bundle: true,
EntryPoints: []string{"app.js"},
Sourcemap: api.SourceMapInline,
SourcesContent: api.SourcesContentExclude,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#ビルドメタデータ
#分析
サポート対象: ビルド
分析機能を使用すると、バンドルの内容に関する読みやすいレポートが生成されます。
esbuild --bundle example.jsx --outfile=out.js --minify --analyze out.js 27.6kb 100.0% ├ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js 19.2kb 69.8% ├ node_modules/react/cjs/react.production.min.js 5.9kb 21.4% ├ node_modules/object-assign/index.js 962b 3.4% ├ example.jsx 137b 0.5% ├ node_modules/react-dom/server.browser.js 50b 0.2% └ node_modules/react/index.js 50b 0.2% ...
import * as esbuild from 'esbuild'
let result = await esbuild.build({
entryPoints: ['example.jsx'],
outfile: 'out.js',
minify: true,
metafile: true,
})
console.log(await esbuild.analyzeMetafile(result.metafile))
package main
import "github.com/evanw/esbuild/pkg/api"
import "fmt"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"example.jsx"},
Outfile: "out.js",
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
Metafile: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
fmt.Printf("%s", api.AnalyzeMetafile(result.Metafile, api.AnalyzeMetafileOptions{}))
}
この情報は、各出力ファイルにどの入力ファイルが入ったか、および入力ファイルが出力ファイルの何パーセントを占めているかを示します。追加情報が必要な場合は、「詳細」モードを有効にできます。現在、これは、エントリーポイントから各入力ファイルへのインポートパスを表示します。これにより、特定の入力ファイルがバンドルに含まれている理由がわかります。
esbuild --bundle example.jsx --outfile=out.js --minify --analyze=verbose out.js ─────────────────────────────────────────────────────────────────── 27.6kb ─ 100.0% ├ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js ─ 19.2kb ── 69.8% │ └ node_modules/react-dom/server.browser.js │ └ example.jsx ├ node_modules/react/cjs/react.production.min.js ───────────────────────── 5.9kb ── 21.4% │ └ node_modules/react/index.js │ └ example.jsx ├ node_modules/object-assign/index.js ──────────────────────────────────── 962b ──── 3.4% │ └ node_modules/react-dom/cjs/react-dom-server.browser.production.min.js │ └ node_modules/react-dom/server.browser.js │ └ example.jsx ├ example.jsx ──────────────────────────────────────────────────────────── 137b ──── 0.5% ├ node_modules/react-dom/server.browser.js ──────────────────────────────── 50b ──── 0.2% │ └ example.jsx └ node_modules/react/index.js ───────────────────────────────────────────── 50b ──── 0.2% └ example.jsx ...
import * as esbuild from 'esbuild'
let result = await esbuild.build({
entryPoints: ['example.jsx'],
outfile: 'out.js',
minify: true,
metafile: true,
})
console.log(await esbuild.analyzeMetafile(result.metafile, {
verbose: true,
}))
package main
import "github.com/evanw/esbuild/pkg/api"
import "fmt"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"example.jsx"},
Outfile: "out.js",
MinifyWhitespace: true,
MinifyIdentifiers: true,
MinifySyntax: true,
Metafile: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
fmt.Printf("%s", api.AnalyzeMetafile(result.Metafile, api.AnalyzeMetafileOptions{
Verbose: true,
}))
}
この分析は、メタファイルで見つけることができる情報の単なる視覚化です。この分析がニーズに完全に合わない場合は、メタファイル内の情報を使用して独自の視覚化を構築してください。
この形式設定された分析の概要は、機械ではなく人間を対象としていることに注意してください。特定の形式は時間とともに変更される可能性があり、それを解析しようとするツールは破損する可能性があります。このデータを解析するツールを記述しないでください。代わりに、JSONメタデータファイルの情報を使用する必要があります。この視覚化のすべてはJSONメタデータから派生しているため、esbuildの形式設定された分析の概要を解析しなくても、情報を失うことはありません。
#メタファイル
サポート対象: ビルド
このオプションは、esbuildに、ビルドに関するメタデータをJSON形式で生成するように指示します。次の例では、メタデータをmeta.json
というファイルに入れます。
esbuild app.js --bundle --metafile=meta.json --outfile=out.js
import * as esbuild from 'esbuild'
import fs from 'node:fs'
let result = await esbuild.build({
entryPoints: ['app.js'],
bundle: true,
metafile: true,
outfile: 'out.js',
})
fs.writeFileSync('meta.json', JSON.stringify(result.metafile))
package main
import "io/ioutil"
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
Bundle: true,
Metafile: true,
Outfile: "out.js",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
ioutil.WriteFile("meta.json", []byte(result.Metafile), 0644)
}
このデータは、他のツールで分析できます。インタラクティブな視覚化の場合は、esbuild独自のバンドルサイズアナライザーを使用できます。テキスト分析をすばやく行う場合は、esbuildの組み込みの分析機能を使用できます。または、この情報を使用する独自の分析を作成することもできます。
メタデータJSON形式は次のようになります(TypeScriptインターフェイスを使用して記述)。
interface Metafile {
inputs: {
[path: string]: {
bytes: number
imports: {
path: string
kind: string
external?: boolean
original?: string
with?: Record<string, string>
}[]
format?: string
with?: Record<string, string>
}
}
outputs: {
[path: string]: {
bytes: number
inputs: {
[path: string]: {
bytesInOutput: number
}
}
imports: {
path: string
kind: string
external?: boolean
}[]
exports: string[]
entryPoint?: string
cssBundle?: string
}
}
}
#ロギング
#色
このオプションは、esbuildがターミナル内のstderrファイル記述子に書き込むエラーおよび警告メッセージの色を有効または無効にします。デフォルトでは、stderrがTTYセッションの場合は色が自動的に有効になり、それ以外の場合は自動的に無効になります。esbuildのカラー出力は次のようになります。
▲ [WARNING] The "typeof" operator will never evaluate to "null" [impossible-typeof] example.js:2:16: 2 │ log(typeof x == "null") ╵ ~~~~~~ The expression "typeof x" actually evaluates to "object" in JavaScript, not "null". You need to use "x === null" to test for null. ✘ [ERROR] Could not resolve "logger" example.js:1:16: 1 │ import log from "logger" ╵ ~~~~~~~~ You can mark the path "logger" as external to exclude it from the bundle, which will remove this error and leave the unresolved path in the bundle.
色をtrue
に設定すると、カラー出力を強制的に有効にできます。これは、esbuildのstderr出力を自分でTTYにパイプしている場合に役立ちます。
echo 'typeof x == "null"' | esbuild --color=true 2> stderr.txt
import * as esbuild from 'esbuild'
let js = 'typeof x == "null"'
await esbuild.transform(js, {
color: true,
})
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "typeof x == 'null'"
result := api.Transform(js, api.TransformOptions{
Color: api.ColorAlways,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
色を無効にするために、カラー出力をfalse
に設定することもできます。
#メッセージの書式設定
このAPI呼び出しは、build APIとtransform APIによって返されるログのエラーと警告を、esbuild自体が使用するのと同じ書式で文字列としてフォーマットするために使用できます。これは、esbuildのロギング方法をカスタマイズしたい場合に役立ちます。たとえば、ログメッセージを印刷前に処理したり、コンソール以外の場所に印刷したりする場合などです。以下に例を示します。
import * as esbuild from 'esbuild'
let formatted = await esbuild.formatMessages([
{
text: 'This is an error',
location: {
file: 'app.js',
line: 10,
column: 4,
length: 3,
lineText: 'let foo = bar',
},
},
], {
kind: 'error',
color: false,
terminalWidth: 100,
})
console.log(formatted.join('\n'))
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
import "strings"
func main() {
formatted := api.FormatMessages([]api.Message{
{
Text: "This is an error",
Location: &api.Location{
File: "app.js",
Line: 10,
Column: 4,
Length: 3,
LineText: "let foo = bar",
},
},
}, api.FormatMessagesOptions{
Kind: api.ErrorMessage,
Color: false,
TerminalWidth: 100,
})
fmt.Printf("%s", strings.Join(formatted, "\n"))
}
#オプション
以下のオプションを指定して、フォーマットを制御できます。
interface FormatMessagesOptions {
kind: 'error' | 'warning';
color?: boolean;
terminalWidth?: number;
}
type FormatMessagesOptions struct {
Kind MessageKind
Color bool
TerminalWidth int
}
kind
これらのログメッセージをエラーとして出力するか、警告として出力するかを制御します。
color
これが
true
の場合、色付き出力のためにUnixスタイルの端末エスケープコードが含まれます。terminalWidth
長い行が指定された列幅を超えてオーバーフローしないように、正の値を指定して折り返します。単語の折り返しを無効にするには、
0
を指定します。
#ログレベル
ログレベルを変更して、esbuildが警告やエラーメッセージを端末に出力しないようにできます。6つのログレベルがあります。
silent
ログ出力を一切表示しません。これは、JS transform APIを使用する場合のデフォルトのログレベルです。error
エラーのみを表示します。warning
警告とエラーのみを表示します。これは、JS build APIを使用する場合のデフォルトのログレベルです。info
警告、エラー、および出力ファイルの概要を表示します。これは、CLIを使用する場合のデフォルトのログレベルです。debug
info
からのすべてのログと、壊れたバンドルをデバッグするのに役立つ可能性のある追加のメッセージをログに記録します。このログレベルはパフォーマンスに影響を与え、一部のメッセージは誤検出である可能性があるため、この情報はデフォルトでは表示されません。verbose
大量のログメッセージを生成し、ファイルシステムドライバの問題をデバッグするために追加されました。一般的な使用は想定されていません。
ログレベルは次のように設定できます。
echo 'typeof x == "null"' | esbuild --log-level=error
import * as esbuild from 'esbuild'
let js = 'typeof x == "null"'
await esbuild.transform(js, {
logLevel: 'error',
})
package main
import "fmt"
import "github.com/evanw/esbuild/pkg/api"
func main() {
js := "typeof x == 'null'"
result := api.Transform(js, api.TransformOptions{
LogLevel: api.LogLevelError,
})
if len(result.Errors) == 0 {
fmt.Printf("%s", result.Code)
}
}
#ログ制限
デフォルトでは、esbuildは10個のメッセージが報告された後、ログメッセージの報告を停止します。これにより、膨大な数のログメッセージが誤って生成されるのを防ぎます。これは、Windowsコマンドプロンプトなどの低速な端末エミュレータを簡単にロックアップさせる可能性があります。また、スクロールバッファが限られている端末エミュレータでスクロールバッファ全体を誤って使い切ってしまうのを防ぎます。
ログ制限は別の値に変更でき、ゼロに設定することで完全に無効にすることもできます。これにより、すべてのログメッセージが表示されます。
esbuild app.js --log-limit=0
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
logLimit: 0,
outfile: 'out.js',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
LogLimit: 0,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#ログのオーバーライド
この機能を使用すると、ログメッセージの個々のタイプのログレベルを変更できます。特定のタイプの警告を抑制したり、デフォルトで有効になっていない追加の警告を有効にしたり、警告をエラーに変換したりすることもできます。
たとえば、古いブラウザーをターゲットにする場合、esbuildは、それらのブラウザーでは新しすぎる機能を使用する正規表現リテラルを自動的にnew
呼び出しに変換し、生成されたコードがブラウザーによって構文エラーと見なされずに実行できるようにします。ただし、その正規表現構文は依然としてサポートされていないため、RegExp
のポリフィルを追加しない場合、これらの呼び出しは実行時に例外をスローします。新しいサポートされていない正規表現構文を使用するときにesbuildに警告を生成させたい場合は、次のように行うことができます。
esbuild app.js --log-override:unsupported-regexp=warning --target=chrome50
import * as esbuild from 'esbuild'
await esbuild.build({
entryPoints: ['app.js'],
logOverride: {
'unsupported-regexp': 'warning',
},
target: 'chrome50',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.js"},
LogOverride: map[string]api.LogLevel{
"unsupported-regexp": api.LogLevelWarning,
},
Engines: []api.Engine{
{Name: api.EngineChrome, Version: "50"},
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
各メッセージタイプのログレベルは、ログレベルの設定でサポートされている任意の値にオーバーライドできます。現在利用可能なすべてのメッセージタイプを以下に示します(各メッセージの例については、クリックしてください)。
-
JS
assign-to-constant
▲ [WARNING] This assignment will throw because "foo" is a constant [assign-to-constant] example.js:1:15: 1 │ const foo = 1; foo = 2 ╵ ~~~ The symbol "foo" was declared a constant here: example.js:1:6: 1 │ const foo = 1; foo = 2 ╵ ~~~
assign-to-import
▲ [WARNING] This assignment will throw because "foo" is an import [assign-to-import] example.js:1:23: 1 │ import foo from "foo"; foo = null ╵ ~~~ Imports are immutable in JavaScript. To modify the value of this import, you must export a setter function in the imported file (e.g. "setFoo") and then import and call that function here instead.
call-import-namespace
▲ [WARNING] Calling "foo" will crash at run-time because it's an import namespace object, not a function [call-import-namespace] example.js:1:28: 1 │ import * as foo from "foo"; foo() ╵ ~~~ Consider changing "foo" to a default import instead: example.js:1:7: 1 │ import * as foo from "foo"; foo() │ ~~~~~~~~ ╵ foo
commonjs-variable-in-esm
▲ [WARNING] The CommonJS "exports" variable is treated as a global variable in an ECMAScript module and may not work as expected [commonjs-variable-in-esm] example.js:1:0: 1 │ exports.foo = 1; export let bar = 2 ╵ ~~~~~~~ This file is considered to be an ECMAScript module because of the "export" keyword here: example.js:1:17: 1 │ exports.foo = 1; export let bar = 2 ╵ ~~~~~~
delete-super-property
▲ [WARNING] Attempting to delete a property of "super" will throw a ReferenceError [delete-super-property] example.js:1:42: 1 │ class Foo extends Object { foo() { delete super.foo } } ╵ ~~~~~
duplicate-case
▲ [WARNING] This case clause will never be evaluated because it duplicates an earlier case clause [duplicate-case] example.js:1:33: 1 │ switch (foo) { case 1: return 1; case 1: return 2 } ╵ ~~~~ The earlier case clause is here: example.js:1:15: 1 │ switch (foo) { case 1: return 1; case 1: return 2 } ╵ ~~~~
duplicate-object-key
▲ [WARNING] Duplicate key "bar" in object literal [duplicate-object-key] example.js:1:16: 1 │ foo = { bar: 1, bar: 2 } ╵ ~~~ The original key "bar" is here: example.js:1:8: 1 │ foo = { bar: 1, bar: 2 } ╵ ~~~
empty-import-meta
▲ [WARNING] "import.meta" is not available in the configured target environment ("chrome50") and will be empty [empty-import-meta] example.js:1:6: 1 │ foo = import.meta ╵ ~~~~~~~~~~~
equals-nan
▲ [WARNING] Comparison with NaN using the "!==" operator here is always true [equals-nan] example.js:1:24: 1 │ foo = foo.filter(x => x !== NaN) ╵ ~~~ Floating-point equality is defined such that NaN is never equal to anything, so "x === NaN" always returns false. You need to use "Number.isNaN(x)" instead to test for NaN.
equals-negative-zero
▲ [WARNING] Comparison with -0 using the "!==" operator will also match 0 [equals-negative-zero] example.js:1:28: 1 │ foo = foo.filter(x => x !== -0) ╵ ~~ Floating-point equality is defined such that 0 and -0 are equal, so "x === -0" returns true for both 0 and -0. You need to use "Object.is(x, -0)" instead to test for -0.
equals-new-object
▲ [WARNING] Comparison using the "!==" operator here is always true [equals-new-object] example.js:1:24: 1 │ foo = foo.filter(x => x !== []) ╵ ~~~ Equality with a new object is always false in JavaScript because the equality operator tests object identity. You need to write code to compare the contents of the object instead. For example, use "Array.isArray(x) && x.length === 0" instead of "x === []" to test for an empty array.
html-comment-in-js
▲ [WARNING] Treating "<!--" as the start of a legacy HTML single-line comment [html-comment-in-js] example.js:1:0: 1 │ <!-- comment --> ╵ ~~~~
impossible-typeof
▲ [WARNING] The "typeof" operator will never evaluate to "null" [impossible-typeof] example.js:1:32: 1 │ foo = foo.map(x => typeof x !== "null") ╵ ~~~~~~ The expression "typeof x" actually evaluates to "object" in JavaScript, not "null". You need to use "x === null" to test for null.
indirect-require
▲ [WARNING] Indirect calls to "require" will not be bundled [indirect-require] example.js:1:8: 1 │ let r = require, fs = r("fs") ╵ ~~~~~~~
private-name-will-throw
▲ [WARNING] Writing to getter-only property "#foo" will throw [private-name-will-throw] example.js:1:39: 1 │ class Foo { get #foo() {} bar() { this.#foo++ } } ╵ ~~~~
semicolon-after-return
▲ [WARNING] The following expression is not returned because of an automatically-inserted semicolon [semicolon-after-return] example.js:1:6: 1 │ return ╵ ^
suspicious-boolean-not
▲ [WARNING] Suspicious use of the "!" operator inside the "in" operator [suspicious-boolean-not] example.js:1:4: 1 │ if (!foo in bar) { │ ~~~~ ╵ (!foo) The code "!x in y" is parsed as "(!x) in y". You need to insert parentheses to get "!(x in y)" instead.
this-is-undefined-in-esm
▲ [WARNING] Top-level "this" will be replaced with undefined since this file is an ECMAScript module [this-is-undefined-in-esm] example.js:1:0: 1 │ this.foo = 1; export let bar = 2 │ ~~~~ ╵ undefined This file is considered to be an ECMAScript module because of the "export" keyword here: example.js:1:14: 1 │ this.foo = 1; export let bar = 2 ╵ ~~~~~~
unsupported-dynamic-import
▲ [WARNING] This "import" expression will not be bundled because the argument is not a string literal [unsupported-dynamic-import] example.js:1:0: 1 │ import(foo) ╵ ~~~~~~
unsupported-jsx-comment
▲ [WARNING] Invalid JSX factory: 123 [unsupported-jsx-comment] example.jsx:1:8: 1 │ // @jsx 123 ╵ ~~~
unsupported-regexp
▲ [WARNING] The regular expression flag "d" is not available in the configured target environment ("chrome50") [unsupported-regexp] example.js:1:3: 1 │ /./d ╵ ^ This regular expression literal has been converted to a "new RegExp()" constructor to avoid generating code with a syntax error. However, you will need to include a polyfill for "RegExp" for your code to have the correct behavior at run-time.
unsupported-require-call
▲ [WARNING] This call to "require" will not be bundled because the argument is not a string literal [unsupported-require-call] example.js:1:0: 1 │ require(foo) ╵ ~~~~~~~
-
CSS
css-syntax-error
▲ [WARNING] Expected identifier but found "]" [css-syntax-error] example.css:1:4: 1 │ div[] { ╵ ^
invalid-@charset
▲ [WARNING] "@charset" must be the first rule in the file [invalid-@charset] example.css:1:19: 1 │ div { color: red } @charset "UTF-8"; ╵ ~~~~~~~~ This rule cannot come before a "@charset" rule example.css:1:0: 1 │ div { color: red } @charset "UTF-8"; ╵ ^
invalid-@import
▲ [WARNING] All "@import" rules must come first [invalid-@import] example.css:1:19: 1 │ div { color: red } @import "foo.css"; ╵ ~~~~~~~ This rule cannot come before an "@import" rule example.css:1:0: 1 │ div { color: red } @import "foo.css"; ╵ ^
invalid-@layer
▲ [WARNING] "initial" cannot be used as a layer name [invalid-@layer] example.css:1:7: 1 │ @layer initial { ╵ ~~~~~~~
invalid-calc
▲ [WARNING] "-" can only be used as an infix operator, not a prefix operator [invalid-calc] example.css:1:20: 1 │ div { z-index: calc(-(1+2)); } ╵ ^ ▲ [WARNING] The "+" operator only works if there is whitespace on both sides [invalid-calc] example.css:1:23: 1 │ div { z-index: calc(-(1+2)); } ╵ ^
js-comment-in-css
▲ [WARNING] Comments in CSS use "/* ... */" instead of "//" [js-comment-in-css] example.css:1:0: 1 │ // comment ╵ ~~
undefined-composes-from
▲ [WARNING] The value of "zoom" in the "foo" class is undefined [undefined-composes-from] example.module.css:1:1: 1 │ .foo { composes: bar from "lib.module.css"; zoom: 1; } ╵ ~~~ The first definition of "zoom" is here: lib.module.css:1:7: 1 │ .bar { zoom: 2 } ╵ ~~~~ The second definition of "zoom" is here: example.module.css:1:44: 1 │ .foo { composes: bar from "lib.module.css"; zoom: 1; } ╵ ~~~~ The specification of "composes" does not define an order when class declarations from separate files are composed together. The value of the "zoom" property for "foo" may change unpredictably as the code is edited. Make sure that all definitions of "zoom" for "foo" are in a single file.
unsupported-@charset
▲ [WARNING] "UTF-8" will be used instead of unsupported charset "ASCII" [unsupported-@charset] example.css:1:9: 1 │ @charset "ASCII"; ╵ ~~~~~~~
unsupported-@namespace
▲ [WARNING] "@namespace" rules are not supported [unsupported-@namespace] example.css:1:0: 1 │ @namespace "ns"; ╵ ~~~~~~~~~~
unsupported-css-property
▲ [WARNING] "widht" is not a known CSS property [unsupported-css-property] example.css:1:6: 1 │ div { widht: 1px } │ ~~~~~ ╵ width Did you mean "width" instead?
unsupported-css-nesting
▲ [WARNING] Transforming this CSS nesting syntax is not supported in the configured target environment ("chrome50") [unsupported-css-nesting] example.css:2:5: 2 │ .foo & { ╵ ^ The nesting transform for this case must generate an ":is(...)" but the configured target environment does not support the ":is" pseudo-class.
-
Bundler
ambiguous-reexport
▲ [WARNING] Re-export of "foo" in "example.js" is ambiguous and has been removed [ambiguous-reexport] One definition of "foo" comes from "a.js" here: a.js:1:11: 1 │ export let foo = 1 ╵ ~~~ Another definition of "foo" comes from "b.js" here: b.js:1:11: 1 │ export let foo = 2 ╵ ~~~
different-path-case
▲ [WARNING] Use "foo.js" instead of "Foo.js" to avoid issues with case-sensitive file systems [different-path-case] example.js:2:7: 2 │ import "./Foo.js" ╵ ~~~~~~~~~~
empty-glob
▲ [WARNING] The glob pattern import("./icon-*.json") did not match any files [empty-glob] example.js:2:16: 2 │ return import("./icon-" + name + ".json") ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~~
ignored-bare-import
▲ [WARNING] Ignoring this import because "node_modules/foo/index.js" was marked as having no side effects [ignored-bare-import] example.js:1:7: 1 │ import "foo" ╵ ~~~~~ "sideEffects" is false in the enclosing "package.json" file: node_modules/foo/package.json:2:2: 2 │ "sideEffects": false ╵ ~~~~~~~~~~~~~
ignored-dynamic-import
▲ [WARNING] Importing "foo" was allowed even though it could not be resolved because dynamic import failures appear to be handled here: [ignored-dynamic-import] example.js:1:7: 1 │ import("foo").catch(e => { ╵ ~~~~~ The handler for dynamic import failures is here: example.js:1:14: 1 │ import("foo").catch(e => { ╵ ~~~~~
import-is-undefined
▲ [WARNING] Import "foo" will always be undefined because the file "foo.js" has no exports [import-is-undefined] example.js:1:9: 1 │ import { foo } from "./foo" ╵ ~~~
require-resolve-not-external
▲ [WARNING] "foo" should be marked as external for use with "require.resolve" [require-resolve-not-external] example.js:1:26: 1 │ let foo = require.resolve("foo") ╵ ~~~~~
-
ソースマップ
invalid-source-mappings
▲ [WARNING] Bad "mappings" data in source map at character 3: Invalid original column value: -2 [invalid-source-mappings] example.js.map:2:18: 2 │ "mappings": "aAAFA,UAAU;;" ╵ ^ The source map "example.js.map" was referenced by the file "example.js" here: example.js:1:21: 1 │ //# sourceMappingURL=example.js.map ╵ ~~~~~~~~~~~~~~
sections-in-source-map
▲ [WARNING] Source maps with "sections" are not supported [sections-in-source-map] example.js.map:2:2: 2 │ "sections": [] ╵ ~~~~~~~~~~ The source map "example.js.map" was referenced by the file "example.js" here: example.js:1:21: 1 │ //# sourceMappingURL=example.js.map ╵ ~~~~~~~~~~~~~~
missing-source-map
▲ [WARNING] Cannot read file ".": is a directory [missing-source-map] example.js:1:21: 1 │ //# sourceMappingURL=. ╵ ^
unsupported-source-map-comment
▲ [WARNING] Unsupported source map comment: could not decode percent-escaped data: invalid URL escape "%\"" [unsupported-source-map-comment] example.js:1:21: 1 │ //# sourceMappingURL=data:application/json,"%" ╵ ~~~~~~~~~~~~~~~~~~~~~~~~~
-
Resolver
package.json
▲ [WARNING] "esm" is not a valid value for the "type" field [package.json] package.json:1:10: 1 │ { "type": "esm" } ╵ ~~~~~ The "type" field must be set to either "commonjs" or "module".
tsconfig.json
▲ [WARNING] Unrecognized target environment "ES4" [tsconfig.json] tsconfig.json:1:33: 1 │ { "compilerOptions": { "target": "ES4" } } ╵ ~~~~~
これらのメッセージタイプは合理的に安定している必要がありますが、新しいものが追加されたり、古いものが将来的に削除されたりする可能性があります。メッセージタイプが削除された場合、そのメッセージタイプに対するオーバーライドは、単にサイレントに無視されます。