🛠️

JavaScriptビルドツールの整理 各ツールの機能と依存関係

2023/10/26に公開

フロントエンドのビルドツールが色々ありすぎて、何がどうなっているのかがわかりづらいため、

  • 各ツールができること、特徴
  • ツール間がどのように依存しあっているか

を一気に調べて整理した。(情報は2023/10時点)

概要

ツールの依存関係整理

  • 上層: dev server付きのバンドラ/ビルドツール。アプリ開発者が直接configなどを書いて取り扱うのはここが多いと思われる。(Next.jsに関しては、ビルド機能に着目した場合)
  • 下層: やや基盤的なdev serverなしのツール群。

矢印は、明示的な依存関係を表す。実際には、明示的な依存関係がなくても、下層のツール群は上層のバンドラ(やRollup)に対してプラグインを提供していることが多い。

各ツールのできること整理

ツールごとに、大まかな機能区分で、できることとできないことをまとめた。
各機能区分の定義は次セクションを参照。

ツールごとの詳細

前提

JSのビルドでは、下記のような処理が行われる。

  • トランスパイル
    • TypeScriptやJSXといった非JSの言語をJSに変換したり、新しいJSの文法を古いブラウザでも動くように変換したりする作業
  • モジュール結合、tree shaking、コード分割
    • バラバラになっている各モジュールのファイルを結合して、一つのJSファイルにする。また、その過程で、未使用のモジュールなどを取り除く(tree shaking)。さらに、必要に応じて、適切な分割単位でJSファイルを分割する
  • ミニファイ
    • 変数名の短縮やスペースの削除により、できるだけファイルサイズを小さくする

また、開発時には、コードを変更したら即座にブラウザに変更が反映される、ホットリロード付きの開発サーバを使うことが多い。

ツールによって、この中のどの処理ができて、どれができない、がある。また、処理を他のツールに投げているものも多い。さらに、処理のパフォーマンスなどにツールごとに特徴がある。これを調べていく。

Vite

0. ツールのコンセプト

開発サーバ付きで、production buildもできる。
開発サーバではESMをバンドルせずにそのままserveすることで、高速なhot module replacement(=ページのリロードなしにモジュールを動的に交換する)を実現する。
productionではバンドルを生成する。なぜならバンドルしないと、nestされたimportのときに余計なnetwork round tripが生じて非効率だから。
https://vitejs.dev/guide/why.html

1. トランスパイル

  • .tsファイルのトランスパイルをout of the boxでサポートしている。
    • esbuildをトランスパイルに使っている。(多分dev/prod両方)
    • ただしtype checkは行わない。(viteの責務は、開発環境ではtsをブラウザで動くように最速で変換することであるため)。production buildではtsc --noEmitでチェックすることが推奨
  • .jsx/.tsxのトランスパイルもout of the boxでサポート。
    • esbuildをトランスパイルに使っている。(多分dev/prod両方)
  • 古いブラウザに対応するためのjsのトランスパイルもesbuildがout of the boxで行う

https://vitejs.dev/guide/features.html#typescript
https://vitejs.dev/guide/features.html#jsx
https://vitejs.dev/config/build-options.html#build-target

2. モジュール結合、tree shaking、コード分割

  • dev serverではconcatenationは行わない
  • prodでは、rollupを使ってconcatenationを行っている
    • esbuildの方が速いが、rollupの柔軟なpluginシステムの良さなどからrollupを採用
    • 将来的にはesbuildを使うかも
  • tree-shakingもrollupで行う
  • code splitting
    • cssを自動で分割してくれる
    • dynamic importしている場合、バンドルを分割する。そのとき、いい感じに共通モジュールをchunkにして二重ロードを防いでくれる

https://vitejs.dev/guide/why.html#why-not-bundle-with-esbuild
https://vitejs.dev/guide/features.html#build-optimizations

3. ミニファイ

  • デフォルトだとjsはesbuildでminifyする。terserも選択可能
    • esbuildの方がterserより数十倍速いが、1~2%圧縮率が悪い
  • cssもesbuildでminifyする。lightningcssも選択可能

https://vitejs.dev/config/build-options.html#build-minify
https://vitejs.dev/config/build-options.html#build-cssminify

4. 開発サーバ

  • 前述の通り、開発環境ではESMをバンドルせずにtranspileしてそのままserveすることで、高速なhot module replacementを実現する。

Parcel

0. ツールのコンセプト

zero configのビルドツールをうたっており、out of the boxでいい感じにビルドできるのが特徴。
jsだけでなく、html, css, 画像、フォント、映像なども含めてビルドする。
dev server付き。
ビルドはSWCベース、マルチコア利用、キャッシュの利用で高速化されている。
https://parceljs.org/

