👻
TypeScriptのビルドをesbuildで置き換えてみる
はじめに
簡単なサンプルプロジェクトを作ったのでこちらに置いておきます:
本記事ではnode環境でもともと tsc
コマンドでビルドを行なっていた環境から esbuild
を使った環境への移行をしたい場合のガイドを書きます。
インストール
npm install -D esbuild
注意点など
TypeScript の項を読んでみるといくつか注意点。
- TSサポート(
.ts
.tsx
)はデフォルトで入っているので、特別必要なことはない - ただし、babelと同じくトランスパイルはしてくれるが、型チェックはしてくれない
-
tsc --noEmit
を別で走らせてチェックはした方がよい
-
- TSにしか存在しないシンタックスもちゃんと動作する
- tsconfig.jsonで
isolatedModules
を有効にする必要がある- ファイルごとに並列でビルドするので、ビルド時にimportされたものが型なのか値なのか判別できないらしい
- tsconfig.jsonで
esModuleInterop
を有効にする必要がある- tscがデフォルトでesmをcommonjsに変換するため
- 以下は サポートされない
-
emitDecoratorMetadata
- TSの型情報に依存した出力が必要なため
-
const enum
- コンパイル時に普通の
enum
に変換される
- コンパイル時に普通の
-
tsconfig.jsonについて
esbuild実行時に tsconfig.json
を探して、
実行時のパスからディレクトリを上に探索する。
ただし、esbuildは tsconfig.json
のうち次のプロパティしか参照しない
baseurl
extends
importsNotUsedAsValues
jsxFactory
jsxFragmentFactory
paths
useDefineForClassFields
ほかのプロパティは全て無視される。
tsconfig.json
の target
オプションも無視されるが、
esbuild自体の方の target
オプションで指定することができる。
ビルドする
注意点
entryPointsに ./src/*
のようなglobパターンを渡すことはできないらしい。
> error: Could not resolve "./src/*.ts" (glob syntax must be expanded first before passing the paths to esbuild)
こういうエラーが出る。
経緯
この辺のissue で議論があり、globの展開はshellが行なっているので、shell-agnosticにesbuildを保つため、globの展開は自分でやれ とのこと。
スクリプト
./scripts/build.js
const { build } = require('esbuild')
const glob = require('glob')
const entryPoints = glob.sync('./src/**/*.ts')// 適宜読み替えてください
build({
entryPoints,
outbase: './src', // outbaseを指定することで指定したディレクトリの構造が出力先ディレクトリに反映されるようになる,
outdir: './lib', // 出力先ディレクトリ
platform: 'node', // 'node' 'browser' 'neutral' のいずれかを指定,
external: [], // バンドルに含めたくないライブラリがある場合は、パッケージ名を文字列で列挙する,
watch: false // trueにすれば、ファイルを監視して自動で再ビルドしてくれるようになる
})
package.json
- "build": 'tsc'
+ "build": 'node ./scripts/build.js',
これでビルドできるようになる。
トランスパイル速度
サンプルプロジェクトを筆者の手元でトランスパイルした結果
-
tsc
: 3.34s -
esbuild
: 0.35s
約9.5倍↑
おまけ
こういうプラグインがあった。
globでのimportを自動でやってくれそう?
Discussion