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/AggressiveMergingPlugin.js | 202 +- .../lib/optimize/AggressiveSplittingPlugin.js | 482 ++-- .../lib/optimize/ChunkModuleIdRangePlugin.js | 121 +- .../webpack/lib/optimize/CommonsChunkPlugin.js | 404 ---- .../webpack/lib/optimize/ConcatenatedModule.js | 2322 +++++++++++++------- node_modules/webpack/lib/optimize/DedupePlugin.js | 15 - .../lib/optimize/EnsureChunkConditionsPlugin.js | 110 +- .../lib/optimize/FlagIncludedChunksPlugin.js | 134 +- .../webpack/lib/optimize/LimitChunkCountPlugin.js | 125 +- .../lib/optimize/MergeDuplicateChunksPlugin.js | 106 +- .../webpack/lib/optimize/MinChunkSizePlugin.js | 142 +- .../lib/optimize/ModuleConcatenationPlugin.js | 791 ++++--- .../webpack/lib/optimize/OccurrenceOrderPlugin.js | 237 +- .../lib/optimize/RemoveEmptyChunksPlugin.js | 63 +- .../lib/optimize/RemoveParentModulesPlugin.js | 192 +- .../webpack/lib/optimize/UglifyJsPlugin.js | 9 - 16 files changed, 3114 insertions(+), 2341 deletions(-) delete mode 100644 node_modules/webpack/lib/optimize/CommonsChunkPlugin.js delete mode 100644 node_modules/webpack/lib/optimize/DedupePlugin.js delete mode 100644 node_modules/webpack/lib/optimize/UglifyJsPlugin.js (limited to 'node_modules/webpack/lib/optimize') diff --git a/node_modules/webpack/lib/optimize/AggressiveMergingPlugin.js b/node_modules/webpack/lib/optimize/AggressiveMergingPlugin.js index dc14ac300..e3a4c37ee 100644 --- a/node_modules/webpack/lib/optimize/AggressiveMergingPlugin.js +++ b/node_modules/webpack/lib/optimize/AggressiveMergingPlugin.js @@ -1,115 +1,87 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class AggressiveMergingPlugin { - constructor(options) { - if(options !== undefined && typeof options !== "object" || Array.isArray(options)) { - throw new Error("Argument should be an options object. To use defaults, pass in nothing.\nFor more info on options, see https://webpack.js.org/plugins/"); - } - this.options = options || {}; - } - - apply(compiler) { - const options = this.options; - const minSizeReduce = options.minSizeReduce || 1.5; - - function getParentsWeight(chunk) { - return chunk.parents.map((p) => { - return p.isInitial() ? options.entryChunkMultiplicator || 10 : 1; - }).reduce((a, b) => { - return a + b; - }, 0); - } - compiler.plugin("this-compilation", (compilation) => { - compilation.plugin("optimize-chunks-advanced", (chunks) => { - let combinations = []; - chunks.forEach((a, idx) => { - if(a.isInitial()) return; - for(let i = 0; i < idx; i++) { - const b = chunks[i]; - if(b.isInitial()) continue; - combinations.push({ - a, - b, - improvement: undefined - }); - } - }); - - combinations.forEach((pair) => { - const a = pair.b.size({ - chunkOverhead: 0 - }); - const b = pair.a.size({ - chunkOverhead: 0 - }); - const ab = pair.b.integratedSize(pair.a, { - chunkOverhead: 0 - }); - let newSize; - if(ab === false) { - pair.improvement = false; - return; - } else if(options.moveToParents) { - const aOnly = ab - b; - const bOnly = ab - a; - const common = a + b - ab; - newSize = common + getParentsWeight(pair.b) * aOnly + getParentsWeight(pair.a) * bOnly; - } else { - newSize = ab; - } - - pair.improvement = (a + b) / newSize; - }); - combinations = combinations.filter((pair) => { - return pair.improvement !== false; - }); - combinations.sort((a, b) => { - return b.improvement - a.improvement; - }); - - const pair = combinations[0]; - - if(!pair) return; - if(pair.improvement < minSizeReduce) return; - - if(options.moveToParents) { - const commonModules = pair.b.modules.filter((m) => { - return pair.a.modules.indexOf(m) >= 0; - }); - const aOnlyModules = pair.b.modules.filter((m) => { - return commonModules.indexOf(m) < 0; - }); - const bOnlyModules = pair.a.modules.filter((m) => { - return commonModules.indexOf(m) < 0; - }); - aOnlyModules.forEach((m) => { - pair.b.removeModule(m); - m.removeChunk(pair.b); - pair.b.parents.forEach((c) => { - c.addModule(m); - m.addChunk(c); - }); - }); - bOnlyModules.forEach((m) => { - pair.a.removeModule(m); - m.removeChunk(pair.a); - pair.a.parents.forEach((c) => { - c.addModule(m); - m.addChunk(c); - }); - }); - } - if(pair.b.integrate(pair.a, "aggressive-merge")) { - chunks.splice(chunks.indexOf(pair.a), 1); - return true; - } - }); - }); - } -} - -module.exports = AggressiveMergingPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +class AggressiveMergingPlugin { + constructor(options) { + if ( + (options !== undefined && typeof options !== "object") || + Array.isArray(options) + ) { + throw new Error( + "Argument should be an options object. To use defaults, pass in nothing.\nFor more info on options, see https://webpack.js.org/plugins/" + ); + } + this.options = options || {}; + } + + apply(compiler) { + const options = this.options; + const minSizeReduce = options.minSizeReduce || 1.5; + + compiler.hooks.thisCompilation.tap( + "AggressiveMergingPlugin", + compilation => { + compilation.hooks.optimizeChunksAdvanced.tap( + "AggressiveMergingPlugin", + chunks => { + let combinations = []; + chunks.forEach((a, idx) => { + if (a.canBeInitial()) return; + for (let i = 0; i < idx; i++) { + const b = chunks[i]; + if (b.canBeInitial()) continue; + combinations.push({ + a, + b, + improvement: undefined + }); + } + }); + + for (const pair of combinations) { + const a = pair.b.size({ + chunkOverhead: 0 + }); + const b = pair.a.size({ + chunkOverhead: 0 + }); + const ab = pair.b.integratedSize(pair.a, { + chunkOverhead: 0 + }); + let newSize; + if (ab === false) { + pair.improvement = false; + return; + } else { + newSize = ab; + } + + pair.improvement = (a + b) / newSize; + } + combinations = combinations.filter(pair => { + return pair.improvement !== false; + }); + combinations.sort((a, b) => { + return b.improvement - a.improvement; + }); + + const pair = combinations[0]; + + if (!pair) return; + if (pair.improvement < minSizeReduce) return; + + if (pair.b.integrate(pair.a, "aggressive-merge")) { + chunks.splice(chunks.indexOf(pair.a), 1); + return true; + } + } + ); + } + ); + } +} + +module.exports = AggressiveMergingPlugin; 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; diff --git a/node_modules/webpack/lib/optimize/ChunkModuleIdRangePlugin.js b/node_modules/webpack/lib/optimize/ChunkModuleIdRangePlugin.js index 262014dc5..f507e426c 100644 --- a/node_modules/webpack/lib/optimize/ChunkModuleIdRangePlugin.js +++ b/node_modules/webpack/lib/optimize/ChunkModuleIdRangePlugin.js @@ -1,53 +1,68 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; -class ChunkModuleIdRangePlugin { - constructor(options) { - this.options = options; - } - apply(compiler) { - const options = this.options; - compiler.plugin("compilation", (compilation) => { - compilation.plugin("module-ids", (modules) => { - const chunk = this.chunks.find((chunk) => chunk.name === options.name); - if(!chunk) throw new Error("ChunkModuleIdRangePlugin: Chunk with name '" + options.name + "' was not found"); - let currentId = options.start; - let chunkModules; - if(options.order) { - chunkModules = chunk.modules.slice(); - switch(options.order) { - case "index": - chunkModules.sort((a, b) => { - return a.index - b.index; - }); - break; - case "index2": - chunkModules.sort((a, b) => { - return a.index2 - b.index2; - }); - break; - default: - throw new Error("ChunkModuleIdRangePlugin: unexpected value of order"); - } - - } else { - chunkModules = modules.filter((m) => { - return m.chunks.indexOf(chunk) >= 0; - }); - } - - for(let i = 0; i < chunkModules.length; i++) { - const m = chunkModules[i]; - if(m.id === null) { - m.id = currentId++; - } - if(options.end && currentId > options.end) - break; - } - }); - }); - } -} -module.exports = ChunkModuleIdRangePlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const sortByIndex = (a, b) => { + return a.index - b.index; +}; + +const sortByIndex2 = (a, b) => { + return a.index2 - b.index2; +}; + +class ChunkModuleIdRangePlugin { + constructor(options) { + this.options = options; + } + + apply(compiler) { + const options = this.options; + compiler.hooks.compilation.tap("ChunkModuleIdRangePlugin", compilation => { + compilation.hooks.moduleIds.tap("ChunkModuleIdRangePlugin", modules => { + const chunk = compilation.chunks.find( + chunk => chunk.name === options.name + ); + if (!chunk) { + throw new Error( + `ChunkModuleIdRangePlugin: Chunk with name '${ + options.name + }"' was not found` + ); + } + + let chunkModules; + if (options.order) { + chunkModules = Array.from(chunk.modulesIterable); + switch (options.order) { + case "index": + chunkModules.sort(sortByIndex); + break; + case "index2": + chunkModules.sort(sortByIndex2); + break; + default: + throw new Error( + "ChunkModuleIdRangePlugin: unexpected value of order" + ); + } + } else { + chunkModules = modules.filter(m => { + return m.chunksIterable.has(chunk); + }); + } + + let currentId = options.start || 0; + for (let i = 0; i < chunkModules.length; i++) { + const m = chunkModules[i]; + if (m.id === null) { + m.id = currentId++; + } + if (options.end && currentId > options.end) break; + } + }); + }); + } +} +module.exports = ChunkModuleIdRangePlugin; diff --git a/node_modules/webpack/lib/optimize/CommonsChunkPlugin.js b/node_modules/webpack/lib/optimize/CommonsChunkPlugin.js deleted file mode 100644 index c0e438a40..000000000 --- a/node_modules/webpack/lib/optimize/CommonsChunkPlugin.js +++ /dev/null @@ -1,404 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; -let nextIdent = 0; - -class CommonsChunkPlugin { - constructor(options) { - if(arguments.length > 1) { - throw new Error(`Deprecation notice: CommonsChunkPlugin now only takes a single argument. Either an options -object *or* the name of the chunk. -Example: if your old code looked like this: - new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.bundle.js') -You would change it to: - new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', filename: 'vendor.bundle.js' }) -The available options are: - name: string - names: string[] - filename: string - minChunks: number - chunks: string[] - children: boolean - async: boolean - minSize: number`); - } - - const normalizedOptions = this.normalizeOptions(options); - - this.chunkNames = normalizedOptions.chunkNames; - this.filenameTemplate = normalizedOptions.filenameTemplate; - this.minChunks = normalizedOptions.minChunks; - this.selectedChunks = normalizedOptions.selectedChunks; - this.children = normalizedOptions.children; - this.deepChildren = normalizedOptions.deepChildren; - this.async = normalizedOptions.async; - this.minSize = normalizedOptions.minSize; - this.ident = __filename + (nextIdent++); - } - - normalizeOptions(options) { - if(Array.isArray(options)) { - return { - chunkNames: options, - }; - } - - if(typeof options === "string") { - return { - chunkNames: [options], - }; - } - - // options.children and options.chunk may not be used together - if(options.children && options.chunks) { - throw new Error("You can't and it does not make any sense to use \"children\" and \"chunk\" options together."); - } - - /** - * options.async and options.filename are also not possible together - * as filename specifies how the chunk is called but "async" implies - * that webpack will take care of loading this file. - */ - if(options.async && options.filename) { - throw new Error(`You can not specify a filename if you use the "async" option. -You can however specify the name of the async chunk by passing the desired string as the "async" option.`); - } - - /** - * Make sure this is either an array or undefined. - * "name" can be a string and - * "names" a string or an array - */ - const chunkNames = options.name || options.names ? [].concat(options.name || options.names) : undefined; - return { - chunkNames: chunkNames, - filenameTemplate: options.filename, - minChunks: options.minChunks, - selectedChunks: options.chunks, - children: options.children, - deepChildren: options.deepChildren, - async: options.async, - minSize: options.minSize - }; - } - - apply(compiler) { - compiler.plugin("this-compilation", (compilation) => { - compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => { - // only optimize once - if(compilation[this.ident]) return; - compilation[this.ident] = true; - - /** - * Creates a list of "common"" chunks based on the options. - * The list is made up of preexisting or newly created chunks. - * - If chunk has the name as specified in the chunkNames it is put in the list - * - If no chunk with the name as given in chunkNames exists a new chunk is created and added to the list - * - * These chunks are the "targets" for extracted modules. - */ - const targetChunks = this.getTargetChunks(chunks, compilation, this.chunkNames, this.children, this.async); - - // iterate over all our new chunks - targetChunks.forEach((targetChunk, idx) => { - - /** - * These chunks are subject to get "common" modules extracted and moved to the common chunk - */ - const affectedChunks = this.getAffectedChunks(compilation, chunks, targetChunk, targetChunks, idx, this.selectedChunks, this.async, this.children, this.deepChildren); - - // bail if no chunk is affected - if(!affectedChunks) { - return; - } - - // If we are async create an async chunk now - // override the "commonChunk" with the newly created async one and use it as commonChunk from now on - let asyncChunk; - if(this.async) { - // If async chunk is one of the affected chunks, just use it - asyncChunk = affectedChunks.filter(c => c.name === this.async)[0]; - // Elsewise create a new one - if(!asyncChunk) { - asyncChunk = this.createAsyncChunk( - compilation, - targetChunks.length <= 1 || typeof this.async !== "string" ? this.async : - targetChunk.name ? `${this.async}-${targetChunk.name}` : - true, - targetChunk - ); - } - targetChunk = asyncChunk; - } - - /** - * Check which modules are "common" and could be extracted to a "common" chunk - */ - const extractableModules = this.getExtractableModules(this.minChunks, affectedChunks, targetChunk); - - // If the minSize option is set check if the size extracted from the chunk is reached - // else bail out here. - // As all modules/commons are interlinked with each other, common modules would be extracted - // if we reach this mark at a later common chunk. (quirky I guess). - if(this.minSize) { - const modulesSize = this.calculateModulesSize(extractableModules); - // if too small, bail - if(modulesSize < this.minSize) - return; - } - - // Remove modules that are moved to commons chunk from their original chunks - // return all chunks that are affected by having modules removed - we need them later (apparently) - const chunksWithExtractedModules = this.extractModulesAndReturnAffectedChunks(extractableModules, affectedChunks); - - // connect all extracted modules with the common chunk - this.addExtractedModulesToTargetChunk(targetChunk, extractableModules); - - // set filenameTemplate for chunk - if(this.filenameTemplate) - targetChunk.filenameTemplate = this.filenameTemplate; - - // if we are async connect the blocks of the "reallyUsedChunk" - the ones that had modules removed - - // with the commonChunk and get the origins for the asyncChunk (remember "asyncChunk === commonChunk" at this moment). - // bail out - if(this.async) { - this.moveExtractedChunkBlocksToTargetChunk(chunksWithExtractedModules, targetChunk); - asyncChunk.origins = this.extractOriginsOfChunksWithExtractedModules(chunksWithExtractedModules); - return; - } - - // we are not in "async" mode - // connect used chunks with commonChunk - shouldnt this be reallyUsedChunks here? - this.makeTargetChunkParentOfAffectedChunks(affectedChunks, targetChunk); - }); - return true; - }); - }); - } - - getTargetChunks(allChunks, compilation, chunkNames, children, asyncOption) { - const asyncOrNoSelectedChunk = children || asyncOption; - - // we have specified chunk names - if(chunkNames) { - // map chunks by chunkName for quick access - const allChunksNameMap = allChunks.reduce((map, chunk) => { - if(chunk.name) { - map.set(chunk.name, chunk); - } - return map; - }, new Map()); - - // Ensure we have a chunk per specified chunk name. - // Reuse existing chunks if possible - return chunkNames.map(chunkName => { - if(allChunksNameMap.has(chunkName)) { - return allChunksNameMap.get(chunkName); - } - // add the filtered chunks to the compilation - return compilation.addChunk(chunkName); - }); - } - - // we dont have named chunks specified, so we just take all of them - if(asyncOrNoSelectedChunk) { - return allChunks; - } - - /** - * No chunk name(s) was specified nor is this an async/children commons chunk - */ - throw new Error(`You did not specify any valid target chunk settings. -Take a look at the "name"/"names" or async/children option.`); - } - - getAffectedUnnamedChunks(affectedChunks, targetChunk, rootChunk, asyncOption, deepChildrenOption) { - let chunks = targetChunk.chunks; - chunks && chunks.forEach((chunk) => { - if(chunk.isInitial()) { - return; - } - // If all the parents of a chunk are either - // a) the target chunk we started with - // b) themselves affected chunks - // we can assume that this chunk is an affected chunk too, as there is no way a chunk that - // isn't only depending on the target chunk is a parent of the chunk tested - if(asyncOption || chunk.parents.every((parentChunk) => parentChunk === rootChunk || affectedChunks.has(parentChunk))) { - // This check not only dedupes the affectedChunks but also guarantees we avoid endless loops - if(!affectedChunks.has(chunk)) { - // We mutate the affected chunks before going deeper, so the deeper levels and other branches - // have the information of this chunk being affected for their assertion if a chunk should - // not be affected - affectedChunks.add(chunk); - - // We recurse down to all the children of the chunk, applying the same assumption. - // This guarantees that if a chunk should be an affected chunk, - // at the latest the last connection to the same chunk meets the - // condition to add it to the affected chunks. - if(deepChildrenOption === true) { - this.getAffectedUnnamedChunks(affectedChunks, chunk, rootChunk, asyncOption, deepChildrenOption); - } - } - } - }); - } - - getAffectedChunks(compilation, allChunks, targetChunk, targetChunks, currentIndex, selectedChunks, asyncOption, childrenOption, deepChildrenOption) { - const asyncOrNoSelectedChunk = childrenOption || asyncOption; - - if(Array.isArray(selectedChunks)) { - return allChunks.filter(chunk => { - const notCommmonChunk = chunk !== targetChunk; - const isSelectedChunk = selectedChunks.indexOf(chunk.name) > -1; - return notCommmonChunk && isSelectedChunk; - }); - } - - if(asyncOrNoSelectedChunk) { - let affectedChunks = new Set(); - this.getAffectedUnnamedChunks(affectedChunks, targetChunk, targetChunk, asyncOption, deepChildrenOption); - return Array.from(affectedChunks); - } - - /** - * past this point only entry chunks are allowed to become commonChunks - */ - if(targetChunk.parents.length > 0) { - compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + targetChunk.name + ")")); - return; - } - - /** - * If we find a "targetchunk" that is also a normal chunk (meaning it is probably specified as an entry) - * and the current target chunk comes after that and the found chunk has a runtime* - * make that chunk be an 'affected' chunk of the current target chunk. - * - * To understand what that means take a look at the "examples/chunkhash", this basically will - * result in the runtime to be extracted to the current target chunk. - * - * *runtime: the "runtime" is the "webpack"-block you may have seen in the bundles that resolves modules etc. - */ - return allChunks.filter((chunk) => { - const found = targetChunks.indexOf(chunk); - if(found >= currentIndex) return false; - return chunk.hasRuntime(); - }); - } - - createAsyncChunk(compilation, asyncOption, targetChunk) { - const asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined); - asyncChunk.chunkReason = "async commons chunk"; - asyncChunk.extraAsync = true; - asyncChunk.addParent(targetChunk); - targetChunk.addChunk(asyncChunk); - return asyncChunk; - } - - // If minChunks is a function use that - // otherwhise check if a module is used at least minChunks or 2 or usedChunks.length time - getModuleFilter(minChunks, targetChunk, usedChunksLength) { - if(typeof minChunks === "function") { - return minChunks; - } - const minCount = (minChunks || Math.max(2, usedChunksLength)); - const isUsedAtLeastMinTimes = (module, count) => count >= minCount; - return isUsedAtLeastMinTimes; - } - - getExtractableModules(minChunks, usedChunks, targetChunk) { - if(minChunks === Infinity) { - return []; - } - - // count how many chunks contain a module - const commonModulesToCountMap = usedChunks.reduce((map, chunk) => { - for(const module of chunk.modulesIterable) { - const count = map.has(module) ? map.get(module) : 0; - map.set(module, count + 1); - } - return map; - }, new Map()); - - // filter by minChunks - const moduleFilterCount = this.getModuleFilter(minChunks, targetChunk, usedChunks.length); - // filter by condition - const moduleFilterCondition = (module, chunk) => { - if(!module.chunkCondition) { - return true; - } - return module.chunkCondition(chunk); - }; - - return Array.from(commonModulesToCountMap).filter(entry => { - const module = entry[0]; - const count = entry[1]; - // if the module passes both filters, keep it. - return moduleFilterCount(module, count) && moduleFilterCondition(module, targetChunk); - }).map(entry => entry[0]); - } - - calculateModulesSize(modules) { - return modules.reduce((totalSize, module) => totalSize + module.size(), 0); - } - - extractModulesAndReturnAffectedChunks(reallyUsedModules, usedChunks) { - return reallyUsedModules.reduce((affectedChunksSet, module) => { - for(const chunk of usedChunks) { - // removeChunk returns true if the chunk was contained and succesfully removed - // false if the module did not have a connection to the chunk in question - if(module.removeChunk(chunk)) { - affectedChunksSet.add(chunk); - } - } - return affectedChunksSet; - }, new Set()); - } - - addExtractedModulesToTargetChunk(chunk, modules) { - for(const module of modules) { - chunk.addModule(module); - module.addChunk(chunk); - } - } - - makeTargetChunkParentOfAffectedChunks(usedChunks, commonChunk) { - for(const chunk of usedChunks) { - // set commonChunk as new sole parent - chunk.parents = [commonChunk]; - // add chunk to commonChunk - commonChunk.addChunk(chunk); - - for(const entrypoint of chunk.entrypoints) { - entrypoint.insertChunk(commonChunk, chunk); - } - } - } - - moveExtractedChunkBlocksToTargetChunk(chunks, targetChunk) { - for(const chunk of chunks) { - if(chunk === targetChunk) continue; - for(const block of chunk.blocks) { - if(block.chunks.indexOf(targetChunk) === -1) { - block.chunks.unshift(targetChunk); - } - targetChunk.addBlock(block); - } - } - } - - extractOriginsOfChunksWithExtractedModules(chunks) { - const origins = []; - for(const chunk of chunks) { - for(const origin of chunk.origins) { - const newOrigin = Object.create(origin); - newOrigin.reasons = (origin.reasons || []).concat("async commons"); - origins.push(newOrigin); - } - } - return origins; - } -} - -module.exports = CommonsChunkPlugin; diff --git a/node_modules/webpack/lib/optimize/ConcatenatedModule.js b/node_modules/webpack/lib/optimize/ConcatenatedModule.js index bfb772496..52d33e6f3 100644 --- a/node_modules/webpack/lib/optimize/ConcatenatedModule.js +++ b/node_modules/webpack/lib/optimize/ConcatenatedModule.js @@ -1,827 +1,1495 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -const Module = require("../Module"); -const Template = require("../Template"); -const Parser = require("../Parser"); -const crypto = require("crypto"); -const acorn = require("acorn"); -const escope = require("escope"); -const ReplaceSource = require("webpack-sources/lib/ReplaceSource"); -const ConcatSource = require("webpack-sources/lib/ConcatSource"); -const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); -const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency"); -const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency"); -const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency"); -const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency"); -const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); - -function ensureNsObjSource(info, moduleToInfoMap, requestShortener) { - if(!info.hasNamespaceObject) { - info.hasNamespaceObject = true; - const name = info.exportMap.get(true); - const nsObj = [`var ${name} = {};`]; - for(const exportName of info.module.providedExports) { - const finalName = getFinalName(info, exportName, moduleToInfoMap, requestShortener, false); - nsObj.push(`__webpack_require__.d(${name}, ${JSON.stringify(exportName)}, function() { return ${finalName}; });`); - } - info.namespaceObjectSource = nsObj.join("\n") + "\n"; - } -} - -function getExternalImport(importedModule, info, exportName, asCall) { - if(exportName === true) return info.name; - const used = importedModule.isUsed(exportName); - if(!used) return "/* unused reexport */undefined"; - if(info.interop && exportName === "default") { - return asCall ? `${info.interopName}()` : `${info.interopName}.a`; - } - // TODO use Template.toNormalComment when merging with pure-module - const comment = used !== exportName ? ` /* ${exportName} */` : ""; - const reference = `${info.name}[${JSON.stringify(used)}${comment}]`; - if(asCall) - return `Object(${reference})`; - return reference; -} - -function getFinalName(info, exportName, moduleToInfoMap, requestShortener, asCall) { - switch(info.type) { - case "concatenated": - { - const directExport = info.exportMap.get(exportName); - if(directExport) { - if(exportName === true) - ensureNsObjSource(info, moduleToInfoMap, requestShortener); - const name = info.internalNames.get(directExport); - if(!name) - throw new Error(`The export "${directExport}" in "${info.module.readableIdentifier(requestShortener)}" has no internal name`); - return name; - } - const reexport = info.reexportMap.get(exportName); - if(reexport) { - const refInfo = moduleToInfoMap.get(reexport.module); - if(refInfo) { - // module is in the concatenation - return getFinalName(refInfo, reexport.exportName, moduleToInfoMap, requestShortener, asCall); - } - } - const problem = `Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier(requestShortener)}"` + - ` (known exports: ${Array.from(info.exportMap.keys()).filter(name => name !== true).join(" ")}, ` + - `known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`; - // TODO use Template.toNormalComment when merging with pure-module - return `/* ${problem} */ undefined`; - } - case "external": - { - const importedModule = info.module; - return getExternalImport(importedModule, info, exportName, asCall); - } - } -} - -function getSymbolsFromScope(s, untilScope) { - const allUsedNames = new Set(); - let scope = s; - while(scope) { - if(untilScope === scope) break; - scope.variables.forEach(variable => allUsedNames.add(variable.name)); - scope = scope.upper; - } - return allUsedNames; -} - -function getAllReferences(variable) { - let set = variable.references; - // Look for inner scope variables too (like in class Foo { t() { Foo } }) - const identifiers = new Set(variable.identifiers); - for(const scope of variable.scope.childScopes) { - for(const innerVar of scope.variables) { - if(innerVar.identifiers.some(id => identifiers.has(id))) { - set = set.concat(innerVar.references); - break; - } - } - } - return set; -} - -function reduceSet(a, b) { - for(const item of b) - a.add(item); - return a; -} - -function getPathInAst(ast, node) { - if(ast === node) { - return []; - } - const nr = node.range; - var i; - if(Array.isArray(ast)) { - for(i = 0; i < ast.length; i++) { - const enterResult = enterNode(ast[i]); - if(typeof enterResult !== "undefined") - return enterResult; - } - } else if(ast && typeof ast === "object") { - const keys = Object.keys(ast); - for(i = 0; i < keys.length; i++) { - const value = ast[keys[i]]; - if(Array.isArray(value)) { - const pathResult = getPathInAst(value, node); - if(typeof pathResult !== "undefined") - return pathResult; - } else if(value && typeof value === "object") { - const enterResult = enterNode(value); - if(typeof enterResult !== "undefined") - return enterResult; - } - } - } - - function enterNode(n) { - const r = n.range; - if(r) { - if(r[0] <= nr[0] && r[1] >= nr[1]) { - const path = getPathInAst(n, node); - if(path) { - path.push(n); - return path; - } - } - } - return undefined; - } -} - -class ConcatenatedModule extends Module { - constructor(rootModule, modules) { - super(); - super.setChunks(rootModule._chunks); - this.rootModule = rootModule; - this.usedExports = rootModule.usedExports; - this.providedExports = rootModule.providedExports; - this.optimizationBailout = rootModule.optimizationBailout; - this.used = rootModule.used; - this.index = rootModule.index; - this.index2 = rootModule.index2; - this.depth = rootModule.depth; - this.built = modules.some(m => m.built); - this.cacheable = modules.every(m => m.cacheable); - const modulesSet = new Set(modules); - this.reasons = rootModule.reasons.filter(reason => !(reason.dependency instanceof HarmonyImportDependency) || !modulesSet.has(reason.module)); - this.meta = rootModule.meta; - this.moduleArgument = rootModule.moduleArgument; - this.exportsArgument = rootModule.exportsArgument; - this.strict = true; - this._numberOfConcatenatedModules = modules.length; - - this.dependencies = []; - this.dependenciesWarnings = []; - this.dependenciesErrors = []; - this.fileDependencies = []; - this.contextDependencies = []; - this.warnings = []; - this.errors = []; - this.assets = {}; - this._orderedConcatenationList = this._createOrderedConcatenationList(rootModule, modulesSet); - for(const info of this._orderedConcatenationList) { - if(info.type === "concatenated") { - const m = info.module; - - // populate dependencies - m.dependencies.filter(dep => !(dep instanceof HarmonyImportDependency) || !modulesSet.has(dep.module)) - .forEach(d => this.dependencies.push(d)); - // populate dep warning - m.dependenciesWarnings.forEach(depWarning => this.dependenciesWarnings.push(depWarning)); - // populate dep errors - m.dependenciesErrors.forEach(depError => this.dependenciesErrors.push(depError)); - // populate file dependencies - if(m.fileDependencies) m.fileDependencies.forEach(file => this.fileDependencies.push(file)); - // populate context dependencies - if(m.contextDependencies) m.contextDependencies.forEach(context => this.contextDependencies.push(context)); - // populate warnings - m.warnings.forEach(warning => this.warnings.push(warning)); - // populate errors - m.errors.forEach(error => this.errors.push(error)); - - Object.assign(this.assets, m.assets); - } - } - this._identifier = this._createIdentifier(); - } - - get modules() { - return this._orderedConcatenationList - .filter(info => info.type === "concatenated") - .map(info => info.module); - } - - identifier() { - return this._identifier; - } - - readableIdentifier(requestShortener) { - return this.rootModule.readableIdentifier(requestShortener) + ` + ${this._numberOfConcatenatedModules - 1} modules`; - } - - libIdent(options) { - return this.rootModule.libIdent(options); - } - - nameForCondition() { - return this.rootModule.nameForCondition(); - } - - build(options, compilation, resolver, fs, callback) { - throw new Error("Cannot build this module. It should be already built."); - } - - size() { - // Guess size from embedded modules - return this._orderedConcatenationList.reduce((sum, info) => { - switch(info.type) { - case "concatenated": - return sum + info.module.size(); - case "external": - return sum + 5; - } - return sum; - }, 0); - } - - _createOrderedConcatenationList(rootModule, modulesSet) { - const list = []; - const set = new Set(); - - function getConcatenatedImports(module) { - // TODO need changes when merging with the pure-module branch - const allDeps = module.dependencies - .filter(dep => dep instanceof HarmonyImportDependency && dep.module); - - return allDeps.map(dep => () => dep.module); - } - - function enterModule(getModule) { - const module = getModule(); - if(set.has(module)) return; - set.add(module); - if(modulesSet.has(module)) { - const imports = getConcatenatedImports(module); - imports.forEach(enterModule); - list.push({ - type: "concatenated", - module - }); - } else { - list.push({ - type: "external", - get module() { - // We need to use a getter here, because the module in the dependency - // could be replaced by some other process (i. e. also replaced with a - // concatenated module) - return getModule(); - } - }); - } - } - - enterModule(() => rootModule); - - return list; - } - - _createIdentifier() { - let orderedConcatenationListIdentifiers = ""; - for(let i = 0; i < this._orderedConcatenationList.length; i++) { - if(this._orderedConcatenationList[i].type === "concatenated") { - orderedConcatenationListIdentifiers += this._orderedConcatenationList[i].module.identifier(); - orderedConcatenationListIdentifiers += " "; - } - } - const hash = crypto.createHash("md5"); - hash.update(orderedConcatenationListIdentifiers); - return this.rootModule.identifier() + " " + hash.digest("hex"); - } - - source(dependencyTemplates, outputOptions, requestShortener) { - // Metainfo for each module - const modulesWithInfo = this._orderedConcatenationList.map((info, idx) => { - switch(info.type) { - case "concatenated": - { - const exportMap = new Map(); - const reexportMap = new Map(); - info.module.dependencies.forEach(dep => { - if(dep instanceof HarmonyExportSpecifierDependency) { - if(!exportMap.has(dep.name)) - exportMap.set(dep.name, dep.id); - } else if(dep instanceof HarmonyExportExpressionDependency) { - if(!exportMap.has("default")) - exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__"); - } else if(dep instanceof HarmonyExportImportedSpecifierDependency) { - const exportName = dep.name; - const importName = dep.id; - const importedModule = dep.importDependency.module; - if(exportName && importName) { - if(!reexportMap.has(exportName)) { - reexportMap.set(exportName, { - module: importedModule, - exportName: importName, - dependency: dep - }); - } - } else if(exportName) { - if(!reexportMap.has(exportName)) { - reexportMap.set(exportName, { - module: importedModule, - exportName: true, - dependency: dep - }); - } - } else if(importedModule) { - importedModule.providedExports.forEach(name => { - if(dep.activeExports.has(name) || name === "default") - return; - if(!reexportMap.has(name)) { - reexportMap.set(name, { - module: importedModule, - exportName: name, - dependency: dep - }); - } - }); - } - } - }); - return { - type: "concatenated", - module: info.module, - index: idx, - ast: undefined, - source: undefined, - globalScope: undefined, - moduleScope: undefined, - internalNames: new Map(), - exportMap: exportMap, - reexportMap: reexportMap, - hasNamespaceObject: false, - namespaceObjectSource: null - }; - } - case "external": - return { - type: "external", - module: info.module, - index: idx, - name: undefined, - interopName: undefined, - interop: undefined - }; - default: - throw new Error(`Unsupported concatenation entry type ${info.type}`); - } - }); - - // Create mapping from module to info - const moduleToInfoMap = new Map(); - modulesWithInfo.forEach(m => moduleToInfoMap.set(m.module, m)); - - // Configure template decorators for dependencies - const innerDependencyTemplates = new Map(dependencyTemplates); - - innerDependencyTemplates.set(HarmonyImportSpecifierDependency, new HarmonyImportSpecifierDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyImportSpecifierDependency), - moduleToInfoMap - )); - innerDependencyTemplates.set(HarmonyImportDependency, new HarmonyImportDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyImportDependency), - moduleToInfoMap - )); - innerDependencyTemplates.set(HarmonyExportSpecifierDependency, new HarmonyExportSpecifierDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyExportSpecifierDependency), - this.rootModule - )); - innerDependencyTemplates.set(HarmonyExportExpressionDependency, new HarmonyExportExpressionDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyExportExpressionDependency), - this.rootModule, - moduleToInfoMap - )); - innerDependencyTemplates.set(HarmonyExportImportedSpecifierDependency, new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyExportImportedSpecifierDependency), - this.rootModule, - moduleToInfoMap - )); - innerDependencyTemplates.set(HarmonyCompatibilityDependency, new HarmonyCompatibilityDependencyConcatenatedTemplate( - dependencyTemplates.get(HarmonyCompatibilityDependency), - this.rootModule, - moduleToInfoMap - )); - innerDependencyTemplates.set("hash", innerDependencyTemplates.get("hash") + this.rootModule.identifier()); - - // Generate source code and analyse scopes - // Prepare a ReplaceSource for the final source - modulesWithInfo.forEach(info => { - if(info.type === "concatenated") { - const m = info.module; - const source = m.source(innerDependencyTemplates, outputOptions, requestShortener); - const code = source.source(); - let ast; - try { - ast = acorn.parse(code, { - ranges: true, - locations: true, - ecmaVersion: Parser.ECMA_VERSION, - sourceType: "module" - }); - } catch(err) { - if(err.loc && typeof err.loc === "object" && typeof err.loc.line === "number") { - const lineNumber = err.loc.line; - const lines = code.split("\n"); - err.message += "\n| " + lines.slice(Math.max(0, lineNumber - 3), lineNumber + 2).join("\n| "); - } - throw err; - } - const scopeManager = escope.analyze(ast, { - ecmaVersion: 6, - sourceType: "module", - optimistic: true, - ignoreEval: true, - impliedStrict: true - }); - const globalScope = scopeManager.acquire(ast); - const moduleScope = globalScope.childScopes[0]; - const resultSource = new ReplaceSource(source); - info.ast = ast; - info.source = resultSource; - info.globalScope = globalScope; - info.moduleScope = moduleScope; - } - }); - - // List of all used names to avoid conflicts - const allUsedNames = new Set([ - "__WEBPACK_MODULE_DEFAULT_EXPORT__", // avoid using this internal name - - "abstract", "arguments", "async", "await", "boolean", "break", "byte", "case", "catch", "char", "class", - "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "eval", - "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", - "implements", "import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", - "null", "package", "private", "protected", "public", "return", "short", "static", "super", - "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", - "var", "void", "volatile", "while", "with", "yield", - - "module", "__dirname", "__filename", "exports", - - "Array", "Date", "eval", "function", "hasOwnProperty", "Infinity", "isFinite", "isNaN", - "isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String", - "toString", "undefined", "valueOf", - - "alert", "all", "anchor", "anchors", "area", "assign", "blur", "button", "checkbox", - "clearInterval", "clearTimeout", "clientInformation", "close", "closed", "confirm", "constructor", - "crypto", "decodeURI", "decodeURIComponent", "defaultStatus", "document", "element", "elements", - "embed", "embeds", "encodeURI", "encodeURIComponent", "escape", "event", "fileUpload", "focus", - "form", "forms", "frame", "innerHeight", "innerWidth", "layer", "layers", "link", "location", - "mimeTypes", "navigate", "navigator", "frames", "frameRate", "hidden", "history", "image", - "images", "offscreenBuffering", "open", "opener", "option", "outerHeight", "outerWidth", - "packages", "pageXOffset", "pageYOffset", "parent", "parseFloat", "parseInt", "password", "pkcs11", - "plugin", "prompt", "propertyIsEnum", "radio", "reset", "screenX", "screenY", "scroll", "secure", - "select", "self", "setInterval", "setTimeout", "status", "submit", "taint", "text", "textarea", - "top", "unescape", "untaint", "window", - - "onblur", "onclick", "onerror", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onmouseover", - "onload", "onmouseup", "onmousedown", "onsubmit" - ]); - - // get all global names - modulesWithInfo.forEach(info => { - if(info.globalScope) { - info.globalScope.through.forEach(reference => { - const name = reference.identifier.name; - if(/^__WEBPACK_MODULE_REFERENCE__\d+_([\da-f]+|ns)(_call)?__$/.test(name)) { - for(const s of getSymbolsFromScope(reference.from, info.moduleScope)) { - allUsedNames.add(s); - } - } else { - allUsedNames.add(name); - } - }); - } - }); - - // generate names for symbols - modulesWithInfo.forEach(info => { - switch(info.type) { - case "concatenated": - { - const namespaceObjectName = this.findNewName("namespaceObject", allUsedNames, null, info.module.readableIdentifier(requestShortener)); - allUsedNames.add(namespaceObjectName); - info.internalNames.set(namespaceObjectName, namespaceObjectName); - info.exportMap.set(true, namespaceObjectName); - info.moduleScope.variables.forEach(variable => { - const name = variable.name; - if(allUsedNames.has(name)) { - const references = getAllReferences(variable); - const symbolsInReferences = references.map(ref => getSymbolsFromScope(ref.from, info.moduleScope)).reduce(reduceSet, new Set()); - const newName = this.findNewName(name, allUsedNames, symbolsInReferences, info.module.readableIdentifier(requestShortener)); - allUsedNames.add(newName); - info.internalNames.set(name, newName); - const source = info.source; - const allIdentifiers = new Set(references.map(r => r.identifier).concat(variable.identifiers)); - for(const identifier of allIdentifiers) { - const r = identifier.range; - const path = getPathInAst(info.ast, identifier); - if(path && path.length > 1 && path[1].type === "Property" && path[1].shorthand) { - source.insert(r[1], `: ${newName}`); - } else { - source.replace(r[0], r[1] - 1, newName); - } - } - } else { - allUsedNames.add(name); - info.internalNames.set(name, name); - } - }); - break; - } - case "external": - { - info.interop = info.module.meta && !info.module.meta.harmonyModule; - const externalName = this.findNewName("", allUsedNames, null, info.module.readableIdentifier(requestShortener)); - allUsedNames.add(externalName); - info.name = externalName; - if(info.interop) { - const externalNameInterop = this.findNewName("default", allUsedNames, null, info.module.readableIdentifier(requestShortener)); - allUsedNames.add(externalNameInterop); - info.interopName = externalNameInterop; - } - break; - } - } - }); - - // Find and replace referenced to modules - modulesWithInfo.forEach(info => { - if(info.type === "concatenated") { - info.globalScope.through.forEach(reference => { - const name = reference.identifier.name; - const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?__$/.exec(name); - if(match) { - const referencedModule = modulesWithInfo[+match[1]]; - let exportName; - if(match[2] === "ns") { - exportName = true; - } else { - const exportData = match[2]; - exportName = new Buffer(exportData, "hex").toString("utf-8"); // eslint-disable-line node/no-deprecated-api - } - const asCall = !!match[3]; - const finalName = getFinalName(referencedModule, exportName, moduleToInfoMap, requestShortener, asCall); - const r = reference.identifier.range; - const source = info.source; - source.replace(r[0], r[1] - 1, finalName); - } - }); - } - }); - - const result = new ConcatSource(); - - // add harmony compatibility flag (must be first because of possible circular dependencies) - const usedExports = this.rootModule.usedExports; - if(usedExports === true) { - result.add(`Object.defineProperty(${this.exportsArgument || "exports"}, "__esModule", { value: true });\n`); - } - - // define required namespace objects (must be before evaluation modules) - modulesWithInfo.forEach(info => { - if(info.namespaceObjectSource) { - result.add(info.namespaceObjectSource); - } - }); - - // evaluate modules in order - modulesWithInfo.forEach(info => { - switch(info.type) { - case "concatenated": - result.add(`\n// CONCATENATED MODULE: ${info.module.readableIdentifier(requestShortener)}\n`); - result.add(info.source); - break; - case "external": - result.add(`\n// EXTERNAL MODULE: ${info.module.readableIdentifier(requestShortener)}\n`); - result.add(`var ${info.name} = __webpack_require__(${JSON.stringify(info.module.id)});\n`); - if(info.interop) { - result.add(`var ${info.interopName} = /*#__PURE__*/__webpack_require__.n(${info.name});\n`); - } - break; - default: - throw new Error(`Unsupported concatenation entry type ${info.type}`); - } - }); - - return result; - } - - findNewName(oldName, usedNamed1, usedNamed2, extraInfo) { - let name = oldName; - - if(name === "__WEBPACK_MODULE_DEFAULT_EXPORT__") - name = ""; - - // Remove uncool stuff - extraInfo = extraInfo.replace(/\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g, ""); - - const splittedInfo = extraInfo.split("/"); - while(splittedInfo.length) { - name = splittedInfo.pop() + (name ? "_" + name : ""); - const nameIdent = Template.toIdentifier(name); - if(!usedNamed1.has(nameIdent) && (!usedNamed2 || !usedNamed2.has(nameIdent))) return nameIdent; - } - - let i = 0; - let nameWithNumber = Template.toIdentifier(`${name}_${i}`); - while(usedNamed1.has(nameWithNumber) || (usedNamed2 && usedNamed2.has(nameWithNumber))) { - i++; - nameWithNumber = Template.toIdentifier(`${name}_${i}`); - } - return nameWithNumber; - } - - updateHash(hash) { - for(const info of this._orderedConcatenationList) { - switch(info.type) { - case "concatenated": - info.module.updateHash(hash); - break; - case "external": - hash.update(`${info.module.id}`); - break; - } - } - super.updateHash(hash); - } - -} - -class HarmonyImportSpecifierDependencyConcatenatedTemplate { - constructor(originalTemplate, modulesMap) { - this.originalTemplate = originalTemplate; - this.modulesMap = modulesMap; - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - const module = dep.importDependency.module; - const info = this.modulesMap.get(module); - if(!info) { - this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates); - return; - } - let content; - if(dep.id === null) { - content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`; - } else if(dep.namespaceObjectAsContext) { - content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__[${JSON.stringify(dep.id)}]`; - } else { - const exportData = new Buffer(dep.id, "utf-8").toString("hex"); // eslint-disable-line node/no-deprecated-api - content = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}${dep.call ? "_call" : ""}__`; - } - if(dep.shorthand) { - content = dep.name + ": " + content; - } - source.replace(dep.range[0], dep.range[1] - 1, content); - } -} - -class HarmonyImportDependencyConcatenatedTemplate { - constructor(originalTemplate, modulesMap) { - this.originalTemplate = originalTemplate; - this.modulesMap = modulesMap; - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - const module = dep.module; - const info = this.modulesMap.get(module); - if(!info) { - this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates); - return; - } - source.replace(dep.range[0], dep.range[1] - 1, ""); - } -} - -class HarmonyExportSpecifierDependencyConcatenatedTemplate { - constructor(originalTemplate, rootModule) { - this.originalTemplate = originalTemplate; - this.rootModule = rootModule; - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - if(dep.originModule === this.rootModule) { - this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates); - } - } -} - -class HarmonyExportExpressionDependencyConcatenatedTemplate { - constructor(originalTemplate, rootModule) { - this.originalTemplate = originalTemplate; - this.rootModule = rootModule; - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - let content = "/* harmony default export */ var __WEBPACK_MODULE_DEFAULT_EXPORT__ = "; - if(dep.originModule === this.rootModule) { - const used = dep.originModule.isUsed("default"); - const exportsName = dep.originModule.exportsArgument || "exports"; - if(used) content += `${exportsName}[${JSON.stringify(used)}] = `; - } - - if(dep.range) { - source.replace(dep.rangeStatement[0], dep.range[0] - 1, content + "("); - source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");"); - return; - } - - source.replace(dep.rangeStatement[0], dep.rangeStatement[1] - 1, content); - } -} - -class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate { - constructor(originalTemplate, rootModule, modulesMap) { - this.originalTemplate = originalTemplate; - this.rootModule = rootModule; - this.modulesMap = modulesMap; - } - - getExports(dep) { - const importModule = dep.importDependency.module; - if(dep.id) { - // export { named } from "module" - return [{ - name: dep.name, - id: dep.id, - module: importModule - }]; - } - if(dep.name) { - // export * as abc from "module" - return [{ - name: dep.name, - id: true, - module: importModule - }]; - } - // export * from "module" - return importModule.providedExports.filter(exp => exp !== "default" && !dep.activeExports.has(exp)).map(exp => { - return { - name: exp, - id: exp, - module: importModule - }; - }); - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - if(dep.originModule === this.rootModule) { - if(this.modulesMap.get(dep.importDependency.module)) { - const exportDefs = this.getExports(dep); - exportDefs.forEach(def => { - const info = this.modulesMap.get(def.module); - const used = dep.originModule.isUsed(def.name); - if(!used) { - source.insert(-1, `/* unused concated harmony import ${dep.name} */\n`); - } - let finalName; - if(def.id === true) { - finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns__`; - } else { - const exportData = new Buffer(def.id, "utf-8").toString("hex"); // eslint-disable-line node/no-deprecated-api - finalName = `__WEBPACK_MODULE_REFERENCE__${info.index}_${exportData}__`; - } - const exportsName = this.rootModule.exportsArgument || "exports"; - const content = `/* concated harmony reexport */__webpack_require__.d(${exportsName}, ${JSON.stringify(used)}, function() { return ${finalName}; });\n`; - source.insert(-1, content); - }); - } else { - this.originalTemplate.apply(dep, source, outputOptions, requestShortener, dependencyTemplates); - } - } - } -} - -class HarmonyCompatibilityDependencyConcatenatedTemplate { - constructor(originalTemplate, rootModule, modulesMap) { - this.originalTemplate = originalTemplate; - this.rootModule = rootModule; - this.modulesMap = modulesMap; - } - - apply(dep, source, outputOptions, requestShortener, dependencyTemplates) { - // do nothing - } -} - -module.exports = ConcatenatedModule; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const Module = require("../Module"); +const Template = require("../Template"); +const Parser = require("../Parser"); +const eslintScope = require("eslint-scope"); +const { ConcatSource, ReplaceSource } = require("webpack-sources"); +const DependencyReference = require("../dependencies/DependencyReference"); +const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); +const HarmonyImportSideEffectDependency = require("../dependencies/HarmonyImportSideEffectDependency"); +const HarmonyImportSpecifierDependency = require("../dependencies/HarmonyImportSpecifierDependency"); +const HarmonyExportSpecifierDependency = require("../dependencies/HarmonyExportSpecifierDependency"); +const HarmonyExportExpressionDependency = require("../dependencies/HarmonyExportExpressionDependency"); +const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency"); +const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); +const createHash = require("../util/createHash"); + +/** @typedef {import("../Dependency")} Dependency */ +/** @typedef {import("../Compilation")} Compilation */ +/** @typedef {import("../util/createHash").Hash} Hash */ + +/** + * @typedef {Object} ConcatenationEntry + * @property {"concatenated" | "external"} type + * @property {Module} module + */ + +const ensureNsObjSource = ( + info, + moduleToInfoMap, + requestShortener, + strictHarmonyModule +) => { + if (!info.hasNamespaceObject) { + info.hasNamespaceObject = true; + const name = info.exportMap.get(true); + const nsObj = [`var ${name} = {};`, `__webpack_require__.r(${name});`]; + for (const exportName of info.module.buildMeta.providedExports) { + const finalName = getFinalName( + info, + exportName, + moduleToInfoMap, + requestShortener, + false, + strictHarmonyModule + ); + nsObj.push( + `__webpack_require__.d(${name}, ${JSON.stringify( + exportName + )}, function() { return ${finalName}; });` + ); + } + info.namespaceObjectSource = nsObj.join("\n") + "\n"; + } +}; + +const getExternalImport = ( + importedModule, + info, + exportName, + asCall, + strictHarmonyModule +) => { + const used = importedModule.isUsed(exportName); + if (!used) return "/* unused reexport */undefined"; + const comment = + used !== exportName ? ` ${Template.toNormalComment(exportName)}` : ""; + switch (importedModule.buildMeta.exportsType) { + case "named": + if (exportName === "default") { + return info.name; + } else if (exportName === true) { + info.interopNamespaceObjectUsed = true; + return info.interopNamespaceObjectName; + } else { + break; + } + case "namespace": + if (exportName === true) { + return info.name; + } else { + break; + } + default: + if (strictHarmonyModule) { + if (exportName === "default") { + return info.name; + } else if (exportName === true) { + info.interopNamespaceObjectUsed = true; + return info.interopNamespaceObjectName; + } else { + return "/* non-default import from non-esm module */undefined"; + } + } else { + if (exportName === "default") { + info.interopDefaultAccessUsed = true; + return asCall + ? `${info.interopDefaultAccessName}()` + : `${info.interopDefaultAccessName}.a`; + } else if (exportName === true) { + return info.name; + } else { + break; + } + } + } + const reference = `${info.name}[${JSON.stringify(used)}${comment}]`; + if (asCall) return `Object(${reference})`; + return reference; +}; + +const getFinalName = ( + info, + exportName, + moduleToInfoMap, + requestShortener, + asCall, + strictHarmonyModule, + alreadyVisited = new Set() +) => { + switch (info.type) { + case "concatenated": { + const directExport = info.exportMap.get(exportName); + if (directExport) { + if (exportName === true) { + ensureNsObjSource( + info, + moduleToInfoMap, + requestShortener, + strictHarmonyModule + ); + } else if (!info.module.isUsed(exportName)) { + return "/* unused export */ undefined"; + } + if (info.globalExports.has(directExport)) { + return directExport; + } + const name = info.internalNames.get(directExport); + if (!name) { + throw new Error( + `The export "${directExport}" in "${info.module.readableIdentifier( + requestShortener + )}" has no internal name` + ); + } + return name; + } + const reexport = info.reexportMap.get(exportName); + if (reexport) { + if (alreadyVisited.has(reexport)) { + throw new Error( + `Circular reexports ${Array.from( + alreadyVisited, + e => + `"${e.module.readableIdentifier(requestShortener)}".${ + e.exportName + }` + ).join( + " --> " + )} -(circular)-> "${reexport.module.readableIdentifier( + requestShortener + )}".${reexport.exportName}` + ); + } + alreadyVisited.add(reexport); + const refInfo = moduleToInfoMap.get(reexport.module); + if (refInfo) { + // module is in the concatenation + return getFinalName( + refInfo, + reexport.exportName, + moduleToInfoMap, + requestShortener, + asCall, + strictHarmonyModule, + alreadyVisited + ); + } + } + const problem = + `Cannot get final name for export "${exportName}" in "${info.module.readableIdentifier( + requestShortener + )}"` + + ` (known exports: ${Array.from(info.exportMap.keys()) + .filter(name => name !== true) + .join(" ")}, ` + + `known reexports: ${Array.from(info.reexportMap.keys()).join(" ")})`; + return `${Template.toNormalComment(problem)} undefined`; + } + case "external": { + const importedModule = info.module; + return getExternalImport( + importedModule, + info, + exportName, + asCall, + strictHarmonyModule + ); + } + } +}; + +const addScopeSymbols1 = (s, nameSet, scopeSet) => { + let scope = s; + while (scope) { + if (scopeSet.has(scope)) break; + scopeSet.add(scope); + for (const variable of scope.variables) { + nameSet.add(variable.name); + } + scope = scope.upper; + } +}; + +const addScopeSymbols2 = (s, nameSet, scopeSet1, scopeSet2) => { + let scope = s; + while (scope) { + if (scopeSet1.has(scope)) break; + if (scopeSet2.has(scope)) break; + scopeSet1.add(scope); + for (const variable of scope.variables) { + nameSet.add(variable.name); + } + scope = scope.upper; + } +}; + +const getAllReferences = variable => { + let set = variable.references; + // Look for inner scope variables too (like in class Foo { t() { Foo } }) + const identifiers = new Set(variable.identifiers); + for (const scope of variable.scope.childScopes) { + for (const innerVar of scope.variables) { + if (innerVar.identifiers.some(id => identifiers.has(id))) { + set = set.concat(innerVar.references); + break; + } + } + } + return set; +}; + +const getPathInAst = (ast, node) => { + if (ast === node) { + return []; + } + + const nr = node.range; + + const enterNode = n => { + if (!n) return undefined; + const r = n.range; + if (r) { + if (r[0] <= nr[0] && r[1] >= nr[1]) { + const path = getPathInAst(n, node); + if (path) { + path.push(n); + return path; + } + } + } + return undefined; + }; + + var i; + if (Array.isArray(ast)) { + for (i = 0; i < ast.length; i++) { + const enterResult = enterNode(ast[i]); + if (enterResult !== undefined) return enterResult; + } + } else if (ast && typeof ast === "object") { + const keys = Object.keys(ast); + for (i = 0; i < keys.length; i++) { + const value = ast[keys[i]]; + if (Array.isArray(value)) { + const pathResult = getPathInAst(value, node); + if (pathResult !== undefined) return pathResult; + } else if (value && typeof value === "object") { + const enterResult = enterNode(value); + if (enterResult !== undefined) return enterResult; + } + } + } +}; + +class ConcatenatedModule extends Module { + constructor(rootModule, modules, concatenationList) { + super("javascript/esm", null); + super.setChunks(rootModule._chunks); + + // Info from Factory + this.rootModule = rootModule; + this.factoryMeta = rootModule.factoryMeta; + + // Info from Compilation + this.index = rootModule.index; + this.index2 = rootModule.index2; + this.depth = rootModule.depth; + + // Info from Optimization + this.used = rootModule.used; + this.usedExports = rootModule.usedExports; + + // Info from Build + this.buildInfo = { + strict: true, + cacheable: modules.every(m => m.buildInfo.cacheable), + moduleArgument: rootModule.buildInfo.moduleArgument, + exportsArgument: rootModule.buildInfo.exportsArgument, + fileDependencies: new Set(), + contextDependencies: new Set(), + assets: undefined + }; + this.built = modules.some(m => m.built); + this.buildMeta = rootModule.buildMeta; + + // Caching + this._numberOfConcatenatedModules = modules.length; + + // Graph + const modulesSet = new Set(modules); + this.reasons = rootModule.reasons.filter( + reason => + !(reason.dependency instanceof HarmonyImportDependency) || + !modulesSet.has(reason.module) + ); + + this.dependencies = []; + + this.warnings = []; + this.errors = []; + this._orderedConcatenationList = + concatenationList || + ConcatenatedModule.createConcatenationList(rootModule, modulesSet, null); + for (const info of this._orderedConcatenationList) { + if (info.type === "concatenated") { + const m = info.module; + + // populate dependencies + for (const d of m.dependencies.filter( + dep => + !(dep instanceof HarmonyImportDependency) || + !modulesSet.has(dep._module) + )) { + this.dependencies.push(d); + } + // populate file dependencies + if (m.buildInfo.fileDependencies) { + for (const file of m.buildInfo.fileDependencies) { + this.buildInfo.fileDependencies.add(file); + } + } + // populate context dependencies + if (m.buildInfo.contextDependencies) { + for (const context of m.buildInfo.contextDependencies) { + this.buildInfo.contextDependencies.add(context); + } + } + // populate warnings + for (const warning of m.warnings) { + this.warnings.push(warning); + } + // populate errors + for (const error of m.errors) { + this.errors.push(error); + } + + if (m.buildInfo.assets) { + if (this.buildInfo.assets === undefined) { + this.buildInfo.assets = Object.create(null); + } + Object.assign(this.buildInfo.assets, m.buildInfo.assets); + } + } + } + this._identifier = this._createIdentifier(); + } + + get modules() { + return this._orderedConcatenationList + .filter(info => info.type === "concatenated") + .map(info => info.module); + } + + identifier() { + return this._identifier; + } + + readableIdentifier(requestShortener) { + return ( + this.rootModule.readableIdentifier(requestShortener) + + ` + ${this._numberOfConcatenatedModules - 1} modules` + ); + } + + libIdent(options) { + return this.rootModule.libIdent(options); + } + + nameForCondition() { + return this.rootModule.nameForCondition(); + } + + build(options, compilation, resolver, fs, callback) { + throw new Error("Cannot build this module. It should be already built."); + } + + size() { + // Guess size from embedded modules + return this._orderedConcatenationList.reduce((sum, info) => { + switch (info.type) { + case "concatenated": + return sum + info.module.size(); + case "external": + return sum + 5; + } + return sum; + }, 0); + } + + /** + * @param {Module} rootModule the root of the concatenation + * @param {Set} modulesSet a set of modules which should be concatenated + * @param {Compilation} compilation the compilation context + * @returns {ConcatenationEntry[]} concatenation list + */ + static createConcatenationList(rootModule, modulesSet, compilation) { + const list = []; + const set = new Set(); + + /** + * @param {Module} module a module + * @returns {(function(): Module)[]} imported modules in order + */ + const getConcatenatedImports = module => { + /** @type {WeakMap} */ + const map = new WeakMap(); + const references = module.dependencies + .filter(dep => dep instanceof HarmonyImportDependency) + .map(dep => { + const ref = compilation.getDependencyReference(module, dep); + if (ref) map.set(ref, dep); + return ref; + }) + .filter(ref => ref); + DependencyReference.sort(references); + // TODO webpack 5: remove this hack, see also DependencyReference + return references.map(ref => { + const dep = map.get(ref); + return () => compilation.getDependencyReference(module, dep).module; + }); + }; + + const enterModule = getModule => { + const module = getModule(); + if (!module) return; + if (set.has(module)) return; + set.add(module); + if (modulesSet.has(module)) { + const imports = getConcatenatedImports(module); + imports.forEach(enterModule); + list.push({ + type: "concatenated", + module + }); + } else { + list.push({ + type: "external", + get module() { + // We need to use a getter here, because the module in the dependency + // could be replaced by some other process (i. e. also replaced with a + // concatenated module) + return getModule(); + } + }); + } + }; + + enterModule(() => rootModule); + + return list; + } + + _createIdentifier() { + let orderedConcatenationListIdentifiers = ""; + for (let i = 0; i < this._orderedConcatenationList.length; i++) { + if (this._orderedConcatenationList[i].type === "concatenated") { + orderedConcatenationListIdentifiers += this._orderedConcatenationList[ + i + ].module.identifier(); + orderedConcatenationListIdentifiers += " "; + } + } + const hash = createHash("md4"); + hash.update(orderedConcatenationListIdentifiers); + return this.rootModule.identifier() + " " + hash.digest("hex"); + } + + source(dependencyTemplates, runtimeTemplate) { + const requestShortener = runtimeTemplate.requestShortener; + // Metainfo for each module + const modulesWithInfo = this._orderedConcatenationList.map((info, idx) => { + switch (info.type) { + case "concatenated": { + const exportMap = new Map(); + const reexportMap = new Map(); + for (const dep of info.module.dependencies) { + if (dep instanceof HarmonyExportSpecifierDependency) { + if (!exportMap.has(dep.name)) { + exportMap.set(dep.name, dep.id); + } + } else if (dep instanceof HarmonyExportExpressionDependency) { + if (!exportMap.has("default")) { + exportMap.set("default", "__WEBPACK_MODULE_DEFAULT_EXPORT__"); + } + } else if ( + dep instanceof HarmonyExportImportedSpecifierDependency + ) { + const exportName = dep.name; + const importName = dep.id; + const importedModule = dep._module; + if (exportName && importName) { + if (!reexportMap.has(exportName)) { + reexportMap.set(exportName, { + module: importedModule, + exportName: importName, + dependency: dep + }); + } + } else if (exportName) { + if (!reexportMap.has(exportName)) { + reexportMap.set(exportName, { + module: importedModule, + exportName: true, + dependency: dep + }); + } + } else if (importedModule) { + for (const name of importedModule.buildMeta.providedExports) { + if (dep.activeExports.has(name) || name === "default") { + continue; + } + if (!reexportMap.has(name)) { + reexportMap.set(name, { + module: importedModule, + exportName: name, + dependency: dep + }); + } + } + } + } + } + return { + type: "concatenated", + module: info.module, + index: idx, + ast: undefined, + internalSource: undefined, + source: undefined, + globalScope: undefined, + moduleScope: undefined, + internalNames: new Map(), + globalExports: new Set(), + exportMap: exportMap, + reexportMap: reexportMap, + hasNamespaceObject: false, + namespaceObjectSource: null + }; + } + case "external": + return { + type: "external", + module: info.module, + index: idx, + name: undefined, + interopNamespaceObjectUsed: false, + interopNamespaceObjectName: undefined, + interopDefaultAccessUsed: false, + interopDefaultAccessName: undefined + }; + default: + throw new Error(`Unsupported concatenation entry type ${info.type}`); + } + }); + + // Create mapping from module to info + const moduleToInfoMap = new Map(); + for (const m of modulesWithInfo) { + moduleToInfoMap.set(m.module, m); + } + + // Configure template decorators for dependencies + const innerDependencyTemplates = new Map(dependencyTemplates); + + innerDependencyTemplates.set( + HarmonyImportSpecifierDependency, + new HarmonyImportSpecifierDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyImportSpecifierDependency), + moduleToInfoMap + ) + ); + innerDependencyTemplates.set( + HarmonyImportSideEffectDependency, + new HarmonyImportSideEffectDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyImportSideEffectDependency), + moduleToInfoMap + ) + ); + innerDependencyTemplates.set( + HarmonyExportSpecifierDependency, + new HarmonyExportSpecifierDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyExportSpecifierDependency), + this.rootModule + ) + ); + innerDependencyTemplates.set( + HarmonyExportExpressionDependency, + new HarmonyExportExpressionDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyExportExpressionDependency), + this.rootModule + ) + ); + innerDependencyTemplates.set( + HarmonyExportImportedSpecifierDependency, + new HarmonyExportImportedSpecifierDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyExportImportedSpecifierDependency), + this.rootModule, + moduleToInfoMap + ) + ); + innerDependencyTemplates.set( + HarmonyCompatibilityDependency, + new HarmonyCompatibilityDependencyConcatenatedTemplate( + dependencyTemplates.get(HarmonyCompatibilityDependency), + this.rootModule, + moduleToInfoMap + ) + ); + + // Must use full identifier in our cache here to ensure that the source + // is updated should our dependencies list change. + // TODO webpack 5 refactor + innerDependencyTemplates.set( + "hash", + innerDependencyTemplates.get("hash") + this.identifier() + ); + + // Generate source code and analyse scopes + // Prepare a ReplaceSource for the final source + for (const info of modulesWithInfo) { + if (info.type === "concatenated") { + const m = info.module; + const source = m.source(innerDependencyTemplates, runtimeTemplate); + const code = source.source(); + let ast; + try { + ast = Parser.parse(code, { + sourceType: "module" + }); + } catch (err) { + if ( + err.loc && + typeof err.loc === "object" && + typeof err.loc.line === "number" + ) { + const lineNumber = err.loc.line; + const lines = code.split("\n"); + err.message += + "\n| " + + lines + .slice(Math.max(0, lineNumber - 3), lineNumber + 2) + .join("\n| "); + } + throw err; + } + const scopeManager = eslintScope.analyze(ast, { + ecmaVersion: 6, + sourceType: "module", + optimistic: true, + ignoreEval: true, + impliedStrict: true + }); + const globalScope = scopeManager.acquire(ast); + const moduleScope = globalScope.childScopes[0]; + const resultSource = new ReplaceSource(source); + info.ast = ast; + info.internalSource = source; + info.source = resultSource; + info.globalScope = globalScope; + info.moduleScope = moduleScope; + } + } + + // List of all used names to avoid conflicts + const allUsedNames = new Set([ + "__WEBPACK_MODULE_DEFAULT_EXPORT__", // avoid using this internal name + + "abstract", + "arguments", + "async", + "await", + "boolean", + "break", + "byte", + "case", + "catch", + "char", + "class", + "const", + "continue", + "debugger", + "default", + "delete", + "do", + "double", + "else", + "enum", + "eval", + "export", + "extends", + "false", + "final", + "finally", + "float", + "for", + "function", + "goto", + "if", + "implements", + "import", + "in", + "instanceof", + "int", + "interface", + "let", + "long", + "native", + "new", + "null", + "package", + "private", + "protected", + "public", + "return", + "short", + "static", + "super", + "switch", + "synchronized", + "this", + "throw", + "throws", + "transient", + "true", + "try", + "typeof", + "var", + "void", + "volatile", + "while", + "with", + "yield", + + "module", + "__dirname", + "__filename", + "exports", + + "Array", + "Date", + "eval", + "function", + "hasOwnProperty", + "Infinity", + "isFinite", + "isNaN", + "isPrototypeOf", + "length", + "Math", + "NaN", + "name", + "Number", + "Object", + "prototype", + "String", + "toString", + "undefined", + "valueOf", + + "alert", + "all", + "anchor", + "anchors", + "area", + "assign", + "blur", + "button", + "checkbox", + "clearInterval", + "clearTimeout", + "clientInformation", + "close", + "closed", + "confirm", + "constructor", + "crypto", + "decodeURI", + "decodeURIComponent", + "defaultStatus", + "document", + "element", + "elements", + "embed", + "embeds", + "encodeURI", + "encodeURIComponent", + "escape", + "event", + "fileUpload", + "focus", + "form", + "forms", + "frame", + "innerHeight", + "innerWidth", + "layer", + "layers", + "link", + "location", + "mimeTypes", + "navigate", + "navigator", + "frames", + "frameRate", + "hidden", + "history", + "image", + "images", + "offscreenBuffering", + "open", + "opener", + "option", + "outerHeight", + "outerWidth", + "packages", + "pageXOffset", + "pageYOffset", + "parent", + "parseFloat", + "parseInt", + "password", + "pkcs11", + "plugin", + "prompt", + "propertyIsEnum", + "radio", + "reset", + "screenX", + "screenY", + "scroll", + "secure", + "select", + "self", + "setInterval", + "setTimeout", + "status", + "submit", + "taint", + "text", + "textarea", + "top", + "unescape", + "untaint", + "window", + + "onblur", + "onclick", + "onerror", + "onfocus", + "onkeydown", + "onkeypress", + "onkeyup", + "onmouseover", + "onload", + "onmouseup", + "onmousedown", + "onsubmit" + ]); + + // Set of already checked scopes + const alreadyCheckedScopes = new Set(); + + // get all global names + for (const info of modulesWithInfo) { + const superClassExpressions = []; + + // ignore symbols from moduleScope + if (info.moduleScope) { + alreadyCheckedScopes.add(info.moduleScope); + + // The super class expression in class scopes behaves weird + // We store ranges of all super class expressions to make + // renaming to work correctly + for (const childScope of info.moduleScope.childScopes) { + if (childScope.type !== "class") continue; + if (!childScope.block.superClass) continue; + superClassExpressions.push({ + range: childScope.block.superClass.range, + variables: childScope.variables + }); + } + } + + // add global symbols + if (info.globalScope) { + for (const reference of info.globalScope.through) { + const name = reference.identifier.name; + if ( + /^__WEBPACK_MODULE_REFERENCE__\d+_([\da-f]+|ns)(_call)?(_strict)?__$/.test( + name + ) + ) { + for (const expr of superClassExpressions) { + if ( + expr.range[0] <= reference.identifier.range[0] && + expr.range[1] >= reference.identifier.range[1] + ) { + for (const variable of expr.variables) { + allUsedNames.add(variable.name); + } + } + } + addScopeSymbols1( + reference.from, + allUsedNames, + alreadyCheckedScopes + ); + } else { + allUsedNames.add(name); + } + } + } + + // add exported globals + if (info.type === "concatenated") { + const variables = new Set(); + for (const variable of info.moduleScope.variables) { + variables.add(variable.name); + } + for (const [, variable] of info.exportMap) { + if (!variables.has(variable)) { + info.globalExports.add(variable); + } + } + } + } + + // generate names for symbols + for (const info of modulesWithInfo) { + switch (info.type) { + case "concatenated": { + const namespaceObjectName = this.findNewName( + "namespaceObject", + allUsedNames, + null, + info.module.readableIdentifier(requestShortener) + ); + allUsedNames.add(namespaceObjectName); + info.internalNames.set(namespaceObjectName, namespaceObjectName); + info.exportMap.set(true, namespaceObjectName); + for (const variable of info.moduleScope.variables) { + const name = variable.name; + if (allUsedNames.has(name)) { + const references = getAllReferences(variable); + const symbolsInReferences = new Set(); + const alreadyCheckedInnerScopes = new Set(); + for (const ref of references) { + addScopeSymbols2( + ref.from, + symbolsInReferences, + alreadyCheckedInnerScopes, + alreadyCheckedScopes + ); + } + const newName = this.findNewName( + name, + allUsedNames, + symbolsInReferences, + info.module.readableIdentifier(requestShortener) + ); + allUsedNames.add(newName); + info.internalNames.set(name, newName); + const source = info.source; + const allIdentifiers = new Set( + references.map(r => r.identifier).concat(variable.identifiers) + ); + for (const identifier of allIdentifiers) { + const r = identifier.range; + const path = getPathInAst(info.ast, identifier); + if ( + path && + path.length > 1 && + path[1].type === "Property" && + path[1].shorthand + ) { + source.insert(r[1], `: ${newName}`); + } else { + source.replace(r[0], r[1] - 1, newName); + } + } + } else { + allUsedNames.add(name); + info.internalNames.set(name, name); + } + } + break; + } + case "external": { + const externalName = this.findNewName( + "", + allUsedNames, + null, + info.module.readableIdentifier(requestShortener) + ); + allUsedNames.add(externalName); + info.name = externalName; + if ( + info.module.buildMeta.exportsType === "named" || + !info.module.buildMeta.exportsType + ) { + const externalNameInterop = this.findNewName( + "namespaceObject", + allUsedNames, + null, + info.module.readableIdentifier(requestShortener) + ); + allUsedNames.add(externalNameInterop); + info.interopNamespaceObjectName = externalNameInterop; + } + if (!info.module.buildMeta.exportsType) { + const externalNameInterop = this.findNewName( + "default", + allUsedNames, + null, + info.module.readableIdentifier(requestShortener) + ); + allUsedNames.add(externalNameInterop); + info.interopDefaultAccessName = externalNameInterop; + } + break; + } + } + } + + // Find and replace referenced to modules + for (const info of modulesWithInfo) { + if (info.type === "concatenated") { + for (const reference of info.globalScope.through) { + const name = reference.identifier.name; + const match = /^__WEBPACK_MODULE_REFERENCE__(\d+)_([\da-f]+|ns)(_call)?(_strict)?__$/.exec( + name + ); + if (match) { + const referencedModule = modulesWithInfo[+match[1]]; + let exportName; + if (match[2] === "ns") { + exportName = true; + } else { + const exportData = match[2]; + exportName = Buffer.from(exportData, "hex").toString("utf-8"); + } + const asCall = !!match[3]; + const strictHarmonyModule = !!match[4]; + const finalName = getFinalName( + referencedModule, + exportName, + moduleToInfoMap, + requestShortener, + asCall, + strictHarmonyModule + ); + const r = reference.identifier.range; + const source = info.source; + source.replace(r[0], r[1] - 1, finalName); + } + } + } + } + + const result = new ConcatSource(); + + // add harmony compatibility flag (must be first because of possible circular dependencies) + const usedExports = this.rootModule.usedExports; + if (usedExports === true) { + result.add( + runtimeTemplate.defineEsModuleFlagStatement({ + exportsArgument: this.exportsArgument + }) + ); + } + + // define required namespace objects (must be before evaluation modules) + for (const info of modulesWithInfo) { + if (info.namespaceObjectSource) { + result.add(info.namespaceObjectSource); + } + } + + // evaluate modules in order + for (const info of modulesWithInfo) { + switch (info.type) { + case "concatenated": + result.add( + `\n// CONCATENATED MODULE: ${info.module.readableIdentifier( + requestShortener + )}\n` + ); + result.add(info.source); + break; + case "external": + result.add( + `\n// EXTERNAL MODULE: ${info.module.readableIdentifier( + requestShortener + )}\n` + ); + result.add( + `var ${info.name} = __webpack_require__(${JSON.stringify( + info.module.id + )});\n` + ); + if (info.interopNamespaceObjectUsed) { + if (info.module.buildMeta.exportsType === "named") { + result.add( + `var ${ + info.interopNamespaceObjectName + } = /*#__PURE__*/__webpack_require__.t(${info.name}, 2);\n` + ); + } else if (!info.module.buildMeta.exportsType) { + result.add( + `var ${ + info.interopNamespaceObjectName + } = /*#__PURE__*/__webpack_require__.t(${info.name});\n` + ); + } + } + if (info.interopDefaultAccessUsed) { + result.add( + `var ${ + info.interopDefaultAccessName + } = /*#__PURE__*/__webpack_require__.n(${info.name});\n` + ); + } + break; + default: + throw new Error(`Unsupported concatenation entry type ${info.type}`); + } + } + + return result; + } + + findNewName(oldName, usedNamed1, usedNamed2, extraInfo) { + let name = oldName; + + if (name === "__WEBPACK_MODULE_DEFAULT_EXPORT__") name = ""; + + // Remove uncool stuff + extraInfo = extraInfo.replace( + /\.+\/|(\/index)?\.([a-zA-Z0-9]{1,4})($|\s|\?)|\s*\+\s*\d+\s*modules/g, + "" + ); + + const splittedInfo = extraInfo.split("/"); + while (splittedInfo.length) { + name = splittedInfo.pop() + (name ? "_" + name : ""); + const nameIdent = Template.toIdentifier(name); + if ( + !usedNamed1.has(nameIdent) && + (!usedNamed2 || !usedNamed2.has(nameIdent)) + ) + return nameIdent; + } + + let i = 0; + let nameWithNumber = Template.toIdentifier(`${name}_${i}`); + while ( + usedNamed1.has(nameWithNumber) || + (usedNamed2 && usedNamed2.has(nameWithNumber)) + ) { + i++; + nameWithNumber = Template.toIdentifier(`${name}_${i}`); + } + return nameWithNumber; + } + + /** + * @param {Hash} hash the hash used to track dependencies + * @returns {void} + */ + updateHash(hash) { + for (const info of this._orderedConcatenationList) { + switch (info.type) { + case "concatenated": + info.module.updateHash(hash); + break; + case "external": + hash.update(`${info.module.id}`); + break; + } + } + super.updateHash(hash); + } +} + +class HarmonyImportSpecifierDependencyConcatenatedTemplate { + constructor(originalTemplate, modulesMap) { + this.originalTemplate = originalTemplate; + this.modulesMap = modulesMap; + } + + getHarmonyInitOrder(dep) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + return this.originalTemplate.getHarmonyInitOrder(dep); + } + return NaN; + } + + harmonyInit(dep, source, runtimeTemplate, dependencyTemplates) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + this.originalTemplate.harmonyInit( + dep, + source, + runtimeTemplate, + dependencyTemplates + ); + return; + } + } + + apply(dep, source, runtime, dependencyTemplates) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + this.originalTemplate.apply(dep, source, runtime, dependencyTemplates); + return; + } + let content; + const callFlag = dep.call ? "_call" : ""; + const strictFlag = dep.originModule.buildMeta.strictHarmonyModule + ? "_strict" + : ""; + if (dep._id === null) { + content = `__WEBPACK_MODULE_REFERENCE__${info.index}_ns${strictFlag}__`; + } else if (dep.namespaceObjectAsContext) { + content = `__WEBPACK_MODULE_REFERENCE__${ + info.index + }_ns${strictFlag}__[${JSON.stringify(dep._id)}]`; + } else { + const exportData = Buffer.from(dep._id, "utf-8").toString("hex"); + content = `__WEBPACK_MODULE_REFERENCE__${ + info.index + }_${exportData}${callFlag}${strictFlag}__`; + } + if (dep.shorthand) { + content = dep.name + ": " + content; + } + source.replace(dep.range[0], dep.range[1] - 1, content); + } +} + +class HarmonyImportSideEffectDependencyConcatenatedTemplate { + constructor(originalTemplate, modulesMap) { + this.originalTemplate = originalTemplate; + this.modulesMap = modulesMap; + } + + getHarmonyInitOrder(dep) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + return this.originalTemplate.getHarmonyInitOrder(dep); + } + return NaN; + } + + harmonyInit(dep, source, runtime, dependencyTemplates) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + this.originalTemplate.harmonyInit( + dep, + source, + runtime, + dependencyTemplates + ); + return; + } + } + + apply(dep, source, runtime, dependencyTemplates) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + this.originalTemplate.apply(dep, source, runtime, dependencyTemplates); + return; + } + } +} + +class HarmonyExportSpecifierDependencyConcatenatedTemplate { + constructor(originalTemplate, rootModule) { + this.originalTemplate = originalTemplate; + this.rootModule = rootModule; + } + + getHarmonyInitOrder(dep) { + if (dep.originModule === this.rootModule) { + return this.originalTemplate.getHarmonyInitOrder(dep); + } + return NaN; + } + + harmonyInit(dep, source, runtime, dependencyTemplates) { + if (dep.originModule === this.rootModule) { + this.originalTemplate.harmonyInit( + dep, + source, + runtime, + dependencyTemplates + ); + return; + } + } + + apply(dep, source, runtime, dependencyTemplates) { + if (dep.originModule === this.rootModule) { + this.originalTemplate.apply(dep, source, runtime, dependencyTemplates); + } + } +} + +class HarmonyExportExpressionDependencyConcatenatedTemplate { + constructor(originalTemplate, rootModule) { + this.originalTemplate = originalTemplate; + this.rootModule = rootModule; + } + + apply(dep, source, runtime, dependencyTemplates) { + let content = + "/* harmony default export */ var __WEBPACK_MODULE_DEFAULT_EXPORT__ = "; + if (dep.originModule === this.rootModule) { + const used = dep.originModule.isUsed("default"); + const exportsName = dep.originModule.exportsArgument; + if (used) content += `${exportsName}[${JSON.stringify(used)}] = `; + } + + if (dep.range) { + source.replace(dep.rangeStatement[0], dep.range[0] - 1, content + "("); + source.replace(dep.range[1], dep.rangeStatement[1] - 1, ");"); + return; + } + + source.replace(dep.rangeStatement[0], dep.rangeStatement[1] - 1, content); + } +} + +class HarmonyExportImportedSpecifierDependencyConcatenatedTemplate { + constructor(originalTemplate, rootModule, modulesMap) { + this.originalTemplate = originalTemplate; + this.rootModule = rootModule; + this.modulesMap = modulesMap; + } + + getExports(dep) { + const importModule = dep._module; + if (dep.id) { + // export { named } from "module" + return [ + { + name: dep.name, + id: dep.id, + module: importModule + } + ]; + } + if (dep.name) { + // export * as abc from "module" + return [ + { + name: dep.name, + id: true, + module: importModule + } + ]; + } + // export * from "module" + return importModule.buildMeta.providedExports + .filter(exp => exp !== "default" && !dep.activeExports.has(exp)) + .map(exp => { + return { + name: exp, + id: exp, + module: importModule + }; + }); + } + + getHarmonyInitOrder(dep) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + return this.originalTemplate.getHarmonyInitOrder(dep); + } + return NaN; + } + + harmonyInit(dep, source, runtime, dependencyTemplates) { + const module = dep._module; + const info = this.modulesMap.get(module); + if (!info) { + this.originalTemplate.harmonyInit( + dep, + source, + runtime, + dependencyTemplates + ); + return; + } + } + + apply(dep, source, runtime, dependencyTemplates) { + if (dep.originModule === this.rootModule) { + if (this.modulesMap.get(dep._module)) { + const exportDefs = this.getExports(dep); + for (const def of exportDefs) { + const info = this.modulesMap.get(def.module); + const used = dep.originModule.isUsed(def.name); + if (!used) { + source.insert( + -1, + `/* unused concated harmony import ${def.name} */\n` + ); + continue; + } + let finalName; + const strictFlag = dep.originModule.buildMeta.strictHarmonyModule + ? "_strict" + : ""; + if (def.id === true) { + finalName = `__WEBPACK_MODULE_REFERENCE__${ + info.index + }_ns${strictFlag}__`; + } else { + const exportData = Buffer.from(def.id, "utf-8").toString("hex"); + finalName = `__WEBPACK_MODULE_REFERENCE__${ + info.index + }_${exportData}${strictFlag}__`; + } + const exportsName = this.rootModule.exportsArgument; + const content = + `/* concated harmony reexport ${def.name} */` + + `__webpack_require__.d(${exportsName}, ` + + `${JSON.stringify(used)}, ` + + `function() { return ${finalName}; });\n`; + source.insert(-1, content); + } + } else { + this.originalTemplate.apply(dep, source, runtime, dependencyTemplates); + } + } + } +} + +class HarmonyCompatibilityDependencyConcatenatedTemplate { + constructor(originalTemplate, rootModule, modulesMap) { + this.originalTemplate = originalTemplate; + this.rootModule = rootModule; + this.modulesMap = modulesMap; + } + + apply(dep, source, runtime, dependencyTemplates) { + // do nothing + } +} + +module.exports = ConcatenatedModule; diff --git a/node_modules/webpack/lib/optimize/DedupePlugin.js b/node_modules/webpack/lib/optimize/DedupePlugin.js deleted file mode 100644 index cffa43aaf..000000000 --- a/node_modules/webpack/lib/optimize/DedupePlugin.js +++ /dev/null @@ -1,15 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class DedupePlugin { - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - compilation.warnings.push(new Error("DedupePlugin: This plugin was removed from webpack. Remove it from your configuration.")); - }); - } -} - -module.exports = DedupePlugin; diff --git a/node_modules/webpack/lib/optimize/EnsureChunkConditionsPlugin.js b/node_modules/webpack/lib/optimize/EnsureChunkConditionsPlugin.js index 2b291fdad..5d05ec834 100644 --- a/node_modules/webpack/lib/optimize/EnsureChunkConditionsPlugin.js +++ b/node_modules/webpack/lib/optimize/EnsureChunkConditionsPlugin.js @@ -1,40 +1,70 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class EnsureChunkConditionsPlugin { - - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - const triesMap = new Map(); - compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => { - let changed = false; - chunks.forEach((chunk) => { - chunk.forEachModule((module) => { - if(!module.chunkCondition) return; - if(!module.chunkCondition(chunk)) { - let usedChunks = triesMap.get(module); - if(!usedChunks) triesMap.set(module, usedChunks = new Set()); - usedChunks.add(chunk); - const newChunks = []; - chunk.parents.forEach((parent) => { - if(!usedChunks.has(parent)) { - parent.addModule(module); - module.addChunk(parent); - newChunks.push(parent); - } - }); - module.rewriteChunkInReasons(chunk, newChunks); - chunk.removeModule(module); - changed = true; - } - }); - }); - if(changed) return true; - }); - }); - } -} -module.exports = EnsureChunkConditionsPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const GraphHelpers = require("../GraphHelpers"); + +class EnsureChunkConditionsPlugin { + apply(compiler) { + compiler.hooks.compilation.tap( + "EnsureChunkConditionsPlugin", + compilation => { + const handler = chunks => { + let changed = false; + for (const module of compilation.modules) { + if (!module.chunkCondition) continue; + const sourceChunks = new Set(); + const chunkGroups = new Set(); + for (const chunk of module.chunksIterable) { + if (!module.chunkCondition(chunk)) { + sourceChunks.add(chunk); + for (const group of chunk.groupsIterable) { + chunkGroups.add(group); + } + } + } + if (sourceChunks.size === 0) continue; + const targetChunks = new Set(); + chunkGroupLoop: for (const chunkGroup of chunkGroups) { + // Can module be placed in a chunk of this group? + for (const chunk of chunkGroup.chunks) { + if (module.chunkCondition(chunk)) { + targetChunks.add(chunk); + continue chunkGroupLoop; + } + } + // We reached the entrypoint: fail + if (chunkGroup.isInitial()) { + throw new Error( + "Cannot fullfil chunk condition of " + module.identifier() + ); + } + // Try placing in all parents + for (const group of chunkGroup.parentsIterable) { + chunkGroups.add(group); + } + } + for (const sourceChunk of sourceChunks) { + GraphHelpers.disconnectChunkAndModule(sourceChunk, module); + } + for (const targetChunk of targetChunks) { + GraphHelpers.connectChunkAndModule(targetChunk, module); + } + } + if (changed) return true; + }; + compilation.hooks.optimizeChunksBasic.tap( + "EnsureChunkConditionsPlugin", + handler + ); + compilation.hooks.optimizeExtractedChunksBasic.tap( + "EnsureChunkConditionsPlugin", + handler + ); + } + ); + } +} +module.exports = EnsureChunkConditionsPlugin; diff --git a/node_modules/webpack/lib/optimize/FlagIncludedChunksPlugin.js b/node_modules/webpack/lib/optimize/FlagIncludedChunksPlugin.js index 75277b5aa..1890f0581 100644 --- a/node_modules/webpack/lib/optimize/FlagIncludedChunksPlugin.js +++ b/node_modules/webpack/lib/optimize/FlagIncludedChunksPlugin.js @@ -1,35 +1,99 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class FlagIncludedChunksPlugin { - - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - compilation.plugin("optimize-chunk-ids", (chunks) => { - chunks.forEach((chunkA) => { - chunks.forEach((chunkB) => { - // as we iterate the same iterables twice - // skip if we find ourselves - if(chunkA === chunkB) return; - - // instead of swapping A and B just bail - // as we loop twice the current A will be B and B then A - if(chunkA.getNumberOfModules() < chunkB.getNumberOfModules()) return; - - if(chunkB.getNumberOfModules() === 0) return; - - // is chunkB in chunkA? - for(const m of chunkB.modulesIterable) { - if(!chunkA.containsModule(m)) return; - } - chunkA.ids.push(chunkB.id); - }); - }); - }); - }); - } -} -module.exports = FlagIncludedChunksPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +class FlagIncludedChunksPlugin { + apply(compiler) { + compiler.hooks.compilation.tap("FlagIncludedChunksPlugin", compilation => { + compilation.hooks.optimizeChunkIds.tap( + "FlagIncludedChunksPlugin", + chunks => { + // prepare two bit integers for each module + // 2^31 is the max number represented as SMI in v8 + // we want the bits distributed this way: + // the bit 2^31 is pretty rar and only one module should get it + // so it has a probability of 1 / modulesCount + // the first bit (2^0) is the easiest and every module could get it + // if it doesn't get a better bit + // from bit 2^n to 2^(n+1) there is a probability of p + // so 1 / modulesCount == p^31 + // <=> p = sqrt31(1 / modulesCount) + // so we use a modulo of 1 / sqrt31(1 / modulesCount) + const moduleBits = new WeakMap(); + const modulesCount = compilation.modules.length; + + // precalculate the modulo values for each bit + const modulo = 1 / Math.pow(1 / modulesCount, 1 / 31); + const modulos = Array.from( + { length: 31 }, + (x, i) => Math.pow(modulo, i) | 0 + ); + + // iterate all modules to generate bit values + let i = 0; + for (const module of compilation.modules) { + let bit = 30; + while (i % modulos[bit] !== 0) { + bit--; + } + moduleBits.set(module, 1 << bit); + i++; + } + + // interate all chunks to generate bitmaps + const chunkModulesHash = new WeakMap(); + for (const chunk of chunks) { + let hash = 0; + for (const module of chunk.modulesIterable) { + hash |= moduleBits.get(module); + } + chunkModulesHash.set(chunk, hash); + } + + for (const chunkA of chunks) { + const chunkAHash = chunkModulesHash.get(chunkA); + const chunkAModulesCount = chunkA.getNumberOfModules(); + if (chunkAModulesCount === 0) continue; + let bestModule = undefined; + for (const module of chunkA.modulesIterable) { + if ( + bestModule === undefined || + bestModule.getNumberOfChunks() > module.getNumberOfChunks() + ) + bestModule = module; + } + loopB: for (const chunkB of bestModule.chunksIterable) { + // as we iterate the same iterables twice + // skip if we find ourselves + if (chunkA === chunkB) continue; + + const chunkBModulesCount = chunkB.getNumberOfModules(); + + // ids for empty chunks are not included + if (chunkBModulesCount === 0) continue; + + // instead of swapping A and B just bail + // as we loop twice the current A will be B and B then A + if (chunkAModulesCount > chunkBModulesCount) continue; + + // is chunkA in chunkB? + + // we do a cheap check for the hash value + const chunkBHash = chunkModulesHash.get(chunkB); + if ((chunkBHash & chunkAHash) !== chunkAHash) continue; + + // compare all modules + for (const m of chunkA.modulesIterable) { + if (!chunkB.containsModule(m)) continue loopB; + } + chunkB.ids.push(chunkA.id); + } + } + } + ); + }); + } +} +module.exports = FlagIncludedChunksPlugin; diff --git a/node_modules/webpack/lib/optimize/LimitChunkCountPlugin.js b/node_modules/webpack/lib/optimize/LimitChunkCountPlugin.js index c156338ff..f38b942b1 100644 --- a/node_modules/webpack/lib/optimize/LimitChunkCountPlugin.js +++ b/node_modules/webpack/lib/optimize/LimitChunkCountPlugin.js @@ -1,59 +1,66 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class LimitChunkCountPlugin { - constructor(options) { - if(options !== undefined && typeof options !== "object" || Array.isArray(options)) { - throw new Error("Argument should be an options object.\nFor more info on options, see https://webpack.js.org/plugins/"); - } - this.options = options || {}; - } - apply(compiler) { - const options = this.options; - compiler.plugin("compilation", (compilation) => { - compilation.plugin("optimize-chunks-advanced", (chunks) => { - const maxChunks = options.maxChunks; - if(!maxChunks) return; - if(maxChunks < 1) return; - if(chunks.length <= maxChunks) return; - - if(chunks.length > maxChunks) { - const sortedExtendedPairCombinations = chunks.reduce((combinations, a, idx) => { - // create combination pairs - for(let i = 0; i < idx; i++) { - const b = chunks[i]; - combinations.push([b, a]); - } - return combinations; - }, []).map((pair) => { - // extend combination pairs with size and integrated size - const a = pair[0].size(options); - const b = pair[1].size(options); - const ab = pair[0].integratedSize(pair[1], options); - return [a + b - ab, ab, pair[0], pair[1], a, b]; - }).filter((extendedPair) => { - // filter pairs that do not have an integratedSize - // meaning they can NOT be integrated! - return extendedPair[1] !== false; - }).sort((a, b) => { // sadly javascript does an inplace sort here - // sort them by size - const diff = b[0] - a[0]; - if(diff !== 0) return diff; - return a[1] - b[1]; - }); - - const pair = sortedExtendedPairCombinations[0]; - - if(pair && pair[2].integrate(pair[3], "limit")) { - chunks.splice(chunks.indexOf(pair[3]), 1); - return true; - } - } - }); - }); - } -} -module.exports = LimitChunkCountPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const validateOptions = require("schema-utils"); +const schema = require("../../schemas/plugins/optimize/LimitChunkCountPlugin.json"); + +class LimitChunkCountPlugin { + constructor(options) { + validateOptions(schema, options || {}, "Limit Chunk Count Plugin"); + this.options = options || {}; + } + apply(compiler) { + const options = this.options; + compiler.hooks.compilation.tap("LimitChunkCountPlugin", compilation => { + compilation.hooks.optimizeChunksAdvanced.tap( + "LimitChunkCountPlugin", + chunks => { + const maxChunks = options.maxChunks; + if (!maxChunks) return; + if (maxChunks < 1) return; + if (chunks.length <= maxChunks) return; + + const sortedExtendedPairCombinations = chunks + .reduce((combinations, a, idx) => { + // create combination pairs + for (let i = 0; i < idx; i++) { + const b = chunks[i]; + combinations.push([b, a]); + } + return combinations; + }, []) + .map(pair => { + // extend combination pairs with size and integrated size + const a = pair[0].size(options); + const b = pair[1].size(options); + const ab = pair[0].integratedSize(pair[1], options); + return [a + b - ab, ab, pair[0], pair[1], a, b]; + }) + .filter(extendedPair => { + // filter pairs that do not have an integratedSize + // meaning they can NOT be integrated! + return extendedPair[1] !== false; + }) + .sort((a, b) => { + // sadly javascript does an inplace sort here + // sort them by size + const diff = b[0] - a[0]; + if (diff !== 0) return diff; + return a[1] - b[1]; + }); + + const pair = sortedExtendedPairCombinations[0]; + + if (pair && pair[2].integrate(pair[3], "limit")) { + chunks.splice(chunks.indexOf(pair[3]), 1); + return true; + } + } + ); + }); + } +} +module.exports = LimitChunkCountPlugin; diff --git a/node_modules/webpack/lib/optimize/MergeDuplicateChunksPlugin.js b/node_modules/webpack/lib/optimize/MergeDuplicateChunksPlugin.js index 7b006fd17..1c3e23aa3 100644 --- a/node_modules/webpack/lib/optimize/MergeDuplicateChunksPlugin.js +++ b/node_modules/webpack/lib/optimize/MergeDuplicateChunksPlugin.js @@ -1,28 +1,78 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class MergeDuplicateChunksPlugin { - - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - compilation.plugin("optimize-chunks-basic", (chunks) => { - const map = Object.create(null); - chunks.slice().forEach((chunk) => { - if(chunk.hasRuntime() || chunk.hasEntryModule()) return; - const ident = chunk.getModulesIdent(); - const otherChunk = map[ident]; - if(otherChunk) { - if(otherChunk.integrate(chunk, "duplicate")) - chunks.splice(chunks.indexOf(chunk), 1); - return; - } - map[ident] = chunk; - }); - }); - }); - } -} -module.exports = MergeDuplicateChunksPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +class MergeDuplicateChunksPlugin { + apply(compiler) { + compiler.hooks.compilation.tap( + "MergeDuplicateChunksPlugin", + compilation => { + compilation.hooks.optimizeChunksBasic.tap( + "MergeDuplicateChunksPlugin", + chunks => { + // remember already tested chunks for performance + const notDuplicates = new Set(); + + // for each chunk + for (const chunk of chunks) { + // track a Set of all chunk that could be duplicates + let possibleDuplicates; + for (const module of chunk.modulesIterable) { + if (possibleDuplicates === undefined) { + // when possibleDuplicates is not yet set, + // create a new Set from chunks of the current module + // including only chunks with the same number of modules + for (const dup of module.chunksIterable) { + if ( + dup !== chunk && + chunk.getNumberOfModules() === dup.getNumberOfModules() && + !notDuplicates.has(dup) + ) { + // delay allocating the new Set until here, reduce memory pressure + if (possibleDuplicates === undefined) { + possibleDuplicates = new Set(); + } + possibleDuplicates.add(dup); + } + } + // when no chunk is possible we can break here + if (possibleDuplicates === undefined) break; + } else { + // validate existing possible duplicates + for (const dup of possibleDuplicates) { + // remove possible duplicate when module is not contained + if (!dup.containsModule(module)) { + possibleDuplicates.delete(dup); + } + } + // when all chunks has been removed we can break here + if (possibleDuplicates.size === 0) break; + } + } + + // when we found duplicates + if ( + possibleDuplicates !== undefined && + possibleDuplicates.size > 0 + ) { + for (const otherChunk of possibleDuplicates) { + if (otherChunk.hasRuntime() !== chunk.hasRuntime()) continue; + // merge them + if (chunk.integrate(otherChunk, "duplicate")) { + chunks.splice(chunks.indexOf(otherChunk), 1); + } + } + } + + // don't check already processed chunks twice + notDuplicates.add(chunk); + } + } + ); + } + ); + } +} +module.exports = MergeDuplicateChunksPlugin; diff --git a/node_modules/webpack/lib/optimize/MinChunkSizePlugin.js b/node_modules/webpack/lib/optimize/MinChunkSizePlugin.js index 87a4c21af..0f3893910 100644 --- a/node_modules/webpack/lib/optimize/MinChunkSizePlugin.js +++ b/node_modules/webpack/lib/optimize/MinChunkSizePlugin.js @@ -1,65 +1,77 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class MinChunkSizePlugin { - constructor(options) { - if(typeof options !== "object" || Array.isArray(options)) { - throw new Error("Argument should be an options object.\nFor more info on options, see https://webpack.js.org/plugins/"); - } - this.options = options; - } - - apply(compiler) { - const options = this.options; - const minChunkSize = options.minChunkSize; - compiler.plugin("compilation", (compilation) => { - compilation.plugin("optimize-chunks-advanced", (chunks) => { - const equalOptions = { - chunkOverhead: 1, - entryChunkMultiplicator: 1 - }; - - const sortedSizeFilteredExtendedPairCombinations = chunks.reduce((combinations, a, idx) => { - // create combination pairs - for(let i = 0; i < idx; i++) { - const b = chunks[i]; - combinations.push([b, a]); - } - return combinations; - }, []).filter((pair) => { - // check if one of the chunks sizes is smaller than the minChunkSize - const p0SmallerThanMinChunkSize = pair[0].size(equalOptions) < minChunkSize; - const p1SmallerThanMinChunkSize = pair[1].size(equalOptions) < minChunkSize; - return p0SmallerThanMinChunkSize || p1SmallerThanMinChunkSize; - }).map((pair) => { - // extend combination pairs with size and integrated size - const a = pair[0].size(options); - const b = pair[1].size(options); - const ab = pair[0].integratedSize(pair[1], options); - return [a + b - ab, ab, pair[0], pair[1]]; - }).filter((pair) => { - // filter pairs that do not have an integratedSize - // meaning they can NOT be integrated! - return pair[1] !== false; - }).sort((a, b) => { // sadly javascript does an inplace sort here - // sort by size - const diff = b[0] - a[0]; - if(diff !== 0) return diff; - return a[1] - b[1]; - }); - - if(sortedSizeFilteredExtendedPairCombinations.length === 0) return; - - const pair = sortedSizeFilteredExtendedPairCombinations[0]; - - pair[2].integrate(pair[3], "min-size"); - chunks.splice(chunks.indexOf(pair[3]), 1); - return true; - }); - }); - } -} -module.exports = MinChunkSizePlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const validateOptions = require("schema-utils"); +const schema = require("../../schemas/plugins/optimize/MinChunkSizePlugin.json"); + +class MinChunkSizePlugin { + constructor(options) { + validateOptions(schema, options, "Min Chunk Size Plugin"); + this.options = options; + } + + apply(compiler) { + const options = this.options; + const minChunkSize = options.minChunkSize; + compiler.hooks.compilation.tap("MinChunkSizePlugin", compilation => { + compilation.hooks.optimizeChunksAdvanced.tap( + "MinChunkSizePlugin", + chunks => { + const equalOptions = { + chunkOverhead: 1, + entryChunkMultiplicator: 1 + }; + + const sortedSizeFilteredExtendedPairCombinations = chunks + .reduce((combinations, a, idx) => { + // create combination pairs + for (let i = 0; i < idx; i++) { + const b = chunks[i]; + combinations.push([b, a]); + } + return combinations; + }, []) + .filter(pair => { + // check if one of the chunks sizes is smaller than the minChunkSize + const p0SmallerThanMinChunkSize = + pair[0].size(equalOptions) < minChunkSize; + const p1SmallerThanMinChunkSize = + pair[1].size(equalOptions) < minChunkSize; + return p0SmallerThanMinChunkSize || p1SmallerThanMinChunkSize; + }) + .map(pair => { + // extend combination pairs with size and integrated size + const a = pair[0].size(options); + const b = pair[1].size(options); + const ab = pair[0].integratedSize(pair[1], options); + return [a + b - ab, ab, pair[0], pair[1]]; + }) + .filter(pair => { + // filter pairs that do not have an integratedSize + // meaning they can NOT be integrated! + return pair[1] !== false; + }) + .sort((a, b) => { + // sadly javascript does an inplace sort here + // sort by size + const diff = b[0] - a[0]; + if (diff !== 0) return diff; + return a[1] - b[1]; + }); + + if (sortedSizeFilteredExtendedPairCombinations.length === 0) return; + + const pair = sortedSizeFilteredExtendedPairCombinations[0]; + + pair[2].integrate(pair[3], "min-size"); + chunks.splice(chunks.indexOf(pair[3]), 1); + return true; + } + ); + }); + } +} +module.exports = MinChunkSizePlugin; diff --git a/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js b/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js index 8a6a660cb..15b7d796a 100644 --- a/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js +++ b/node_modules/webpack/lib/optimize/ModuleConcatenationPlugin.js @@ -1,308 +1,483 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); -const ModuleHotAcceptDependency = require("../dependencies/ModuleHotAcceptDependency"); -const ModuleHotDeclineDependency = require("../dependencies/ModuleHotDeclineDependency"); -const ConcatenatedModule = require("./ConcatenatedModule"); -const HarmonyExportImportedSpecifierDependency = require("../dependencies/HarmonyExportImportedSpecifierDependency"); -const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); - -function formatBailoutReason(msg) { - return "ModuleConcatenation bailout: " + msg; -} - -class ModuleConcatenationPlugin { - constructor(options) { - if(typeof options !== "object") options = {}; - this.options = options; - } - - apply(compiler) { - compiler.plugin("compilation", (compilation, params) => { - params.normalModuleFactory.plugin("parser", (parser, parserOptions) => { - parser.plugin("call eval", () => { - parser.state.module.meta.hasEval = true; - }); - }); - const bailoutReasonMap = new Map(); - - function setBailoutReason(module, reason) { - bailoutReasonMap.set(module, reason); - module.optimizationBailout.push(typeof reason === "function" ? (rs) => formatBailoutReason(reason(rs)) : formatBailoutReason(reason)); - } - - function getBailoutReason(module, requestShortener) { - const reason = bailoutReasonMap.get(module); - if(typeof reason === "function") return reason(requestShortener); - return reason; - } - - compilation.plugin("optimize-chunk-modules", (chunks, modules) => { - const relevantModules = []; - const possibleInners = new Set(); - for(const module of modules) { - // Only harmony modules are valid for optimization - if(!module.meta || !module.meta.harmonyModule || !module.dependencies.some(d => d instanceof HarmonyCompatibilityDependency)) { - setBailoutReason(module, "Module is not an ECMAScript module"); - continue; - } - - // Because of variable renaming we can't use modules with eval - if(module.meta && module.meta.hasEval) { - setBailoutReason(module, "Module uses eval()"); - continue; - } - - // Exports must be known (and not dynamic) - if(!Array.isArray(module.providedExports)) { - setBailoutReason(module, "Module exports are unknown"); - continue; - } - - // Using dependency variables is not possible as this wraps the code in a function - if(module.variables.length > 0) { - setBailoutReason(module, `Module uses injected variables (${module.variables.map(v => v.name).join(", ")})`); - continue; - } - - // Hot Module Replacement need it's own module to work correctly - if(module.dependencies.some(dep => dep instanceof ModuleHotAcceptDependency || dep instanceof ModuleHotDeclineDependency)) { - setBailoutReason(module, "Module uses Hot Module Replacement"); - continue; - } - - relevantModules.push(module); - - // Module must not be the entry points - if(module.getChunks().some(chunk => chunk.entryModule === module)) { - setBailoutReason(module, "Module is an entry point"); - continue; - } - - // Module must only be used by Harmony Imports - const nonHarmonyReasons = module.reasons.filter(reason => !(reason.dependency instanceof HarmonyImportDependency)); - if(nonHarmonyReasons.length > 0) { - const importingModules = new Set(nonHarmonyReasons.map(r => r.module)); - const importingModuleTypes = new Map(Array.from(importingModules).map(m => [m, new Set(nonHarmonyReasons.filter(r => r.module === m).map(r => r.dependency.type).sort())])); - setBailoutReason(module, (requestShortener) => { - const names = Array.from(importingModules).map(m => `${m.readableIdentifier(requestShortener)} (referenced with ${Array.from(importingModuleTypes.get(m)).join(", ")})`).sort(); - return `Module is referenced from these modules with unsupported syntax: ${names.join(", ")}`; - }); - continue; - } - - possibleInners.add(module); - } - // sort by depth - // modules with lower depth are more likely suited as roots - // this improves performance, because modules already selected as inner are skipped - relevantModules.sort((a, b) => { - return a.depth - b.depth; - }); - const concatConfigurations = []; - const usedAsInner = new Set(); - for(const currentRoot of relevantModules) { - // when used by another configuration as inner: - // the other configuration is better and we can skip this one - if(usedAsInner.has(currentRoot)) - continue; - - // create a configuration with the root - const currentConfiguration = new ConcatConfiguration(currentRoot); - - // cache failures to add modules - const failureCache = new Map(); - - // try to add all imports - for(const imp of this.getImports(currentRoot)) { - const problem = this.tryToAdd(currentConfiguration, imp, possibleInners, failureCache); - if(problem) { - failureCache.set(imp, problem); - currentConfiguration.addWarning(imp, problem); - } - } - if(!currentConfiguration.isEmpty()) { - concatConfigurations.push(currentConfiguration); - for(const module of currentConfiguration.modules) { - if(module !== currentConfiguration.rootModule) - usedAsInner.add(module); - } - } - } - // HACK: Sort configurations by length and start with the longest one - // to get the biggers groups possible. Used modules are marked with usedModules - // TODO: Allow to reuse existing configuration while trying to add dependencies. - // This would improve performance. O(n^2) -> O(n) - concatConfigurations.sort((a, b) => { - return b.modules.size - a.modules.size; - }); - const usedModules = new Set(); - for(const concatConfiguration of concatConfigurations) { - if(usedModules.has(concatConfiguration.rootModule)) - continue; - const newModule = new ConcatenatedModule(concatConfiguration.rootModule, Array.from(concatConfiguration.modules)); - concatConfiguration.sortWarnings(); - for(const warning of concatConfiguration.warnings) { - newModule.optimizationBailout.push((requestShortener) => { - const reason = getBailoutReason(warning[0], requestShortener); - const reasonWithPrefix = reason ? ` (<- ${reason})` : ""; - if(warning[0] === warning[1]) - return formatBailoutReason(`Cannot concat with ${warning[0].readableIdentifier(requestShortener)}${reasonWithPrefix}`); - else - return formatBailoutReason(`Cannot concat with ${warning[0].readableIdentifier(requestShortener)} because of ${warning[1].readableIdentifier(requestShortener)}${reasonWithPrefix}`); - }); - } - const chunks = concatConfiguration.rootModule.getChunks(); - for(const m of concatConfiguration.modules) { - usedModules.add(m); - chunks.forEach(chunk => chunk.removeModule(m)); - } - chunks.forEach(chunk => { - chunk.addModule(newModule); - newModule.addChunk(chunk); - if(chunk.entryModule === concatConfiguration.rootModule) - chunk.entryModule = newModule; - }); - compilation.modules.push(newModule); - newModule.reasons.forEach(reason => reason.dependency.module = newModule); - newModule.dependencies.forEach(dep => { - if(dep.module) { - dep.module.reasons.forEach(reason => { - if(reason.dependency === dep) - reason.module = newModule; - }); - } - }); - } - compilation.modules = compilation.modules.filter(m => !usedModules.has(m)); - }); - }); - } - - getImports(module) { - return Array.from(new Set(module.dependencies - - // Only harmony Dependencies - .filter(dep => dep instanceof HarmonyImportDependency && dep.module) - - // Dependencies are simple enough to concat them - .filter(dep => { - return !module.dependencies.some(d => - d instanceof HarmonyExportImportedSpecifierDependency && - d.importDependency === dep && - !d.id && - !Array.isArray(dep.module.providedExports) - ); - }) - - // Take the imported module - .map(dep => dep.module) - )); - } - - tryToAdd(config, module, possibleModules, failureCache) { - const cacheEntry = failureCache.get(module); - if(cacheEntry) { - return cacheEntry; - } - - // Already added? - if(config.has(module)) { - return null; - } - - // Not possible to add? - if(!possibleModules.has(module)) { - failureCache.set(module, module); // cache failures for performance - return module; - } - - // module must be in the same chunks - if(!config.rootModule.hasEqualsChunks(module)) { - failureCache.set(module, module); // cache failures for performance - return module; - } - - // Clone config to make experimental changes - const testConfig = config.clone(); - - // Add the module - testConfig.add(module); - - // Every module which depends on the added module must be in the configuration too. - for(const reason of module.reasons) { - const problem = this.tryToAdd(testConfig, reason.module, possibleModules, failureCache); - if(problem) { - failureCache.set(module, problem); // cache failures for performance - return problem; - } - } - - // Eagerly try to add imports too if possible - for(const imp of this.getImports(module)) { - const problem = this.tryToAdd(testConfig, imp, possibleModules, failureCache); - if(problem) { - config.addWarning(module, problem); - } - } - - // Commit experimental changes - config.set(testConfig); - return null; - } -} - -class ConcatConfiguration { - constructor(rootModule) { - this.rootModule = rootModule; - this.modules = new Set([rootModule]); - this.warnings = new Map(); - } - - add(module) { - this.modules.add(module); - } - - has(module) { - return this.modules.has(module); - } - - isEmpty() { - return this.modules.size === 1; - } - - addWarning(module, problem) { - this.warnings.set(module, problem); - } - - sortWarnings() { - this.warnings = new Map(Array.from(this.warnings).sort((a, b) => { - const ai = a[0].identifier(); - const bi = b[0].identifier(); - if(ai < bi) return -1; - if(ai > bi) return 1; - return 0; - })); - } - - clone() { - const clone = new ConcatConfiguration(this.rootModule); - for(const module of this.modules) - clone.add(module); - for(const pair of this.warnings) - clone.addWarning(pair[0], pair[1]); - return clone; - } - - set(config) { - this.rootModule = config.rootModule; - this.modules = new Set(config.modules); - this.warnings = new Map(config.warnings); - } -} - -module.exports = ModuleConcatenationPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +const HarmonyImportDependency = require("../dependencies/HarmonyImportDependency"); +const ModuleHotAcceptDependency = require("../dependencies/ModuleHotAcceptDependency"); +const ModuleHotDeclineDependency = require("../dependencies/ModuleHotDeclineDependency"); +const ConcatenatedModule = require("./ConcatenatedModule"); +const HarmonyCompatibilityDependency = require("../dependencies/HarmonyCompatibilityDependency"); +const StackedSetMap = require("../util/StackedSetMap"); + +const formatBailoutReason = msg => { + return "ModuleConcatenation bailout: " + msg; +}; + +class ModuleConcatenationPlugin { + constructor(options) { + if (typeof options !== "object") options = {}; + this.options = options; + } + + apply(compiler) { + compiler.hooks.compilation.tap( + "ModuleConcatenationPlugin", + (compilation, { normalModuleFactory }) => { + const handler = (parser, parserOptions) => { + parser.hooks.call.for("eval").tap("ModuleConcatenationPlugin", () => { + // Because of variable renaming we can't use modules with eval. + parser.state.module.buildMeta.moduleConcatenationBailout = "eval()"; + }); + }; + + normalModuleFactory.hooks.parser + .for("javascript/auto") + .tap("ModuleConcatenationPlugin", handler); + normalModuleFactory.hooks.parser + .for("javascript/dynamic") + .tap("ModuleConcatenationPlugin", handler); + normalModuleFactory.hooks.parser + .for("javascript/esm") + .tap("ModuleConcatenationPlugin", handler); + + const bailoutReasonMap = new Map(); + + const setBailoutReason = (module, reason) => { + bailoutReasonMap.set(module, reason); + module.optimizationBailout.push( + typeof reason === "function" + ? rs => formatBailoutReason(reason(rs)) + : formatBailoutReason(reason) + ); + }; + + const getBailoutReason = (module, requestShortener) => { + const reason = bailoutReasonMap.get(module); + if (typeof reason === "function") return reason(requestShortener); + return reason; + }; + + compilation.hooks.optimizeChunkModules.tap( + "ModuleConcatenationPlugin", + (chunks, modules) => { + const relevantModules = []; + const possibleInners = new Set(); + for (const module of modules) { + // Only harmony modules are valid for optimization + if ( + !module.buildMeta || + module.buildMeta.exportsType !== "namespace" || + !module.dependencies.some( + d => d instanceof HarmonyCompatibilityDependency + ) + ) { + setBailoutReason(module, "Module is not an ECMAScript module"); + continue; + } + + // Some expressions are not compatible with module concatenation + // because they may produce unexpected results. The plugin bails out + // if some were detected upfront. + if ( + module.buildMeta && + module.buildMeta.moduleConcatenationBailout + ) { + setBailoutReason( + module, + `Module uses ${module.buildMeta.moduleConcatenationBailout}` + ); + continue; + } + + // Exports must be known (and not dynamic) + if (!Array.isArray(module.buildMeta.providedExports)) { + setBailoutReason(module, "Module exports are unknown"); + continue; + } + + // Using dependency variables is not possible as this wraps the code in a function + if (module.variables.length > 0) { + setBailoutReason( + module, + `Module uses injected variables (${module.variables + .map(v => v.name) + .join(", ")})` + ); + continue; + } + + // Hot Module Replacement need it's own module to work correctly + if ( + module.dependencies.some( + dep => + dep instanceof ModuleHotAcceptDependency || + dep instanceof ModuleHotDeclineDependency + ) + ) { + setBailoutReason(module, "Module uses Hot Module Replacement"); + continue; + } + + relevantModules.push(module); + + // Module must not be the entry points + if (module.isEntryModule()) { + setBailoutReason(module, "Module is an entry point"); + continue; + } + + // Module must be in any chunk (we don't want to do useless work) + if (module.getNumberOfChunks() === 0) { + setBailoutReason(module, "Module is not in any chunk"); + continue; + } + + // Module must only be used by Harmony Imports + const nonHarmonyReasons = module.reasons.filter( + reason => + !reason.dependency || + !(reason.dependency instanceof HarmonyImportDependency) + ); + if (nonHarmonyReasons.length > 0) { + const importingModules = new Set( + nonHarmonyReasons.map(r => r.module).filter(Boolean) + ); + const importingExplanations = new Set( + nonHarmonyReasons.map(r => r.explanation).filter(Boolean) + ); + const importingModuleTypes = new Map( + Array.from(importingModules).map( + m => /** @type {[string, Set]} */ ([ + m, + new Set( + nonHarmonyReasons + .filter(r => r.module === m) + .map(r => r.dependency.type) + .sort() + ) + ]) + ) + ); + setBailoutReason(module, requestShortener => { + const names = Array.from(importingModules) + .map( + m => + `${m.readableIdentifier( + requestShortener + )} (referenced with ${Array.from( + importingModuleTypes.get(m) + ).join(", ")})` + ) + .sort(); + const explanations = Array.from(importingExplanations).sort(); + if (names.length > 0 && explanations.length === 0) { + return `Module is referenced from these modules with unsupported syntax: ${names.join( + ", " + )}`; + } else if (names.length === 0 && explanations.length > 0) { + return `Module is referenced by: ${explanations.join( + ", " + )}`; + } else if (names.length > 0 && explanations.length > 0) { + return `Module is referenced from these modules with unsupported syntax: ${names.join( + ", " + )} and by: ${explanations.join(", ")}`; + } else { + return "Module is referenced in a unsupported way"; + } + }); + continue; + } + + possibleInners.add(module); + } + // sort by depth + // modules with lower depth are more likely suited as roots + // this improves performance, because modules already selected as inner are skipped + relevantModules.sort((a, b) => { + return a.depth - b.depth; + }); + const concatConfigurations = []; + const usedAsInner = new Set(); + for (const currentRoot of relevantModules) { + // when used by another configuration as inner: + // the other configuration is better and we can skip this one + if (usedAsInner.has(currentRoot)) continue; + + // create a configuration with the root + const currentConfiguration = new ConcatConfiguration(currentRoot); + + // cache failures to add modules + const failureCache = new Map(); + + // try to add all imports + for (const imp of this._getImports(compilation, currentRoot)) { + const problem = this._tryToAdd( + compilation, + currentConfiguration, + imp, + possibleInners, + failureCache + ); + if (problem) { + failureCache.set(imp, problem); + currentConfiguration.addWarning(imp, problem); + } + } + if (!currentConfiguration.isEmpty()) { + concatConfigurations.push(currentConfiguration); + for (const module of currentConfiguration.getModules()) { + if (module !== currentConfiguration.rootModule) { + usedAsInner.add(module); + } + } + } + } + // HACK: Sort configurations by length and start with the longest one + // to get the biggers groups possible. Used modules are marked with usedModules + // TODO: Allow to reuse existing configuration while trying to add dependencies. + // This would improve performance. O(n^2) -> O(n) + concatConfigurations.sort((a, b) => { + return b.modules.size - a.modules.size; + }); + const usedModules = new Set(); + for (const concatConfiguration of concatConfigurations) { + if (usedModules.has(concatConfiguration.rootModule)) continue; + const modules = concatConfiguration.getModules(); + const rootModule = concatConfiguration.rootModule; + const newModule = new ConcatenatedModule( + rootModule, + Array.from(modules), + ConcatenatedModule.createConcatenationList( + rootModule, + modules, + compilation + ) + ); + for (const warning of concatConfiguration.getWarningsSorted()) { + newModule.optimizationBailout.push(requestShortener => { + const reason = getBailoutReason(warning[0], requestShortener); + const reasonWithPrefix = reason ? ` (<- ${reason})` : ""; + if (warning[0] === warning[1]) { + return formatBailoutReason( + `Cannot concat with ${warning[0].readableIdentifier( + requestShortener + )}${reasonWithPrefix}` + ); + } else { + return formatBailoutReason( + `Cannot concat with ${warning[0].readableIdentifier( + requestShortener + )} because of ${warning[1].readableIdentifier( + requestShortener + )}${reasonWithPrefix}` + ); + } + }); + } + const chunks = concatConfiguration.rootModule.getChunks(); + for (const m of modules) { + usedModules.add(m); + for (const chunk of chunks) { + chunk.removeModule(m); + } + } + for (const chunk of chunks) { + chunk.addModule(newModule); + newModule.addChunk(chunk); + if (chunk.entryModule === concatConfiguration.rootModule) { + chunk.entryModule = newModule; + } + } + compilation.modules.push(newModule); + for (const reason of newModule.reasons) { + if (reason.dependency.module === concatConfiguration.rootModule) + reason.dependency.module = newModule; + if ( + reason.dependency.redirectedModule === + concatConfiguration.rootModule + ) + reason.dependency.redirectedModule = newModule; + } + // TODO: remove when LTS node version contains fixed v8 version + // @see https://github.com/webpack/webpack/pull/6613 + // Turbofan does not correctly inline for-of loops with polymorphic input arrays. + // Work around issue by using a standard for loop and assigning dep.module.reasons + for (let i = 0; i < newModule.dependencies.length; i++) { + let dep = newModule.dependencies[i]; + if (dep.module) { + let reasons = dep.module.reasons; + for (let j = 0; j < reasons.length; j++) { + let reason = reasons[j]; + if (reason.dependency === dep) { + reason.module = newModule; + } + } + } + } + } + compilation.modules = compilation.modules.filter( + m => !usedModules.has(m) + ); + } + ); + } + ); + } + + _getImports(compilation, module) { + return new Set( + module.dependencies + + // Get reference info only for harmony Dependencies + .map(dep => { + if (!(dep instanceof HarmonyImportDependency)) return null; + if (!compilation) return dep.getReference(); + return compilation.getDependencyReference(module, dep); + }) + + // Reference is valid and has a module + // Dependencies are simple enough to concat them + .filter( + ref => + ref && + ref.module && + (Array.isArray(ref.importedNames) || + Array.isArray(ref.module.buildMeta.providedExports)) + ) + + // Take the imported module + .map(ref => ref.module) + ); + } + + _tryToAdd(compilation, config, module, possibleModules, failureCache) { + const cacheEntry = failureCache.get(module); + if (cacheEntry) { + return cacheEntry; + } + + // Already added? + if (config.has(module)) { + return null; + } + + // Not possible to add? + if (!possibleModules.has(module)) { + failureCache.set(module, module); // cache failures for performance + return module; + } + + // module must be in the same chunks + if (!config.rootModule.hasEqualsChunks(module)) { + failureCache.set(module, module); // cache failures for performance + return module; + } + + // Clone config to make experimental changes + const testConfig = config.clone(); + + // Add the module + testConfig.add(module); + + // Every module which depends on the added module must be in the configuration too. + for (const reason of module.reasons) { + // Modules that are not used can be ignored + if ( + reason.module.factoryMeta.sideEffectFree && + reason.module.used === false + ) + continue; + + const problem = this._tryToAdd( + compilation, + testConfig, + reason.module, + possibleModules, + failureCache + ); + if (problem) { + failureCache.set(module, problem); // cache failures for performance + return problem; + } + } + + // Commit experimental changes + config.set(testConfig); + + // Eagerly try to add imports too if possible + for (const imp of this._getImports(compilation, module)) { + const problem = this._tryToAdd( + compilation, + config, + imp, + possibleModules, + failureCache + ); + if (problem) { + config.addWarning(imp, problem); + } + } + return null; + } +} + +class ConcatConfiguration { + constructor(rootModule, cloneFrom) { + this.rootModule = rootModule; + if (cloneFrom) { + this.modules = cloneFrom.modules.createChild(5); + this.warnings = cloneFrom.warnings.createChild(5); + } else { + this.modules = new StackedSetMap(); + this.modules.add(rootModule); + this.warnings = new StackedSetMap(); + } + } + + add(module) { + this.modules.add(module); + } + + has(module) { + return this.modules.has(module); + } + + isEmpty() { + return this.modules.size === 1; + } + + addWarning(module, problem) { + this.warnings.set(module, problem); + } + + getWarningsSorted() { + return new Map( + this.warnings.asPairArray().sort((a, b) => { + const ai = a[0].identifier(); + const bi = b[0].identifier(); + if (ai < bi) return -1; + if (ai > bi) return 1; + return 0; + }) + ); + } + + getModules() { + return this.modules.asSet(); + } + + clone() { + return new ConcatConfiguration(this.rootModule, this); + } + + set(config) { + this.rootModule = config.rootModule; + this.modules = config.modules; + this.warnings = config.warnings; + } +} + +module.exports = ModuleConcatenationPlugin; diff --git a/node_modules/webpack/lib/optimize/OccurrenceOrderPlugin.js b/node_modules/webpack/lib/optimize/OccurrenceOrderPlugin.js index 45ec34719..c73ec8e57 100644 --- a/node_modules/webpack/lib/optimize/OccurrenceOrderPlugin.js +++ b/node_modules/webpack/lib/optimize/OccurrenceOrderPlugin.js @@ -1,102 +1,135 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class OccurrenceOrderPlugin { - constructor(preferEntry) { - if(preferEntry !== undefined && typeof preferEntry !== "boolean") { - throw new Error("Argument should be a boolean.\nFor more info on this plugin, see https://webpack.js.org/plugins/"); - } - this.preferEntry = preferEntry; - } - apply(compiler) { - const preferEntry = this.preferEntry; - compiler.plugin("compilation", (compilation) => { - compilation.plugin("optimize-module-order", (modules) => { - const occursInInitialChunksMap = new Map(); - const occursInAllChunksMap = new Map(); - - const initialChunkChunkMap = new Map(); - const entryCountMap = new Map(); - modules.forEach(m => { - let initial = 0; - let entry = 0; - m.forEachChunk(c => { - if(c.isInitial()) initial++; - if(c.entryModule === m) entry++; - }); - initialChunkChunkMap.set(m, initial); - entryCountMap.set(m, entry); - }); - - const countOccursInEntry = (sum, r) => { - if(!r.module) return sum; - return sum + initialChunkChunkMap.get(r.module); - }; - const countOccurs = (sum, r) => { - if(!r.module) return sum; - return sum + r.module.getNumberOfChunks(); - }; - - if(preferEntry) { - modules.forEach(m => { - const result = m.reasons.reduce(countOccursInEntry, 0) + initialChunkChunkMap.get(m) + entryCountMap.get(m); - occursInInitialChunksMap.set(m, result); - }); - } - - modules.forEach(m => { - const result = m.reasons.reduce(countOccurs, 0) + m.getNumberOfChunks() + entryCountMap.get(m); - occursInAllChunksMap.set(m, result); - }); - - modules.sort((a, b) => { - if(preferEntry) { - const aEntryOccurs = occursInInitialChunksMap.get(a); - const bEntryOccurs = occursInInitialChunksMap.get(b); - if(aEntryOccurs > bEntryOccurs) return -1; - if(aEntryOccurs < bEntryOccurs) return 1; - } - const aOccurs = occursInAllChunksMap.get(a); - const bOccurs = occursInAllChunksMap.get(b); - if(aOccurs > bOccurs) return -1; - if(aOccurs < bOccurs) return 1; - if(a.index > b.index) return 1; - if(a.index < b.index) return -1; - return 0; - }); - }); - compilation.plugin("optimize-chunk-order", (chunks) => { - const occursInInitialChunksMap = new Map(); - - chunks.forEach(c => { - const result = c.parents.reduce((sum, p) => { - if(p.isInitial()) return sum + 1; - return sum; - }, 0); - return occursInInitialChunksMap.set(c, result); - }); - - function occurs(c) { - return c.blocks.length; - } - - chunks.sort((a, b) => { - const aEntryOccurs = occursInInitialChunksMap.get(a); - const bEntryOccurs = occursInInitialChunksMap.get(b); - if(aEntryOccurs > bEntryOccurs) return -1; - if(aEntryOccurs < bEntryOccurs) return 1; - const aOccurs = occurs(a); - const bOccurs = occurs(b); - if(aOccurs > bOccurs) return -1; - if(aOccurs < bOccurs) return 1; - return a.compareTo(b); - }); - }); - }); - } -} - -module.exports = OccurrenceOrderPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +// TODO webpack 5 remove this plugin +// It has been splitted into separate plugins for modules and chunks +class OccurrenceOrderPlugin { + constructor(preferEntry) { + if (preferEntry !== undefined && typeof preferEntry !== "boolean") { + throw new Error( + "Argument should be a boolean.\nFor more info on this plugin, see https://webpack.js.org/plugins/" + ); + } + this.preferEntry = preferEntry; + } + apply(compiler) { + const preferEntry = this.preferEntry; + compiler.hooks.compilation.tap("OccurrenceOrderPlugin", compilation => { + compilation.hooks.optimizeModuleOrder.tap( + "OccurrenceOrderPlugin", + modules => { + const occursInInitialChunksMap = new Map(); + const occursInAllChunksMap = new Map(); + + const initialChunkChunkMap = new Map(); + const entryCountMap = new Map(); + for (const m of modules) { + let initial = 0; + let entry = 0; + for (const c of m.chunksIterable) { + if (c.canBeInitial()) initial++; + if (c.entryModule === m) entry++; + } + initialChunkChunkMap.set(m, initial); + entryCountMap.set(m, entry); + } + + const countOccursInEntry = (sum, r) => { + if (!r.module) { + return sum; + } + return sum + initialChunkChunkMap.get(r.module); + }; + const countOccurs = (sum, r) => { + if (!r.module) { + return sum; + } + let factor = 1; + if (typeof r.dependency.getNumberOfIdOccurrences === "function") { + factor = r.dependency.getNumberOfIdOccurrences(); + } + if (factor === 0) { + return sum; + } + return sum + factor * r.module.getNumberOfChunks(); + }; + + if (preferEntry) { + for (const m of modules) { + const result = + m.reasons.reduce(countOccursInEntry, 0) + + initialChunkChunkMap.get(m) + + entryCountMap.get(m); + occursInInitialChunksMap.set(m, result); + } + } + + const originalOrder = new Map(); + let i = 0; + for (const m of modules) { + const result = + m.reasons.reduce(countOccurs, 0) + + m.getNumberOfChunks() + + entryCountMap.get(m); + occursInAllChunksMap.set(m, result); + originalOrder.set(m, i++); + } + + modules.sort((a, b) => { + if (preferEntry) { + const aEntryOccurs = occursInInitialChunksMap.get(a); + const bEntryOccurs = occursInInitialChunksMap.get(b); + if (aEntryOccurs > bEntryOccurs) return -1; + if (aEntryOccurs < bEntryOccurs) return 1; + } + const aOccurs = occursInAllChunksMap.get(a); + const bOccurs = occursInAllChunksMap.get(b); + if (aOccurs > bOccurs) return -1; + if (aOccurs < bOccurs) return 1; + const orgA = originalOrder.get(a); + const orgB = originalOrder.get(b); + return orgA - orgB; + }); + } + ); + compilation.hooks.optimizeChunkOrder.tap( + "OccurrenceOrderPlugin", + chunks => { + const occursInInitialChunksMap = new Map(); + const originalOrder = new Map(); + + let i = 0; + for (const c of chunks) { + let occurs = 0; + for (const chunkGroup of c.groupsIterable) { + for (const parent of chunkGroup.parentsIterable) { + if (parent.isInitial()) occurs++; + } + } + occursInInitialChunksMap.set(c, occurs); + originalOrder.set(c, i++); + } + + chunks.sort((a, b) => { + const aEntryOccurs = occursInInitialChunksMap.get(a); + const bEntryOccurs = occursInInitialChunksMap.get(b); + if (aEntryOccurs > bEntryOccurs) return -1; + if (aEntryOccurs < bEntryOccurs) return 1; + const aOccurs = a.getNumberOfGroups(); + const bOccurs = b.getNumberOfGroups(); + if (aOccurs > bOccurs) return -1; + if (aOccurs < bOccurs) return 1; + const orgA = originalOrder.get(a); + const orgB = originalOrder.get(b); + return orgA - orgB; + }); + } + ); + }); + } +} + +module.exports = OccurrenceOrderPlugin; diff --git a/node_modules/webpack/lib/optimize/RemoveEmptyChunksPlugin.js b/node_modules/webpack/lib/optimize/RemoveEmptyChunksPlugin.js index 4b75e4617..42ba24a15 100644 --- a/node_modules/webpack/lib/optimize/RemoveEmptyChunksPlugin.js +++ b/node_modules/webpack/lib/optimize/RemoveEmptyChunksPlugin.js @@ -1,21 +1,42 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -class RemoveEmptyChunksPlugin { - - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => { - chunks.filter((chunk) => chunk.isEmpty() && !chunk.hasRuntime() && !chunk.hasEntryModule()) - .forEach((chunk) => { - chunk.remove("empty"); - chunks.splice(chunks.indexOf(chunk), 1); - }); - }); - }); - } -} -module.exports = RemoveEmptyChunksPlugin; +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ +"use strict"; + +class RemoveEmptyChunksPlugin { + apply(compiler) { + compiler.hooks.compilation.tap("RemoveEmptyChunksPlugin", compilation => { + const handler = chunks => { + for (let i = chunks.length - 1; i >= 0; i--) { + const chunk = chunks[i]; + if ( + chunk.isEmpty() && + !chunk.hasRuntime() && + !chunk.hasEntryModule() + ) { + chunk.remove("empty"); + chunks.splice(i, 1); + } + } + }; + compilation.hooks.optimizeChunksBasic.tap( + "RemoveEmptyChunksPlugin", + handler + ); + compilation.hooks.optimizeChunksAdvanced.tap( + "RemoveEmptyChunksPlugin", + handler + ); + compilation.hooks.optimizeExtractedChunksBasic.tap( + "RemoveEmptyChunksPlugin", + handler + ); + compilation.hooks.optimizeExtractedChunksAdvanced.tap( + "RemoveEmptyChunksPlugin", + handler + ); + }); + } +} +module.exports = RemoveEmptyChunksPlugin; diff --git a/node_modules/webpack/lib/optimize/RemoveParentModulesPlugin.js b/node_modules/webpack/lib/optimize/RemoveParentModulesPlugin.js index 43550819c..7fff59207 100644 --- a/node_modules/webpack/lib/optimize/RemoveParentModulesPlugin.js +++ b/node_modules/webpack/lib/optimize/RemoveParentModulesPlugin.js @@ -1,65 +1,127 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -function hasModule(chunk, module, checkedChunks) { - if(chunk.containsModule(module)) return [chunk]; - if(chunk.parents.length === 0) return false; - return allHaveModule(chunk.parents.filter((c) => { - return !checkedChunks.has(c); - }), module, checkedChunks); -} - -function allHaveModule(someChunks, module, checkedChunks) { - if(!checkedChunks) checkedChunks = new Set(); - var chunks = new Set(); - for(var i = 0; i < someChunks.length; i++) { - checkedChunks.add(someChunks[i]); - var subChunks = hasModule(someChunks[i], module, checkedChunks); - if(!subChunks) return false; - - for(var index = 0; index < subChunks.length; index++) { - var item = subChunks[index]; - - chunks.add(item); - } - } - return chunks; -} - -class RemoveParentModulesPlugin { - apply(compiler) { - compiler.plugin("compilation", (compilation) => { - compilation.plugin(["optimize-chunks-basic", "optimize-extracted-chunks-basic"], (chunks) => { - for(var index = 0; index < chunks.length; index++) { - var chunk = chunks[index]; - if(chunk.parents.length === 0) continue; - - // TODO consider Map when performance has improved https://gist.github.com/sokra/b36098368da7b8f6792fd7c85fca6311 - var cache = Object.create(null); - var modules = chunk.getModules(); - for(var i = 0; i < modules.length; i++) { - var module = modules[i]; - - var dId = module.getChunkIdsIdent(); - var parentChunksWithModule; - if(dId === null) { - parentChunksWithModule = allHaveModule(chunk.parents, module); - } else if(dId in cache) { - parentChunksWithModule = cache[dId]; - } else { - parentChunksWithModule = cache[dId] = allHaveModule(chunk.parents, module); - } - if(parentChunksWithModule) { - module.rewriteChunkInReasons(chunk, Array.from(parentChunksWithModule)); - chunk.removeModule(module); - } - } - } - }); - }); - } -} -module.exports = RemoveParentModulesPlugin; +/* + 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; diff --git a/node_modules/webpack/lib/optimize/UglifyJsPlugin.js b/node_modules/webpack/lib/optimize/UglifyJsPlugin.js deleted file mode 100644 index 1caa10817..000000000 --- a/node_modules/webpack/lib/optimize/UglifyJsPlugin.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - MIT License http://www.opensource.org/licenses/mit-license.php - Author Tobias Koppers @sokra -*/ -"use strict"; - -const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); - -module.exports = UglifyJsPlugin; -- cgit v1.2.3