From 3f93c864427512d03d436d6c4c7ede8f2ffa9e58 Mon Sep 17 00:00:00 2001 From: Kenrick Date: Wed, 13 Jan 2021 10:27:07 +0800 Subject: [PATCH 1/5] Adapt to Webpack 5 --- src/index.js | 64 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/index.js b/src/index.js index db75312..a3ef05a 100644 --- a/src/index.js +++ b/src/index.js @@ -38,7 +38,7 @@ loader.pitch = function(request) { const cb = this.async(); - const filename = loaderUtils.interpolateName(this, `${options.name || '[hash]'}.worker.js`, { + const filename = loaderUtils.interpolateName(this, `${options.name || '[fullhash]'}.worker.js`, { context: options.context || this.rootContext || this.options.context, regExp: options.regExp }); @@ -89,22 +89,37 @@ loader.pitch = function(request) { compilationHook(worker.compiler, (compilation, data) => { if (compilation.cache) { - if (!compilation.cache[subCache]) compilation.cache[subCache] = {}; - - compilation.cache = compilation.cache[subCache]; + let cache; + if (compilation.cache instanceof Map) { + cache = compilation.cache.get(subCache); + if (!cache) { + cache = new Map(); + compilation.cache.set(subCache, cache); + } + } + else if (!compilation.cache[subCache]) { + cache = compilation.cache[subCache] = {}; + } + + compilation.cache = cache; } parseHook(data, (parser, options) => { exportDeclarationHook(parser, expr => { - let decl = expr.declaration || expr, - { compilation, current } = parser.state, - entry = compilation.entries[0].resource; + let decl = expr.declaration || expr; + let { compilation, current } = parser.state; + + let entryModule = + compilation.entries instanceof Map + ? compilation.moduleGraph.getModule( + compilation.entries.get('main').dependencies[0] + ) + : compilation.entries[0]; // only process entry exports - if (current.resource!==entry) return; + if (current.resource!==entryModule.resource) return; let key = current.nameForCondition(); let exports = CACHE[key] || (CACHE[key] = {}); - if (decl.id) { exports[decl.id.name] = true; } @@ -116,6 +131,20 @@ loader.pitch = function(request) { else { console.warn('[workerize] unknown export declaration: ', expr); } + + // This is for Webpack 5: mark the exports as used so it does not get tree-shaken away on production build + if (compilation.moduleGraph) { + const { getEntryRuntime } = require('webpack/lib/util/runtime'); + const { UsageState } = require('webpack'); + const runtime = getEntryRuntime(compilation, 'main'); + for (const exportName of Object.keys(exports)) { + const exportInfo = compilation.moduleGraph.getExportInfo(entryModule, exportName); + exportInfo.setUsed(UsageState.Used, runtime); + exportInfo.canMangleUse = false; + exportInfo.canMangleProvide = false; + } + compilation.moduleGraph.addExtraReason(entryModule, 'used by workerize-loader'); + } }); }); }); @@ -124,9 +153,20 @@ loader.pitch = function(request) { if (err) return cb(err); if (entries[0]) { - worker.file = entries[0].files[0]; - - let key = entries[0].entryModule.nameForCondition(); + worker.file = Array.from(entries[0].files)[0]; + const entryModules = + compilation.chunkGraph && + compilation.chunkGraph.getChunkEntryModulesIterable + ? Array.from( + compilation.chunkGraph.getChunkEntryModulesIterable(entries[0]) + ) + : null; + const entryModule = + entryModules && entryModules.length > 0 + ? entryModules[0] + : entries[0].entryModule; + + let key = entryModule.nameForCondition(); let contents = compilation.assets[worker.file].source(); let exports = Object.keys(CACHE[key] || {}); From d91a2f64cd11651396e4557e4d249fd372988f54 Mon Sep 17 00:00:00 2001 From: Naoaki Yamada Date: Mon, 1 Mar 2021 20:33:43 +0900 Subject: [PATCH 2/5] Avoid using compilation.cache --- src/index.js | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/index.js b/src/index.js index a3ef05a..df389a1 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,6 @@ import WebWorkerTemplatePlugin from 'webpack/lib/webworker/WebWorkerTemplatePlug export default function loader() {} -const CACHE = {}; const tapName = 'workerize-loader'; function compilationHook(compiler, handler) { @@ -86,23 +85,9 @@ loader.pitch = function(request) { (new SingleEntryPlugin(this.context, `!!${path.resolve(__dirname, 'rpc-worker-loader.js')}!${request}`, 'main')).apply(worker.compiler); const subCache = `subcache ${__dirname} ${request}`; + const CACHE = this._compilation.getCache(subCache); compilationHook(worker.compiler, (compilation, data) => { - if (compilation.cache) { - let cache; - if (compilation.cache instanceof Map) { - cache = compilation.cache.get(subCache); - if (!cache) { - cache = new Map(); - compilation.cache.set(subCache, cache); - } - } - else if (!compilation.cache[subCache]) { - cache = compilation.cache[subCache] = {}; - } - - compilation.cache = cache; - } parseHook(data, (parser, options) => { exportDeclarationHook(parser, expr => { let decl = expr.declaration || expr; From 1be1a8015bab7d49e776e020e50ccde0a5176971 Mon Sep 17 00:00:00 2001 From: Naoaki Yamada Date: Thu, 1 Apr 2021 00:12:40 +0900 Subject: [PATCH 3/5] Fix browser memory leaks --- src/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.js b/src/index.js index df389a1..6bb1b2b 100644 --- a/src/index.js +++ b/src/index.js @@ -176,11 +176,15 @@ loader.pitch = function(request) { workerUrl = `"data:,importScripts('"+location.origin+${workerUrl}+"')"`; } + // workerUrl will be URL.revokeObjectURL() to avoid memory leaks on browsers + // https://github.com/webpack-contrib/worker-loader/issues/208 + return cb(null, ` var addMethods = require(${loaderUtils.stringifyRequest(this, path.resolve(__dirname, 'rpc-wrapper.js'))}) var methods = ${JSON.stringify(exports)} module.exports = function() { var w = new Worker(${workerUrl}, { name: ${JSON.stringify(filename)} }) + URL.revokeObjectURL(${workerUrl}); addMethods(w, methods) ${ options.ready ? 'w.ready = new Promise(function(r) { w.addEventListener("ready", function(){ r(w) }) })' : '' } return w From 1d80d8f7f87ce5f0a8e31c84941cf7673383266f Mon Sep 17 00:00:00 2001 From: Naoaki Yamada Date: Sat, 3 Apr 2021 05:25:26 +0900 Subject: [PATCH 4/5] Fix webpack memory leaks --- src/index.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/index.js b/src/index.js index 6bb1b2b..c40f6b2 100644 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,7 @@ import WebWorkerTemplatePlugin from 'webpack/lib/webworker/WebWorkerTemplatePlug export default function loader() {} +const CACHE = {}; const tapName = 'workerize-loader'; function compilationHook(compiler, handler) { @@ -37,25 +38,24 @@ loader.pitch = function(request) { const cb = this.async(); - const filename = loaderUtils.interpolateName(this, `${options.name || '[fullhash]'}.worker.js`, { - context: options.context || this.rootContext || this.options.context, - regExp: options.regExp - }); + const compilerOptions = this._compiler.options || {}; + + const filename = (options.name || '[fullhash]') + '.worker.js'; const worker = {}; worker.options = { filename, - chunkFilename: `[id].${filename}`, - namedChunkFilename: null + chunkFilename: filename, + publicPath: options.publicPath || compilerOptions.output.publicPath, + globalObject: 'self' }; - const compilerOptions = this._compiler.options || {}; if (compilerOptions.output && compilerOptions.output.globalObject==='window') { console.warn('Warning (workerize-loader): output.globalObject is set to "window". It should be set to "self" or "this" to support HMR in Workers.'); } - worker.compiler = this._compilation.createChildCompiler('worker', worker.options); + worker.compiler = this._compilation.createChildCompiler(`worker ${request}`, worker.options); (new WebWorkerTemplatePlugin(worker.options)).apply(worker.compiler); @@ -82,10 +82,9 @@ loader.pitch = function(request) { }).apply(worker.compiler); } - (new SingleEntryPlugin(this.context, `!!${path.resolve(__dirname, 'rpc-worker-loader.js')}!${request}`, 'main')).apply(worker.compiler); + const bundleName = path.parse(this.resourcePath).name; - const subCache = `subcache ${__dirname} ${request}`; - const CACHE = this._compilation.getCache(subCache); + (new SingleEntryPlugin(this.context, `!!${path.resolve(__dirname, 'rpc-worker-loader.js')}!${request}`, bundleName)).apply(worker.compiler); compilationHook(worker.compiler, (compilation, data) => { parseHook(data, (parser, options) => { @@ -96,7 +95,7 @@ loader.pitch = function(request) { let entryModule = compilation.entries instanceof Map ? compilation.moduleGraph.getModule( - compilation.entries.get('main').dependencies[0] + compilation.entries.get(bundleName).dependencies[0] ) : compilation.entries[0]; @@ -121,7 +120,7 @@ loader.pitch = function(request) { if (compilation.moduleGraph) { const { getEntryRuntime } = require('webpack/lib/util/runtime'); const { UsageState } = require('webpack'); - const runtime = getEntryRuntime(compilation, 'main'); + const runtime = getEntryRuntime(compilation, bundleName); for (const exportName of Object.keys(exports)) { const exportInfo = compilation.moduleGraph.getExportInfo(entryModule, exportName); exportInfo.setUsed(UsageState.Used, runtime); From bce8febb7ade88b8e1630bb609413983f2c94306 Mon Sep 17 00:00:00 2001 From: Naoaki Yamada Date: Sat, 3 Apr 2021 05:25:48 +0900 Subject: [PATCH 5/5] Update dev webpack@5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c3393d8..551db24 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "eslint-config-developit": "^1.2.0", "karmatic": "^1.4.0", "microbundle": "^0.12.1", - "webpack": "^4.43.0" + "webpack": "^5.30.0" }, "dependencies": { "loader-utils": "^2.0.0"