1. トランスパイル

  • SWCベースのトランスパイラがデフォルト
  • tscやbabelも一応選択できる
  • トランスパイルのtargetも柔軟に設定可能

https://parceljs.org/#performance
https://parceljs.org/languages/typescript/#transpilation
https://parceljs.org/features/targets/

2. モジュール結合、tree shaking、コード分割

  • tree-shaking可能
  • dynamic importしてる場合のcode splittingをいい感じにしてくれる

https://parceljs.org/features/scope-hoisting/
https://parceljs.org/features/code-splitting/

3. ミニファイ

  • jsはSWC、cssはlightningss、htmlはhtmlnano、svgはsvgoでminifyする。
  • 画像のリサイズ、フォーマット変換によるサイズ削減、gzip/brotli圧縮などもout of the boxでサポート

https://parceljs.org/features/production/#minification

4. 開発サーバ

  • dev serverが付属している。
  • デフォルトでは、ファイル変更のたびにページ自体をリロードするが、hot module replacementもできる。(viteのようにESMをネイティブにロードするのではない)

https://parceljs.org/features/development
https://vitejs.dev/guide/why.html#the-problems

webpack

0. ツールのコンセプト

モダンJSアプリのためのstatic module bundler。バージョン4.0.0から、configなしでもバンドルできるようになった。ただし、鬼のようなカスタマイズも可能。

なお、コアとなる用語は下記。

  • entry: エントリポイント。モジュールのdependency graphの計算の出発点となる
  • output: 出力。
  • loaders: webpackはデフォルトでJSとJSONを理解する。loaderは、.cssファイルその他の形式のファイルを検知し、有効なjavascript moduleに変換し、それをdependency graphに加える。
  • plugins: loaderが、特定のタイプのモジュールの変換を担うのに対し、pluginはさらに広いタスク(e.g., バンドル最適化、環境変数の注入、アセットの管理)を行う

https://webpack.js.org/concepts/

1. トランスパイル

  • 各種loaderにより、トランスパイルが行われる。
  • babel, esbuild, swcなど、各種トランスパイラがwebpack向けのloaderを提供している。

https://webpack.js.org/loaders/#transpiling
https://swc.rs/docs/usage/swc-loader

2. モジュール結合、tree shaking、コード分割

  • tree-shaking可能。webpack 2以降、ESMとtree shakingのビルトインサポートが行われるようになった。
  • いい感じのcode splittingもしてくれる。webpack 4.6.0以降、特定の形式のimport文を書くとprefetchをいい感じにしてくれる仕組みもできた

https://webpack.js.org/guides/tree-shaking/#root
https://webpack.js.org/guides/code-splitting/

3. ミニファイ

  • webpack 4以降、terserによってデフォルトでminifyされるようになった。

https://webpack.js.org/guides/production/#minification

4. 開発サーバ

  • webpack-dev-serverというdev serverが付属している。大体の場合、これを使う。HMRもある。
  • また、watch modeでビルドするだけというオプションや、webpackのビルド結果をserverに吐き出すwebpack-dev-middlewareというオプションもある。

https://webpack.js.org/guides/development
https://webpack.js.org/guides/hot-module-replacement/

Rspack

0. ツールのコンセプト

Rustベースのwebバンドラー。特徴は下記

  • dev serverが高速に立ち上がり、incremental compilationによりHMRも高速。production buildも高速。
  • webpackと互換性があり、既存のwebpackのプラグインやconfigがそのまま使える
  • ts, jsx, css, css modules, sassなどが読める
  • tree shaking, minification, code splittingなどがプラグインなしで使える。
  • さまざまなfrontend frameworkで使える

2023/3にOSSとしてlaunchされたばかりなので新しい

https://www.rspack.dev/
https://www.rspack.dev/guide/introduction

1. トランスパイル

  • ts, jsx, css, json, 画像などのassetは、webpackと異なり、babel-loaderなどのloaderを使わなくてもデフォルトでトランスパイルできる。ts -> jsへのトランスパイルは、デフォルトではSWCによって行われる。

https://www.rspack.dev/guide/language-support.html
https://www.rspack.dev/guide/asset-module.html

2. モジュール結合、tree shaking、コード分割

  • tree shaking可能。
  • 複数エントリポイント、dynamic importを考慮したcode splittingができる。

https://www.rspack.dev/guide/tree-shaking.html
https://www.rspack.dev/guide/code-splitting.html

3. ミニファイ

  • ビルトインのminifierがついている。configでterserなど他のminifierも使える

https://www.rspack.dev/guide/production.html

4. 開発サーバ

  • 高速HMRを備えたdev serverが特徴。

https://www.rspack.dev/guide/dev-server.html

Turbopack

0. ツールのコンセプト

