From 0fe03c7af0ece416245cedf10142a199d2c4acbf Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 14 Oct 2020 21:08:14 +0200 Subject: [PATCH] fix unused modules in chunk when optimizing runtime-specific fixes #11673 --- lib/ExportsInfo.js | 13 ++++- lib/FlagDependencyUsagePlugin.js | 93 ++++++++++++++++++++++++-------- lib/util/runtime.js | 2 +- types.d.ts | 1 + 4 files changed, 85 insertions(+), 24 deletions(-) diff --git a/lib/ExportsInfo.js b/lib/ExportsInfo.js index 1a64f1a0781..9daff97466d 100644 --- a/lib/ExportsInfo.js +++ b/lib/ExportsInfo.js @@ -416,7 +416,7 @@ class ExportsInfo { /** * @param {RuntimeSpec} runtime the runtime - * @returns {boolean} true, when the module is used in any way + * @returns {boolean} true, when the module exports are used in any way */ isUsed(runtime) { if (this._redirectTo !== undefined) { @@ -436,6 +436,17 @@ class ExportsInfo { return false; } + /** + * @param {RuntimeSpec} runtime the runtime + * @returns {boolean} true, when the module is used in any way + */ + isModuleUsed(runtime) { + if (this.isUsed(runtime)) return true; + if (this._sideEffectsOnlyInfo.getUsed(runtime) !== UsageState.Unused) + return true; + return false; + } + /** * @param {RuntimeSpec} runtime the runtime * @returns {SortableSet | boolean | null} set of used exports, or true (when namespace object is used), or false (when unused), or null (when unknown) diff --git a/lib/FlagDependencyUsagePlugin.js b/lib/FlagDependencyUsagePlugin.js index cd4ea4b6058..dbfdcff83f7 100644 --- a/lib/FlagDependencyUsagePlugin.js +++ b/lib/FlagDependencyUsagePlugin.js @@ -63,9 +63,15 @@ class FlagDependencyUsagePlugin { * @param {Module} module module to process * @param {(string[] | ReferencedExport)[]} usedExports list of used exports * @param {RuntimeSpec} runtime part of which runtime + * @param {boolean} forceSideEffects always apply side effects * @returns {void} */ - const processReferencedModule = (module, usedExports, runtime) => { + const processReferencedModule = ( + module, + usedExports, + runtime, + forceSideEffects + ) => { const exportsInfo = moduleGraph.getExportsInfo(module); if (usedExports.length > 0) { if (!module.buildMeta || !module.buildMeta.exportsType) { @@ -143,10 +149,12 @@ class FlagDependencyUsagePlugin { // This module won't be evaluated in this case // TODO webpack 6 remove this check if ( + !forceSideEffects && module.factoryMeta !== undefined && module.factoryMeta.sideEffectFree - ) + ) { return; + } if (exportsInfo.setUsedForSideEffectsOnly(runtime)) { queue.enqueue(module, runtime); } @@ -242,12 +250,18 @@ class FlagDependencyUsagePlugin { for (const [module, referencedExports] of map) { if (Array.isArray(referencedExports)) { - processReferencedModule(module, referencedExports, runtime); + processReferencedModule( + module, + referencedExports, + runtime, + false + ); } else { processReferencedModule( module, Array.from(referencedExports.values()), - runtime + runtime, + false ); } } @@ -270,8 +284,12 @@ class FlagDependencyUsagePlugin { const processEntryDependency = (dep, runtime) => { const module = moduleGraph.getModule(dep); if (module) { - processReferencedModule(module, NO_EXPORTS_REFERENCED, runtime); - queue.enqueue(module, runtime); + processReferencedModule( + module, + NO_EXPORTS_REFERENCED, + runtime, + true + ); } }; /** @type {RuntimeSpec} */ @@ -307,28 +325,59 @@ class FlagDependencyUsagePlugin { ); if (!this.global) { compilation.hooks.afterChunks.tap("FlagDependencyUsagePlugin", () => { - /** @type {Map} */ - const runtimeChunks = new Map(); - for (const entrypoint of compilation.entrypoints.values()) { - runtimeChunks.set( - entrypoint.getRuntimeChunk(), - entrypoint.options.runtime + const processEntrypoint = entrypoint => { + const runtime = getEntryRuntime( + compilation, + entrypoint.name, + entrypoint.options ); + for (const chunk of entrypoint + .getEntrypointChunk() + .getAllReferencedChunks()) { + chunk.runtime = mergeRuntime(chunk.runtime, runtime); + } + }; + for (const entrypoint of compilation.entrypoints.values()) { + processEntrypoint(entrypoint); } - for (const entrypoint of compilation.asyncEntrypoints) { - runtimeChunks.set( - entrypoint.getRuntimeChunk(), - entrypoint.options.runtime - ); + for (const asyncEntry of compilation.asyncEntrypoints) { + processEntrypoint(asyncEntry); } + }); - for (const [runtimeChunk, runtimeName] of runtimeChunks) { - const runtime = runtimeName || runtimeChunk.name; - for (const chunk of runtimeChunk.getAllReferencedChunks()) { - chunk.runtime = mergeRuntime(chunk.runtime, runtime); + compilation.hooks.optimizeChunkModules.tap( + "FlagDependencyUsagePlugin", + (chunks, modules) => { + for (const module of modules) { + const exportsInfo = compilation.moduleGraph.getExportsInfo( + module + ); + let removeFromRuntimes = undefined; + for (const runtime of compilation.chunkGraph.getModuleRuntimes( + module + )) { + if (!exportsInfo.isModuleUsed(runtime)) { + if (removeFromRuntimes === undefined) { + removeFromRuntimes = new Set(); + } + removeFromRuntimes.add(runtime); + } + } + if (removeFromRuntimes !== undefined) { + for (const chunk of compilation.chunkGraph.getModuleChunksIterable( + module + )) { + if (removeFromRuntimes.has(chunk.runtime)) { + compilation.chunkGraph.disconnectChunkAndModule( + chunk, + module + ); + } + } + } } } - }); + ); } }); } diff --git a/lib/util/runtime.js b/lib/util/runtime.js index 70e56e92093..321aec4b462 100644 --- a/lib/util/runtime.js +++ b/lib/util/runtime.js @@ -44,7 +44,7 @@ exports.getEntryRuntime = (compilation, name, options) => { result = mergeRuntimeOwned(result, runtime || name); } } - return result; + return result || name; } else { return runtime || name; } diff --git a/types.d.ts b/types.d.ts index ebe3e0a0417..be24277d056 100644 --- a/types.d.ts +++ b/types.d.ts @@ -3076,6 +3076,7 @@ declare abstract class ExportsInfo { setAllKnownExportsUsed(runtime: string | SortableSet): boolean; setUsedForSideEffectsOnly(runtime: string | SortableSet): boolean; isUsed(runtime: string | SortableSet): boolean; + isModuleUsed(runtime: string | SortableSet): boolean; getUsedExports( runtime: string | SortableSet ): boolean | SortableSet;