@2024/05/06 - ViteとRollupの関係(仕組み)
仕事で一部プロダクトにViteを採用している。(自分が採用したわけではなく、前任者が選定し、それをメンテしている) もともとプロダクションで動いていたプロダクトのリプレイスを行って、それがViteベースで動いているわけだが、要件定義をしっかりしてなかったのか、初期リリース後、ちらほらお客さん先の一部デバイスで動作しない、という事象があった。 調査すると、トランスパイル後のJSで使用している構文がサポートされていないブラウザバージョンをお客さんのデバイスで利用している模様。そのためトランスパイルバージョンを指定することになった。
そこでViteのトランスパイルの仕組みを調べ始めたのだがこれがいろいろと沼った。おかげでいい勉強になった。 当方の環境
Vite v4.0.4 (これを機にv4系の最新までアップデートしようかと思う) TL;DR;
Viteでトランスパイルするときに、指定のECMAScriptバージョンを指定したい場合はbuild.targetオプションに指定する v4もv5も同じ
調べたこと
結論は上記に記載した通りだが、その確証を得るためにかなり調査をした。
(やっぱりプロジェクトの途中でこういった下回りというかを調査するのはよくない)
まずはViteを全くわかってなかったので基本的な仕組みから理解する。 これを読むまではなんとなく「開発環境ビルドと本番環境ビルドでビルドの仕組みが違う」とか「開発環境ビルドではesbuild、本番環境ビルドではRollupを使っている」くらいの理解だった。 上記のページにも大きくはそんなことが書いてあるが、TypeScriptのトランスパイルをするには以下のところを意識するとよさそうだった。 tsconfigのtargetオプションはESNextを指定する
で、今回はトランスパイル後のECMAScriptバージョンを指定したいので、「じゃあどこで指定するの?」となった。
次に見たのはesbuild.targetオプション。
ただ、ここに指定してもビルドの場合はbuild.targetオプションが優先される、ということで次にそっちを確認した。(今回はプロダクションビルドで指定できればOKだった)
で、ここの文章がことさら素人にはわかりにくい... 文章を何度か読みながらViteの実装も確認した。 まずは以下の部分。
Vite は 'modules' を ['es2020', 'edge88', 'firefox78', 'chrome87', 'safari14'] へ置き換えます。
もうひとつの特別な値は 'esnext' で、これはネイディブの動的インポートをサポートしていることを前提としており、トランスパイルが可能な限り少なくなります:
これは以下の実装の部分となる模様。
次に以下の文章。
変換は esbuild で実行され、この値は有効な esbuild の target オプションでなければいけません。カスタムターゲットは ES のバージョン(例: es2015)、バージョン付きのブラウザー(例: chrome58)、または複数のターゲットの文字列の配列を指定できます。
これを一発で読んで理解できる人生を歩みたかった・・・。
この文章の意図を理解するためにかなり時間がかかった。
まず、「変換は esbuild で実行され」だが、本当にesbuildで変換(=トランスパイル)が実行されている。 ここ以降の文章は「値を指定する場合はesbuildのtargetオプションの仕様に合わせて指定してね」ってことなのでまぁそのまま。 正確には、上記の下にあるgenerate関数が呼び出されたとき、になるのだが、Rollupの初期化はここでやっている。 code:TypeScript
const bundle = await rollup(rollupOptions)
Webpackと同じく、トランスパイルなど便利機能などの処理系はプラグイン機構を採用しており、基本プラグインがなければバンドルするだけである。 ので、以下の実行部分もバンドルを実行する命令となる。
code:TypeScript
で、ややこしいのがこのバンドル実行の引数にあるoutput.generatedCodeオプション。
こいつがいるので、バンドラーでありながらデフォルトでトランスパイルの機能を持っているのかと想像したのだがこれが外れた。
やっぱりトランスパイルするにはプラグインが必要であり、そのプラグインに渡されるオプションにgeneratedCodeが渡される、という仕組みだった。
generatedCodeのデフォルト値
で、プラグイン、プラグインと言ってるがこのトランスパイル用のプラグインを誰が用意しているのかというとViteが用意している。 上記のプラグインの中で、transformWithEsbuild関数を呼び出していることからも分かる通り、本番環境ビルドのトランスパイルでもesbuildを使用していることがわかる。
ここで呼び出すesbuildに、最初の方で触れたbuild.targetが渡されるため、esbuildのtargetオプションの仕様に則ったオプションパラメータを渡せば良い、が結論となる。
改めて勉強不足を実感したと同時によい機会を得たなと思った。。
継続してこういった仕組みを理解していって正しくツールを使えるようにしていきたい。
---
以下、調査にあたって確認したドキュメントを一覧にする。