A somewhat opinionated Rollup plugin for emitting web workers as chunks.
Instead of directly specifying the web worker's URL, you can import it through the plugin:
import webWorkerUrl from 'web-worker-url:./path-to-web-worker-entrypoint';
Add the registry to .npmrc
:
@alorel:registry=https://npm.pkg.github.com
Then install it:
npm install @alorel/rollup-plugin-web-worker
Of course there's caveats and gotchas...
Feel free to open a PR if you want to add support for other output formats.
I.e. you must either load Systemjs from a static URL or emit it as a separate asset as part of your build. Since this asset will be accessed twice - once by the main thread and once by the worker thread - it should really be cached so it doesn't slow down the user experience.
The worker chunk needs to be imported via System.import
to make use of shared dependencies and generally work with
the output format. It also needs to import systemjs as the worker doesn't have access to the main thread's Systemjs
instance. As a result, each worker effectively results in two files:
- The worker chunk containing worker code
- A loader asset that imports the module loader and the worker:
importScripts('/path-to-systemjs.js');
System.import('/path-to-worker.js');
// some-bundled-file.js
import webWorkerUrl from 'web-worker-url:./path-to-web-worker-entrypoint';
const worker = new Worker(webWorkerUrl);
This will create a chunk for ./path-to-web-worker-entrypoint
.
// rollup.config.js
import {RollupWebWorkerPlugin} from '@alorel/rollup-plugin-web-worker';
// Accepts an optional RollupWebWorkerPluginOpts object; see API below
const webWorkerPlugin = new RollupWebWorkerPlugin();
export default {
// ...
output: {
// ...
format: 'system', // only systemjs currently supported
plugins: [
webWorkerPlugin.createOutputPlugin({/* See RollupWebWorkerPluginOutputOpts API for required and optional options */})
]
},
plugins: [
webWorkerPlugin.createPlugin()
]
}
/** Worker chunk name generator function */
type NameFunction = (absolutePath: string) => string;
/** Plugin options */
interface RollupWebWorkerPluginOpts {
/**
* Worker chunk name.
*
* If given a string, [name] will be replaced with the worker file name.
* If given a function, it'll be called with the worker file's absolute path and needs to return a string.
* @default [name]
*/
name?: string | NameFunction;
}
/** Function for filtering through emitted chunks & asset to find the one for the module loader */
type ModuleLoaderFunction = (file: OutputAsset | OutputChunk) => boolean;
/** Output plugin options */
interface RollupWebWorkerPluginOutputOpts {
/**
* If given a string, this is the static URL of your module loader file
* If given a function, it will be used to filter through all the emitted chunks and should return true for at least
* one of them; that chunk's URL will then be used as an importScripts() arg.
*/
moduleLoader: string | ModuleLoaderFunction;
/**
* Your asset output base path. If emitted chunks' and assets' URL is /assets/<filename> then this needs to be set
* to /assets/
* @default /
*/
publicPath?: string;
}
If your module loader is located at a static URL then this can be set to a string. If you bundle your module loader as part of the build then you'll need to specify a function.
The function will be passed as an Array.prototype.filter
argument on the array of chunks and assets generated during
the Rollup build and must match exactly one chunk or asset. The matched chunk or asset's URL will then be used to
import the module loader in the web worker chunk.
You can let typescript know how to handle web-worker-url:*
imports by adding the following to your tsconfig.json
:
{
"files": [
"node_modules/@alorel/rollup-plugin-web-worker/moduledef.d.ts"
]
}
See also: src/**/*.spec.ts
files and test/fixtures
directory.
const webWorkerPlugin = new RollupWebWorkerPlugin();
const regex = /system(\..+)?\.js/
function isSystemjsAsset(chunk) {
return chunk.type === 'asset' && regex.test(chunk.fileName);
}
export default {
// ...
output: {
// ...
plugins: [
webWorkerPlugin.createOutputPlugin({moduleLoader: isSystemjsAsset})
]
}
}