🥦

フロントエンド界隈のビルドツール用語の違いを分かりやすく!

2024/03/31に公開1

「ここ間違ってるよ!」があれば、コメントで指摘して頂けるとうれしいです。

フォーマッター

フォーマッターは「俺が決めた書き方に修正してやるぜ」なツールです。

たとえば「この行は文字数が多すぎるから改行してやるぜ」とかをしてくれます。

現状は、Prettierがよく使われているようです。

フォーマットするルールは自分で追加できますが、公式には「フォーマットルールを議論することは無駄です」的なことが書かれてるので、暗にデフォルト設定のまま使うことを推奨している気がします。

リンター

リンターは「その書き方はやめたほうが身のためだぜ」を警告してくれるツールです。

警告するルールは自分で追加できます。たとえば「関数を書くときは絶対に関数式で書け!関数宣言は使うな!」とかを警告できたりします。

ルールを追加すればするほど「俺に指図するなああーッ!!」となりますが、👇のようなメリットが生まれます。

  • 書き方を統一できる
  • バグを生みやすい書き方を排除できる

現状は、ESLintがよく使われているようです。

ESLintは--fixオプションを渡すと「フォーマッターとしても少しだけ働くよ」モードになります。ただしPrettierと組み合わせる場合、この機能をOFFにするのが一般的みたいです。

また、大体のフレームワークは「痛い目に遭いたくなければこれを守ってね」という設定を公開しています。たとえば最近のReactチームの記事でも「アップデートの恩恵を受けれる書き方をしたいならリンターを入れるといいよ」と書かれています。

ちなみに、リンターは"警告"しかしてくれません。なのでGitHub ActionsなどのCI/CDと組み合わせて「リンターが警告を出してる状態ならマージさせないよ」をするのが一般的です。

トランスパイラー

トランスパイラーは「言語Aのソースコードを言語Bのソースコードに変換するぜ」なツールです。

たとえば、👇のような変換をトランスパイルと呼びます。

  • JavaScript(ES2020)→JavaScript(ES5)
  • TypeScript→JavaScript
  • JSX→JavaScript
  • Sass→CSS

1番目のJavaScript→JavaScriptの変換は「同じ言語への変換なのにトランスパイルなの?」という気がしますが、「バージョンが違うってことは違う言語みたいなものじゃね?」みたいな理屈でトランスパイルと呼ぶのかもです。実際、ES5→ES2015の進化は「もはや別言語じゃん」という感じなので。

あとトランスパイラーは、単に「コンパイラ」「トランスフォーマー」と呼ばれることもある気がします。

厳密にいうと👇のような違いがあるみたいですが、意識せずに使っている人もいる気がします。

  • トランスパイラー:高水準言語→高水準言語に変換するやつ
  • コンパイラ:高水準言語→低水準言語に変換するやつ
  • トランスフォーマー:ある形式→別の形式に変換するやつ

なので、これらの用語出てきたら「何かを変換するんだなぁ」と思えばいい気がします。

JavaScriptのトランスパイラーとしては、👇などがあります。

  • SWC:Rust製。
  • Babel:JS製。
  • tsc:JS製。TS→JS専用。型チェックができる。
  • esbuild:Go製。

ミニファイヤー

ミニファイヤーは「コードを圧縮してやるぜ」なツールです。

たとえば、不要な改行を削除したり、変数名を短くしてくれたりします。

👇が定番のようですが

  • JavaScript:Terser
  • CSS:CSSNano

最近は👇のようにRust/Go製の採用が増えているようです。

  • JavaScript: SWC / esbuild
  • CSS:Lightningcss

「SWCとかesbuildってトランスパイラーじゃなかったの?」と思うかもですが、これらのツールはミニファイヤー機能を含めいろいろな機能を備えています。ややこしいです。

タスクランナー

タスクランナーは「指示されたことを順番に実行するぜ」なツールです。

昔はGulpやGruntがよく使われていたようですが、今はnpm scriptsが主に使われている気がします。

npm scriptsは、package.jsonに👇のように書く記述のことです。

この場合npm run devを実行すると代わりにnext dev -p 3333が呼ばれる・・・という感じで使えます。

「あだ名をつけれる機能」と理解すると早いかもです。

バンドラー

バンドラーは「複数のファイルを1つのファイルにまとめるぜ」ができるツールです。

たとえば、「100個のJSファイルを1個にまとめるぜ」ができたりします。

具体的には、👇のようなバンドラーがあります。

  • Webpack:老舗のやつ。
  • Parcel:「Webpackを簡単に使えるようにしようよ!」なやつ
  • Rollup:ライブラリのバンドルでよく使われてるやつ。
  • esbuild:Go製の早いやつ。
  • Rspack:「WebpackをRust製にしようよ!」なやつ
  • Vite:なんかすごいやつ
  • Turbopack:なんかすごいやつ

HTTP1.1のときは、複数のファイルを一括で配信するのがキツかったらしいので、特にバンドラーは重宝されたらしいです。

