From bbff7403fbf46f9ad92240ac213df8d30ef31b64 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Thu, 20 Sep 2018 02:56:13 +0200 Subject: update packages --- .../lib/optimize/AggressiveSplittingPlugin.js | 482 ++++++++++++--------- 1 file changed, 287 insertions(+), 195 deletions(-) (limited to 'node_modules/webpack/lib/optimize/AggressiveSplittingPlugin.js') diff --git a/node_modules/webpack/lib/optimize/AggressiveSplittingPlugin.js b/node_modules/webpack/lib/optimize/AggressiveSplittingPlugin.js index b6f566a1c..9aec3644b 100644 --- a/node_modules/webpack/lib/optimize/AggressiveSplittingPlugin.js +++ b/node_modules/webpack/lib/optimize/AggressiveSplittingPlugin.js @@ -1,195 +1,287 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -const identifierUtils = require("../util/identifier"); - -function moveModuleBetween(oldChunk, newChunk) { - return function(module) { - oldChunk.moveModule(module, newChunk); - }; -} - -function isNotAEntryModule(entryModule) { - return function(module) { - return entryModule !== module; - }; -} - -function copyWithReason(obj) { - const newObj = {}; - Object.keys(obj).forEach((key) => { - newObj[key] = obj[key]; - }); - if(!newObj.reasons || newObj.reasons.indexOf("aggressive-splitted") < 0) - newObj.reasons = (newObj.reasons || []).concat("aggressive-splitted"); - return newObj; -} - -class AggressiveSplittingPlugin { - constructor(options) { - this.options = options || {}; - if(typeof this.options.minSize !== "number") this.options.minSize = 30 * 1024; - if(typeof this.options.maxSize !== "number") this.options.maxSize = 50 * 1024; - if(typeof this.options.chunkOverhead !== "number") this.options.chunkOverhead = 0; - if(typeof this.options.entryChunkMultiplicator !== "number") this.options.entryChunkMultiplicator = 1; - } - apply(compiler) { - compiler.plugin("this-compilation", (compilation) => { - compilation.plugin("optimize-chunks-advanced", (chunks) => { - // Precompute stuff - const nameToModuleMap = new Map(); - compilation.modules.forEach(m => { - const name = identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache); - nameToModuleMap.set(name, m); - }); - - const savedSplits = compilation.records && compilation.records.aggressiveSplits || []; - const usedSplits = compilation._aggressiveSplittingSplits ? - savedSplits.concat(compilation._aggressiveSplittingSplits) : savedSplits; - - const minSize = this.options.minSize; - const maxSize = this.options.maxSize; - // 1. try to restore to recorded splitting - for(let j = 0; j < usedSplits.length; j++) { - const splitData = usedSplits[j]; - const selectedModules = splitData.modules.map(name => nameToModuleMap.get(name)); - - // Does the modules exist at all? - if(selectedModules.every(Boolean)) { - - // Find all chunks containing all modules in the split - for(let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - - // Cheap check if chunk is suitable at all - if(chunk.getNumberOfModules() < splitData.modules.length) - continue; - - // Check if all modules are in the chunk - if(selectedModules.every(m => chunk.containsModule(m))) { - - // Is chunk identical to the split or do we need to split it? - if(chunk.getNumberOfModules() > splitData.modules.length) { - // split the chunk into two parts - const newChunk = compilation.addChunk(); - selectedModules.forEach(moveModuleBetween(chunk, newChunk)); - chunk.split(newChunk); - chunk.name = null; - newChunk._fromAggressiveSplitting = true; - if(j < savedSplits.length) - newChunk._fromAggressiveSplittingIndex = j; - if(splitData.id !== null && splitData.id !== undefined) { - newChunk.id = splitData.id; - } - newChunk.origins = chunk.origins.map(copyWithReason); - chunk.origins = chunk.origins.map(copyWithReason); - return true; - } else { // chunk is identical to the split - if(j < savedSplits.length) - chunk._fromAggressiveSplittingIndex = j; - chunk.name = null; - if(splitData.id !== null && splitData.id !== undefined) { - chunk.id = splitData.id; - } - } - } - } - } - } - - // 2. for any other chunk which isn't splitted yet, split it - for(let i = 0; i < chunks.length; i++) { - const chunk = chunks[i]; - const size = chunk.size(this.options); - if(size > maxSize && chunk.getNumberOfModules() > 1) { - const newChunk = compilation.addChunk(); - const modules = chunk.getModules() - .filter(isNotAEntryModule(chunk.entryModule)) - .sort((a, b) => { - a = a.identifier(); - b = b.identifier(); - if(a > b) return 1; - if(a < b) return -1; - return 0; - }); - for(let k = 0; k < modules.length; k++) { - chunk.moveModule(modules[k], newChunk); - const newSize = newChunk.size(this.options); - const chunkSize = chunk.size(this.options); - // break early if it's fine - if(chunkSize < maxSize && newSize < maxSize && newSize >= minSize && chunkSize >= minSize) - break; - if(newSize > maxSize && k === 0) { - // break if there is a single module which is bigger than maxSize - break; - } - if(newSize > maxSize || chunkSize < minSize) { - // move it back - newChunk.moveModule(modules[k], chunk); - // check if it's fine now - if(newSize < maxSize && newSize >= minSize && chunkSize >= minSize) - break; - } - } - if(newChunk.getNumberOfModules() > 0) { - chunk.split(newChunk); - chunk.name = null; - newChunk.origins = chunk.origins.map(copyWithReason); - chunk.origins = chunk.origins.map(copyWithReason); - compilation._aggressiveSplittingSplits = (compilation._aggressiveSplittingSplits || []).concat({ - modules: newChunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache)) - }); - return true; - } else { - chunks.splice(chunks.indexOf(newChunk), 1); - } - } - } - }); - compilation.plugin("record-hash", (records) => { - // 3. save to made splittings to records - const minSize = this.options.minSize; - if(!records.aggressiveSplits) records.aggressiveSplits = []; - compilation.chunks.forEach((chunk) => { - if(chunk.hasEntryModule()) return; - const size = chunk.size(this.options); - const incorrectSize = size < minSize; - const modules = chunk.mapModules(m => identifierUtils.makePathsRelative(compiler.context, m.identifier(), compilation.cache)); - if(typeof chunk._fromAggressiveSplittingIndex === "undefined") { - if(incorrectSize) return; - chunk.recorded = true; - records.aggressiveSplits.push({ - modules: modules, - hash: chunk.hash, - id: chunk.id - }); - } else { - const splitData = records.aggressiveSplits[chunk._fromAggressiveSplittingIndex]; - if(splitData.hash !== chunk.hash || incorrectSize) { - if(chunk._fromAggressiveSplitting) { - chunk._aggressiveSplittingInvalid = true; - splitData.invalid = true; - } else { - splitData.hash = chunk.hash; - } - } - } - }); - records.aggressiveSplits = records.aggressiveSplits.filter((splitData) => { - return !splitData.invalid; - }); - }); - compilation.plugin("need-additional-seal", (callback) => { - const invalid = compilation.chunks.some((chunk) => { - return chunk._aggressiveSplittingInvalid; - }); - if(invalid) - return true; - }); - }); - } -} -module.exports = AggressiveSplittingPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const identifierUtils = require("../util/identifier"); +const { intersect } = require("../util/SetHelpers"); +const validateOptions = require("schema-utils"); +const schema = require("../../schemas/plugins/optimize/AggressiveSplittingPlugin.json"); + +const moveModuleBetween = (oldChunk, newChunk) => { + return module => { + oldChunk.moveModule(module, newChunk); + }; +}; + +const isNotAEntryModule = entryModule => { + return module => { + return entryModule !== module; + }; +}; + +class AggressiveSplittingPlugin { + constructor(options) { + validateOptions(schema, options || {}, "Aggressive Splitting Plugin"); + + this.options = options || {}; + if (typeof this.options.minSize !== "number") { + this.options.minSize = 30 * 1024; + } + if (typeof this.options.maxSize !== "number") { + this.options.maxSize = 50 * 1024; + } + if (typeof this.options.chunkOverhead !== "number") { + this.options.chunkOverhead = 0; + } + if (typeof this.options.entryChunkMultiplicator !== "number") { + this.options.entryChunkMultiplicator = 1; + } + } + apply(compiler) { + compiler.hooks.thisCompilation.tap( + "AggressiveSplittingPlugin", + compilation => { + let needAdditionalSeal = false; + let newSplits; + let fromAggressiveSplittingSet; + let chunkSplitDataMap; + compilation.hooks.optimize.tap("AggressiveSplittingPlugin", () => { + newSplits = []; + fromAggressiveSplittingSet = new Set(); + chunkSplitDataMap = new Map(); + }); + compilation.hooks.optimizeChunksAdvanced.tap( + "AggressiveSplittingPlugin", + chunks => { + // Precompute stuff + const nameToModuleMap = new Map(); + const moduleToNameMap = new Map(); + for (const m of compilation.modules) { + const name = identifierUtils.makePathsRelative( + compiler.context, + m.identifier(), + compilation.cache + ); + nameToModuleMap.set(name, m); + moduleToNameMap.set(m, name); + } + + // Check used chunk ids + const usedIds = new Set(); + for (const chunk of chunks) { + usedIds.add(chunk.id); + } + + const recordedSplits = + (compilation.records && compilation.records.aggressiveSplits) || + []; + const usedSplits = newSplits + ? recordedSplits.concat(newSplits) + : recordedSplits; + + const minSize = this.options.minSize; + const maxSize = this.options.maxSize; + + const applySplit = splitData => { + // Cannot split if id is already taken + if (splitData.id !== undefined && usedIds.has(splitData.id)) { + return false; + } + + // Get module objects from names + const selectedModules = splitData.modules.map(name => + nameToModuleMap.get(name) + ); + + // Does the modules exist at all? + if (!selectedModules.every(Boolean)) return false; + + // Check if size matches (faster than waiting for hash) + const size = selectedModules.reduce( + (sum, m) => sum + m.size(), + 0 + ); + if (size !== splitData.size) return false; + + // get chunks with all modules + const selectedChunks = intersect( + selectedModules.map(m => new Set(m.chunksIterable)) + ); + + // No relevant chunks found + if (selectedChunks.size === 0) return false; + + // The found chunk is already the split or similar + if ( + selectedChunks.size === 1 && + Array.from(selectedChunks)[0].getNumberOfModules() === + selectedModules.length + ) { + const chunk = Array.from(selectedChunks)[0]; + if (fromAggressiveSplittingSet.has(chunk)) return false; + fromAggressiveSplittingSet.add(chunk); + chunkSplitDataMap.set(chunk, splitData); + return true; + } + + // split the chunk into two parts + const newChunk = compilation.addChunk(); + newChunk.chunkReason = "aggressive splitted"; + for (const chunk of selectedChunks) { + selectedModules.forEach(moveModuleBetween(chunk, newChunk)); + chunk.split(newChunk); + chunk.name = null; + } + fromAggressiveSplittingSet.add(newChunk); + chunkSplitDataMap.set(newChunk, splitData); + + if (splitData.id !== null && splitData.id !== undefined) { + newChunk.id = splitData.id; + } + return true; + }; + + // try to restore to recorded splitting + let changed = false; + for (let j = 0; j < usedSplits.length; j++) { + const splitData = usedSplits[j]; + if (applySplit(splitData)) changed = true; + } + + // for any chunk which isn't splitted yet, split it and create a new entry + // start with the biggest chunk + const sortedChunks = chunks.slice().sort((a, b) => { + const diff1 = b.modulesSize() - a.modulesSize(); + if (diff1) return diff1; + const diff2 = a.getNumberOfModules() - b.getNumberOfModules(); + if (diff2) return diff2; + const modulesA = Array.from(a.modulesIterable); + const modulesB = Array.from(b.modulesIterable); + modulesA.sort(); + modulesB.sort(); + const aI = modulesA[Symbol.iterator](); + const bI = modulesB[Symbol.iterator](); + // eslint-disable-next-line no-constant-condition + while (true) { + const aItem = aI.next(); + const bItem = bI.next(); + if (aItem.done) return 0; + const aModuleIdentifier = aItem.value.identifier(); + const bModuleIdentifier = bItem.value.identifier(); + if (aModuleIdentifier > bModuleIdentifier) return -1; + if (aModuleIdentifier < bModuleIdentifier) return 1; + } + }); + for (const chunk of sortedChunks) { + if (fromAggressiveSplittingSet.has(chunk)) continue; + const size = chunk.modulesSize(); + if (size > maxSize && chunk.getNumberOfModules() > 1) { + const modules = chunk + .getModules() + .filter(isNotAEntryModule(chunk.entryModule)) + .sort((a, b) => { + a = a.identifier(); + b = b.identifier(); + if (a > b) return 1; + if (a < b) return -1; + return 0; + }); + const selectedModules = []; + let selectedModulesSize = 0; + for (let k = 0; k < modules.length; k++) { + const module = modules[k]; + const newSize = selectedModulesSize + module.size(); + if (newSize > maxSize && selectedModulesSize >= minSize) { + break; + } + selectedModulesSize = newSize; + selectedModules.push(module); + } + if (selectedModules.length === 0) continue; + const splitData = { + modules: selectedModules + .map(m => moduleToNameMap.get(m)) + .sort(), + size: selectedModulesSize + }; + + if (applySplit(splitData)) { + newSplits = (newSplits || []).concat(splitData); + changed = true; + } + } + } + if (changed) return true; + } + ); + compilation.hooks.recordHash.tap( + "AggressiveSplittingPlugin", + records => { + // 4. save made splittings to records + const allSplits = new Set(); + const invalidSplits = new Set(); + + // Check if some splittings are invalid + // We remove invalid splittings and try again + for (const chunk of compilation.chunks) { + const splitData = chunkSplitDataMap.get(chunk); + if (splitData !== undefined) { + if (splitData.hash && chunk.hash !== splitData.hash) { + // Split was successful, but hash doesn't equal + // We can throw away the split since it's useless now + invalidSplits.add(splitData); + } + } + } + + if (invalidSplits.size > 0) { + records.aggressiveSplits = records.aggressiveSplits.filter( + splitData => !invalidSplits.has(splitData) + ); + needAdditionalSeal = true; + } else { + // set hash and id values on all (new) splittings + for (const chunk of compilation.chunks) { + const splitData = chunkSplitDataMap.get(chunk); + if (splitData !== undefined) { + splitData.hash = chunk.hash; + splitData.id = chunk.id; + allSplits.add(splitData); + // set flag for stats + chunk.recorded = true; + } + } + + // Also add all unused historial splits (after the used ones) + // They can still be used in some future compilation + const recordedSplits = + compilation.records && compilation.records.aggressiveSplits; + if (recordedSplits) { + for (const splitData of recordedSplits) { + if (!invalidSplits.has(splitData)) allSplits.add(splitData); + } + } + + // record all splits + records.aggressiveSplits = Array.from(allSplits); + + needAdditionalSeal = false; + } + } + ); + compilation.hooks.needAdditionalSeal.tap( + "AggressiveSplittingPlugin", + () => { + if (needAdditionalSeal) { + needAdditionalSeal = false; + return true; + } + } + ); + } + ); + } +} +module.exports = AggressiveSplittingPlugin; -- cgit v1.2.3