From 56f2ca5547218df78ce8fb88f7be7da0b0ee0794 Mon Sep 17 00:00:00 2001 From: Francois Valdy Date: Tue, 10 Mar 2020 00:55:20 +0100 Subject: [PATCH 1/4] Change worker chunk name from to w Fixes #43, as it avoids conflicts with other number-based webpack chunks. Note that the conflict may not be on the filename (since this plugin adds '.worker' to it) but on the internal key used by webpack in referencing modules. --- src/index.js | 2 +- test/index.test.js | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index 561a1f9..73bd467 100644 --- a/src/index.js +++ b/src/index.js @@ -67,7 +67,7 @@ export default class WorkerPlugin { return false; } - const loaderOptions = { name: opts.name || workerId + '' }; + const loaderOptions = { name: opts.name || 'w' + workerId }; const req = `require(${JSON.stringify(workerLoader + '?' + JSON.stringify(loaderOptions) + '!' + dep.string)})`; const id = `__webpack__worker__${workerId++}`; ParserHelpers.toConstantDependency(parser, id)(expr.arguments[0]); diff --git a/test/index.test.js b/test/index.test.js index fe88507..0df7bba 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -38,7 +38,7 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('0.worker.js'); + expect(assetNames).toContainEqual('w0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new\s+Worker\s*\([^)]*\)[^\n]*/g); @@ -56,7 +56,7 @@ describe('worker-plugin', () => { /new\s+Worker\s*\(\s*__webpack__worker__\d\s*(,\s*\{\s+type\:\svoid [0]\s+\}\s*)?\)/g ); - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"0\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w0\.worker\.js"/g); }); test('it replaces multiple Worker exports with __webpack_require__', async () => { @@ -68,12 +68,12 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(3); - expect(assetNames).toContainEqual('0.worker.js'); - expect(assetNames).toContainEqual('1.worker.js'); + expect(assetNames).toContainEqual('w0.worker.js'); + expect(assetNames).toContainEqual('w1.worker.js'); const main = stats.assets['main.js']; - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"0\.worker\.js"/g); - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"1\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w0\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w1\.worker\.js"/g); }); test('retainModule:true leaves {type:module} in worker init', async () => { @@ -248,8 +248,8 @@ describe('worker-plugin', () => { await sleep(1000); stats = await ready; await sleep(1000); - expect(Object.keys(stats.assets).sort()).toEqual(['0.worker.js', 'main.js']); - expect(stats.assets['0.worker.js']).toContain(`hello from worker ${i}`); + expect(Object.keys(stats.assets).sort()).toEqual(['main.js', 'w0.worker.js']); + expect(stats.assets['w0.worker.js']).toContain(`hello from worker ${i}`); } } finally { watcher.close(); From 13e462ad50b0b3caade2e232de0873f9165d69d3 Mon Sep 17 00:00:00 2001 From: Jason Miller Date: Tue, 10 Mar 2020 18:18:38 -0400 Subject: [PATCH 2/4] update ESM tests too --- test/index.test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/index.test.js b/test/index.test.js index 817cd04..4126098 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -193,7 +193,7 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('0.worker.js'); + expect(assetNames).toContainEqual('w0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new Worker\s*\([^)]*\)[^\n]*/g); @@ -202,7 +202,7 @@ describe('worker-plugin', () => { expect(log).toMatch(/_worker__WEBPACK_IMPORTED_MODULE_\d__\["default"\]/gi); // should also put the loader into ESM mode: - expect(main).toMatch(/__webpack_exports__\["default"\]\s*=\s*\(?\s*__webpack_require__\.p\s*\+\s*"0\.worker\.js"\)?;?/g); + expect(main).toMatch(/__webpack_exports__\["default"\]\s*=\s*\(?\s*__webpack_require__\.p\s*\+\s*"w0\.worker\.js"\)?;?/g); // the output (in dev mode) looks like this: // /* harmony default export */ __webpack_exports__[\"default\"] = (__webpack_require__.p + \"0.worker.js\"); }); @@ -221,13 +221,13 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('0.worker.js'); + expect(assetNames).toContainEqual('w0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new Worker\s*\([^)]*\)[^\n]*/g); const log = main.match(/new Worker\s*\(([^)]*)\)[^\n]*/)[1]; - expect(log).toMatch(/^[a-z0-9$_]+\.p\s*\+\s*(['"])0\.worker\.js\1/gi); + expect(log).toMatch(/^[a-z0-9$_]+\.p\s*\+\s*(['"])w0\.worker\.js\1/gi); // shouldn't be any trace of the intermediary url provider module left expect(main).not.toMatch(/export default/g); From 5fc7484a3cc21559d5e55de09fedd4973b219b60 Mon Sep 17 00:00:00 2001 From: Francois Valdy Date: Wed, 11 Mar 2020 15:05:23 +0100 Subject: [PATCH 3/4] Implement alternative approach - chunkFilename is preserved from compilerOptions - use chunkName = worker name (or ID) + '.worker' - allow to override chunkFilename is needed (#19) --- src/index.js | 2 +- src/loader.js | 5 +++-- test/index.test.js | 32 ++++++++++++++++---------------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/index.js b/src/index.js index 5096e8b..1ecd73c 100644 --- a/src/index.js +++ b/src/index.js @@ -79,7 +79,7 @@ export default class WorkerPlugin { const isStrictModule = esModule || (parser.state.buildMeta && parser.state.buildMeta.strictHarmonyModule); // Querystring-encoded loader prefix (faster/cleaner than JSON parameters): - const workerName = opts.name || `w${workerId}`; + const workerName = `${opts.name || workerId}.worker`; const loaderRequest = `${workerLoader}?name=${encodeURIComponent(workerName)}${isStrictModule ? '&esModule' : ''}!${dep.string}`; // Unique ID for the worker URL variable: diff --git a/src/loader.js b/src/loader.js index ae8da6e..0947534 100644 --- a/src/loader.js +++ b/src/loader.js @@ -38,7 +38,8 @@ export function pitch (request) { } const options = loaderUtils.getOptions(this) || {}; - const chunkFilename = compilerOptions.output.chunkFilename.replace(/\.([a-z]+)$/i, '.worker.$1'); + const chunkName = options.name || 'worker'; + const chunkFilename = pluginOptions.chunkFilename || compilerOptions.output.chunkFilename; const workerOptions = { filename: chunkFilename.replace(/\[(?:chunkhash|contenthash)(:\d+(?::\d+)?)?\]/g, '[hash$1]'), chunkFilename, @@ -62,7 +63,7 @@ export function pitch (request) { (new FetchCompileWasmTemplatePlugin({ mangleImports: compilerOptions.optimization.mangleWasmImports })).apply(workerCompiler); - (new SingleEntryPlugin(this.context, request, options.name)).apply(workerCompiler); + (new SingleEntryPlugin(this.context, request, chunkName)).apply(workerCompiler); const subCache = `subcache ${__dirname} ${request}`; workerCompiler.hooks.compilation.tap(NAME, compilation => { diff --git a/test/index.test.js b/test/index.test.js index 4126098..d58e521 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -38,7 +38,7 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('w0.worker.js'); + expect(assetNames).toContainEqual('0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new\s+Worker\s*\([^)]*\)[^\n]*/g); @@ -56,7 +56,7 @@ describe('worker-plugin', () => { /new\s+Worker\s*\(\s*__webpack__worker__\d\s*(,\s*\{\s+type\:\svoid [0]\s+\}\s*)?\)/g ); - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w0\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"0\.worker\.js"/g); }); test('it replaces multiple Worker exports with __webpack_require__', async () => { @@ -68,12 +68,12 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(3); - expect(assetNames).toContainEqual('w0.worker.js'); - expect(assetNames).toContainEqual('w1.worker.js'); + expect(assetNames).toContainEqual('0.worker.js'); + expect(assetNames).toContainEqual('1.worker.js'); const main = stats.assets['main.js']; - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w0\.worker\.js"/g); - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"w1\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"0\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"1\.worker\.js"/g); }); test('retainModule:true leaves {type:module} in worker init', async () => { @@ -150,8 +150,8 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual(expect.stringMatching(/^foo\.[a-zA-Z0-9]+\.worker\.js$/)); - expect(stats.assets['main.js']).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"foo\.[a-zA-Z0-9]+\.worker\.js"/g); + expect(assetNames).toContainEqual(expect.stringMatching(/^foo\.worker\.[a-zA-Z0-9]+\.js$/)); + expect(stats.assets['main.js']).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"foo\.worker\.[a-zA-Z0-9]+\.js"/g); }); test('it bundles WASM file which imported dynamically', async () => { @@ -193,7 +193,7 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('w0.worker.js'); + expect(assetNames).toContainEqual('0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new Worker\s*\([^)]*\)[^\n]*/g); @@ -202,7 +202,7 @@ describe('worker-plugin', () => { expect(log).toMatch(/_worker__WEBPACK_IMPORTED_MODULE_\d__\["default"\]/gi); // should also put the loader into ESM mode: - expect(main).toMatch(/__webpack_exports__\["default"\]\s*=\s*\(?\s*__webpack_require__\.p\s*\+\s*"w0\.worker\.js"\)?;?/g); + expect(main).toMatch(/__webpack_exports__\["default"\]\s*=\s*\(?\s*__webpack_require__\.p\s*\+\s*"0\.worker\.js"\)?;?/g); // the output (in dev mode) looks like this: // /* harmony default export */ __webpack_exports__[\"default\"] = (__webpack_require__.p + \"0.worker.js\"); }); @@ -221,13 +221,13 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('w0.worker.js'); + expect(assetNames).toContainEqual('0.worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*new Worker\s*\([^)]*\)[^\n]*/g); const log = main.match(/new Worker\s*\(([^)]*)\)[^\n]*/)[1]; - expect(log).toMatch(/^[a-z0-9$_]+\.p\s*\+\s*(['"])w0\.worker\.js\1/gi); + expect(log).toMatch(/^[a-z0-9$_]+\.p\s*\+\s*(['"])0\.worker\.js\1/gi); // shouldn't be any trace of the intermediary url provider module left expect(main).not.toMatch(/export default/g); @@ -246,7 +246,7 @@ describe('worker-plugin', () => { const assetNames = Object.keys(stats.assets); expect(assetNames).toHaveLength(2); - expect(assetNames).toContainEqual('0.worker.js'); + expect(assetNames).toContainEqual('worker.js'); const main = stats.assets['main.js']; expect(main).toMatch(/[^\n]*console.log\s*\([^)]*\)[^\n]*/g); @@ -254,7 +254,7 @@ describe('worker-plugin', () => { const log = main.match(/\bconsole\.log\s*\(([^)]*)\)[^\n]*/)[1]; expect(log).toMatch(/worker_plugin_loader_worker__WEBPACK_IMPORTED_MODULE_\d___default.[a-z0-9]+/gi); - expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"0\.worker\.js"/g); + expect(main).toMatch(/module.exports = __webpack_require__\.p\s*\+\s*"worker\.js"/g); }); }); @@ -300,8 +300,8 @@ describe('worker-plugin', () => { await sleep(1000); stats = await ready; await sleep(1000); - expect(Object.keys(stats.assets).sort()).toEqual(['main.js', 'w0.worker.js']); - expect(stats.assets['w0.worker.js']).toContain(`hello from worker ${i}`); + expect(Object.keys(stats.assets).sort()).toEqual(['0.worker.js', 'main.js']); + expect(stats.assets['0.worker.js']).toContain(`hello from worker ${i}`); } } finally { watcher.close(); From a874ca1af7ebdb7ea9de65a7d94012e944681c69 Mon Sep 17 00:00:00 2001 From: Francois Valdy Date: Wed, 11 Mar 2020 15:14:33 +0100 Subject: [PATCH 4/4] Doc contribution --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e28bb27..8240536 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,12 @@ Similarly, if you need to have WorkerPlugin output a specific `type` value, use ] ``` +### `chunkFilename` _(string)_ + +By default this plugin will output assets according to parent `output.chunkFilename` setting (with chunk `[name]` being `WORKERNAME.worker`). + +If you want another pattern (e.g. constant, no hash) for your workers, you may override this default behavior with a pattern of your choosing. + ## Loader At its core, worker-plugin provides two features: parsing and handling of `new Worker()`, and standalone bundling of modules for use in a different JavaScript context. @@ -163,7 +169,7 @@ If all you want is to compile separate bundles for a module, `worker-plugin/load ```js import workerUrl from 'worker-plugin/loader!./my-worker'; -console.log(workerUrl); // "/0.worker.js" +console.log(workerUrl); // "/worker.js" CSS.paintWorklet.addModule(workerUrl); ``` @@ -178,7 +184,7 @@ Two options are available: Options can be supplied inline: ```js -import url from 'worker-plugin/loader?name=foo&esModule!./foo'; +import url from 'worker-plugin/loader?name=foo&esModule!./foo'; // "/foo.worker.js" ``` ... or by setting up a loader alias: