How to bundle WebWorker coded with TypeScript in npm package

dannadori
4 min readOct 1, 2021

Note:
This article is also available here.(Japanese)
https://zenn.dev/wok/articles/0020_bundle-webworker

Note2:
The content described here was tested with webpack5, but it seems to be for webpack4.

Note that this is specific to webpack 4. To use Web Workers in webpack 5, see https://webpack.js.org/guides/web-workers/.

The linked page says that webpack5 will work without the worker-loader, but I couldn’t for my purpose. I looked at github and it looks like it can be done by using asset/inline, but this issue finally use webworker coded with Javascript. “no, it’s not”. I want to start the WebWorker by specifying the WebWorker’s Typescript file on the WebWorker caller’s code.

So, for now, I’ve settled on what I’ve described here. (The worker-loader is deprecated in webpack5, but I’ve decided that it’s not an option for now. If there is a better way, please let me know…)

Introduction

I’ve been creating npm packages to run various machine learning models with WebWorker, but I couldn’t figure out how to bundle WebWorker created with TypeScript into a single file. So I had written an installation in the readme that would have you copy the WebWorker script to a public folder after npm install. Like this.

$ npx create-react-app demo  --template typescript 
$ cd demo/
$ npm install
$ npm install @dannadori/bodypix-worker-js
$ cp node_modules/\@dannadori/bodypix-worker-js/dist/bodypix-worker-worker.js public/ # <- Here!! Too lame!!

It’s lame. I decided to do something about it, so I researched and found that I could do it, and I’ll show you how to do it here.

Premise

I’m assuming webpack 5 is used, I haven’t tried it with webpack 4.

Bundle a file created with a js file

The first step is to bundle a WebWorker created with javascript.

When using webpack to bundle WebWorker, it is convenient to use worker-loader.

$ npm install -D worker-loader

Next, add a rule to webpack.config.js. Add a rule to webpack.config.js to use the worker-loader for WebWorker javascript (in this case, the file name ends with worker.js).

Add inline: “no-fallback” as an option. This will cause the WebWorker javascript to be bundled with the output file.

rules: [
{ test: /\.ts$/, loader: 'ts-loader' },
{
test: /worker\.js$/i,
loader: "worker-loader",
options: {
inline:"no-fallback"
},
},
],

Here is the code for the worker. If you are reading this article, you are probably familiar with creating a WebWorker, so I will skip the explanation.

onmessage = function (event) {
setTimeout(() => {
postMessage(`[WORKER_JS] Waited ${event.data}ms`);
}, event.data)
};

The main code of the npm package imports and calls the WebWorker file.

import libJs from '../src-worker/worker.js'export class WebWorkerLibJs{
worker:Worker
constructor(){
this.worker = libJs()
this.worker.onmessage = (mess) =>{
console.log(`[WebWorkerLibJs] ${mess.data}`)
}
}
sendMessage = () =>{
this.worker.postMessage(2 * 1000);
}
}

That’s all. It’s really easy.

Bundle a file created with a ts file

The next is to bundle the WebWorker created in the typescript file.

When using webpack to bundle WebWorker, it is convenient to use worker-loader. Same as the javascript case.

$ npm install -D worker-loader

No additional configuration to webpack.config.js is required. Of course, you need to configure ts-loader to compile TS.

rules: [
{ test: /\.ts$/, loader: 'ts-loader' },
],

Configure the type setting to use worker-loader. ./typings/worker-loader.d.ts

declare module "worker-loader!*" {
class WebpackWorker extends Worker {
constructor();
}
export default WebpackWorker;
}

This is worker code.

const ctx: Worker = self as any;

onmessage = async (event) => {
setTimeout(() => {
ctx.postMessage(`[WORKER_TS] Waited ${event.data}ms`);
}, event.data)

}

In the main code of the npm package, we will import the WebWorker files and make them new. The key point is that when importing the WebWorker file, we use ? to set the inline parameter. Without this, it will not bundle them into a single file.

import libTs from "worker-loader?inline=no-fallback!./worker.ts";

export class WebWorkerLibTs{
worker:Worker
constructor(){
this.worker = new libTs()
this.worker.onmessage = (mess) =>{
console.log(`[WebWorkerLibTs] ${mess.data}`)
}
}

sendMessage = () =>{
this.worker.postMessage(3 * 1000);
}
}

That’s all. It’s really easy too.

Repository

The contents described here are stored in the following repository.

Follow the readme and try to run the commands.

The amount of code is small, so you should be able to understand it in about 10 minutes while running it.

I am very thirsty!!

Reference

https://v4.webpack.js.org/loaders/worker-loader/

--

--