💢

viteでwasmを読み込むのに苦労した話

2023/04/28に公開

webpackとvue-cliで使っていたBase64でエンコードしてバンドルしたwasmモジュールを読み込む方法がviteに移行したときに動かなくなってしまったので、解決するまでの備忘録です。

原因

いくつか原因がありました。

  1. wasmをES6オプションをつけてコンパイルしていない
  2. jsから呼び出すときにrequireを使っている
  3. viteのwasmプラグインの方式に対応していない

ひとつづつ見ていきましょう。

wasmをES6オプションをつけてコンパイルしていない

viteはES6なので、CommonJSのexportだとダメらしい。
emscriptenのオプションに、-s MODULARIZE=1を追加して再コンパイルすればOK!
例えばこんな風に。

$(PATH_OUTPUT)/$(NAME_MOD):$(TARGET_FILES)
	$(CC) \
	-s AUTO_NATIVE_LIBRARIES=0 \
	-s ENVIRONMENT=web \
	-s MODULARIZE=1 \
	-s EXPORT_ES6=1 \
	-O2 -o $(PATH_OUTPUT)/$(NAME_MOD)  $(FLGS) $(TARGETS) ../libtbskmodem/libtbskmodem.a

吐き出されたグルーコードを見るとES6で読めるようになってますね!ヨシッ!

jsから呼び出すときにrequireを使っている

import tbskmodemjsWASM from "./wasm/tbskmodem_wasm_mod.wasm"
:
const mod = require("./wasm/tbskmodem_wasm_mod.js");
const b = Buffer.from(tbskmodemjsWASM.split(",")[1], "base64");
let wasm = await mod({ wasmBinary: b });
TBSKmodemJS._instance=new TBSKmodemJS(wasm);
return TBSKmodemJS._instance;

requireはダメなのでimportに替えましょう。

import tbskmodemjsWASM from "./wasm/tbskmodem_wasm_mod.wasm"
:
const mod = await import('./wasm/tbskmodem_wasm_mod.js');
console.log(tbskmodemjsWASM.split(','));
const b = Buffer.from(tbskmodemjsWASM.split(',')[1], 'base64');
const wasm = await mod.default({ wasmBinary: b });
TBSKmodemJS._instance=new TBSKmodemJS(wasm);
return TBSKmodemJS._instance;

グルーコードがES6向けにコンパイルされていればすんなりいきます。ヨシッ!ヨシッ!

viteのwasmプラグインの方式に対応していない

今週の二度とやりたくないランキング第一位です。結論から言えばvite.congig.jsにちょろっと書けば突破できます。

import {dataToEsm} from '@rollup/pluginutils'
import { readFileSync } from 'fs';
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import wasm from 'vite-plugin-wasm'

export default defineConfig({
  plugins: [
    vue(),
    {
      name: 'vite-plugin-base64',
      async transform(source, id) {
          if (!id.endsWith('.wasm')) return
          const file = readFileSync(id);
          const base64 = file.toString('base64');
          const code = `data:application/wasm;base64,${base64}";`;
          return dataToEsm(code)
      },
    },
    wasm(),
  ],
});

はい!簡単ですね!プラグインを設定ファイルにベタ書きです。idからファイルを読み込んでbase64にしてベタっと張ります。

@rollup/pluginutilsのインストールを忘れずに!

npm install -D @rollup/pluginutils vite-plugin-wasm

さて、「結局wasmプラグインセットアップしてない?」と思うかもしれませんが、この部分、使わなくても書いておかないとエラーが出るんですね。

viteには「プラグインでwasm処理できますから大丈夫でーす」と言いながら、流れていく前に引っ剥がす必要があります。

これでwasmが動くようになりました!ヨシッ!ヨシッ!ヨシッ!

あとがき

wasmプラグイン用にソースを書き換えられる場合はこんな方法は使わないほうが良いですが、webpackやvue-cliでもそのまま動かしたい時には使える方法だと思います。

なお、chat-GPT(無料版)さんに聞くと、一発で解決しそうな名前のライブラリをたくさん考えてくれます。 そして、ひたすらwasmプラグインを売り込まれます。

有料版にすれば正しい答えを教えてくれるのかな。

Discussion