Skip to content

Commit

Permalink
fix unused modules in chunk when optimizing runtime-specific
Browse files Browse the repository at this point in the history
fixes #11673
  • Loading branch information
sokra committed Oct 15, 2020
1 parent 5d9d9b9 commit 50c3a83
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 24 deletions.
13 changes: 12 additions & 1 deletion lib/ExportsInfo.js
Expand Up @@ -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) {
Expand All @@ -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<string> | boolean | null} set of used exports, or true (when namespace object is used), or false (when unused), or null (when unknown)
Expand Down
93 changes: 71 additions & 22 deletions lib/FlagDependencyUsagePlugin.js
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
);
}
}
Expand All @@ -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} */
Expand Down Expand Up @@ -307,28 +325,59 @@ class FlagDependencyUsagePlugin {
);
if (!this.global) {
compilation.hooks.afterChunks.tap("FlagDependencyUsagePlugin", () => {
/** @type {Map<Chunk, string>} */
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
);
}
}
}
}
}
});
);
}
});
}
Expand Down
2 changes: 1 addition & 1 deletion lib/util/runtime.js
Expand Up @@ -44,7 +44,7 @@ exports.getEntryRuntime = (compilation, name, options) => {
result = mergeRuntimeOwned(result, runtime || name);
}
}
return result;
return result || name;
} else {
return runtime || name;
}
Expand Down
1 change: 1 addition & 0 deletions types.d.ts
Expand Up @@ -3076,6 +3076,7 @@ declare abstract class ExportsInfo {
setAllKnownExportsUsed(runtime: string | SortableSet<string>): boolean;
setUsedForSideEffectsOnly(runtime: string | SortableSet<string>): boolean;
isUsed(runtime: string | SortableSet<string>): boolean;
isModuleUsed(runtime: string | SortableSet<string>): boolean;
getUsedExports(
runtime: string | SortableSet<string>
): boolean | SortableSet<string>;
Expand Down

0 comments on commit 50c3a83

Please sign in to comment.