Hi I just discovered esbuild, coming from Parcel. One thing I like about parcel is that if I instaniate a WebWorker with the string literal filename, like
const ww = new Worker ('./myWorker.js')
ww.postMessage('work!')
Parcel will recognize the Worker
constructor and create another bundle starting at ./myWorker.js
. It also handles cache busting for the filename. So the bundle would be called ./myWorker8a68r8912q.js
or something and that string would be updated in the code above.
Does ESBuild do something like that? If not, where would I look to implement that?
Oh, interesting. That's pretty cool. I didn't know about that feature of Parcel. I assume this also works for the SharedWorker
constructor. You should be able to get this to work with the current version of esbuild by just adding myWorker.js
as an additional entry point in the list of entry points passed to esbuild.
However, the cache busting part won't work yet. None of the entry points for esbuild are cache-busted right now. I'm working on enabling this as part of general code splitting support (see #16) but it's still a work in progress. Introducing cache busting in entry point names is also a breaking change and I've been waiting to do this until the next batch of breaking changes. Once that's in, I think it should be relatively straightforward to make something like this work.
This sort of feature is sorely needed in Rollup and can be achieved in Webpack with something like worker-plugin.
Having this built into esbuild as an opt-in would be absolutely fantastic! I think deferring to plugins would likely not do it 'right' as these would need to parse an AST, figure out bindings, etc.. (or just RegEx and yolo).
There are other web apis that would benefit from this form of non require
, non import()
-based code splitting like audio worklets.
I think this would be a really nice feature.
As a stopgap, I was thinking this could be implemented as an esbuild plugin. This would impose some rather unfortunate syntax, but in a pinch, something like this would work:
import workerUrl from "workerUrl(./worker.js)";
const worker = new Worker(workerUrl)
import path from "path";
import { build } from "esbuild";
let workerLoader = (plugin) => {
plugin.setName("worker-loader");
plugin.addResolver({ filter: /^workerUrl\((.+)\)/ }, (args) => {
return { path: args.path, namespace: "workerUrl" };
});
plugin.addLoader(
{ filter: /^workerUrl\((.+)\)/, namespace: "workerUrl" },
async (args) => {
let match = /^workerUrl\((.+)\)/.exec(args.path),
workerPath = match[1];
let outfile = path.join("dist", path.basename(workerPath));
try {
// bundle worker entry in a sub-process
await build({
entryPoints: [workerPath],
outfile,
minify: true,
bundle: true,
});
// return the bundled path
return { contents: `export default ${JSON.stringify(workerPath)};` };
} catch (e) {
// ...
}
}
);
};
Besides path resolution within new Worker(…)
etc, it would also be nice to enable an entrypoint to be generated as a worker script, which means it will not use import * as x from "./x.js"
but rather importScripts("./x.js")
– at least until all browsers implement new Worker(…, { type: "module" })
(https://github.com/whatwg/html/issues/550).
Most helpful comment
Oh, interesting. That's pretty cool. I didn't know about that feature of Parcel. I assume this also works for the
SharedWorker
constructor. You should be able to get this to work with the current version of esbuild by just addingmyWorker.js
as an additional entry point in the list of entry points passed to esbuild.However, the cache busting part won't work yet. None of the entry points for esbuild are cache-busted right now. I'm working on enabling this as part of general code splitting support (see #16) but it's still a work in progress. Introducing cache busting in entry point names is also a breaking change and I've been waiting to do this until the next batch of breaking changes. Once that's in, I think it should be relatively straightforward to make something like this work.