LoginSignup
0
0

Next.js で wasm が含まれるライブラリを使う場合は `NODE_OPTIONS=--experimental-wasm-modules` が必要

Last updated at Posted at 2024-01-10

Next.js で wasm ベースのライブラリを使いたくなった。
というか自前で Rust のコードを wasm にしてライブラリを作ったので、それを使いたくなった。

だが一筋縄ではいかなかったので、その備忘録

wasm のコードを asset として配信する場合はそんな問題ない

自分で asset として wasm ファイルを置いておき、それを自前で読み込むとかの場合には特に罠はない。webpack の設定をよしなにしてやれば良いだけである。(SSR とかで Node が絡んでくると後述のように厄介になるので、dynamic import なりで解決する必要はある)

こちらの記事がわかりやすかった。
https://zenn.dev/razokulover/articles/fb64150be7a667

wasm が含まれるライブラリを普通に import して使いたい場合

この場合、巷に溢れている情報だけだと上手くいかなかった。

まずは必要な Webpack の設定

next.config.js
/** @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-modulesnext dev の前に入れてあげると普通に読み込んでくれるようになった。結構これに辿り着くまでに時間かけてしまったので、誰かがこの記事で時間を節約できることを祈って :pray:

余談:production build で Error: ENOENT: no such file or directory が発生する場合

本筋から逸れるが、Production Build だと上記の webpack config では動かなかった...:cry:

調べると、一番手軽な解決のための hack としては

if (isServer) {
  config.output.webassemblyModuleFilename = './../static/wasm/[hash].wasm';
} else {
  config.output.webassemblyModuleFilename = 'static/wasm/[hash].wasm';
}

のようなものがよく見られたが、next: 14.0.4 だと全然上手く動かず苦労した...

結果的には

next.config.js
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 周りは未解決の罠が多い...

0
0
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
0
0