/* 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`); } if(Array.isArray(options) || typeof options === "string") { options = { name: options }; } this.chunkNames = options.name || options.names; this.filenameTemplate = options.filename; this.minChunks = options.minChunks; this.selectedChunks = options.chunks; if(options.children) this.selectedChunks = false; this.async = options.async; this.minSize = options.minSize; this.ident = __filename + (nextIdent++); } apply(compiler) { const chunkNames = this.chunkNames; const filenameTemplate = this.filenameTemplate; const minChunks = this.minChunks; const selectedChunks = this.selectedChunks; const asyncOption = this.async; const minSize = this.minSize; const ident = this.ident; compiler.plugin("this-compilation", (compilation) => { compilation.plugin(["optimize-chunks", "optimize-extracted-chunks"], (chunks) => { // only optimize once if(compilation[ident]) return; compilation[ident] = true; let commonChunks; if(!chunkNames && (selectedChunks === false || asyncOption)) { commonChunks = chunks; } else if(Array.isArray(chunkNames) || typeof chunkNames === "string") { commonChunks = [].concat(chunkNames).map((chunkName) => { let chunk = chunks.filter((chunk) => chunk.name === chunkName)[0]; if(!chunk) { chunk = compilation.addChunk(chunkName); } return chunk; }); } else { throw new Error("Invalid chunkNames argument"); } commonChunks.forEach(function processCommonChunk(commonChunk, idx) { let usedChunks; if(Array.isArray(selectedChunks)) { usedChunks = chunks.filter(chunk => chunk !== commonChunk && selectedChunks.indexOf(chunk.name) >= 0); } else if(selectedChunks === false || asyncOption) { usedChunks = (commonChunk.chunks || []).filter((chunk) => { // we can only move modules from this chunk if the "commonChunk" is the only parent return asyncOption || chunk.parents.length === 1; }); } else { if(commonChunk.parents.length > 0) { compilation.errors.push(new Error("CommonsChunkPlugin: While running in normal mode it's not allowed to use a non-entry chunk (" + commonChunk.name + ")")); return; } usedChunks = chunks.filter((chunk) => { const found = commonChunks.indexOf(chunk); if(found >= idx) return false; return chunk.hasRuntime(); }); } let asyncChunk; if(asyncOption) { asyncChunk = compilation.addChunk(typeof asyncOption === "string" ? asyncOption : undefined); asyncChunk.chunkReason = "async commons chunk"; asyncChunk.extraAsync = true; asyncChunk.addParent(commonChunk); commonChunk.addChunk(asyncChunk); commonChunk = asyncChunk; } const reallyUsedModules = []; if(minChunks !== Infinity) { const commonModulesCount = []; const commonModules = []; usedChunks.forEach((chunk) => { chunk.modules.forEach((module) => { const idx = commonModules.indexOf(module); if(idx < 0) { commonModules.push(module); commonModulesCount.push(1); } else { commonModulesCount[idx]++; } }); }); const _minChunks = (minChunks || Math.max(2, usedChunks.length)); commonModulesCount.forEach((count, idx) => { const module = commonModules[idx]; if(typeof minChunks === "function") { if(!minChunks(module, count)) return; } else if(count < _minChunks) { return; } if(module.chunkCondition && !module.chunkCondition(commonChunk)) return; reallyUsedModules.push(module); }); } if(minSize) { const size = reallyUsedModules.reduce((a, b) => { return a + b.size(); }, 0); if(size < minSize) return; } const reallyUsedChunks = new Set(); reallyUsedModules.forEach((module) => { usedChunks.forEach((chunk) => { if(module.removeChunk(chunk)) { reallyUsedChunks.add(chunk); } }); commonChunk.addModule(module); module.addChunk(commonChunk); }); if(asyncOption) { for(const chunk of reallyUsedChunks) { if(chunk.isInitial()) continue; chunk.blocks.forEach((block) => { block.chunks.unshift(commonChunk); commonChunk.addBlock(block); }); } asyncChunk.origins = Array.from(reallyUsedChunks).map((chunk) => { return chunk.origins.map((origin) => { const newOrigin = Object.create(origin); newOrigin.reasons = (origin.reasons || []).slice(); newOrigin.reasons.push("async commons"); return newOrigin; }); }).reduce((arr, a) => { arr.push.apply(arr, a); return arr; }, []); } else { usedChunks.forEach((chunk) => { chunk.parents = [commonChunk]; chunk.entrypoints.forEach((ep) => { ep.insertChunk(commonChunk, chunk); }); commonChunk.addChunk(chunk); }); } if(filenameTemplate) commonChunk.filenameTemplate = filenameTemplate; }); return true; }); }); } } module.exports = CommonsChunkPlugin;