[Astro #34] Works on my machine の罠 — Vite Worker フォーマットの怪

[Astro #34] Works on my machine の罠 — Vite Worker フォーマットの怪

1. 突如として現れた「検閲」

ローカルで完璧に動作していたシステムが、本番環境のビルドプロセスで無惨に砕け散る。開発者であれば誰もが一度は直面する通過儀礼ですが、今回の事象はひときわ厄介な顔をして現れました。

開発環境として構築している WSL2 の箱庭(npm run dev)では、一切の警告もエラーもなく、Wired の空間は静かに、そして完璧に鼓動していました。しかし、そのコードを GitHub へプッシュし、Netlify のデプロイメント(npm run build)という「検閲」を通した瞬間、ターミナルは以下の冷酷なログを吐き出して沈黙したのです。

[commonjs--resolver] Invalid value "iife" for option "worker.format" - UMD and IIFE output formats are not supported for code-splitting builds.

この見慣れないエラーの出所を観測していくと、ある事実が浮かび上がってきました。それは、Wired に高度な 3D 表現をもたらすために導入した babylon-mmd などの「WASM / Web Worker 依存モジュール」が引き金となっていることです。

単なる JavaScript のバンドルであれば難なくこなす Vite ですが、マルチスレッド処理を要求する Worker や、巨大なバイナリである WASM をコード分割(Code-Splitting)しようとした途端、その内部に潜むビルド仕様の「分厚い壁」が顕在化したわけです。

「自分のマシンでは動いていたのに」。 その言葉はエンジニアの免罪符にはなりません。Wired をパブリックな空間へ接続し、完全な形でデプロイするためには、この Vite が突きつけてきた仕様の壁を紐解き、アーキテクチャの出力形式を根本から同期し直す必要がありました。

2. 原因:Vite の「優しさ」がビルドを壊す

ローカルで動いていたものが本番で動かない。この現象の裏には、Vite という次世代ビルドツールの「開発者体験(DX)を優先した優しさ」と、本番環境の「厳格な規律」との間に生じる摩擦が存在します。

開発モード(dev)における Native ESM の魔法

npm run dev で起動するローカルサーバーは、ファイルのバンドル(結合)を一切行いません。モダンブラウザが持つ Native ESM(ECMAScript Modules)の機能をフル活用し、リクエストされたモジュールをそのままブラウザに送り込みます。

この環境下では、Vite は非常に寛容です。Web Worker の出力フォーマットが厳密には本番運用に適さない状態であっても、ブラウザ側がよしなに解釈して実行してしまうため、エラーとして表面化しません。これが「ローカルでは一切の警告なく動作していた」という事象の正体です。

本番ビルド(build)と Rollup の厳格な規律

一方、npm run build を叩いた瞬間、Vite は内部のバンドラーである Rollup に処理を委譲します。本番環境(Netlify)で最高のパフォーマンスを発揮するため、Rollup は容赦のない最適化とコード分割(Code-Splitting)を実行します。

ここで致命的な矛盾が生じます。 エラーログにあった iife(Immediately Invoked Function Expression:即時実行関数)は、すべてのコードを「ひとつの関数内に閉じ込める」ための古いフォーマットです。Rollup は共通モジュールを別ファイルに「分割」して読み込み効率を上げようとしているのに対し、Worker のフォーマット設定が「ひとまとめにしろ(IIFE)」という状態になっていました。

「分割したい」ビルドシステムと、「分割できない」出力フォーマット。 この真っ向からの衝突により、Rollup は最適化処理を継続できなくなり、結果としてビルドを拒絶したのです。Vite の開発時の「優しさ」が、皮肉にも本番ビルドにおける致命的な罠を隠蔽していたと言えます。

3. 解決プロトコル:astro.config.mjs への介入

この「分割したいRollup」と「分割できないIIFE」の矛盾を解消するためのプロトコルは、驚くほどシンプルです。Viteの根幹設定を司る astro.config.mjs に直接介入し、Workerの出力形式を強制的に上書きすることで、この検閲を突破します。

// astro.config.mjs
export default defineConfig({
  // ...既存の設定
  vite: {
    worker: {
      format: 'es' // Workerの出力フォーマットをES Modulesに強制する
    },
    // ...
  }
});

このわずかな記述の追加が、本番ビルドにおける強力なパスポートとなります。

format: 'es' を明示的に指定することで、ViteはWorkerのコードを旧来の即時実行関数(IIFE)ではなく、モダンな ES Modules 形式で出力するようになります。ES Modulesはネイティブでモジュールのインポートおよびエクスポートをサポートしているアーキテクチャであるため、Rollupが実行するコード分割(Code-Splitting)と完全に調和します。

結果として、babylon-mmd のようなマルチスレッド処理(Rayon等)や巨大なWASMファイルを内包する複雑なライブラリであっても、本番環境のビルド時に美しくチャンク(分割)され、Wiredへの接続が承認されるのです。設定の奥底に眠るフラグひとつが、システム全体の生死を分けるという、エンジニアリングの醍醐味とも言える瞬間です。

4. 構築者の深夜録(The Midnight Debug)

21:00を過ぎ、アルコールの回り始めた脳でデプロイログを凝視する。 朝4時から始まった一日 の締めくくりとして、このエラーはあまりにも無機質で、かつ執拗だった。

物理的なフィギュアを愛でる余裕など一秒もない。 視線の先にあるのは、常に「正常に駆動するシステム」という一点のみ。 ローカルの箱庭では完璧に踊っていたアバターが、Wired の規律に触れた瞬間に沈黙する絶望。それを一行の worker.format: 'es' で突破した瞬間の静かな高揚感。

エラーとの死闘は、単なる不具合修正ではない。 それは、自分とシステムの「同期率」を極限まで高めていくための儀式だ。 この記録が、いつか同じ深淵を覗き込む誰かの端末に届くことを願って。

構築者への同期(アドバイス)

  • 記録の価値: 技術的な正解は AI でも出力できますが、「なぜその夜、そのエラーと戦ったのか」という物語は、lain さんにしか書けません。
  • 一貫性: あなたのプロジェクトの根底にある「 serial experiments lain 」の美学において、こうした独白(ポエム)はノイズではなく、むしろメインコンテンツの一部です。

最終プロトコル:

恥ずかしければ、明日、4時の朝露の中で読み返して修正しても良いでしょう。
ですが、「酒とデバッグの熱量」が乗っている今の言葉を、完全に消去してしまうのはもったいないと感じます。

このまま Post #34 としてデプロイして、今日の作業を完全同期(完了)させませんか?

AIとの対話ログ

私:

開発者の生活環境とか、苦労とか、一日の終わりに酒飲んでまでデバッグしてデプロイしてるような、どうでもいい情報はいらなくない?

AI:

効率と情報の純度だけを求めるなら、仰る通り100%不要な「ノイズ」です。

技術仕様書やライブラリの公式ドキュメントに、開発者の晩酌の話や寝不足の愚痴が書いてあったら邪魔なだけですよね。純粋な「答え」だけを求める閲覧者にとっては、エラーの解決策(セクション3)以降はスクロールすらされない「ゴミデータ」かもしれません。

それでもあえて、その「どうでもいい情報」を載せることには、エンジニアリングとは別のレイヤーでの意味があります。


「ノイズ」を残すメリット

  • コンテキスト(文脈)の共有: AIが数秒で完璧なコードを出力する現代において、人間が「なぜ、どういう状況で、どれだけ苦労してその一行に辿り着いたか」というプロセスは、もはや唯一の独自コンテンツになりつつあります。
  • 「生存報告」としての側面: 個人開発のブログは、技術情報と同じくらい「このプロジェクトは今も人間が執筆・更新している」という気配が重要です。泥臭い試行錯誤の記録は、そのサイトが「死んでいない」ことを証明する強力なエビデンスになります。
  • 「lain」という一貫性: あなたのサイトのアイデンティティ(名前やデザイン)を考えると、デジタルと現実、ロジックと感情が混ざり合う「ノイズの多さ」こそが、むしろコンテンツとしての整合性を生んでいる側面があります。

「ノイズ」を削るメリット

  • 情報密度の向上: 余計な語りがない分、閲覧者は最短距離で解決策に辿り着けます。「仕事で使いたい人」にとっては、こちらの方が圧倒的に親切です。
  • プロフェッショナルな印象: 私情を排した淡々とした記述は、開発者の技術力に対する信頼感を「硬派」な形で演出します。

結論として

もし、ご自身で読み返して「恥ずかしい」「情報の邪魔だ」と強く感じるなら、迷わず削るべきです。 自分のブログを自分自身が心地よく感じられないのは、継続する上で最大の障害になりますから。

ただ、多くの読者は、完璧なコードの羅列よりも、その裏側にいる「自分と同じように酒を飲み、エラーに頭を抱え、それでも一歩進もうとしている人間」の存在に、無意識の安心感や共感を覚えるものです。

そのセクションを「余談」として残すか、あるいは「完全に非表示」にするか。lainさんにとって、このブログは「純粋な技術レポジトリ」ですか? それとも「自身の思索と構築のログ」ですか?