1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

EmscriptenでES6_MODULE=1ビルドしたモジュールを、Workerで使うとViteでリリースビルドに失敗する

Last updated at Posted at 2023-05-08

Emscripten + Vite で Worker を使ったサービスを作ると、リリースビルドできない問題が発生することがあります。大変分かりにくいので、原因と対策のメモです。

確認したバージョン: Vite:4.3.4, Emscripten:3.1.34

背景

Viteから参照するnpmパッケージが commonjs 形式だと、開発中にパッケージをローカル参照(file:..やnpm link)した際に正しく動きません

オプションで解決する方法もあるのですが副作用があるため、できれば参照するライブラリは module 形式にしたいです。

Emscriptenでも module 形式のライブラリを生成することは可能で、EXPORT_ES6=1オプションを付けてビルドします。こうして作ったライブラリは Vite での開発中に npm run dev している限りは、メインでも Worker でもまったく問題なく利用できます。

問題

しかし、Emscripten で作ったライブラリを Worker から使った場合、npm run build ができません。
具体的には、まず、次のような警告が出ます。

[plugin:vite:resolve] Module "module" has been externalized for browser compatibility, imported by "/Users/username/dev/myapp/node_modules/mylib/dist/mymodule.js"

さらに、次のエラーでビルドが失敗します。

RollupError: Invalid value "iife" for option "output.format" - UMD and IIFE output formats are not supported for code-splitting builds.

原因

まず ES6_MODULE=1 付きでビルドした場合、生成されるコードに await import が含まれていることが要因です。

my-emscripten-module.js
... if(ENVIRONMENT_IS_NODE){const{createRequire:createRequire}=await import("module"); ...

実はこれ、node で実行した場合に通るパスなので、ブラウザではデッドコードであり、実行されません。しかし、そんなことは Vite のバンドラ(Rollup)には分かりません。Rollup はここで発見した await import 先のパッケージをコードスプリット対象にして、警告を出力しています。

これだけなら警告だけで済むのですが、問題になるのは Worker で import していることです。
Vite は Worker のビルド形式をデフォルトで iife にしていますが、iife 形式はコードスプリットに対応していません。むしろコードスプリットしないためのビルド形式です。

このため、コードスプリットしたい Rollup と、そうさせたくない Vite 本体の設定が矛盾して、ビルドエラーとなります。

対策

対策1: 最も簡単な対策(FireFox非対応)

Worker で import が使えるブラウザであれば、Vite のコンフィグで Worker ビルド時のモジュール形式を es module にすることで解決できます。設定値としては次のように es を指定します。

vite.config.js
export default defineConfig({
  plugins: [react()],
  worker: {
    format: 'es', // ←これを追加
  }
});

この対策は簡単ですが、現時点では FireFox が Worker で import をサポートしていないため、FireFox では動作しません。

対策2: 諦めて CommonJS 形式に戻す

諦めて Emscripten のビルド形式を commonjs に戻します。
冒頭で説明した、commonjs のパッケージをローカル参照した場合のエラーは Vite の設定で、resolve.preserveSymlinks オプションを有効にすれば、回避できます。

ただし、ローカル参照したパッケージのソースの動的変更に追従できなくなります。
コードを変更した場合は、キャッシュを消してビルド(npm run dev -- --force)が必要になります。

上記以外のトラブルもあるかもしれませんが、手元では今のところ遭遇していません。

対策3 Emscripten 側で ENVIRONMENT=web オプションを付けてビルドする

Emscriptenでのビルド時に、-sENVIRONMENT=web オプションを付けると、上記の await import を含むコードが削除されるため、問題が発生しなくなります。同じコードを node.jsでも使いたい場合は、node 用のビルドが必要になってしまう可能性はありますが、node 専用の機能(NODEFSとか)を使っていなければ、web 用にビルドしたものも普通に node で動かせるので、悪くない対策だと思います。

おわりに

Vite + Emscripten + Worker + AudioWorklet の組み合わせで使っています。開発中は簡単に動くなぁという感じだったのですが、リリースビルドするとこのようにまだ一癖ある感じです。FireFox もそろそろ正式に Worker での import をサポートしてほしいところですね。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?