| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const Queue = require("../util/Queue"); |
| const { intersect } = require("../util/SetHelpers"); |
| |
| const getParentChunksWithModule = (currentChunk, module) => { |
| const chunks = []; |
| const stack = new Set(currentChunk.parentsIterable); |
| |
| for (const chunk of stack) { |
| if (chunk.containsModule(module)) { |
| chunks.push(chunk); |
| } else { |
| for (const parent of chunk.parentsIterable) { |
| stack.add(parent); |
| } |
| } |
| } |
| |
| return chunks; |
| }; |
| |
| class RemoveParentModulesPlugin { |
| apply(compiler) { |
| compiler.hooks.compilation.tap("RemoveParentModulesPlugin", compilation => { |
| const handler = (chunks, chunkGroups) => { |
| const queue = new Queue(); |
| const availableModulesMap = new WeakMap(); |
| |
| for (const chunkGroup of compilation.entrypoints.values()) { |
| // initialize available modules for chunks without parents |
| availableModulesMap.set(chunkGroup, new Set()); |
| for (const child of chunkGroup.childrenIterable) { |
| queue.enqueue(child); |
| } |
| } |
| |
| while (queue.length > 0) { |
| const chunkGroup = queue.dequeue(); |
| let availableModules = availableModulesMap.get(chunkGroup); |
| let changed = false; |
| for (const parent of chunkGroup.parentsIterable) { |
| const availableModulesInParent = availableModulesMap.get(parent); |
| if (availableModulesInParent !== undefined) { |
| // If we know the available modules in parent: process these |
| if (availableModules === undefined) { |
| // if we have not own info yet: create new entry |
| availableModules = new Set(availableModulesInParent); |
| for (const chunk of parent.chunks) { |
| for (const m of chunk.modulesIterable) { |
| availableModules.add(m); |
| } |
| } |
| availableModulesMap.set(chunkGroup, availableModules); |
| changed = true; |
| } else { |
| for (const m of availableModules) { |
| if ( |
| !parent.containsModule(m) && |
| !availableModulesInParent.has(m) |
| ) { |
| availableModules.delete(m); |
| changed = true; |
| } |
| } |
| } |
| } |
| } |
| if (changed) { |
| // if something changed: enqueue our children |
| for (const child of chunkGroup.childrenIterable) { |
| queue.enqueue(child); |
| } |
| } |
| } |
| |
| // now we have available modules for every chunk |
| for (const chunk of chunks) { |
| const availableModulesSets = Array.from( |
| chunk.groupsIterable, |
| chunkGroup => availableModulesMap.get(chunkGroup) |
| ); |
| if (availableModulesSets.some(s => s === undefined)) continue; // No info about this chunk group |
| const availableModules = |
| availableModulesSets.length === 1 |
| ? availableModulesSets[0] |
| : intersect(availableModulesSets); |
| const numberOfModules = chunk.getNumberOfModules(); |
| const toRemove = new Set(); |
| if (numberOfModules < availableModules.size) { |
| for (const m of chunk.modulesIterable) { |
| if (availableModules.has(m)) { |
| toRemove.add(m); |
| } |
| } |
| } else { |
| for (const m of availableModules) { |
| if (chunk.containsModule(m)) { |
| toRemove.add(m); |
| } |
| } |
| } |
| for (const module of toRemove) { |
| module.rewriteChunkInReasons( |
| chunk, |
| getParentChunksWithModule(chunk, module) |
| ); |
| chunk.removeModule(module); |
| } |
| } |
| }; |
| compilation.hooks.optimizeChunksBasic.tap( |
| "RemoveParentModulesPlugin", |
| handler |
| ); |
| compilation.hooks.optimizeExtractedChunksBasic.tap( |
| "RemoveParentModulesPlugin", |
| handler |
| ); |
| }); |
| } |
| } |
| module.exports = RemoveParentModulesPlugin; |