ただHTTP2.0になって「一気に並列でダウンロードできるよ」になったので、「そろそろバンドラーは引退か?」と思いきや、「とは言っても10,000個のファイルとかを配信するのはキツいよね」な事情があるので、いまだにバンドラーは必須みたいです。

ここまで訊くと「バンドラーはファイルを1つにまとめるヤツなのね。簡単だね」と思うかもですが、実際はもっとややこしいです。なぜならバンドラーの中には「バンドラー以外の機能を持ってるやつが多いから」です。

たとえば、多くのバンドラーは👇のような機能を持ってたり持ってなかったりします。

  • ツリーシェイキング:
    • 使っていないコードを削除する機能。
    • import文を解析して「この関数は使われてないね」を判断する。
    • たとえばFirebaseはv9から関数ベースの書き方に変わりましたが、これはツリーシェイキングが効くようにするためです。
  • コードスプリッティング:
    • 「1つのファイルにまとめちゃおう!」じゃなくて「◯個に分けてまとめちゃおう!」ができる機能。
    • 要するに「10000個のファイルを1個にまとめちゃうと流石にファイルサイズがデカくなりすぎるし、ユーザーによっては不要なコードも含まれちゃうかもだから、必要なタイミングで必要なコードだけ読み込めるように10個とかに分けておく方が良いんじゃね?」なやつです。
    • JSのimport()を使ったダイナミックインポートや、Reactの場合はReact.lazy()を使うと開発者が「このファイルは分けてほしい」を明示できます。
  • 開発サーバー
    • ファイルを監視して自動的にビルドやリロードを行う機能。
    • ページのリロードなしにページの一部だけを書き換えてくれるHMR(ホットモジュールリプレイスメント)を備えているものが多いです。

あとは、esbuildのように「トランスパイラーとしての機能も備えてるよ。しかもトランスパイラ単独としても使えるよ」なバンドラーもあります。

なので、たとえば👇みたいに組み合わせて使うこともできます。

バンドラー トランスパイラー
Webpack SWC
Webpack esbuild
Parcel esbuild

あと更にややこしいことに「バンドラーを使ってバンドルするバンドラー」もあります。

たとえばViteは、内部でesbuildとrollupを使っていて「本番時はrollupでビルドするけど開発時はesbuildでビルドするよ」なことをやってて、開発時はESModuleでファイルを直接読み込むことで「そもそもバンドルしない」という戦略を取っています。

ただ最近は、RolldownというRust製のソフトで「esbuildとrollupを置き換えよう」な計画が進んでるらしく、最近リリースされました。

正直、このへんの技術は移り変わりが激しすぎる割に、実務でもあまり役立たないので「こんなのがあるんだな~」くらいの理解でいいのかなと自分は思っています。

あと、「そもそもバンドルせずに10,000個のファイルとかを一括で配信できるようにすればバンドルしなくていいんじゃね?」な提案や議論もされているらしいです。👇のページなどが参考になります。

https://zenn.dev/uhyo/articles/what-is-native-esm-era

ちなみに、モノレポ(後述しています)でパッケージを分けて開発する場合は、いずれかのバンドラーを使う必要があると思いますが、フレームワークによっては「俺が依存してるパッケージは俺がバンドルするぜ」な設定もあります。

たとえばNext.jsだとtranspilePackagesというオプションを使うと、Next.jsが依存しているパッケージはNext.jsがバンドルしてくれます。

モノレポ管理ツール

これはフロントエンドに限定するツールではないですが、関連する話なので書きます。

まず「モノレポ」というのは「1つのリポジトリで全部管理しよう」な考えで作られたリポジトリのことです。

そのうえで、モノレポ管理ツールは「モノレポ上の複数のパッケージやアプリを効率的に管理するぜ」ができるツールです。

たとえば、Next.jsでアプリを作るとします。すると開発が進むにつれてコードが巨大になってきます。それに伴ってビルド時間もどんどん伸びていってしまいます。

こういうとき、「この部分は切り出せるよね」な部分をどんどんパッケージとして切り出すようにします👇(イメージ)。

Next.js
 ├ パッケージA
 └ パッケージB

(「パッケージって何よ」と思うかもですが、ただの関数の集まりだと思ってください)

すると、ビルドするときに「パッケージAは前回から変更がないからキャッシュを使えばいいか」ができるようになります。責務も分離できて一石二鳥です。

ただ、「パッケージAがパッケージBに依存している」みたいな場合、自分で「パッケージBをビルドした後にビルドAをビルドして・・・」を設定する必要があります。

こういうとき、Turborepoのようなモノレポ管理ツールを使うと、👇のように自動で良い感じにしてくれます。

  • パッケージの依存関係を見て、「パッケージAはパッケージBに依存してるので先にパッケージBをビルドしないとな」を考えた上でビルドしてくれる
  • 「パッケージAは前回から変更されてなくね?」な場合に、前回のキャッシュを使ってくれる
  • 「パッケージAとパッケージBは依存関係がないから並列にビルドしたろ!」ができる

