Search code examples
reactjsvitewebassemblyweb-workercomlink

WebAssembly module cant locate .data file when loaded in Comlink WebWorker in React Vite


I try to load a C WebAssembly Module in a React Vite App inside a WebWorker. When loading the Module in the main thread everything works as expected and the wasm.js can locate the wasm.data file inside /public folder. But when loading the Module inside a Comlink Webworker the Module can not locate the .data file and throws following Error:

pFlowWeb.js:9 Uncaught Error: Not Found : http://localhost:5173/src/wasm/internal:comlink:../webWorker/pFlowWeb.data
    at xhr.onload (pFlowWeb.js:9:2505)

For the worker i followed How to use Web Workers with React and Vite . The worker on its own works fine. Also the module loads fine inside the main thread.

The WebAssembly Module is compiled with following flags:

$EMSCRIPTEN_CC -O2  ... --preload-file 00Input/ \
    -s ALLOW_MEMORY_GROWTH=1 -s --no-heap-copy -sFORCE_FILESYSTEM\
    -s EXPORTED_FUNCTIONS="['_main']" \
    -s EXPORT_ES6=1 -s ENVIRONMENT='web' -s MODULARIZE=1 -s USE_ES6_IMPORT_META=0 -o pFlowWeb.js ...

The Module is loaded like this

// wasmClient.ts
import pFLowWeb from "./pFlowWeb.js";

export class pFlowModule {
  ...
  // initialize the module
  setup = async () => {
    this.module = await pFLowWeb();
  };
  ...
}

in the same file the worker instance is declared

// wasmClient.ts
export const workerInstance = new ComlinkWorker<
  typeof import("../webWorker/worker")
>(new URL("../webWorker/worker", import.meta.url));

Then finally the worker

// webWorker/worker.ts
/// <reference lib="webworker" />
declare const self: DedicatedWorkerGlobalScope;

import { pFlowModule } from "./../wasm/wasmClient";

export const setupModule = async () => {
  const newModule = new pFlowModule();
  await newModule.setup();
};

Which is then called in the main thread:

// Simulation.ts
import { workerInstance } from "../../wasm/wasmClient";

const handleInitClick = () => {
    workerInstance.setupModule();
  };

I had the module inside the web worker running in a Vue App by following the instructions from this post on How to Use WebAssembly Modules in a Web Worker . I tried to add webpack and the loaders to the react app but in my understanding vite should replace webpack and its loaders so i did not bring it to work.

Then i tried to include the wasm assets in the vite config

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [comlink(), react()],
  worker: {
    plugins: [comlink()],
  },
  assetsInclude: ["**/*.wasm", "**/*.data"],
});

which had no effect.

I have also taken a look at the Module.locateFile method of the web assembly Module. But i don't see how to call the method before initializing the module.


Solution

  • I worked it out! I used vite-static-copy plugin to create a copy of the files the wasm module needed and placed them at the destination it was looking for them.

    Feels kinda hacky but works for me!

    // vite.config.ts
    
    import { viteStaticCopy } from "vite-plugin-static-copy";
    
    export default defineConfig({
     plugins: [
        viteStaticCopy({
              targets: [
                {
                  src: "public/simulation/pFlowWeb.data",
                  dest: "src/wasm/internal:comlink:../webWorker/",
                },
                {
                  src: "public/simulation/pFlowWeb.wasm",
                  dest: "src/wasm/internal:comlink:../webWorker/",
                },
              ],
            }),
    ...