Open19

フロントエンドビルドツールの整理、どのツールが何をやっているのか

nakaakistnakaakist

もうビルド関連ツールが色々あって、それらの依存関係が頭の中でぐちゃぐちゃになっているので調べる。

調べた概要

情報は2023/10時点

ツールの依存関係整理

上層が、dev server付きのバンドラ。アプリ開発者が直接configなどを書いて取り扱うのはここが多いと思われる。(Next.jsに関しては、ビルド機能に着目した場合)
下層が、やや基盤的なdev serverなしのツール群。矢印は、明示的な依存関係を表す。
実際には、明示的な依存関係がなくても、下層のツール群は上層のバンドラ(やRollup)に対してプラグインを提供していることが多い。

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

nakaakistnakaakist

用語

  • minification: コードのサイズを最小化する
  • concatenation: モジュールを一つのファイルにする
  • bundle: concatenateされ、minifyされて一つになったjsファイル。(場合によってはこの後code-splittingによってさらに最適化される)
  • transpilation: 新しいjsの文法(やts, jsxなど)を、多くのブラウザで動くjsに変換
  • module: 他のコードからimportされるexportを持った一つのファイル
nakaakistnakaakist

歴史

  1. 最初は全部scriptタグを直書きしてた。global namespaceに色々詰め込む。スクリプト同士の依存関係も暗黙
  2. Node.jsのCommonJSが出てきて、Nodeではrequireとかによりコードをモジュール化できるように
  3. CommonJSはブラウザでは問題だった。なぜなら依存関係がネットワーク越しにロードされる場合があるから。そこで、AMD (Asynchronous Module Definition)という仕様が生まれた。RequireJSというライブラリを読み込むとブラウザでAMDが使えるようになり、モジュールっぽいことができるようになる。
  4. CJSとAMDのギャップを埋めるためにUMDという形式が生まれた。UMDは、CJSとAMD、どちらが動作する環境かをチェックし、適切な方で実行する。(UMD形式にjsを変換するのは通常ビルドツールがおこなう)
  5. その後ESMが生まれた。これはCJS, AMDの色々な問題を解決する。ESMでは、コード評価前に静的解析でimport/exportのモジュールの依存グラフを作る(CJSのconst xxx = require(yyy)は、あくまで動的に実行される文なので、静的解析に向かない)。これにより、tree-shakingができるようになった。
  6. Node.jsも12からESMをサポートするようになった。
nakaakistnakaakist

トランスパイル

  • tsのコンパイル
    • babelでもできるし、tscでも他のトランスパイラでもできる。(declaration file (d.ts)を作りたい場合は、tscで基本的に生成する)
  • ブラウザの後方互換性の確保
    • babelなら、targets: '> 0.25%, not dead' みたいな感じでの指定もできる。(これはbabelのdependencyであるbrowserslistのクエリ)
nakaakistnakaakist

バンドル

  • CJSのアプリ/ライブラリ(=serverでの使用を想定)は、当然バンドルする必要ない。必要に応じてtranspileだけすれば良い。
    • バンドル(=concatenate + minify)は、network越しのダウンロードを最適化するための行為
nakaakistnakaakist

調べるツール

記事に出てきたもの + 気になるもの

  • vite
  • rollup
  • parcel
  • webpack
  • babel
  • terser
  • swc
  • esbuild
  • turbopack
  • rspack
  • bun (ビルド部分)
  • next.js (ビルド部分)

ビルド対象も多岐に渡るともう手に負えないので、React + TypeScriptをビルドする前提で進める。
また、下記観点でまとめる

  1. ツールのコンセプト
  2. transpilation
    • tsx/ts -> jsへの変換、古い仕様のjsへの変換
  3. concatenation/tree shaking/code splitting
    • モジュールファイルを一つにまとめる
    • tree shakngによる不要モジュールのスキップ
    • code splittingによる最適化
  4. minification
    • jsの軽量化
    • 画像最適化(サイズなど)
  5. dev server
    • hot reloadつきサーバ
nakaakistnakaakist

Vite

0. ツールのコンセプト

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

1. transpilation

  • .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. concatenation/tree shaking/code splitting

  • 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. minification

  • デフォルトだと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. dev server

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

Rollup

0. ツールのコンセプト

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

1. transpilation

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

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

2. concatenation/tree shaking/code splitting

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

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

3. minification

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

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

4. dev server

  • serverは無いが、watchモードはあるので開発時はこれを使うのが良さそう

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

nakaakistnakaakist

Parcel

0. ツールのコンセプト

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

1. transpilation

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

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

2. concatenation/tree shaking/code splitting

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

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

3. minification

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

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

4. dev server

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

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

nakaakistnakaakist

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. transpilation

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

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

2. concatenation/tree shaking/code splitting

  • 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. minification

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

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

4. dev server

  • 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/

nakaakistnakaakist

Babel

0. ツールのコンセプト

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

https://babeljs.io/docs

1. transpilation

  • 上述の通り

2. concatenation/tree shaking/code splitting

  • なし

3. minification

  • なし

4. dev server

  • なし
nakaakistnakaakist

Terser

0. ツールのコンセプト

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

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

1. transpilation

  • なし

2. concatenation/tree shaking/code splitting

  • なし

3. minification

  • 上述の通り

4. dev server

  • なし
nakaakistnakaakist

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. transpilation

  • @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. concatenation/tree shaking/code splitting

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

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

3. minification

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

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

4. dev server

  • なし
nakaakistnakaakist

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. transpilation

  • 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. concatenation/tree shaking/code splitting

  • 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. minification

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

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

4. dev server

  • 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

nakaakistnakaakist

Turbopack

0. ツールのコンセプト

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

ViteはESMネイティブなバンドルしないdeb 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. transpilation

  • 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. concatenation/tree shaking/code splitting

  • なし(将来予定)

3. minification

  • なし(将来予定)

4. dev server

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

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

nakaakistnakaakist

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. transpilation

  • 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. concatenation/tree shaking/code splitting

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

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

3. minification

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

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

4. dev server

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

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

nakaakistnakaakist

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. transpilation

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

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

2. concatenation/tree shaking/code splitting

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

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

3. minification

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

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

4. dev server

  • なし
nakaakistnakaakist

Next.js

0. ツールのコンセプト

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

https://nextjs.org/

1. transpilation

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

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

2. concatenation/tree shaking/code splitting

  • これもSWCで行う

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

3. minification

  • これもSWCで行う

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

4. dev server

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

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