Cannot import wasm in web workers

Bug report

What is the current behavior?

Importing wasm in web workers throws an error.

If the current behavior is a bug, please provide the steps to reproduce.

Webpack configuration:

This is basically a merge of the worker + wasm examples.

const webpack = require('webpack')
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const prod = process.env.NODE_ENV === 'production'

module.exports = {
    entry: './src/js/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: prod ? 'js/main.[chunkhash].js' : 'js/main.js',
        webassemblyModuleFilename: '[modulehash].wasm',
        globalObject: 'this'
    },
    module: {
        rules: [
            {
                test: /\.wasm$/,
                type: 'webassembly/experimental'
            }
        ]
    },
    plugins: [
        new HtmlWebpackPlugin({ template: './src/index.html' }),
        new webpack.LoaderOptionsPlugin()
    ]
};

index.js

var Worker = require("worker-loader!./worker")
var worker = new Worker()
worker.onmessage = event => {
    console.log('mainthread got:', event.data)
}

worker.js

onmessage = event => {
    import('../wasm/hello_world.wasm').then(module => {
        postMessage(module.add_one(event.data))
    })
}

This leads to :

Uncaught (in promise) TypeError: Cannot read property './src/wasm/hello_world.wasm' of undefined

But the same wasm import works when put in index.js.

What is the expected behavior?

We should be able to import wasm in workers just as we can in normal js files.

Other relevant information:
webpack version: 4.14
Node.js version: 10.5.0
Operating System: Arch
Additional tools:

Author: Fantashit

3 thoughts on “Cannot import wasm in web workers

  1. Okay, thanks for the info.

    For anyone trying to solve the same thing, i got it working like this: (see https://github.com/cars10/rust-wasm-webworker-example for an example)

    webpack config

    const path = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    const prod = process.env.NODE_ENV === 'production'
    
    module.exports = {
        entry: './src/js/index.js',
        output: {
            path: path.resolve(__dirname, 'dist'),
            filename: 'js/main.[hash].js',
            globalObject: 'this'
        },
        module: {
            rules: [
                {
                    test: /worker\.js$/,
                    use: {
                        loader: 'worker-loader',
                        options: {
                            name: 'js/worker.[hash].js'
                        }
                    }
                },
                {
                    test: /\.wasm$/,
                    type: 'javascript/auto',
                    use: [
                        {
                            loader: 'file-loader',
                            options: {
                                name: 'wasm/[name].[hash].[ext]',
                                publicPath: '../'
                            }
                        }
                    ]
                }
            ]
        },
        plugins: [
            new HtmlWebpackPlugin({ template: './src/index.html' })
        ],
        mode: prod ? 'production' : 'development'
    };

    index.js

    import Worker from './worker.js';
    
    var worker = new Worker
    worker.postMessage(1)
    worker.onmessage = event => {
        console.log('mainthread got:', event.data)
    }

    worker.js

    import wasm from '../wasm/hello_world.wasm';
    
    onmessage = event => {
        WebAssembly.instantiateStreaming(fetch(wasm))
            .then(results => console.log(results.instance.exports.add_one(12)));
        console.log('got some vent')
    }
  2. I was able to use Wasm in a web worker by avoiding use of the worker-loader plugin and choosing the webworker target:

    webpack.config.js

    const path = require('path')
    
    const browserConfig = {
      entry: './app.js',
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "app.js",
      },
      mode: "development"
    }
    
    const workerConfig = {
      entry: "./worker.js",
      target: 'webworker',
      output: {
        path: path.resolve(__dirname, "dist"),
        filename: "worker.js"
      },
      mode: "development",
    }
    
    module.exports = [browserConfig, workerConfig]

    worker.js

    // A dependency graph that contains any wasm must all be imported
    // Can also import() any module that has 
    // `import * as wasm from '../pkg/hello_world_bg.wasm'`
    import("../pkg/hello_world_bg.wasm")
      .then(wasm => {
        self.postMessage(wasm.hello_world())
      })
      .catch(err => console.error("Error importing `hello_world_bg.wasm`:", err));

    app.js

    var worker = new Worker('./worker.js')
    worker.addEventListener('message', function (evt) {
      console.log(evt.data) // 'Hello World'
    })

Comments are closed.