SvelteKit + Cloudflare Workers で WASM の制約を回避する:ビルド時生成パターン
背景
Rust で定義した enum をフロントエンドの SvelteKit アプリケーションで共有したいというユースケースがあった。Rust 側で enum のバリアントやラベル文字列を一元管理し、wasm-pack でビルドした WASM パッケージを SvelteKit から利用する構成。
ローカル開発(Vite dev server / Node.js)では vite-plugin-wasm を使えば問題なく動作する。しかし、デプロイ先が Cloudflare Workers になると話が変わる。
課題:vite-plugin-wasm と SSR ビルドの問題
vite-plugin-wasm を使って SSR ビルドを行うと、出力されるサーバーコードに以下のような行が生成される。
var URL = globalThis.URL;
Cloudflare Workers ランタイムでは globalThis.URL が期待通りに解決されず、ReferenceError が発生した。
解決策:ビルド時生成パターン
ランタイムでは WASM を使わないアーキテクチャにする。
ビルド時に Node.js 上で WASM を実行し、結果を JSON ファイルとして出力する。SvelteKit アプリはその JSON を import するだけになる。
アーキテクチャ
Rust enum
↓ wasm-pack build
WASM + JS バインディング (_bg.js)
↓ ビルドスクリプト (Node.js)
JSON ファイル
↓ import
SvelteKit アプリ ($lib/wasm/index.ts)
ビルドスクリプト: scripts/generate-labels.js
以下のスクリプトを scripts/generate-labels.js に配置する。wasm-bindgen が生成する JS バインディング(*_bg.js)を直接 import し、__wbg_set_wasm 関数で WASM インスタンスを手動で接続する。
import { readFile, writeFile } from "node:fs/promises";
import { __wbg_set_wasm, get_all_labels } from "../pkg/my_crate_bg.js";
async function main() {
const wasmBuffer = await readFile(
new URL("../pkg/my_crate_bg.wasm", import.meta.url)
);
const wasmModule = await WebAssembly.instantiate(wasmBuffer, {
// wasm-bindgen が要求する import を空で渡す
// Rust 側から JS の関数を呼び出していない場合は空オブジェクトで十分
"./my_crate_bg.js": {},
});
__wbg_set_wasm(wasmModule.instance.exports);
const labels = get_all_labels();
await writeFile(
new URL("../src/lib/generated/labels.json", import.meta.url),
JSON.stringify(labels, null, 2)
);
console.log("labels.json generated");
}
main();
__wbg_set_wasm は wasm-bindgen が _bg.js 内に生成する内部関数で、通常は init() 関数の中で自動的に呼ばれる。これを直接使うことで、Node.js 環境での手動インスタンス化が可能になる。
ランタイムラッパー: $lib/wasm/index.ts
ランタイム側は JSON を読み込むだけのシンプルなラッパーになる。
// src/lib/wasm/index.ts
import labels from "$lib/generated/labels.json";
export type Label = {
value: string;
label: string;
};
export function getAllLabels(): Label[] {
return labels;
}
WASM への依存がビルド時に解決されるため、Workers ランタイムでの制約を一切受けない。
ビルドの統合
package.json の build スクリプトにラベル生成を組み込む。
{
"scripts": {
"generate": "node scripts/generate-labels.js",
"build": "npm run generate && vite build"
}
}
まとめ
Cloudflare Workers 上で WASM を直接実行するのではなく、ビルド時に Node.js で WASM を実行して結果を JSON として出力するパターンを用意した。ランタイムからは WASM への依存がなくなるため、Workers 固有の制約を回避できる。Rust 側でデータを一元管理しつつフロントエンドと共有したい場合に有効。