コンテンツタイプ
以下に、すべての組み込みコンテンツタイプをリストアップします。各コンテンツタイプには、ファイルの内容をどのように解釈するかをesbuildに指示する関連付けられた「ローダー」があります。一部のファイル拡張子は、デフォルトでローダーが既に構成されていますが、デフォルトは上書きできます。
#JavaScript
ローダー: js
このローダーは、.js
、.cjs
、および.mjs
ファイルに対してデフォルトで有効になっています。.cjs
拡張子は、Node.jsでCommonJSモジュールに使用され、.mjs
拡張子は、Node.jsでECMAScriptモジュールに使用されます。
デフォルトでは、esbuildの出力はすべての最新のJS機能を活用します。たとえば、a !==
は、ミニファイが有効になっている場合、a ?? b
になります。これはES2020バージョンのJavaScriptの構文を使用しています。これが望ましくない場合は、esbuildのtarget設定を指定して、出力で正しく動作する必要があるブラウザを指定する必要があります。そうすると、esbuildはそれらのブラウザにとって古すぎるJavaScript機能の使用を避けられます。
esbuildは、最新のJavaScript構文をすべてサポートしています。ただし、新しい構文は古いブラウザではサポートされない場合があるため、targetオプションを設定して、必要に応じて新しい構文を古い構文に変換するようにesbuildに指示することをお勧めします。
これらの構文機能は、古いブラウザに対して常に変換されます。
構文変換 | 言語バージョン | 例 |
---|---|---|
関数のパラメータリストと呼び出しにおける末尾のカンマ | es2017 |
foo(a, b, ) |
数値セパレータ | esnext |
1_000_000 |
これらの構文機能は、構成された言語targetに応じて、古いブラウザに対して条件付きで変換されます。
構文変換 | --target が以下の場合に変換されます。 |
例 |
---|---|---|
べき乗演算子 | es2016 |
a ** b |
非同期関数 | es2017 |
async () => {} |
非同期イテレーション | es2018 |
for await (let x of y) {} |
非同期ジェネレータ | es2018 |
async function* foo() {} |
スプレッドプロパティ | es2018 |
let x = {...y} |
レストプロパティ | es2018 |
let {...x} = y |
省略可能なcatchバインディング | es2019 |
try {} catch {} |
オプショナルチェイニング | es2020 |
a?.b |
Nullish 合体演算子 | es2020 |
a ?? b |
import.meta |
es2020 |
import.meta |
論理代入演算子 | es2021 |
a ??= b |
クラスインスタンスフィールド | es2022 |
class { x } |
静的クラスフィールド | es2022 |
class { static x } |
プライベートインスタンスメソッド | es2022 |
class { #x() {} } |
プライベートインスタンスフィールド | es2022 |
class { #x } |
プライベート静的メソッド | es2022 |
class { static #x() {} } |
プライベート静的フィールド | es2022 |
class { static #x } |
人間工学的なブランドチェック | es2022 |
#x in y |
クラス静的ブロック | es2022 |
class { static {} } |
importアサーション | esnext |
import "x" assert {} |
自動アクセサ | esnext |
class { accessor x } |
using 宣言 |
esnext |
using x = y |
これらの構文機能は、現在、常に変換されずに渡されます。
構文変換 | --target が以下の場合、サポートされていません。 |
例 |
---|---|---|
RegExp dotAll フラグ |
es2018 |
/./s 1 |
RegExp後方参照アサーション | es2018 |
/(?<=x)y/ 1 |
RegExp名前付きキャプチャグループ | es2018 |
/(?<foo>\d+)/ 1 |
RegExp Unicodeプロパティエスケープ | es2018 |
/\p{ASCII}/u 1 |
BigInt | es2020 |
123n |
トップレベルのawait | es2022 |
await import(x) |
任意のモジュール名前空間識別子 | es2022 |
export {foo as 'f o o'} |
RegExpマッチインデックス | es2022 |
/x(.+)y/d 1 |
ハッシュバング構文 | esnext |
#!/usr/bin/env node |
デコレータ | esnext |
@foo class Bar {} |
RegExp集合表記 | esnext |
/[\w--\d]/ 1 |
完了したECMAScript提案のリストとアクティブなECMAScript提案のリストも参照してください。トップレベルのawaitを含むコードの変換はサポートされていますが、トップレベルのawaitを含むコードのバンドルは、出力形式がesm
に設定されている場合にのみサポートされていることに注意してください。
#JavaScriptの注意点
esbuildでJavaScriptを使用する際には、次の点に注意してください。
#ES5は十分にサポートされていません
ES6+構文をES5に変換することはまだサポートされていません。ただし、esbuildを使用してES5コードを変換する場合は、targetをes5
に設定する必要があります。これにより、esbuildがES6構文をES5コードに導入することがなくなります。たとえば、このフラグがないと、オブジェクトリテラル{x: x}
は{x}
になり、文字列"a\nb"
はミニファイ時に複数行のテンプレートリテラルになります。これらの置換はどちらも結果のコードが短くなるために行われますが、targetがes5
の場合は、これらの置換は行われません。
#プライベートメンバーのパフォーマンス
プライベートメンバー変換(#name
構文用)は、WeakMap
とWeakSet
を使用して、この機能のプライバシープロパティを保持します。これは、BabelとTypeScriptコンパイラにおける対応する変換に似ています。最新のJavaScriptエンジン(V8、JavaScriptCore、およびSpiderMonkey、ただしChakraCoreではない)は、大規模なWeakMap
とWeakSet
オブジェクトに対して良好なパフォーマンス特性を持たない場合があります。
この構文変換がアクティブな状態で、プライベートフィールドまたはプライベートメソッドを持つクラスのインスタンスを多数作成すると、ガベージコレクタにとって大きなオーバーヘッドが発生する可能性があります。これは、最新のエンジン(ChakraCore以外)が弱い値を実際のマップオブジェクトに格納し、キー自体の非表示プロパティとして格納しないためであり、大規模なマップオブジェクトはガベージコレクションでパフォーマンスの問題を引き起こす可能性があります。この参照で詳細を確認してください。
#インポートはECMAScriptモジュールの動作に従います
そのグローバル状態を必要とするモジュールをインポートする前にグローバル状態を変更しようとすると、動作すると期待するかもしれません。しかし、JavaScript(したがってesbuild)はすべてのimport
文をファイルの先頭に効果的に「ホイスティング」するため、これは機能しません。
window.foo = {}
import './something-that-needs-foo'
この点でJavaScript仕様に従っていない、ECMAScriptモジュールの壊れた実装がいくつか存在します(たとえば、TypeScriptコンパイラ)。これらのツールでコンパイルされたコードは、import
がrequire()
へのインライン呼び出しで置き換えられるため、「動作する」可能性があります。これはホイスティングの要件を無視します。しかし、そのようなコードは、Node.js、ブラウザ、またはesbuildなどの実際のECMAScriptモジュール実装では動作しないため、このようなコードの記述は移植性がなく、推奨されません。
これを正しく行う方法は、グローバル状態の変更を独自のインポートに移動することです。そうすれば、他のインポートの前に実行されます。
import './assign-to-foo-on-window'
import './something-that-needs-foo'
#バンドル時に直接eval
を避ける
式eval(x)
は通常の関数呼び出しのように見えますが、実際にはJavaScriptで特別な動作をします。このようにeval
を使用すると、x
に格納された評価されたコードは、名前で任意の包含スコープ内の任意の変数を参照できます。たとえば、コードlet y =
は123
を返します。
これは「直接eval」と呼ばれ、多くの理由からコードをバンドルする際に問題となります。
最新のバンドラには「スコープホイスティング」と呼ばれる最適化が含まれており、すべてのバンドルされたファイルを1つのファイルにマージし、変数を名前変更して名前の衝突を回避します。ただし、これは、直接
eval
によって評価されたコードがバンドル内の任意のファイルの任意の変数を読み書きできることを意味します!これは、評価されたコードがグローバル変数にアクセスしようとするが、代わりに別のファイルの同じ名前のプライベート変数に誤ってアクセスする可能性があるため、正確性の問題です。別のファイルのプライベート変数に機密データが含まれている場合は、潜在的なセキュリティ問題にもなり得ます。import
文を使用してインポートされた変数を参照する場合、評価されたコードは正しく動作しない可能性があります。インポートされた変数は、別のファイルの変数へのライブバインディングです。それらの変数のコピーではありません。したがって、esbuildがコードをバンドルすると、インポートはインポートされたファイルの変数への直接参照に置き換えられます。しかし、その変数名は異なる可能性があり、その場合は、直接eval
によって評価されたコードはその名前で参照できなくなります。直接の
eval
を使用すると、esbuildは、直接のeval
呼び出しを含むすべてのスコープ内のすべてのコードを最適化解除します。正確性を確保するために、評価されたコードはそのeval
呼び出しから到達可能なファイル内の他のコードにアクセスする必要があると仮定する必要があります。つまり、そのコードのいずれもデッドコードとして削除されず、そのコードのいずれも縮小されません。直接の
eval
によって評価されるコードは、名前で到達可能な変数を参照する必要がある可能性があるため、esbuildは到達可能なすべての変数の名前変更を妨げられます。つまり、バンドル内の他の変数との名前の衝突を回避するために変数の名前を変更できません。そのため、直接のeval
はesbuildにファイルをCommonJSクロージャでラップさせます。これにより、新しいスコープを導入することで名前の衝突が回避されます。ただし、エクスポートされた変数はコンパイル時の静的バインディングではなく、実行時の動的バインディングを使用するため、生成されるコードは大きくなり、遅くなります。
幸いなことに、通常は直接のeval
の使用を回避するのは簡単です。上記のすべての欠点を回避する、一般的に使用される2つの代替手段があります。
(0, eval)('x')
これは、
eval
が直接呼び出されていないため「間接的eval」と呼ばれ、JavaScript VMでの直接evalの文法的特殊ケースをトリガーしません。eval('x')
という正確な形式の式を除く、あらゆる構文を使用して間接的evalを呼び出すことができます。たとえば、var eval2 =
とeval; eval2('x') [eval][0]('x')
とwindow.
はすべて間接的eval呼び出しです。間接的evalを使用する場合、コードは呼び出し元のインラインスコープではなく、グローバルスコープで評価されます。eval('x') new Function('x')
これにより、実行時に新しい関数オブジェクトが構築されます。グローバルスコープで
function()
と記述した場合と同じですが、{ x } x
は任意のコード文字列にすることができます。この形式は、関数に引数を追加し、それらの引数を使用して評価されたコードにアクセス可能な変数を公開できるため、便利な場合があります。たとえば、(new Function('env',
は、'x'))( someEnv) (function(env)
と記述した場合と同じです。評価されたコードがローカル変数にアクセスする必要がある場合、ローカル変数を引数として渡すことができるため、これは多くの場合、直接の{ x })( someEnv) eval
の十分な代替手段となります。
#toString()
の値は、関数(およびクラス)では保持されません。
JavaScriptの関数オブジェクトでtoString()
を呼び出し、その文字列を何らかの形式のeval
に渡して新しい関数オブジェクトを取得することは、ある程度一般的です。これは、関数を包含ファイルから「引き剥がして」、そのファイル内のすべての変数とのリンクを切断する効果があります。esbuildでのこの操作はサポートされておらず、機能しない可能性があります。特に、esbuildはヘルパーメソッドを使用して特定の機能を実装することが多く、JavaScriptのスコープルールが改ざんされていないことを前提としています。たとえば
let pow = (a, b) => a ** b;
let pow2 = (0, eval)(pow.toString());
console.log(pow2(2, 3));
このコードがES6向けにコンパイルされる場合、**
演算子は使用できないため、**
演算子は__pow
ヘルパー関数の呼び出しに置き換えられます。
let __pow = Math.pow;
let pow = (a, b) => __pow(a, b);
let pow2 = (0, eval)(pow.toString());
console.log(pow2(2, 3));
このコードを実行しようとすると、ReferenceError:
などのエラーが発生します。これは、関数(a, b)
がローカルスコープのシンボル__pow
に依存しており、グローバルスコープでは使用できないためです。これは、async
関数を含む多くのJavaScript言語機能、および名前を保持する設定などの一部のesbuild固有の機能にも当てはまります。
この問題は、ほとんどの場合、人々が.toString()
を使用して関数のソースコードを取得し、それをウェブワーカーの本文として使用しようとした場合に発生します。esbuildを使用したい場合、ウェブワーカーのソースコードを別のビルドステップでビルドし、ウェブワーカーを作成するコードにウェブワーカーのソースコードを文字列として挿入する必要があります。define機能は、ビルド時に文字列を挿入する1つの方法です。
#モジュール名前空間オブジェクトから呼び出された関数のthis
の値は保持されません。
JavaScriptでは、関数内のthis
の値は、関数の呼び出し方法に基づいて自動的に入力されます。たとえば、関数がobj.fn()
を使用して呼び出された場合、関数呼び出し中のthis
の値はobj
になります。この動作はesbuildによって尊重されますが、例外があります。モジュール名前空間オブジェクトから関数を呼び出す場合、this
の値が正しくない可能性があります。たとえば、モジュール名前空間オブジェクトns
からfoo
を呼び出す次のコードを考えてみます。
import * as ns from './foo.js'
ns.foo()
foo.js
がモジュール名前空間オブジェクトをthis
を使用して参照しようとすると、esbuildでコードをバンドルした後は必ずしも機能しません。
// foo.js
export function foo() {
this.bar()
}
export function bar() {
console.log('bar')
}
その理由は、esbuildがモジュール名前空間オブジェクトを使用するほとんどのコードを、代わりに直接インポートするコードに自動的に書き換えるためです。つまり、上記の例コードは代わりに次のように変換され、関数呼び出しのthis
コンテキストが削除されます。
import { foo } from './foo.js'
foo()
この変換により、エクスポートされたシンボルの未使用部分をesbuildが理解できるようになるため、ツリーシェイキング(別名デッドコード除去)が大幅に向上します。関数呼び出しの`this`コンテキストが削除されます。この変換により、エクスポートされたシンボルの未使用部分をesbuildが理解できるようになるため、ツリーシェイキング(別名デッドコード除去)が大幅に向上します。ただし、this
を使用してモジュールのエクスポートにアクセスするコードの動作が変更されるという欠点があります。ただし、そもそも誰もこのような奇妙なコードを書くべきではないため、これは問題ではありません。同じファイルからエクスポートされた関数にアクセスする必要がある場合は、直接呼び出すだけです(つまり、上記の例ではthis.bar()
ではなくbar()
)。
#default
エクスポートはエラーが発生しやすい可能性があります。
ESモジュール形式(つまりESM)には、他のすべてのエクスポート名とは異なる動作をするdefault
と呼ばれる特別なエクスポートがあります。default
エクスポートを持つESM形式のコードをCommonJS形式に変換し、そのCommonJSコードをESM形式の別のモジュールにインポートすると、両方とも広く使用されている2つの異なる解釈があります(Babelの方法とNodeの方法)。これは非常に残念です。特に、JavaScriptライブラリは多くの場合ESMで作成され、CommonJSとして公開されるため、終わりのない互換性の頭痛を引き起こすためです。
esbuildがコードをバンドルする場合、どの解釈を使用するかを決定する必要があり、完璧な答えはありません。esbuildが使用するヒューリスティックは、Webpackが使用するヒューリスティックと同じです(詳細は以下を参照)。Webpackは最も広く使用されているバンドラであるため、esbuildは、この互換性の問題に関して、既存のエコシステムと最大限に互換性を持つことができます。そのため、この問題のあるコードをesbuildで動作させることができれば、Webpackでも動作するはずです。
問題は次の例で示されています。
// index.js
import foo from './somelib.js'
console.log(foo)
// somelib.js
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = 'foo';
そして、これら2つの解釈はどちらも広く使用されています。
Babelの解釈
Babelの解釈が使用されている場合、このコードは
foo
を出力します。彼らの理由は、somelib.js
がESMからCommonJSに変換されたため(__esModule
マーカーからわかるように)であり、元のコードは次のようになっているためです。// somelib.js export default 'foo'
somelib.js
がESMからCommonJSに変換されていなければ、このコードはfoo
を出力するため、モジュール形式に関係なくfoo
を出力する必要があります。これは、__esModule
マーカー(Babel、TypeScript、Webpack、esbuildを含むすべてのモジュール変換ツールが設定する)を使用して、CommonJSモジュールが以前ESモジュールであったかどうかを検出し、__esModule
マーカーが存在する場合はデフォルトのインポートをexports.
に設定することによって実現されます。この動作は、クロスコンパイルされたESMをCommonJS環境で正しく実行するために必要であり、NodeがネイティブのESMサポートを追加するずっと前は、ESMコードをNodeで実行する唯一の方法だったため重要です。default Nodeの解釈
Nodeの解釈が使用されている場合、このコードは
{ default:
を出力します。彼らの理由は、CommonJSコードは動的なエクスポートを使用する一方で、ESMコードは静的なエクスポートを使用するため、CommonJSをESMにインポートする完全な一般的なアプローチは、何らかの形でCommonJSの'foo' } exports
オブジェクト自体を公開することです。たとえば、CommonJSコードはexports[
を実行できますが、ESM構文には同等のものはありません。Math. random() ] = 'foo' default
エクスポートはそのため使用されます。それは実際にはESモジュール仕様の作成者が当初から意図していたとおりだからです。この解釈は、通常のCommonJSモジュールでは完全に妥当です。これは、以前ESモジュールであったCommonJSモジュール(つまり、__esModule
が存在する場合)でのみ互換性の問題を引き起こし、その場合、動作はBabelの解釈とは異なります。
ライブラリの作成者向け:新しいコードを作成する場合は、default
エクスポートを完全に回避することを強く検討してください。残念ながら、互換性の問題に悩まされており、使用すると、いつかユーザーに問題を引き起こす可能性があります。
ライブラリのユーザー向け:デフォルトで、esbuildはBabelの解釈を使用します。代わりにNodeの解釈を使用するようにesbuildに指示するには、コードを.mts
または.mjs
で終わるファイルに配置するか、"type":
をpackage.json
ファイルに追加する必要があります。その理由は、NodeのネイティブESMサポートは、ファイル拡張子が.mjs
であるか、"type":
が存在する場合にのみESMコードを実行できるため、それを行うことは、コードがNodeで実行されることを意図していることを示す良いシグナルであり、したがってNodeのdefault
インポートの解釈を使用する必要があります。これは、Webpackが使用するヒューリスティックと同じです。
#TypeScript
ローダー:ts
またはtsx
このローダーは、.ts
、.tsx
、.mts
、.cts
ファイルに対してデフォルトで有効になっているため、esbuildにはTypeScript構文を解析し、型注釈を破棄するための組み込みサポートがあります。ただし、esbuildは型チェックを行わないため、型をチェックするには、esbuildと並行してtsc -noEmit
を実行する必要があります。これは、esbuild自体が行うものではありません。
これらのTypeScript型宣言は解析され、無視されます(網羅的でないリスト)。
構文機能 | 例 |
---|---|
インターフェース宣言 | interface Foo {} |
型宣言 | type Foo = number |
関数宣言 | function foo(): void; |
環境宣言 | declare module 'foo' {} |
型のみのインポート | import type {Type} from 'foo' |
型のみのエクスポート | export type {Type} from 'foo' |
型のみのインポート指定子 | import {type Type} from 'foo' |
型のみのエクスポート指定子 | export {type Type} from 'foo' |
TypeScript固有の構文拡張がサポートされており、常にJavaScriptに変換されます(網羅的でないリスト)。
構文機能 | 例 | 備考 |
---|---|---|
名前空間 | namespace Foo {} |
|
列挙型 | enum Foo { A, B } |
|
定数列挙型 | const enum Foo { A, B } |
|
ジェネリック型パラメータ | <T>(a: T): T => a |
tsx ローダーでは<T,>( ...と記述する必要があります。 |
型付きJSX | <Element<T>/> |
|
型キャスト | a as B と<B>a |
|
型インポート | import {Type} from 'foo' |
未使用のインポートをすべて削除することで処理されます。 |
型エクスポート | export {Type} from 'foo' |
TypeScriptファイルで不足しているエクスポートを無視することで処理されます。 |
実験的なデコレータ | @sealed class Foo {} |
experimentalDecorators が必要です。emitDecoratorMetadata はサポートされていません。 |
インスタンス化式 | Array<number> |
TypeScript 4.7以降 |
infer でのextends |
infer A extends B |
TypeScript 4.7以降 |
分散注釈 | type A<out B> = () => B |
TypeScript 4.7以降 |
satisfies 演算子 |
a satisfies T |
TypeScript 4.9以降 |
const 型パラメータ |
class Foo<const T> {} |
TypeScript 5.0以降 |
#TypeScriptに関する注意点
esbuildでTypeScriptを使用する際は、以下の点に注意してください(JavaScriptに関する注意点に加えて)。
#ファイルは独立してコンパイルされる
単一のモジュールをトランスパイルする場合でも、TypeScriptコンパイラはインポートされたファイルを解析して、インポートされた名前が型であるか値であるかを判断します。しかし、esbuildやBabel(そしてTypeScriptコンパイラのtranspileModule
API)のようなツールは、各ファイルを独立してコンパイルするため、インポートされた名前が型であるか値であるかを判断できません。
このため、esbuildでTypeScriptを使用する場合は、isolatedModules
TypeScript構成オプションを有効にする必要があります。このオプションは、ファイルごとに独立して型参照をトレースせずにコンパイルされるesbuildなどの環境で、誤ったコンパイルを引き起こす可能性のある機能の使用を防止します。たとえば、export
を使用して別のモジュールから型を再エクスポートすることを防ぎます(代わりにexport
を使用する必要があります)。
#インポートはECMAScriptモジュールの動作に従う
歴史的な理由から、TypeScriptコンパイラはデフォルトでESM(ECMAScriptモジュール)構文をCommonJS構文にコンパイルします。たとえば、import *
はconst foo =
にコンパイルされます。これはおそらく、TypeScriptが構文を採用した時点でECMAScriptモジュールはまだ提案段階だったためです。しかし、これはレガシーな動作であり、nodeなどの実際のプラットフォームでのこの構文の動作とは一致しません。たとえば、require
関数は文字列を含む任意のJavaScript値を返すことができますが、import * as
構文は常にオブジェクトを返し、文字列になることはありません。
このレガシー機能による問題を回避するには、esbuildでTypeScriptを使用する場合は、esModuleInterop
TypeScript構成オプションを有効にする必要があります。これを有効にすると、このレガシー動作が無効になり、TypeScriptの型システムがESMと互換性を持つようになります。このオプションは、既存のTypeScriptプロジェクトにとって破壊的変更となるため、デフォルトでは有効になっていませんが、Microsoftは新規プロジェクトと既存プロジェクトの両方への適用(そしてその後のコードの更新)を強く推奨しており、エコシステムの他の部分との互換性を向上させます。
具体的には、CommonJSモジュールからESMインポート構文を使用して非オブジェクト値をインポートする場合は、import * as
を使用する代わりに、デフォルトインポートを使用する必要があります。したがって、CommonJSモジュールがmodule.
を介して関数をエクスポートする場合は、import *
ではなくimport
を使用する必要があります。
#型システムを必要とする機能はサポートされない
TypeScriptの型はコメントとして扱われ、esbuildによって無視されるため、TypeScriptは「型チェックされたJavaScript」として扱われます。型アノテーションの解釈はTypeScript型チェッカーに依存し、TypeScriptを使用する場合はesbuildに加えて実行する必要があります。これは、BabelのTypeScript実装が使用するのと同じコンパイル戦略です。ただし、これは、動作に型の解釈が必要なTypeScriptのコンパイル機能の一部がesbuildでは動作しないことを意味します。
具体的には
emitDecoratorMetadata
TypeScript構成オプションはサポートされていません。この機能は、対応するTypeScript型のJavaScript表現を添付されたデコレータ関数に渡します。esbuildはTypeScriptの型システムを複製しないため、この機能を実装するための十分な情報がありません。declaration
TypeScript構成オプション(つまり、.d.ts
ファイルの生成)はサポートされていません。TypeScriptでライブラリを作成していて、コンパイルされたJavaScriptコードをパッケージとして公開して他の人が使用できるようにしたい場合は、型宣言も公開したいでしょう。これは、型情報を保持しないため、esbuildが実行できることではありません。TypeScriptコンパイラを使用して生成するか、手動で記述する必要があります。
#特定のtsconfig.json
フィールドのみが尊重される
バンドリング中に、esbuildのパス解決アルゴリズムは、最も近い親ディレクトリにあるtsconfig.json
ファイルの内容を考慮し、その動作をそれに応じて変更します。esbuildのtsconfig
設定を使用してビルドAPIでtsconfig.json
パスの設定を明示的に行い、esbuildのtsconfigRaw
設定を使用して変換APIでtsconfig.json
ファイルの内容を明示的に渡すことも可能です。ただし、esbuildは現在、tsconfig.json
ファイルで以下のフィールドのみを検査します。
experimentalDecorators
このオプションは、TypeScriptファイルでのデコレータ構文の変換を有効にします。変換は、
experimentalDecorators
が有効になっている場合、TypeScript自体が従う古いデコレータ設計に従います。JavaScript、および
experimentalDecorators
が無効になっている場合のTypeScriptに追加されている、デコレータの更新された設計があることに注意してください。これはesbuildがまだ実装していないため、esbuildは現在、experimentalDecorators
が無効になっている場合、デコレータを変換しません。target
useDefineForClassFields
これらのオプションは、TypeScriptファイルのクラスフィールドが「define」セマンティクスまたは「assign」セマンティクスでコンパイルされるかどうかを制御します。
Defineセマンティクス(esbuildのデフォルトの動作):TypeScriptクラスフィールドは、通常のJavaScriptクラスフィールドのように動作します。フィールドイニシャライザは、基底クラスでセッターをトリガーしません。新しいコードは今後すべてこのように記述する必要があります。
Assignセマンティクス(明示的に有効にする必要がある):esbuildはTypeScriptのレガシーなクラスフィールドの動作をエミュレートします。フィールドイニシャライザは基底クラスのセッターをトリガーします。レガシーコードを実行するには、これが必要になる場合があります。
esbuildでdefineセマンティクスを無効にする(したがってassignセマンティクスを有効にする)方法は、TypeScriptで無効にする方法と同じです。
tsconfig.json
ファイルでuseDefineForClassFields
をfalse
に設定します。TypeScriptとの互換性のために、esbuildは、
useDefineForClassFields
が指定されていない場合、tsconfig.json
にES2022
より前のtarget
が含まれている場合、false
にデフォルト値を設定するというTypeScriptの動作もコピーします。ただし、このtarget
設定のデフォルト値に依存するのではなく、必要に応じてuseDefineForClassFields
を明示的に設定することをお勧めします。tsconfig.json
のtarget
設定は、useDefineForClassFields
のデフォルト値を決定するためにesbuildによってのみ使用されることに注意してください。同じ名前を持っているにもかかわらず、esbuild自身のtarget
設定には影響しません。baseUrl
paths
これらのオプションは、ファイルシステム上のファイルへの
import
/require
パスのesbuildによる解決に影響します。これを使用して、パッケージエイリアスを定義し、他の方法でインポートパスを書き換えることができます。インポートパス変換にesbuildを使用するには、esbuildのパス解決はバンドリング中にのみ行われるため、bundling
を有効にする必要があります。また、代わりに使用できるesbuild独自のalias
機能にも注意してください。jsx
jsxFactory
jsxFragmentFactory
jsxImportSource
これらのオプションは、JSX構文をJavaScriptに変換するesbuildの動作に影響します。これらは、これらの設定に対するesbuildのネイティブオプションと同等です。
jsx
、jsxFactory
、jsxFragment
、およびjsxImportSource
。alwaysStrict
strict
これらのオプションのいずれかが有効になっている場合、esbuildはすべてのTypeScriptファイル内のすべてのコードを厳格モードで実行し、出力
format
がesm
に設定されていない限り、生成されたコードに"use strict"
をプレフィックスします(すべてのESMファイルは自動的に厳格モードであるため)。verbatimModuleSyntax
importsNotUsedAsValues
preserveValueImports
デフォルトでは、TypeScriptコンパイラはTypeScriptをJavaScriptに変換する際に未使用のインポートを削除します。これにより、型のみのインポートが誤ってランタイムでエラーが発生することがなくなります。この動作はesbuildでも実装されています。
これらのオプションを使用すると、この動作を無効にして未使用のインポートを保持できます。これは、たとえば、インポートされたファイルに有用な副作用がある場合に役立ちます。古い
importsNotUsedAsValues
およびpreserveValueImports
設定(TypeScriptでは非推奨)に代わるものとして、verbatimModuleSyntax
を使用する必要があります。extends
このオプションを使用すると、
tsconfig.json
ファイルを複数のファイルに分割できます。この値は、単一継承の場合は文字列、複数継承の場合は配列になります(TypeScript 5.0以降の新機能)。
その他のtsconfig.json
フィールド(上記リストにないもの)は無視されます。
#*.ts
ファイルにtsx
ローダーを使用することはできません
tsx
ローダーはts
ローダーの上位セットではありません。これらは、部分的に互換性のない2つの異なる構文です。たとえば、文字列<a>1</a>/g
は、ts
ローダーでは<a>(1 < (/a>/g))
として、tsx
ローダーでは(<a>1</a>) / g
として解析されます。
これによって最もよく発生する問題は、tsx
ローダーで<T>() => {}
などのアロー関数式でジェネリック型パラメータを使用できないことです。これは意図的なものであり、公式のTypeScriptコンパイラの動作と一致します。tsx
構文におけるそのスペースは、JSX要素のために予約されています。
#JSX
ローダー:jsx
またはtsx
JSXは、Reactのために作成された、JavaScriptのXMLのような構文拡張です。ビルドツールによって通常のJavaScriptに変換されることを目的としています。各XML要素は、通常のJavaScript関数呼び出しになります。たとえば、次のJSXコードは
import Button from './button'
let button = <Button>Click me</Button>
render(button)
次のJavaScriptコードに変換されます
import Button from "./button";
let button = React.createElement(Button, null, "Click me");
render(button);
このローダーは、.jsx
ファイルと.tsx
ファイルに対してデフォルトで有効になっています。.js
ファイルでは、デフォルトでJSX構文は有効になっていないことに注意してください。有効にしたい場合は、設定する必要があります。
esbuild app.js --bundle --loader:.js=jsx
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.js': 'jsx' },
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{
".js": api.LoaderJSX,
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#JSXの自動インポート
JSX構文を使用するには、通常、使用しているJSXライブラリを手動でインポートする必要があります。たとえば、Reactを使用している場合、デフォルトでは次のように各JSXファイルにReactをインポートする必要があります。
import * as React from 'react'
render(<div/>)
これは、JSX変換によってJSX構文がReact.
への呼び出しに変換されるためですが、それ自体では何もインポートしないため、React
変数は自動的に存在しません。
各ファイルにJSXライブラリを手動でimport
するのを避けたい場合は、esbuildのJSX変換をautomatic
に設定することで、インポート文を自動生成できます。ただし、これによりJSX変換の動作が完全に変わるため、React以外のJSXライブラリを使用している場合、コードが壊れる可能性があります。設定方法は次のとおりです。
esbuild app.jsx --jsx=automatic
require('esbuild').buildSync({
entryPoints: ['app.jsx'],
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"},
JSX: api.JSXAutomatic,
Outfile: "out.js",
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#Reactを使わないJSXの使用
React以外のライブラリ(Preactなど)でJSXを使用している場合は、デフォルトでそれぞれReact
とReact
になっているJSXファクトリとJSXフラグメントの設定を構成する必要があります。
esbuild app.jsx --jsx-factory=h --jsx-fragment=Fragment
require('esbuild').buildSync({
entryPoints: ['app.jsx'],
jsxFactory: 'h',
jsxFragment: 'Fragment',
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"},
JSXFactory: "h",
JSXFragment: "Fragment",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
あるいは、TypeScriptを使用している場合は、これをtsconfig.json
ファイルに追加するだけでTypeScriptのJSXを構成でき、esbuildは設定なしで自動的に認識します。
{
"compilerOptions": {
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
上記で説明した自動インポートを使用しない限り、JSX構文を含むファイルにimport
を追加する必要があります。
#JSON
json
このローダーは、.json
ファイルに対してデフォルトで有効になっています。ビルド時にJSONファイルをJavaScriptオブジェクトに解析し、そのオブジェクトをデフォルトエクスポートとしてエクスポートします。使用方法は次のようになります。
import object from './example.json'
console.log(object)
デフォルトエクスポートに加えて、JSONオブジェクトの各最上位プロパティの名前付きエクスポートもあります。名前付きエクスポートを直接インポートすると、esbuildは使用されていないJSONファイルの部分をバンドルから自動的に削除し、実際に使用した名前付きエクスポートのみを残すことができます。たとえば、このコードはバンドル時にversion
フィールドのみを含みます。
import { version } from './package.json'
console.log(version)
#CSS
ローダー: css
(CSSモジュールの場合はglobal-css
とlocal-css
も)
css
ローダーは.css
ファイルに対して、local-css
ローダーは.module.css
ファイルに対してデフォルトで有効になっています。これらのローダーは、ファイルをCSS構文として読み込みます。CSSはesbuildにおける第一級のコンテンツタイプであるため、esbuildはJavaScriptコードからCSSをインポートする必要なく、バンドルできます。
esbuild --bundle app.css --outfile=out.css
require('esbuild').buildSync({
entryPoints: ['app.css'],
bundle: true,
outfile: 'out.css',
})
package main
import "github.com/evanw/esbuild/pkg/api"
import "os"
func main() {
result := api.Build(api.BuildOptions{
EntryPoints: []string{"app.css"},
Bundle: true,
Outfile: "out.css",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
他のCSSファイルを@import
したり、url()
で画像やフォントファイルを参照したりできます。esbuildはすべてをまとめてバンドルします。ただし、esbuildには事前に設定されたものがないため、画像やフォントファイルのローダーを設定する必要があります。通常は、data URLローダーまたは外部ファイルローダーのいずれかです。
これらの構文機能は、構成された言語targetに応じて、古いブラウザに対して条件付きで変換されます。
構文変換 | 例 |
---|---|
ネストされた宣言 | a { &:hover { color: red } } |
最新のRGB/HSL構文 | #F008 |
inset shorthand |
inset: 0 |
hwb() |
hwb(120 30% 50%) |
lab() とlch() |
lab(60 -5 58) |
oklab() とoklch() |
oklab(0.5 -0.1 0.1) |
color() |
color(display-p3 1 0 0) |
2つの位置を持つカラーストップ | linear-gradient(red 2% 4%, blue) |
グラデーションの遷移ヒント | linear-gradient(red, 20%, blue) 1 |
グラデーションのカラースペース | linear-gradient(in hsl, red, blue) 1 |
グラデーションのヒューモード | linear-gradient(in hsl longer hue, red, blue) 1 |
デフォルトでは、esbuildの出力は最新のCSS機能を利用します。たとえば、color:
は、minifyが有効になっているとcolor:
になります。これはCSS Color Module Level 4の構文を使用しています。これが望ましくない場合は、esbuildのtarget設定を指定して、出力で正しく動作する必要があるブラウザを指定する必要があります。そうすると、esbuildはそれらのブラウザでは古すぎるCSS機能の使用を避けられます。
target設定を使用してブラウザのバージョンのリストを提供すると、esbuildはベンダープレフィックスを自動的に挿入して、CSSがそれらのブラウザのそれらのバージョン以降で動作するようにします。現在、esbuildは次のCSSプロパティに対してこれを行います。
appearance
backdrop-filter
background-clip: text
box-decoration-break
clip-path
font-kerning
hyphens
initial-letter
mask-composite
mask-image
mask-origin
mask-position
mask-repeat
mask-size
position: sticky
print-color-adjust
tab-size
text-decoration-color
text-decoration-line
text-decoration-skip
text-emphasis-color
text-emphasis-position
text-emphasis-style
text-orientation
text-size-adjust
user-select
#JavaScriptからのインポート
JavaScriptからCSSをインポートすることもできます。これを行うと、esbuildは特定のエントリーポイントから参照されているすべてのCSSファイルを収集し、そのJavaScriptエントリーポイントに対応するJavaScript出力ファイルの隣に、対応するCSS出力ファイルにバンドルします。そのため、esbuildがapp.js
を生成する場合、app.js
から参照されているすべてのCSSファイルを含むapp.css
も生成します。JavaScriptからCSSファイルをインポートする例を次に示します。
import './button.css'
export let Button = ({ text }) =>
<div className="button">{text}</div>
esbuildによって生成されたバンドルされたJavaScriptは、生成されたCSSをHTMLページに自動的にインポートしません。代わりに、生成されたJavaScriptと共に、生成されたCSSをHTMLページに手動でインポートする必要があります。これにより、ブラウザはCSSファイルとJavaScriptファイルを並列でダウンロードできるため、最も効率的な方法です。方法は次のようになります。
<html>
<head>
<link href="app.css" rel="stylesheet">
<script src="app.js"></script>
</head>
</html>
生成された出力名が単純ではない場合(たとえば、entry names設定に[hash]
を追加し、出力ファイル名にコンテンツハッシュが含まれている場合)、metafileで生成された出力名を確認する必要があるでしょう。これを行うには、まずentryPoint
プロパティと一致する出力を検索してJSファイルを見つけます。このファイルは<script>
タグに入ります。関連付けられたCSSファイルは、cssBundle
プロパティを使用して見つけることができます。このファイルは<link>
タグに入ります。
#CSSモジュール
CSSモジュールは、意図しないCSS名の衝突を回避するためのCSSプリプロセッサ技術です。CSSクラス名は通常グローバルですが、CSSモジュールは、代わりにCSSクラス名をファイル内に限定する方法を提供します。2つの別々のCSSファイルが同じローカルクラス名.button
を使用する場合、esbuildはそれらのいずれか1つを自動的に名前変更して、衝突しないようにします。これは、esbuildが名前の衝突を避けるために、別々のJSモジュールで同じ名前のローカル変数を自動的に名前変更する方法に似ています。
esbuildにはCSSモジュールでのバンドリングのサポートがあります。これを使用するには、バンドルを有効にし、CSSファイルにlocal-css
ローダーを使用する(たとえば、.module.css
ファイル拡張子を使用する)必要があり、次にCSSモジュールコードをJSファイルにインポートします。そのファイル内の各ローカルCSS名は、esbuildによって名前変更された名前を取得するためにJSにインポートできます。例を次に示します。
// app.js
import { outerShell } from './app.module.css'
const div = document.createElement('div')
div.className = outerShell
document.body.appendChild(div)
/* app.module.css */
.outerShell {
position: absolute;
inset: 0;
}
これをesbuild app.js
でバンドルすると、次のようになります(ローカルCSS名outerShell
がどのように名前変更されているかに注意してください)。
// out/app.js
(() => {
// app.module.css
var outerShell = "app_outerShell";
// app.js
var div = document.createElement("div");
div.className = outerShell;
document.body.appendChild(div);
})();
/* out/app.css */
.app_outerShell {
position: absolute;
inset: 0;
}
この機能は、コードが名前変更されたローカル名をimport
して使用しなければならないため、そしてesbuildが衝突を回避するために競合するローカル名を正常に名前変更できるように、すべてのローカル名を含むCSSファイルを単一のバンドル操作で処理できる必要があるため、バンドルが有効になっている場合にのみ意味があります。
esbuildがローカルCSS名に対して生成する名前は実装の詳細であり、どこにもハードコードされることを意図していません。JSまたはHTMLでローカルCSS名を参照する唯一の方法は、上記のようにesbuildでバンドルされたJSでのインポートステートメントを使用することです。minifyが有効になっている場合、esbuildは可能な限り短い名前を生成する名前生成アルゴリズムを使用します(esbuildがJSのローカル識別子をminifyする方法に似ています)。
#グローバル名の使用
local-css
ローダーは、デフォルトでファイル内のすべてのCSS名をローカルにします。ただし、同じファイルでローカル名とグローバル名を混在させたい場合があります。これを行うにはいくつかの方法があります。
- クラス名を
:global(...)
でラップしてグローバルにし、:local(...)
を使用してローカルにすることができます。 :global
を使用して名前をデフォルトでグローバルにし、:local
を使用して名前をデフォルトでローカルにすることができます。global-css
ローダーを使用して、ローカルCSS機能を有効にしたまま、名前をデフォルトでグローバルにすることができます。
いくつかの例を次に示します。
/* * This is a local name with the "local-css" loader * and a global name with the "global-css" loader */ .button { } /* This is a local name with both loaders */ :local(.button) { } /* This is a global name with both loaders */ :global(.button) { } /* "foo" is global and "bar" is local */ :global .foo :local .bar { } /* "foo" is global and "bar" is local */ :global { .foo { :local { .bar {} } } }
#composes
ディレクティブ
CSSモジュール仕様では、composes
ディレクティブも説明されています。これにより、ローカル名を持つクラスセレクターが他のクラスセレクターを参照できます。これは、共通のプロパティのセットを分割して重複を回避するために使用できます。また、from
キーワードを使用すると、他のファイルのローカル名を持つクラスセレクターを参照するためにも使用できます。例を次に示します。
// app.js
import { submit } from './style.css'
const div = document.createElement('div')
div.className = submit
document.body.appendChild(div)
/* style.css */
.button {
composes: pulse from "anim.css";
display: inline-block;
}
.submit {
composes: button;
font-weight: bold;
}
/* anim.css */
@keyframes pulse {
from, to { opacity: 1 }
50% { opacity: 0.5 }
}
.pulse {
animation: 2s ease-in-out infinite pulse;
}
これをesbuild
でバンドルすると、次のようになります。
(() => {
// style.css
var submit = "anim_pulse style_button style_submit";
// app.js
var div = document.createElement("div");
div.className = submit;
document.body.appendChild(div);
})();
/* anim.css */
@keyframes anim_pulse {
from, to {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.anim_pulse {
animation: 2s ease-in-out infinite anim_pulse;
}
/* style.css */
.style_button {
display: inline-block;
}
.style_submit {
font-weight: bold;
}
composes
を使用すると、JavaScriptにインポートされる文字列が、合成されたすべてのローカル名のスペース区切りのリストになることに注意してください。これは、DOM要素のclassName
プロパティに渡すことを目的としています。また、from
を使用してcomposes
を使用すると、他のCSSファイルのローカル名を(間接的に)参照できることに注意してください。
別々のファイルから合成されたCSSクラスがバンドルされた出力ファイルに表示される順序は、設計上意図的に未定義であることに注意してください(詳細は仕様を参照)。2つの別々のクラスセレクターで同じCSSプロパティを宣言してからそれらを合成することは想定されていません。重複しないCSSプロパティを宣言するCSSクラスセレクターのみを合成する必要があります。
#CSSに関する注意点
esbuildでCSSを使用する際には、以下の点に注意してください。
#限定的なCSS検証
CSSには、すべてのCSSプロセッサが使用する一般的な構文仕様と、特定のCSSルールの意味を定義する多くの仕様があります。esbuildは一般的なCSS構文を理解し、いくつかのCSSルールを理解できます(CSSファイルをバンドルしてCSSを合理的にミニファイするのに十分です)。しかし、esbuildはCSSに関する完全な知識を持っていません。これは、esbuildがCSSに対して「ゴミが入ればゴミが出る」という哲学を採用していることを意味します。コンパイルされたCSSにタイプミスがないことを確認するには、esbuildに加えてCSSリンターを使用する必要があります。
#@import
の順序はブラウザと一致
CSSの@import
ルールは、JavaScriptのimport
キーワードとは動作が異なります。JavaScriptでは、import
はほぼ「インポートされたファイルがこのファイルが評価される前に評価されるようにする」という意味ですが、CSSでは、@import
はほぼ「ここでインポートされたファイルを再度評価する」という意味です。例えば、以下のファイルがあるとします。
entry.css
@import "foreground.css";
@import "background.css";foreground.css
@import "reset.css";
body {
color: white;
}background.css
@import "reset.css";
body {
background: black;
}reset.css
body {
color: black;
background: white;
}
JavaScriptの直感から、このコードは最初に背景を白、テキストを黒にリセットし、その後、背景を黒、テキストを白に上書きすると考えるかもしれません。これは起こりません。代わりに、本文は完全に黒になります(前景と背景の両方)。これは、@import
がインポートされたファイルによってインポートルールが置き換えられたかのように動作する(C/C++の#include
のような)ためで、ブラウザは以下のコードを見ることになります。
/* reset.css */
body {
color: black;
background: white;
}
/* foreground.css */
body {
color: white;
}
/* reset.css */
body {
color: black;
background: white;
}
/* background.css */
body {
background: black;
}
最終的にはこれになります。
body { color: black; background: black; }
この動作は残念ですが、esbuildはこのように動作するのは、それがCSSの仕様であり、ブラウザでのCSSの動作方法だからです。これは、postcss-import
などの他の一般的に使用されるCSS処理ツールの中には、JavaScriptの順序ではなくCSSの順序でCSSインポートを正しく解決しないものがあるため、重要です。これらのツール用に記述されたCSSコードをesbuildに移植する場合(またはブラウザでCSSコードをネイティブに実行するように切り替える場合でも)、コードが正しくないインポート順序に依存している場合、外観が変更される可能性があります。
#テキスト
ローダー: text
このローダーは、.txt
ファイルに対してデフォルトで有効になっています。ビルド時にファイルを文字列として読み込み、その文字列をデフォルトエクスポートとしてエクスポートします。使用方法は次のようになります。
import string from './example.txt'
console.log(string)
#バイナリ
ローダー: binary
このローダーは、ビルド時にファイルをバイナリバッファとして読み込み、Base64エンコーディングを使用してバンドルに埋め込みます。ファイルの元のバイトは実行時にBase64からデコードされ、デフォルトエクスポートとしてUint8Array
としてエクスポートされます。使用方法は次のようになります。
import uint8array from './example.data'
console.log(uint8array)
ArrayBuffer
が必要な場合は、uint8array
にアクセスするだけです。このローダーはデフォルトでは有効になっていません。適切なファイル拡張子に対して次のように設定する必要があります。
esbuild app.js --bundle --loader:.data=binary
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.data': 'binary' },
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{
".data": api.LoaderBinary,
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#Base64
ローダー: base64
このローダーは、ビルド時にファイルをバイナリバッファとして読み込み、Base64エンコーディングを使用して文字列としてバンドルに埋め込みます。この文字列は、デフォルトエクスポートを使用してエクスポートされます。使用方法は次のようになります。
import base64string from './example.data'
console.log(base64string)
このローダーはデフォルトでは有効になっていません。適切なファイル拡張子に対して次のように設定する必要があります。
esbuild app.js --bundle --loader:.data=base64
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.data': 'base64' },
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{
".data": api.LoaderBase64,
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
これをUint8Array
またはArrayBuffer
に変換する場合は、代わりにbinary
ローダーを使用する必要があります。これは、通常のatob
変換プロセスよりも高速な最適化されたBase64からバイナリへのコンバーターを使用します。
#データURL
ローダー: dataurl
このローダーは、ビルド時にファイルをバイナリバッファとして読み込み、Base64エンコードされたデータURLとしてバンドルに埋め込みます。この文字列は、デフォルトエクスポートを使用してエクスポートされます。使用方法は次のようになります。
import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)
データURLには、ファイル拡張子と/またはファイルの内容に基づいてMIMEタイプの最適な推測が含まれており、バイナリデータの場合、次のようなものになります。
data:image/png;base64,iVBORw0KGgo=
…またはテキストデータの場合、次のようなものになります。
data:image/svg+xml,<svg></svg>%0A
このローダーはデフォルトでは有効になっていません。適切なファイル拡張子に対して次のように設定する必要があります。
esbuild app.js --bundle --loader:.png=dataurl
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.png': 'dataurl' },
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,
},
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
#外部ファイル
目的の動作に応じて、外部ファイルに使用できる2つの異なるローダーがあります。両方のローダーを以下に説明します。
#file
ローダー
ローダー: file
このローダーは、ファイルをアウトプットディレクトリにコピーし、ファイル名を文字列としてバンドルに埋め込みます。この文字列は、デフォルトエクスポートを使用してエクスポートされます。使用方法は次のようになります。
import url from './example.png'
let image = new Image
image.src = url
document.body.appendChild(image)
この動作は、Webpackのfile-loader
パッケージと意図的に似ています。このローダーはデフォルトでは有効になっていません。適切なファイル拡張子に対して次のように設定する必要があります。
esbuild app.js --bundle --loader:.png=file --outdir=out
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.png': 'file' },
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",
Write: true,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
デフォルトでは、エクスポートされた文字列はファイル名だけです。エクスポートされた文字列にベースパスをプリペンドする場合は、公開パスAPIオプションを使用できます。
#copy
ローダー
ローダー: copy
このローダーはファイルをアウトプットディレクトリにコピーし、コピーされたファイルを指すようにインポートパスを書き換えます。つまり、最終的なバンドルにはインポートは依然として存在し、最終的なバンドルはファイルを含める代わりにファイルを依然として参照します。これは、esbuildの出力に追加のバンドルツールを実行する場合、まれに使用されるデータファイルをバンドルから省略して起動パフォーマンスを向上させる場合、またはインポートによってトリガーされるランタイムの特定の動作に依存する場合に役立つ可能性があります。例えば
import json from './example.json' assert { type: 'json' }
console.log(json)
次のコマンドで上記のコードをバンドルする場合
esbuild app.js --bundle --loader:.json=copy --outdir=out --format=esm
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.json': 'copy' },
outdir: 'out',
format: 'esm',
})
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{
".json": api.LoaderCopy,
},
Outdir: "out",
Write: true,
Format: api.FormatESModule,
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
結果のout/app.js
ファイルは次のようになります。
// app.js
import json from "./example-PVCBWCM4.json" assert { type: "json" };
console.log(json);
インポートパスがコピーされたファイルout/example-PVCBWCM4.json
を指すように書き換えられ(アセット名設定のデフォルト値によりコンテンツハッシュが追加されています)、JSONのインポートアサーションが保持されているため、ランタイムがJSONファイルをロードできることに注意してください。
#空ファイル
ローダー: empty
このローダーは、esbuildにファイルを空であると見なすように指示します。特定の状況でバンドルからコンテンツを削除するのに役立つ方法です。たとえば、.css
ファイルをempty
でロードするように設定して、JavaScriptファイルにインポートされたCSSファイルをesbuildがバンドルするのを防ぐことができます。
esbuild app.js --bundle --loader:.css=empty
require('esbuild').buildSync({
entryPoints: ['app.js'],
bundle: true,
loader: { '.css': 'empty' },
})
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{
".css": api.LoaderEmpty,
},
})
if len(result.Errors) > 0 {
os.Exit(1)
}
}
このローダーを使用すると、CSSファイルからインポートされたアセットを削除することもできます。たとえば、.png
ファイルをempty
でロードするように設定すると、url(image.png)
などのCSSコード内の.png
ファイルへの参照がurl()
に置き換えられます。