⤴️
ts-node の代わりに esbuild-register を使ってスピードアップ
Node.js で TypeScript をトランスパイルしながら実行できる、 esbuild-register というパッケージがあります。
非常に速い esbuild を使いながら雑に TypeScript が実行できちゃう頼もしいパッケージです。
今までのメインプレイヤーであった ts-node よりも速いです。次の小さなスクリプトでも 1.5 倍程度の速度が出ています。
function wait(duration: number) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {resolve(true)}, duration)
  })
}
async function main({duration}: {duration: number}) {
  await wait(duration)
  console.log('finished')
}
main({
  duration: 0
})
ベンチマーク結果が次です。
$ hyperfine --warmup 3 \
    'npm run ts-node index.ts' \
    'npm run ts-node-skip-check index.ts' \
    'npm run esbuild index.ts'
Benchmark #1: npm run ts-node index.ts
  Time (mean ± σ):      1.241 s ±  0.022 s    [User: 1.731 s, System: 0.190 s]
  Range (min … max):    1.214 s …  1.294 s    10 runs
Benchmark #2: npm run ts-node-skip-check index.ts
  Time (mean ± σ):     637.9 ms ±   3.7 ms    [User: 531.9 ms, System: 133.2 ms]
  Range (min … max):   632.2 ms … 643.9 ms    10 runs
Benchmark #3: npm run esbuild index.ts
  Time (mean ± σ):     405.5 ms ±   3.7 ms    [User: 323.2 ms, System: 116.3 ms]
  Range (min … max):   401.2 ms … 411.9 ms    10 runs
Summary
  'npm run esbuild index.ts' ran
    1.57 ± 0.02 times faster than 'npm run ts-node-skip-check index.ts'
    3.06 ± 0.06 times faster than 'npm run ts-node index.ts'
表にまとめてみました。単位は秒です。
| tool | user | system | total | 
|---|---|---|---|
| ts-node | 1.731 | 0.190 | 1.241 | 
| ts-node type check なし | 0.532 | 0.133 | 0.638 | 
| esbuild-register | 0.323 | 0.116 | 0.406 | 
お手元で試したい場合は clone すれば試せるものを用意したので esbuild の速さを体感してみてください。
ts-node を使っているプロジェクトでは切り替えると如実に効率が上がります。ぜひ切り替えてみてください。
esbuild-register の仕組み
伝えたいことは以上ですべてですが、 esbuild-register がどのように動くかという点を書いておきます。
esbuild-register を用いてスクリプトを実行する場合、次のコマンドを叩きます。
node --require esbuild-register /path/to/your/script.ts
Node.js の --require オプションを使っていますね。これは雑に説明すると、指定したモジュールを読み込んでから本筋のスクリプトを実行するためのものです。
esbuild-register はこれを用いて esbuild によるトランスパイル処理を仕込んでいるのですね。
Mocha によるテスト実行時によく使ったオプションですが、見なくなって久しいので最近の方向けに説明を書いてみました。
Discussion
ts-nodeについて、トランスパイルの時間だけではなくtypecheckも含めた時間を比較してしまっているように見えます。tsconfig.jsonにを追加する、または
--transpile-onlyオプションを渡すなどして、ts-nodeもトランスパイルのみにした上で比較するのがより適切だと思いました。コメントでのご指摘、ありがとうございます
--transpile-onlyオプションを知らなかったので、使ったバージョンでの検証結果にアップデートしましたまた、ベンチマークをとるのに hyperfine というコマンドを使うように修正しています
ちなみに、指摘とかではなくただの補足情報というかちょっとした豆知識なんですが、最近リリースされた
v10からswcによるトランスパイルがサポートされています。参考までに👍