| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-01 02:52:50 +08:00
										 |  |  | const crypto = require("crypto"); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | const SortableSet = require("../util/SortableSet"); | 
					
						
							|  |  |  | const GraphHelpers = require("../GraphHelpers"); | 
					
						
							| 
									
										
										
										
											2018-03-22 19:05:58 +08:00
										 |  |  | const { isSubset } = require("../util/SetHelpers"); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | const hashFilename = name => { | 
					
						
							|  |  |  | 	return crypto | 
					
						
							|  |  |  | 		.createHash("md4") | 
					
						
							|  |  |  | 		.update(name) | 
					
						
							|  |  |  | 		.digest("hex") | 
					
						
							|  |  |  | 		.slice(0, 8); | 
					
						
							| 
									
										
										
										
											2018-02-01 02:52:50 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | const sortByIdentifier = (a, b) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (a.identifier() > b.identifier()) return 1; | 
					
						
							|  |  |  | 	if (a.identifier() < b.identifier()) return -1; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getRequests = chunk => { | 
					
						
							|  |  |  | 	let requests = 0; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	for (const chunkGroup of chunk.groupsIterable) { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		requests = Math.max(requests, chunkGroup.chunks.length); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return requests; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const getModulesSize = modules => { | 
					
						
							|  |  |  | 	let sum = 0; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	for (const m of modules) sum += m.size(); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	return sum; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const isOverlap = (a, b) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	for (const item of a.keys()) { | 
					
						
							|  |  |  | 		if (b.has(item)) return true; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const compareEntries = (a, b) => { | 
					
						
							| 
									
										
										
										
											2018-01-20 20:11:02 +08:00
										 |  |  | 	// 1. by priority
 | 
					
						
							|  |  |  | 	const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (diffPriority) return diffPriority; | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 	// 2. by number of chunks
 | 
					
						
							|  |  |  | 	const diffCount = a.chunks.size - b.chunks.size; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (diffCount) return diffCount; | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 	// 3. by size reduction
 | 
					
						
							|  |  |  | 	const aSizeReduce = a.size * (a.chunks.size - 1); | 
					
						
							|  |  |  | 	const bSizeReduce = b.size * (b.chunks.size - 1); | 
					
						
							|  |  |  | 	const diffSizeReduce = aSizeReduce - bSizeReduce; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (diffSizeReduce) return diffSizeReduce; | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 	// 4. by number of modules (to be able to compare by identifier)
 | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	const modulesA = a.modules; | 
					
						
							|  |  |  | 	const modulesB = b.modules; | 
					
						
							|  |  |  | 	const diff = modulesA.size - modulesB.size; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (diff) return diff; | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 	// 5. by module identifiers
 | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	modulesA.sort(); | 
					
						
							|  |  |  | 	modulesB.sort(); | 
					
						
							|  |  |  | 	const aI = modulesA[Symbol.iterator](); | 
					
						
							|  |  |  | 	const bI = modulesB[Symbol.iterator](); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:15:37 +08:00
										 |  |  | 	// eslint-disable-next-line no-constant-condition
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	while (true) { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		const aItem = aI.next(); | 
					
						
							|  |  |  | 		const bItem = bI.next(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (aItem.done) return 0; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		const aModuleIdentifier = aItem.value.identifier(); | 
					
						
							|  |  |  | 		const bModuleIdentifier = bItem.value.identifier(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (aModuleIdentifier > bModuleIdentifier) return -1; | 
					
						
							|  |  |  | 		if (aModuleIdentifier < bModuleIdentifier) return 1; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = class SplitChunksPlugin { | 
					
						
							|  |  |  | 	constructor(options) { | 
					
						
							|  |  |  | 		this.options = SplitChunksPlugin.normalizeOptions(options); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-02 08:09:01 +08:00
										 |  |  | 	static normalizeOptions(options = {}) { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		return { | 
					
						
							| 
									
										
										
										
											2018-03-20 20:18:09 +08:00
										 |  |  | 			chunksFilter: SplitChunksPlugin.normalizeChunksFilter( | 
					
						
							|  |  |  | 				options.chunks || "all" | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			minSize: options.minSize || 0, | 
					
						
							|  |  |  | 			minChunks: options.minChunks || 1, | 
					
						
							|  |  |  | 			maxAsyncRequests: options.maxAsyncRequests || 1, | 
					
						
							|  |  |  | 			maxInitialRequests: options.maxInitialRequests || 1, | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 			getName: | 
					
						
							|  |  |  | 				SplitChunksPlugin.normalizeName({ | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 					name: options.name, | 
					
						
							|  |  |  | 					automaticNameDelimiter: options.automaticNameDelimiter | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 				}) || (() => {}), | 
					
						
							| 
									
										
										
										
											2018-03-02 17:14:25 +08:00
										 |  |  | 			filename: options.filename || undefined, | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 			getCacheGroups: SplitChunksPlugin.normalizeCacheGroups({ | 
					
						
							|  |  |  | 				cacheGroups: options.cacheGroups, | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 				automaticNameDelimiter: options.automaticNameDelimiter | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 	static normalizeName({ name, automaticNameDelimiter }) { | 
					
						
							|  |  |  | 		if (name === true) { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			const fn = (module, chunks, cacheGroup) => { | 
					
						
							|  |  |  | 				const names = chunks.map(c => c.name); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				if (!names.every(Boolean)) return; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 				names.sort(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				let name = | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 					(cacheGroup && cacheGroup !== "default" | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 						? cacheGroup + automaticNameDelimiter | 
					
						
							|  |  |  | 						: "") + names.join(automaticNameDelimiter); | 
					
						
							| 
									
										
										
										
											2018-02-01 02:52:50 +08:00
										 |  |  | 				// Filenames and paths can't be too long otherwise an
 | 
					
						
							|  |  |  | 				// ENAMETOOLONG error is raised. If the generated name if too
 | 
					
						
							|  |  |  | 				// long, it is truncated and a hash is appended. The limit has
 | 
					
						
							|  |  |  | 				// been set to 100 to prevent `[name].[chunkhash].[ext]` from
 | 
					
						
							|  |  |  | 				// generating a 256+ character string.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				if (name.length > 100) { | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 					name = | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 						name.slice(0, 100) + automaticNameDelimiter + hashFilename(name); | 
					
						
							| 
									
										
										
										
											2018-02-01 02:52:50 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 				return name; | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			return fn; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-12 17:32:46 +08:00
										 |  |  | 		if (typeof name === "string") { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			const fn = () => { | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 				return name; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			}; | 
					
						
							|  |  |  | 			return fn; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-03-12 17:32:46 +08:00
										 |  |  | 		if (typeof name === "function") return name; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-20 20:18:09 +08:00
										 |  |  | 	static normalizeChunksFilter(chunks) { | 
					
						
							|  |  |  | 		if (chunks === "initial") { | 
					
						
							|  |  |  | 			return chunk => chunk.canBeInitial(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (chunks === "async") { | 
					
						
							|  |  |  | 			return chunk => !chunk.canBeInitial(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (chunks === "all") { | 
					
						
							|  |  |  | 			return () => true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (typeof chunks === "function") return chunks; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 	static normalizeCacheGroups({ cacheGroups, automaticNameDelimiter }) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (typeof cacheGroups === "function") { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			return cacheGroups; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (cacheGroups && typeof cacheGroups === "object") { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			const fn = (module, chunks) => { | 
					
						
							|  |  |  | 				let results; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				for (const key of Object.keys(cacheGroups)) { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 					let option = cacheGroups[key]; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					if (option === false) continue; | 
					
						
							|  |  |  | 					if (option instanceof RegExp || typeof option === "string") { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 						option = { | 
					
						
							|  |  |  | 							test: option | 
					
						
							|  |  |  | 						}; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					if (typeof option === "function") { | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 						let result = option(module); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						if (result) { | 
					
						
							|  |  |  | 							if (results === undefined) results = []; | 
					
						
							|  |  |  | 							for (const r of Array.isArray(result) ? result : [result]) { | 
					
						
							|  |  |  | 								const result = Object.assign( | 
					
						
							|  |  |  | 									{ | 
					
						
							|  |  |  | 										key | 
					
						
							|  |  |  | 									}, | 
					
						
							|  |  |  | 									r | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 								if (result.name) result.getName = () => result.name; | 
					
						
							| 
									
										
										
										
											2018-03-21 03:57:02 +08:00
										 |  |  | 								if (result.chunks) { | 
					
						
							|  |  |  | 									result.chunksFilter = SplitChunksPlugin.normalizeChunksFilter( | 
					
						
							|  |  |  | 										result.chunks | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 								results.push(result); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					} else if (SplitChunksPlugin.checkTest(option.test, module, chunks)) { | 
					
						
							|  |  |  | 						if (results === undefined) results = []; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 						results.push({ | 
					
						
							|  |  |  | 							key: key, | 
					
						
							| 
									
										
										
										
											2018-01-20 20:11:02 +08:00
										 |  |  | 							priority: option.priority, | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 							getName: SplitChunksPlugin.normalizeName({ | 
					
						
							| 
									
										
										
										
											2018-03-12 17:07:31 +08:00
										 |  |  | 								name: option.name, | 
					
						
							|  |  |  | 								automaticNameDelimiter | 
					
						
							| 
									
										
										
										
											2018-03-09 21:27:19 +08:00
										 |  |  | 							}), | 
					
						
							| 
									
										
										
										
											2018-03-20 20:18:09 +08:00
										 |  |  | 							chunksFilter: SplitChunksPlugin.normalizeChunksFilter( | 
					
						
							|  |  |  | 								option.chunks | 
					
						
							|  |  |  | 							), | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							enforce: option.enforce, | 
					
						
							|  |  |  | 							minSize: option.minSize, | 
					
						
							|  |  |  | 							minChunks: option.minChunks, | 
					
						
							|  |  |  | 							maxAsyncRequests: option.maxAsyncRequests, | 
					
						
							|  |  |  | 							maxInitialRequests: option.maxInitialRequests, | 
					
						
							| 
									
										
										
										
											2018-03-02 17:14:25 +08:00
										 |  |  | 							filename: option.filename, | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							reuseExistingChunk: option.reuseExistingChunk | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return results; | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			return fn; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const fn = () => {}; | 
					
						
							|  |  |  | 		return fn; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	static checkTest(test, module, chunks) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (test === undefined) return true; | 
					
						
							|  |  |  | 		if (typeof test === "function") return test(module, chunks); | 
					
						
							|  |  |  | 		if (typeof test === "boolean") return test; | 
					
						
							|  |  |  | 		const names = chunks | 
					
						
							|  |  |  | 			.map(c => c.name) | 
					
						
							|  |  |  | 			.concat(module.nameForCondition ? [module.nameForCondition()] : []) | 
					
						
							|  |  |  | 			.filter(Boolean); | 
					
						
							|  |  |  | 		if (typeof test === "string") { | 
					
						
							|  |  |  | 			for (const name of names) if (name.startsWith(test)) return true; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			return false; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (test instanceof RegExp) { | 
					
						
							|  |  |  | 			for (const name of names) if (test.test(name)) return true; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			return false; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	apply(compiler) { | 
					
						
							| 
									
										
										
										
											2018-01-24 19:00:50 +08:00
										 |  |  | 		compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => { | 
					
						
							| 
									
										
										
										
											2018-01-20 20:11:02 +08:00
										 |  |  | 			let alreadyOptimized = false; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			compilation.hooks.unseal.tap("SplitChunksPlugin", () => { | 
					
						
							| 
									
										
										
										
											2018-01-20 20:11:02 +08:00
										 |  |  | 				alreadyOptimized = false; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			compilation.hooks.optimizeChunksAdvanced.tap( | 
					
						
							|  |  |  | 				"SplitChunksPlugin", | 
					
						
							|  |  |  | 				chunks => { | 
					
						
							|  |  |  | 					if (alreadyOptimized) return; | 
					
						
							|  |  |  | 					alreadyOptimized = true; | 
					
						
							|  |  |  | 					// Give each selected chunk an index (to create strings from chunks)
 | 
					
						
							|  |  |  | 					const indexMap = new Map(); | 
					
						
							|  |  |  | 					let index = 1; | 
					
						
							|  |  |  | 					for (const chunk of chunks) { | 
					
						
							|  |  |  | 						indexMap.set(chunk, index++); | 
					
						
							| 
									
										
										
										
											2018-02-17 16:48:40 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					const getKey = chunks => { | 
					
						
							|  |  |  | 						return Array.from(chunks, c => indexMap.get(c)) | 
					
						
							|  |  |  | 							.sort() | 
					
						
							|  |  |  | 							.join(); | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					// Create a list of possible combinations
 | 
					
						
							|  |  |  | 					const chunkSetsInGraph = new Map(); // Map<string, Set<Chunk>>
 | 
					
						
							|  |  |  | 					for (const module of compilation.modules) { | 
					
						
							|  |  |  | 						const chunkIndices = getKey(module.chunksIterable); | 
					
						
							|  |  |  | 						chunkSetsInGraph.set(chunkIndices, new Set(module.chunksIterable)); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const combinations = new Map(); // Map<string, Set<Chunk>[]>
 | 
					
						
							|  |  |  | 					for (const [key, chunksSet] of chunkSetsInGraph) { | 
					
						
							|  |  |  | 						var array = []; | 
					
						
							|  |  |  | 						for (const set of chunkSetsInGraph.values()) { | 
					
						
							|  |  |  | 							if (isSubset(chunksSet, set)) { | 
					
						
							|  |  |  | 								array.push(set); | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 						combinations.set(key, array); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					// Map a list of chunks to a list of modules
 | 
					
						
							|  |  |  | 					// For the key the chunk "index" is used, the value is a SortableSet of modules
 | 
					
						
							|  |  |  | 					const chunksInfoMap = new Map(); | 
					
						
							|  |  |  | 					// Walk through all modules
 | 
					
						
							|  |  |  | 					for (const module of compilation.modules) { | 
					
						
							|  |  |  | 						// Get array of chunks
 | 
					
						
							|  |  |  | 						const chunks = module.getChunks(); | 
					
						
							|  |  |  | 						// Get cache group
 | 
					
						
							|  |  |  | 						let cacheGroups = this.options.getCacheGroups(module, chunks); | 
					
						
							|  |  |  | 						if (!Array.isArray(cacheGroups)) continue; | 
					
						
							|  |  |  | 						for (const cacheGroupSource of cacheGroups) { | 
					
						
							|  |  |  | 							const cacheGroup = { | 
					
						
							|  |  |  | 								key: cacheGroupSource.key, | 
					
						
							|  |  |  | 								priority: cacheGroupSource.priority || 0, | 
					
						
							| 
									
										
										
										
											2018-03-20 20:18:09 +08:00
										 |  |  | 								chunksFilter: | 
					
						
							|  |  |  | 									cacheGroupSource.chunksFilter || this.options.chunksFilter, | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								minSize: | 
					
						
							|  |  |  | 									cacheGroupSource.minSize !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.minSize | 
					
						
							|  |  |  | 										: cacheGroupSource.enforce ? 0 : this.options.minSize, | 
					
						
							|  |  |  | 								minChunks: | 
					
						
							|  |  |  | 									cacheGroupSource.minChunks !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.minChunks | 
					
						
							|  |  |  | 										: cacheGroupSource.enforce ? 1 : this.options.minChunks, | 
					
						
							|  |  |  | 								maxAsyncRequests: | 
					
						
							|  |  |  | 									cacheGroupSource.maxAsyncRequests !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.maxAsyncRequests | 
					
						
							|  |  |  | 										: cacheGroupSource.enforce | 
					
						
							|  |  |  | 											? Infinity | 
					
						
							|  |  |  | 											: this.options.maxAsyncRequests, | 
					
						
							|  |  |  | 								maxInitialRequests: | 
					
						
							|  |  |  | 									cacheGroupSource.maxInitialRequests !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.maxInitialRequests | 
					
						
							|  |  |  | 										: cacheGroupSource.enforce | 
					
						
							|  |  |  | 											? Infinity | 
					
						
							|  |  |  | 											: this.options.maxInitialRequests, | 
					
						
							|  |  |  | 								getName: | 
					
						
							|  |  |  | 									cacheGroupSource.getName !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.getName | 
					
						
							|  |  |  | 										: this.options.getName, | 
					
						
							| 
									
										
										
										
											2018-03-02 17:14:25 +08:00
										 |  |  | 								filename: | 
					
						
							|  |  |  | 									cacheGroupSource.filename !== undefined | 
					
						
							|  |  |  | 										? cacheGroupSource.filename | 
					
						
							|  |  |  | 										: this.options.filename, | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								reuseExistingChunk: cacheGroupSource.reuseExistingChunk | 
					
						
							|  |  |  | 							}; | 
					
						
							|  |  |  | 							// For all combination of chunk selection
 | 
					
						
							|  |  |  | 							for (const chunkCombination of combinations.get(getKey(chunks))) { | 
					
						
							|  |  |  | 								// Get indices of chunks in which this module occurs
 | 
					
						
							|  |  |  | 								const chunkIndices = Array.from(chunkCombination, chunk => | 
					
						
							|  |  |  | 									indexMap.get(chunk) | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 								// Break if minimum number of chunks is not reached
 | 
					
						
							|  |  |  | 								if (chunkIndices.length < cacheGroup.minChunks) continue; | 
					
						
							|  |  |  | 								// Select chunks by configuration
 | 
					
						
							| 
									
										
										
										
											2018-03-20 20:18:09 +08:00
										 |  |  | 								const selectedChunks = Array.from(chunkCombination).filter( | 
					
						
							|  |  |  | 									cacheGroup.chunksFilter | 
					
						
							|  |  |  | 								); | 
					
						
							| 
									
										
										
										
											2018-03-07 22:31:12 +08:00
										 |  |  | 								// Break if minimum number of chunks is not reached
 | 
					
						
							|  |  |  | 								if (selectedChunks.length < cacheGroup.minChunks) continue; | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								// Determine name for split chunk
 | 
					
						
							|  |  |  | 								const name = cacheGroup.getName( | 
					
						
							|  |  |  | 									module, | 
					
						
							|  |  |  | 									selectedChunks, | 
					
						
							|  |  |  | 									cacheGroup.key | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 								// Create key for maps
 | 
					
						
							|  |  |  | 								// When it has a name we use the name as key
 | 
					
						
							|  |  |  | 								// Elsewise we create the key from chunks and cache group key
 | 
					
						
							|  |  |  | 								// This automatically merges equal names
 | 
					
						
							|  |  |  | 								const chunksKey = getKey(selectedChunks); | 
					
						
							|  |  |  | 								const key = | 
					
						
							|  |  |  | 									(name && `name:${name}`) || | 
					
						
							|  |  |  | 									`chunks:${chunksKey} key:${cacheGroup.key}`; | 
					
						
							|  |  |  | 								// Add module to maps
 | 
					
						
							|  |  |  | 								let info = chunksInfoMap.get(key); | 
					
						
							|  |  |  | 								if (info === undefined) { | 
					
						
							|  |  |  | 									chunksInfoMap.set( | 
					
						
							|  |  |  | 										key, | 
					
						
							|  |  |  | 										(info = { | 
					
						
							|  |  |  | 											modules: new SortableSet(undefined, sortByIdentifier), | 
					
						
							|  |  |  | 											cacheGroup, | 
					
						
							|  |  |  | 											name, | 
					
						
							|  |  |  | 											chunks: new Map(), | 
					
						
							|  |  |  | 											reusedableChunks: new Set(), | 
					
						
							|  |  |  | 											chunksKeys: new Set() | 
					
						
							|  |  |  | 										}) | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								info.modules.add(module); | 
					
						
							|  |  |  | 								if (!info.chunksKeys.has(chunksKey)) { | 
					
						
							|  |  |  | 									info.chunksKeys.add(chunksKey); | 
					
						
							|  |  |  | 									for (const chunk of selectedChunks) { | 
					
						
							|  |  |  | 										info.chunks.set(chunk, chunk.getNumberOfModules()); | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					for (const [key, info] of chunksInfoMap) { | 
					
						
							|  |  |  | 						// Get size of module lists
 | 
					
						
							|  |  |  | 						info.size = getModulesSize(info.modules); | 
					
						
							|  |  |  | 						if (info.size < info.cacheGroup.minSize) { | 
					
						
							|  |  |  | 							chunksInfoMap.delete(key); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					let changed = false; | 
					
						
							|  |  |  | 					while (chunksInfoMap.size > 0) { | 
					
						
							|  |  |  | 						// Find best matching entry
 | 
					
						
							|  |  |  | 						let bestEntryKey; | 
					
						
							|  |  |  | 						let bestEntry; | 
					
						
							|  |  |  | 						for (const pair of chunksInfoMap) { | 
					
						
							|  |  |  | 							const key = pair[0]; | 
					
						
							|  |  |  | 							const info = pair[1]; | 
					
						
							|  |  |  | 							if (bestEntry === undefined) { | 
					
						
							|  |  |  | 								bestEntry = info; | 
					
						
							|  |  |  | 								bestEntryKey = key; | 
					
						
							|  |  |  | 							} else if (compareEntries(bestEntry, info) < 0) { | 
					
						
							|  |  |  | 								bestEntry = info; | 
					
						
							|  |  |  | 								bestEntryKey = key; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						const item = bestEntry; | 
					
						
							|  |  |  | 						chunksInfoMap.delete(bestEntryKey); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						let chunkName = item.name; | 
					
						
							|  |  |  | 						// Variable for the new chunk (lazy created)
 | 
					
						
							|  |  |  | 						let newChunk; | 
					
						
							|  |  |  | 						// When no chunk name, check if we can reuse a chunk instead of creating a new one
 | 
					
						
							|  |  |  | 						let isReused = false; | 
					
						
							|  |  |  | 						if (item.cacheGroup.reuseExistingChunk) { | 
					
						
							|  |  |  | 							for (const pair of item.chunks) { | 
					
						
							|  |  |  | 								if (pair[1] === item.modules.size) { | 
					
						
							|  |  |  | 									const chunk = pair[0]; | 
					
						
							|  |  |  | 									if (chunk.hasEntryModule()) continue; | 
					
						
							|  |  |  | 									if (!newChunk || !newChunk.name) newChunk = chunk; | 
					
						
							|  |  |  | 									else if ( | 
					
						
							|  |  |  | 										chunk.name && | 
					
						
							|  |  |  | 										chunk.name.length < newChunk.name.length | 
					
						
							|  |  |  | 									) | 
					
						
							|  |  |  | 										newChunk = chunk; | 
					
						
							|  |  |  | 									else if ( | 
					
						
							|  |  |  | 										chunk.name && | 
					
						
							|  |  |  | 										chunk.name.length === newChunk.name.length && | 
					
						
							|  |  |  | 										chunk.name < newChunk.name | 
					
						
							|  |  |  | 									) | 
					
						
							|  |  |  | 										newChunk = chunk; | 
					
						
							|  |  |  | 									chunkName = undefined; | 
					
						
							|  |  |  | 									isReused = true; | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						// Walk through all chunks
 | 
					
						
							|  |  |  | 						for (const chunk of item.chunks.keys()) { | 
					
						
							|  |  |  | 							// skip if we address ourself
 | 
					
						
							|  |  |  | 							if (chunk.name === chunkName || chunk === newChunk) continue; | 
					
						
							|  |  |  | 							// respect max requests when not enforced
 | 
					
						
							|  |  |  | 							const maxRequests = chunk.isOnlyInitial() | 
					
						
							|  |  |  | 								? item.cacheGroup.maxInitialRequests | 
					
						
							|  |  |  | 								: chunk.canBeInitial() | 
					
						
							|  |  |  | 									? Math.min( | 
					
						
							|  |  |  | 											item.cacheGroup.maxInitialRequests, | 
					
						
							|  |  |  | 											item.cacheGroup.maxAsyncRequests | 
					
						
							| 
									
										
										
										
											2018-03-26 22:56:10 +08:00
										 |  |  | 									  ) | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 									: item.cacheGroup.maxAsyncRequests; | 
					
						
							|  |  |  | 							if (isFinite(maxRequests) && getRequests(chunk) >= maxRequests) | 
					
						
							|  |  |  | 								continue; | 
					
						
							|  |  |  | 							if (newChunk === undefined) { | 
					
						
							|  |  |  | 								// Create the new chunk
 | 
					
						
							|  |  |  | 								newChunk = compilation.addChunk(chunkName); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 							// Add graph connections for splitted chunk
 | 
					
						
							|  |  |  | 							chunk.split(newChunk); | 
					
						
							|  |  |  | 							// Remove all selected modules from the chunk
 | 
					
						
							|  |  |  | 							for (const module of item.modules) { | 
					
						
							|  |  |  | 								chunk.removeModule(module); | 
					
						
							|  |  |  | 								module.rewriteChunkInReasons(chunk, [newChunk]); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						// If we successfully created a new chunk or reused one
 | 
					
						
							|  |  |  | 						if (newChunk) { | 
					
						
							|  |  |  | 							// Add a note to the chunk
 | 
					
						
							|  |  |  | 							newChunk.chunkReason = isReused | 
					
						
							|  |  |  | 								? "reused as split chunk" | 
					
						
							|  |  |  | 								: "split chunk"; | 
					
						
							|  |  |  | 							if (item.cacheGroup.key) { | 
					
						
							|  |  |  | 								newChunk.chunkReason += ` (cache group: ${ | 
					
						
							|  |  |  | 									item.cacheGroup.key | 
					
						
							|  |  |  | 								})`;
 | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							if (chunkName) { | 
					
						
							|  |  |  | 								newChunk.chunkReason += ` (name: ${chunkName})`; | 
					
						
							| 
									
										
										
										
											2018-02-26 10:26:06 +08:00
										 |  |  | 								// If the chosen name is already an entry point we remove the entry point
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 								const entrypoint = compilation.entrypoints.get(chunkName); | 
					
						
							|  |  |  | 								if (entrypoint) { | 
					
						
							|  |  |  | 									compilation.entrypoints.delete(chunkName); | 
					
						
							|  |  |  | 									entrypoint.remove(); | 
					
						
							|  |  |  | 									newChunk.entryModule = undefined; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-03-02 17:14:25 +08:00
										 |  |  | 							if (item.cacheGroup.filename) { | 
					
						
							|  |  |  | 								if (!newChunk.isOnlyInitial()) { | 
					
						
							|  |  |  | 									throw new Error( | 
					
						
							|  |  |  | 										"SplitChunksPlugin: You are trying to set a filename for a chunk which is (also) loaded on demand. " + | 
					
						
							|  |  |  | 											"The runtime can only handle loading of chunks which match the chunkFilename schema. " + | 
					
						
							|  |  |  | 											"Using a custom filename would fail at runtime. " + | 
					
						
							|  |  |  | 											`(cache group: ${item.cacheGroup.key})` | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								newChunk.filenameTemplate = item.cacheGroup.filename; | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 							if (!isReused) { | 
					
						
							|  |  |  | 								// Add all modules to the new chunk
 | 
					
						
							|  |  |  | 								for (const module of item.modules) { | 
					
						
							|  |  |  | 									GraphHelpers.connectChunkAndModule(newChunk, module); | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 								} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 							// remove all modules from other entries and update size
 | 
					
						
							|  |  |  | 							for (const [key, info] of chunksInfoMap) { | 
					
						
							|  |  |  | 								if (isOverlap(info.chunks, item.chunks)) { | 
					
						
							|  |  |  | 									const oldSize = info.modules.size; | 
					
						
							|  |  |  | 									for (const module of item.modules) { | 
					
						
							|  |  |  | 										info.modules.delete(module); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									if (info.modules.size === 0) { | 
					
						
							| 
									
										
										
										
											2018-02-16 16:12:01 +08:00
										 |  |  | 										chunksInfoMap.delete(key); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 										continue; | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									if (info.modules.size !== oldSize) { | 
					
						
							|  |  |  | 										info.size = getModulesSize(info.modules); | 
					
						
							|  |  |  | 										if (info.size < info.cacheGroup.minSize) | 
					
						
							|  |  |  | 											chunksInfoMap.delete(key); | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 							changed = true; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					if (changed) return true; | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-01-20 00:06:59 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |