3

I am writing an npm package which is a plugin for the popular library leafletjs. I am using webpack to bundle the package. I want this package to be able to spawn and destroy some web workers on command. The web worker code is part of my source files. But I want to be able to distribute my package both as an npm module, or through a cdn, meaning it must be compiled down to a singular file that can be included through an HTML header. I am using webpack to do it. So lets say I have a worker file:

// sample.worker.js

import { doSomeStuff } from './algorithm.js';

onmessage = function (e) {
  const results = doSomeStuff(e.data)
  postMessage({
    id: e.data.id,
    message: results',
  });
};

Fairly simple, but an important point here is that my worker is actually importing some code from an algorithm file, which is in turn importing some node modules. My worker is used in the main module somewhere, like this:

// plugin.js

import SampleWorker from 'worker-loader!./workers/dem.worker.js';

export function plugin(){
  // do some stuff
  const worker = new SampleWorker()
  worker.postMessage(someData);
  worker.onmessage = (event) => {};
}

My webpack config looks like this:

module.exports = {
  entry: './src/index',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'my-plugin.js',
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.json'],
  },
  module: {
    rules: [
      {
        test: /\.worker\.js$/,
        loader: 'worker-loader',
        options: {
          inline: 'fallback',
        },
      },
      {
        test: /\.(ts|js)x?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
      },
    ],
  },
  externals: { ... },
  plugins: { ... }
};

This doesn't work as is - webpack tries to bundle the main bundle and each worker script file under the same name. Changing to filename: '[name].js' under output fixes this issue, but gives me many files - one for the main bundle, and another file for each worker file in my source code.

Reading the webpack options, I thought that using the inline: 'fallback' option would actually create a Blob for each worker and bundle that into the main output file. That is not happening.

So far my solution is to write my workers as blobs, like this:

// workers.js

const workerblob = new Blob([`

  // cannot import in a blob! 
  // have to put algorithm.js code here, copy in any external module code here

  onmessage = function (e) {
    const results = doSomeStuff(e.data)
    postMessage({
      id: e.data.id,
      message: results,
    });
  };

`])

export const sampleWorker = URL.createObjectURL(workerblob);

// my-plugin.js
import { sampleWorker } from 'workers.js'

const worker = new Worker(sampleWorker)

This does in fact work - webpack now outputs 1 single file which includes the worker code. Using a modified version of this from this answer, I can at least place my code inside a function( ...code... ){}.toString() format, so I can at least get intellisense, syntax highlighting, etc. But I cannot use imports.

How can I use webpack to bundle my workers so that the entire bundle ends up in 1 file, worker code and all?

Seth Lutske
  • 9,154
  • 5
  • 29
  • 78
  • 1
    I don't know webpack at all, but from a raw perspective you'd need to import that worker script as text in your file (apparently raw-loader does this), but you'd need this to also be able to interpret inner imports. The best would be that at the end you have `const imported_script = \`[script content]\`; const worker_script = \`${ imported_script } worker script content\`; const worker_url = URL.createObjectURL( new Blob([worker_content]) )`. Could be done manually, but once again I don't know how to do it with webpack... – Kaiido Oct 22 '20 at 08:17
  • So this is interesting...it solves my issue of not being able to use imports, but it does not solve the issue of writing code in a string. [This solution](https://stackoverflow.com/questions/5408406/web-workers-without-a-separate-javascript-file/19201292#19201292) that I had mentioned solves the string issue, but not the import issue. I suppose I could develop the `worker script content` in another environment, and just copy paste. I wonder if there would be a way to solve both at once? – Seth Lutske Oct 22 '20 at 15:27
  • @Kaiido! I'm not sure if this is appropriate SO etiquette, but I posted another question yesterday, and you are exactly the person I was hoping would take a look at it, as you have helped in the past with canvases and image processing: [question here](https://stackoverflow.com/questions/64472039/getimagedata-not-the-same-after-run-through-createimagebitmap) – Seth Lutske Oct 22 '20 at 15:29

0 Answers0