Next.js で wasm ベースのライブラリを使いたくなった。
というか自前で Rust のコードを wasm にしてライブラリを作ったので、それを使いたくなった。
だが一筋縄ではいかなかったので、その備忘録
wasm のコードを asset として配信する場合はそんな問題ない
自分で asset として wasm ファイルを置いておき、それを自前で読み込むとかの場合には特に罠はない。webpack の設定をよしなにしてやれば良いだけである。(SSR とかで Node が絡んでくると後述のように厄介になるので、dynamic import なりで解決する必要はある)
こちらの記事がわかりやすかった。
https://zenn.dev/razokulover/articles/fb64150be7a667
wasm が含まれるライブラリを普通に import して使いたい場合
この場合、巷に溢れている情報だけだと上手くいかなかった。
まずは必要な Webpack の設定
/** @type {import('next').NextConfig} */
const nextConfig = {
webpack: (config) => {
config.module.rules.push({
test: /\.wasm$/,
type: 'webassembly/async',
});
config.experiments = {
asyncWebAssembly: true,
layers: true,
};
return config;
},
};
module.exports = nextConfig;
古い記事だと色々他にも設定入れていたりするが、これが現時点(next@14.0.4
)での最小限の config であった。
ただ、これだけだと Unknown file extension ".wasm"
エラーが出てきてしまう...
Unknown file extension ".wasm"
エラーの解決方法
webpack の指定もちゃんとしてるし、なんで Unknown file extension
となってしまうのか...
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".wasm" for ...
と思っていたが、webpack 以前の問題で Node.js がそもそも .wasm
を読み込んでくれていなかった。
NODE_OPTIONS=--experimental-wasm-modules
を next dev
の前に入れてあげると普通に読み込んでくれるようになった。結構これに辿り着くまでに時間かけてしまったので、誰かがこの記事で時間を節約できることを祈って
余談:production build で Error: ENOENT: no such file or directory
が発生する場合
本筋から逸れるが、Production Build だと上記の webpack config では動かなかった...
調べると、一番手軽な解決のための hack としては
if (isServer) {
config.output.webassemblyModuleFilename = './../static/wasm/[hash].wasm';
} else {
config.output.webassemblyModuleFilename = 'static/wasm/[hash].wasm';
}
のようなものがよく見られたが、next: 14.0.4
だと全然上手く動かず苦労した...
結果的には
function patchWasmModuleImport(config, isServer) {
config.plugins.push(
new (class {
apply(compiler) {
compiler.hooks.afterEmit.tapPromise('SymlinkWebpackPlugin', async compiler => {
if (isServer) {
const from = path.join(compiler.options.output.path, '../static');
const to = path.join(compiler.options.output.path, 'static');
try {
await fs.access(from);
return;
} catch (error) {
if (error.code === 'ENOENT') {
// No link exists
} else {
throw error;
}
}
await fs.symlink(to, from, 'junction');
console.log(`created symlink ${from} -> ${to}`);
}
});
}
})()
);
}
このような symlink を貼る script を用意して NextConfig の webpack に噛ませたら動いた。
まだまだ wasm 周りは未解決の罠が多い...