高速JSバンドラをつくる
Why?
最近,JSバンドラがJS以外の言語で書かれ始めている
これらの技術の根本的な問題意識として,「V8の起動時のJSパフォーマンスがあまりよろしくない」というのがあると思っているtosuke.icon プラグイン機構が簡単に用意できないかもしれない
問題が起きたときに,簡単に解決できないかもしれない
既存のエコシステムを活用できる
ライブラリが提供するBabelプラグインを使いたい場合とかある 余談
babel-preset-envやbabel-preset-typescriptは重いからswcにやらせよう!というアイデア ある程度ユースケースが固まったところを決め打ちして高速化するの,頭いい
How
基本方針
並列化したら速くなるんじゃね?
巨大なデータをスレッド間で受け渡さない
設計
「Host」と「Worker」を使う
Host x1,Workerたくさん
Host
主にモジュールの依存関係を管理する
新しいモジュールが発見されたら,それを空いているWorkerに伝えて割り振る
Workerから集めた識別子の情報を舐めて,使われていないかつ副作用がなければWorkerに「この識別子は消してよい」と伝える
Naming
Terserの持つ最適化機構のうち,名前付けの部分をHostでやりたい ここだけ大域的な情報に依存していて,並列化できない
ある程度妥協して,トップレベルの名前付けだけやって,ローカルはTerserにいい感じにしてもらうとか?
どうやって名前空間分けようか
Worker
モジュールを保持して操作する
ASTは巨大なので,動かさずにWorkerに保持して,情報はモジュールを持ってるWorkerに聞きに行くというスタイルにする どのモジュールを誰が持っているのかという情報が欲しくなるはずで,Mapをv8.serializeしたものをSharedArrayBufferに入れて配るとかするとよさそうな気がする
Hostにモジュールを割り振られたら
1. パース
3. 依存関係の収集
importを調べる
最低限,モジュールの存在と依存は調べる必要がある
モジュール全体への依存があるかimport * as hoge from ...
副作用があるかもしれないモジュールだと,使われてなくても全体を含める必要がある
hogeをオブジェクトとして扱う必要があるかどうかも調べる
moduleをオブジェクトとして作る必要があるかに関わってくる
importされた識別子と,それが使われているか
識別子の登場回数もあるとうれしい?(naming)
モジュール全体への非同期依存があるか(import())
収集した情報をHostに伝えて待機
4. バンドラ特有の変換
モジュールを正しい順番でconcatしたときに,名前空間の衝突なく動作するような変換をする
バンドラが全てのモジュールの情報を知らないとできないことも多いので,それが終わったらゴリゴリやる
トップレベルの識別子に$$abcedfみたいなsuffixを付けて,それへの内部依存を全部変更
Hostに削除しろと言われた関数/変数の定義を,内部依存がないなら削除
importを除去
import * as hoge from (idがabcdefなモジュール)は
hoge.piyoの形ならpiyo$$abcdefに,hoge.defaultの形なら__default$abcdefに
const { piyo } = hogeとかでもやる
それ以外ならコード中のhogeを__module$abcdefみたいなのに
import hoge from ...はコード中のhogeを__default$abcdefに
import { hoge } from ...はhogeをhoge$$abcdefに
import(...)はちょっと厄介
そもそもこのフェーズだと全てのモジュールの解決が終わっていないので,モジュールの解決と分割が終わってから戻ってきて以下のことをやる
そのままimport()の中身をいい感じにして変換してあげればいい
考える場合
import().then(絞り込む関数)みたいにする?
exportの処理
ルートのモジュールだったら
ルートではない場合
Default Exportが依存されている場合にexport default hogeをconst __default$abcdef = hogeに
モジュールをオブジェクトとして扱う必要がある場合,const __module$abcdef = { ... }みたいなコードを生成
実際はprototypeをnullにしないといけないので,Object.freeze(Object.assign(Object.create(null), { ... }))になる
5. 出力
ArrayBufferに書き出して,バイト数を知っておく
ファイルに書き込む