Rustベースの高速なビルドツール。esbuildやswcがある中で、それらを使わずに新しく作った。
2023/10時点ではベータで、Next.js v13のdev serverでのみ使え、production buildもできない。

ViteはESMネイティブなバンドルしないdev serverを推しているが、Turbopackはあえてバンドルした状態でdev serverにserveさせている。それは、モジュール数が増えたときに、逆にVite方式だと起動が遅くなるから。
"incremental computation"をコンセプトにしている。並列処理を行うのに加えて、一度行った処理をキャッシュしておくことで不要な計算を防いでいる。また、dev serverでは、今見ているページに必要なものだけバンドルすることで、さらに高速化を行なっている。
esbuildは、キャッシュをそれほどしないし、dev serverで必要なHMRを持ってないし、部分的なバンドル生成もできないので、不十分。

https://turbo.build/pack
https://turbo.build/pack/docs/why-turbopack
https://turbo.build/pack/docs
https://turbo.build/pack/docs/roadmap#nextjs

1. トランスパイル

  • SWCを使ってjs, ts, jsx, cssのトランスパイルを行う。(babelプラグインは現状無い)

https://turbo.build/pack/docs/features/javascript
https://turbo.build/pack/docs/features/typescript
https://turbo.build/pack/docs/features/frameworks
https://turbo.build/pack/docs/features/css

2. モジュール結合、tree shaking、コード分割

  • dev serverではバンドルしているが、production buildはできない

3. ミニファイ

  • production buildできない(なし?)

4. 開発サーバ

  • 前述の通り、高速HMRを備えたdev serverが特徴。

https://turbo.build/pack/docs/features/dev-server

Next.js

0. ツールのコンセプト

Reactベースのフルスタックフレームワーク。

https://nextjs.org/

1. トランスパイル

  • SWCを使っている。
  • デフォルトではモダンブラウザ想定のトランスパイルを行う

https://nextjs.org/docs/architecture/nextjs-compiler
https://nextjs.org/docs/architecture/supported-browsers

2. モジュール結合、tree shaking、コード分割

  • これもSWCで行う

https://nextjs.org/docs/architecture/nextjs-compiler

3. ミニファイ

  • これもSWCで行う

https://nextjs.org/docs/architecture/nextjs-compiler

4. 開発サーバ

  • fast refresh付きのdev serverがある。
  • next dev --turboコマンドで、Turbopackを使うように変更することも可能

https://nextjs.org/docs/architecture/fast-refresh
https://nextjs.org/docs/architecture/turbopack

Rollup

0. ツールのコンセプト

細切れのjsファイルを一つにまとめるモジュールバンドラ。開発時はESMで書き、バンドルしてCJS、AMD、IIFEなどさまざまな形式にできる。
ESMの使用によりtree shakingもできる。
https://rollupjs.org/
https://rollupjs.org/introduction/

1. トランスパイル

  • 基本的にbabelその他プラグインで行う

https://rollupjs.org/tutorial/#using-plugins

2. モジュール結合、tree shaking、コード分割

  • tree-shaking可能
  • dynamic importしてる場合のcode splittingをいい感じにしてくれる

https://rollupjs.org/
https://rollupjs.org/tutorial/#code-splitting

3. ミニファイ

  • 基本的にterserその他のプラグインで行う

https://rollupjs.org/tutorial/#using-output-plugins

4. 開発サーバ

  • serverは無いが、watchモードはある

https://rollupjs.org/command-line-interface/#w-watch

esbuild

0. ツールのコンセプト

高速なビルドツール。Goで書かれており、ネイティブコードとして実行され、マルチコア環境での並列処理、メモリの最適化などさまざまなパフォーマンス上の工夫がされている。

おもな機能は下記

  • JS, CSS TS, JSXのトランスパイル
  • ESMおよびCJSのバンドリング。CSS modulesを含むCSSもバンドルできる。
  • tree shaking, minify, source map生成
  • dev server, watch mode, 各種プラグイン

https://esbuild.github.io/
https://esbuild.github.io/faq/#why-is-esbuild-fast

1. トランスパイル

  • jsx, ts, css, json, ファイル(e.g., png) などさまざまなタイプのファイルを処理できる。
  • それぞれのファイルタイプには、紐づく"loader"というものがある。デフォルトで組み込まれているloader (e.g., js, ts, jsx, tsx)の他、カスタムでloaderをいれることもできる。
  • ES6+のjsをES5に変換するのがサポートされてないので注意。
  • esbuild-wasmにより、wasmを使ったブラウザ内でのトランスパイル、バンドルも可能

https://esbuild.github.io/content-types/
https://esbuild.github.io/api/#browser