TurborepoはNext.jsのVercelが作っています。最近だとフロントもバックもTypeScriptのプロジェクトなら、Turborepoを採用するケースが多いのかもしれません。

「どのモノレポ管理ツールが何に対応しているのか?」については、以下のページが分かりやすいです。

参考:Monorepo Explained

ビルドツール

ビルドツールは「よし!これで本番に良い感じでデプロイできるぜ!」な状態にするために一役買ってくれるツールのことを言う気がします。

なので、この記事で書いたツールの大半がビルドツールに分類される気がします。

たとえば、esbuildは公式で「バンドラーだよ」と謳っていますが、バンドラー以外にもいろいろな機能を備えているので、単に「ビルドツール」と呼ぶ人も多い気がします。

▲「ウェブ用の超高速なバンドラーです」と書いてる
あとビルドツールは、「ツールチェーン」などとも呼ばれる気がします。いまどきの開発ではGitHub ActionsのようなCI/CD上で「pushされたらこれらをバーっと動かしてね」な自動化を行うチームが大半だと思いますが、バーっと行われる自動処理の中に組み込んで使うことができるツールは「ツールチェーン」とも呼ばれるのかもです。

ポリフィル

これはビルドツールではないのですが、関連する用語なので書きます。

ポリフィルは、トランスパイルする際に「お!このコードは旧バージョンに存在しない機能だから、同じ動きをする代替コードに置き換えたろ!」な処理のときに使われる「代替コード」のことを言います。

たとえば、ES5より前のバージョンにはPromiseが存在しません。なのでトランスパイルのターゲットをES5の状態でPromiseのコードを見つけると「Promiseのような動きをするコードで代替したろ!」という動きをします。このとき、代替コードはcore-jsのような「いろいろな代替コードを提供してあげるよ」なライブラリから持ってきます。

カオスな状況を解消できるソフト

最近のフロントエンド開発では、これらのツールを全て組み合わせて開発が行われます。

ただ、この現状を知らない人からすると、カオスすぎて意味不明だと思います。

なので、「カオスを解消しようぜ」なプロジェクトがあります👇

Bun

上で書いたすべてのツールを含んだツールです。

ほかにも、以下などを含んでいます。

  • JavaScriptランタイム(コードを解釈して動かすよ!なソフト)
  • パッケージマネージャ(npmやyarnの代わり)
  • TypeScript標準対応
  • テストツール

「これ1個あればいいじゃん!」を目指してるツールらしく、それでいて爆速らしいです。

とてもすごいので、エンジニアはみんな「とてもすごい」と言っています。

Biome

「PrettierとESLintを1つのツールにまとめよう」を目指してるツールです。

もともと、Romeという名前で「カオスを解消しよう」なプロジェクトとしてやっていたようですが、名前がBiomeに変更されて「とりあえずフォーマッターとリンター周りを解消しよう」な感じで今は動いているらしいです。

ちなみに、Bunのほうは逆に「フォーマッターとリンターは手が回らないので後回しにします!」と言っています。

corepack

「npmとかyarnとかpnpmとかプロジェクトによって違うし紛らわしいよね」を解消するツールです。

Node v16.9.0から標準でバンドルされていて、package.jsonに以下のように書けば「このプロジェクトではpnpmのバージョン8.14.1を使えばいいのね」と判断して、勝手にpnpmをダウロードしてきて実行する・・・までやってくれます。

{
  ...
  "packageManager": "pnpm@8.14.1",
  ...
}

なので開発者はローカル環境にyarnとかpnpmをインストールする必要がなくなります。

corepack enableを実行するだけで有効になります。

参考:corepack is 何?

まとめ

  • 全体的に、早さを求めてRustに移行しつつある。
  • バンドラー周りは、今も昔もカオス。
  • すべてのカオスが解消されるのはまだ先になりそう。
PrAha

Discussion

fiorizenfiorizen

間違いというほどではないですが……。

ESLintは--fixオプションを渡すと「フォーマッターとしても少しだけ働くよ」モードになります。ただしPrettierと組み合わせる場合、この機能をOFFにするのが一般的みたいです。
リンターは"警告"しかしてくれません。

ESLintはフォーマッターとして以外のルール違反の自動修正も(それが自動修正可能なルールで、--fixを指定していれば)可能です。 例) no-var
ただし、修正内容がPrettierと競合するといろいろと不都合があるので、併用時は Prettierと競合するようなESLint側のルールをまとめてOFFにする 拡張機能がPrettier側では推奨されています(リンク先にも記載の通り、ここに至るまで紆余曲折ありました)。
実際私の環境ではESLintとPrettier両方で自動修正を行っています。

もちろん、ご記載の通りコード修正はフォーマッターにのみ任せるというのも妥当な判断だと思います。
(例えば上記拡張機能がメンテされなくなると、Prettier/ESLintの更新に追従できなくなるといったリスクがあります)