2. モジュール結合、tree shaking、コード分割

  • esbuild app.ts --bundleなどとコマンドを打つとバンドル生成してくれる
  • tree shaking可能
  • code splittingは、現在開発中だが一応使える

https://esbuild.github.io/api/#build
https://esbuild.github.io/api/#tree-shaking
https://esbuild.github.io/api/#splitting

3. ミニファイ

  • minifyのオプションもある。
  • 出力サイズは、industry-standardに比べると数%劣ることはあるが、かなり近い

https://esbuild.github.io/api/#minify

4. 開発サーバ

  • watchモードで、ファイル変更を検知してバンドル生成し直したり、serveモードでローカルに簡単なdevサーバを立てたりできる。
  • ただし、devサーバでlive reloadを直接行うAPIは用意されておらず、watchモードでのビルドとdevサーバと、自動リロード用のjsスクリプトを組み合わせることになる。

https://esbuild.github.io/api/#watch
https://esbuild.github.io/api/#serve
https://esbuild.github.io/api/#live-reload

SWC

0. ツールのコンセプト

Rustベースの、拡張可能な次世代開発ツール。Next.js, Parcel, Denoなどで使われている。
SWCは、トランスパイルおよびバンドリングの両方で使うことができる。
トランスパイルにおいて、SWCはシングルスレッドでBabelの20倍速く、4コアで70倍速い。

おもな機能は下記

  • トランスパイル
    • wasmを使った、ブラウザ内でのトランスパイルもサポート
    • webpackのプラグインとしてトランスパイルもできる(swc-loader)
  • バンドリング (swcpack, 開発中)
  • ミニファイ
  • 高速なjestランナー (@swc/jest)
  • カスタムプラグイン

https://swc.rs/

1. トランスパイル

  • @swc/coreというパッケージで、コードのトランスパイルができる(swc.transform)。また、ASTとして出力することも可能(swc.parse)。
  • tsやjsxのトランスパイルが可能
  • @swc/wasm-webというパッケージを使うと、ブラウザ内でwasmを使ってトランスパイルできる!

https://swc.rs/docs/usage/core
https://swc.rs/blog/swc-1#what-can-swc-do
https://swc.rs/docs/usage/wasm

2. モジュール結合、tree shaking、コード分割

  • まだ開発中であり、またmain authorがTurbopackを作っていることもあり、今後活発に開発されるものではない
  • 複数のモジュールを一つのファイルにできる。tree shakingもサポート。CJSのモジュールのインポートも扱える。

https://swc.rs/docs/usage/bundling

3. ミニファイ

  • @swc/coreというパッケージでminifyできる(swc.minify)

https://swc.rs/docs/usage/core#minify

4. 開発サーバ

  • なし

Bun.build

0. ツールのコンセプト

js/tsのランタイム、all-in-one toolkitであるbunが提供するバンドラ。
高速で、esbuildに比肩する(もしくは速い)スピードを持つ。esbuildと違い、watch modeやdev serverはついていない。(これらをやりたかったら、bunの他の機能と組み合わせる)

https://bun.sh/docs/bundler
https://bun.sh/docs/bundler/vs-esbuild

1. トランスパイル

  • ts, jsx, css, json, 画像などのassetは、ビルトインのloaderでトランスパイルできる。
  • また、プラグインシステムがあり、いろいろなloaderを追加できる。
  • 古いECMAScriptへの変換処理は現状ビルトインではサポートされていない。

https://bun.sh/docs/bundler#content-types

2. モジュール結合、tree shaking、コード分割

  • tree shaking可能。code splittingもできる。

https://bun.sh/docs/bundler#content-types
https://bun.sh/docs/bundler#splitting

3. ミニファイ

  • ビルトインのminifierがついている。

https://bun.sh/docs/bundler#minify

4. 開発サーバ

  • なし

Babel

0. ツールのコンセプト

メイン機能は、ES2015以降で書かれたJSを後方互換性のあるJSにすること。
また、プラグインを使うことで、まだブラウザサポートが完全にされてないような新しい機能を後方互換のJSに変換することもできる。
また、JSXをトランスパイルすることもできるし、TypeScriptのコードからtype annotationを落として素のJSにすることもできる

https://babeljs.io/docs

1. トランスパイル

  • 上述の通り

2. モジュール結合、tree shaking、コード分割

  • なし

3. ミニファイ

  • なし

4. 開発サーバ

  • なし

Terser

0. ツールのコンセプト

JSをminifyするツール。
uglify-esがメンテされなくなり、ES6+をサポートしていないことを受け、uglify-esをforkして作られた。

https://terser.org/
https://github.com/terser/terser

1. トランスパイル

  • なし

2. モジュール結合、tree shaking、コード分割

  • なし

3. ミニファイ

  • 上述の通り

4. 開発サーバ

  • なし

Discussion