| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const SortableSet = require("../util/SortableSet");const GraphHelpers = require("../GraphHelpers");const { isSubset } = require("../util/SetHelpers");const deterministicGrouping = require("../util/deterministicGrouping");const MinMaxSizeWarning = require("./MinMaxSizeWarning");const contextify = require("../util/identifier").contextify;const createHash = require("../util/createHash");/** @typedef {import("../Compiler")} Compiler *//** @typedef {import("../Chunk")} Chunk *//** @typedef {import("../Module")} Module *//** @typedef {import("../util/deterministicGrouping").Options<Module>} DeterministicGroupingOptionsForModule *//** @typedef {import("../util/deterministicGrouping").GroupedItems<Module>} DeterministicGroupingGroupedItemsForModule */const deterministicGroupingForModules = /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[]} */ (deterministicGrouping);const hashFilename = name => {	return createHash("md4")		.update(name)		.digest("hex")		.slice(0, 8);};const sortByIdentifier = (a, b) => {	if (a.identifier() > b.identifier()) return 1;	if (a.identifier() < b.identifier()) return -1;	return 0;};const getRequests = chunk => {	let requests = 0;	for (const chunkGroup of chunk.groupsIterable) {		requests = Math.max(requests, chunkGroup.chunks.length);	}	return requests;};const getModulesSize = modules => {	let sum = 0;	for (const m of modules) {		sum += m.size();	}	return sum;};/** * @template T * @param {Set<T>} a set * @param {Set<T>} b other set * @returns {boolean} true if at least one item of a is in b */const isOverlap = (a, b) => {	for (const item of a) {		if (b.has(item)) return true;	}	return false;};const compareEntries = (a, b) => {	// 1. by priority	const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority;	if (diffPriority) return diffPriority;	// 2. by number of chunks	const diffCount = a.chunks.size - b.chunks.size;	if (diffCount) return diffCount;	// 3. by size reduction	const aSizeReduce = a.size * (a.chunks.size - 1);	const bSizeReduce = b.size * (b.chunks.size - 1);	const diffSizeReduce = aSizeReduce - bSizeReduce;	if (diffSizeReduce) return diffSizeReduce;	// 4. by cache group index	const indexDiff = b.cacheGroupIndex - a.cacheGroupIndex;	if (indexDiff) return indexDiff;	// 5. by number of modules (to be able to compare by identifier)	const modulesA = a.modules;	const modulesB = b.modules;	const diff = modulesA.size - modulesB.size;	if (diff) return diff;	// 6. by module identifiers	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;	}};const compareNumbers = (a, b) => a - b;const INITIAL_CHUNK_FILTER = chunk => chunk.canBeInitial();const ASYNC_CHUNK_FILTER = chunk => !chunk.canBeInitial();const ALL_CHUNK_FILTER = chunk => true;module.exports = class SplitChunksPlugin {	constructor(options) {		this.options = SplitChunksPlugin.normalizeOptions(options);	}	static normalizeOptions(options = {}) {		return {			chunksFilter: SplitChunksPlugin.normalizeChunksFilter(				options.chunks || "all"			),			minSize: options.minSize || 0,			enforceSizeThreshold: options.enforceSizeThreshold || 0,			maxSize: options.maxSize || 0,			minChunks: options.minChunks || 1,			maxAsyncRequests: options.maxAsyncRequests || 1,			maxInitialRequests: options.maxInitialRequests || 1,			hidePathInfo: options.hidePathInfo || false,			filename: options.filename || undefined,			getCacheGroups: SplitChunksPlugin.normalizeCacheGroups({				cacheGroups: options.cacheGroups,				name: options.name,				automaticNameDelimiter: options.automaticNameDelimiter,				automaticNameMaxLength: options.automaticNameMaxLength			}),			automaticNameDelimiter: options.automaticNameDelimiter,			automaticNameMaxLength: options.automaticNameMaxLength || 109,			fallbackCacheGroup: SplitChunksPlugin.normalizeFallbackCacheGroup(				options.fallbackCacheGroup || {},				options			)		};	}	static normalizeName({		name,		automaticNameDelimiter,		automaticNamePrefix,		automaticNameMaxLength	}) {		if (name === true) {			/** @type {WeakMap<Chunk[], Record<string, string>>} */			const cache = new WeakMap();			const fn = (module, chunks, cacheGroup) => {				let cacheEntry = cache.get(chunks);				if (cacheEntry === undefined) {					cacheEntry = {};					cache.set(chunks, cacheEntry);				} else if (cacheGroup in cacheEntry) {					return cacheEntry[cacheGroup];				}				const names = chunks.map(c => c.name);				if (!names.every(Boolean)) {					cacheEntry[cacheGroup] = undefined;					return;				}				names.sort();				const prefix =					typeof automaticNamePrefix === "string"						? automaticNamePrefix						: cacheGroup;				const namePrefix = prefix ? prefix + automaticNameDelimiter : "";				let name = namePrefix + names.join(automaticNameDelimiter);				// 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 109 to prevent `[name].[chunkhash].[ext]` from				// generating a 256+ character string.				if (name.length > automaticNameMaxLength) {					const hashedFilename = hashFilename(name);					const sliceLength =						automaticNameMaxLength -						(automaticNameDelimiter.length + hashedFilename.length);					name =						name.slice(0, sliceLength) +						automaticNameDelimiter +						hashedFilename;				}				cacheEntry[cacheGroup] = name;				return name;			};			return fn;		}		if (typeof name === "string") {			const fn = () => {				return name;			};			return fn;		}		if (typeof name === "function") return name;	}	static normalizeChunksFilter(chunks) {		if (chunks === "initial") {			return INITIAL_CHUNK_FILTER;		}		if (chunks === "async") {			return ASYNC_CHUNK_FILTER;		}		if (chunks === "all") {			return ALL_CHUNK_FILTER;		}		if (typeof chunks === "function") return chunks;	}	static normalizeFallbackCacheGroup(		{			minSize = undefined,			maxSize = undefined,			automaticNameDelimiter = undefined		},		{			minSize: defaultMinSize = undefined,			maxSize: defaultMaxSize = undefined,			automaticNameDelimiter: defaultAutomaticNameDelimiter = undefined		}	) {		return {			minSize: typeof minSize === "number" ? minSize : defaultMinSize || 0,			maxSize: typeof maxSize === "number" ? maxSize : defaultMaxSize || 0,			automaticNameDelimiter:				automaticNameDelimiter || defaultAutomaticNameDelimiter || "~"		};	}	static normalizeCacheGroups({		cacheGroups,		name,		automaticNameDelimiter,		automaticNameMaxLength	}) {		if (typeof cacheGroups === "function") {			// TODO webpack 5 remove this			if (cacheGroups.length !== 1) {				return module => cacheGroups(module, module.getChunks());			}			return cacheGroups;		}		if (cacheGroups && typeof cacheGroups === "object") {			const fn = module => {				let results;				for (const key of Object.keys(cacheGroups)) {					let option = cacheGroups[key];					if (option === false) continue;					if (option instanceof RegExp || typeof option === "string") {						option = {							test: option						};					}					if (typeof option === "function") {						let result = option(module);						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;								if (result.chunks) {									result.chunksFilter = SplitChunksPlugin.normalizeChunksFilter(										result.chunks									);								}								results.push(result);							}						}					} else if (SplitChunksPlugin.checkTest(option.test, module)) {						if (results === undefined) results = [];						results.push({							key: key,							priority: option.priority,							getName:								SplitChunksPlugin.normalizeName({									name: option.name || name,									automaticNameDelimiter:										typeof option.automaticNameDelimiter === "string"											? option.automaticNameDelimiter											: automaticNameDelimiter,									automaticNamePrefix: option.automaticNamePrefix,									automaticNameMaxLength:										option.automaticNameMaxLength || automaticNameMaxLength								}) || (() => {}),							chunksFilter: SplitChunksPlugin.normalizeChunksFilter(								option.chunks							),							enforce: option.enforce,							minSize: option.minSize,							enforceSizeThreshold: option.enforceSizeThreshold,							maxSize: option.maxSize,							minChunks: option.minChunks,							maxAsyncRequests: option.maxAsyncRequests,							maxInitialRequests: option.maxInitialRequests,							filename: option.filename,							reuseExistingChunk: option.reuseExistingChunk						});					}				}				return results;			};			return fn;		}		const fn = () => {};		return fn;	}	static checkTest(test, module) {		if (test === undefined) return true;		if (typeof test === "function") {			if (test.length !== 1) {				return test(module, module.getChunks());			}			return test(module);		}		if (typeof test === "boolean") return test;		if (typeof test === "string") {			if (				module.nameForCondition &&				module.nameForCondition().startsWith(test)			) {				return true;			}			for (const chunk of module.chunksIterable) {				if (chunk.name && chunk.name.startsWith(test)) {					return true;				}			}			return false;		}		if (test instanceof RegExp) {			if (module.nameForCondition && test.test(module.nameForCondition())) {				return true;			}			for (const chunk of module.chunksIterable) {				if (chunk.name && test.test(chunk.name)) {					return true;				}			}			return false;		}		return false;	}	/**	 * @param {Compiler} compiler webpack compiler	 * @returns {void}	 */	apply(compiler) {		compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => {			let alreadyOptimized = false;			compilation.hooks.unseal.tap("SplitChunksPlugin", () => {				alreadyOptimized = false;			});			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++);					}					const getKey = chunks => {						return Array.from(chunks, c => indexMap.get(c))							.sort(compareNumbers)							.join();					};					/** @type {Map<string, Set<Chunk>>} */					const chunkSetsInGraph = new Map();					for (const module of compilation.modules) {						const chunksKey = getKey(module.chunksIterable);						if (!chunkSetsInGraph.has(chunksKey)) {							chunkSetsInGraph.set(chunksKey, new Set(module.chunksIterable));						}					}					// group these set of chunks by count					// to allow to check less sets via isSubset					// (only smaller sets can be subset)					/** @type {Map<number, Array<Set<Chunk>>>} */					const chunkSetsByCount = new Map();					for (const chunksSet of chunkSetsInGraph.values()) {						const count = chunksSet.size;						let array = chunkSetsByCount.get(count);						if (array === undefined) {							array = [];							chunkSetsByCount.set(count, array);						}						array.push(chunksSet);					}					// Create a list of possible combinations					const combinationsCache = new Map(); // Map<string, Set<Chunk>[]>					const getCombinations = key => {						const chunksSet = chunkSetsInGraph.get(key);						var array = [chunksSet];						if (chunksSet.size > 1) {							for (const [count, setArray] of chunkSetsByCount) {								// "equal" is not needed because they would have been merge in the first step								if (count < chunksSet.size) {									for (const set of setArray) {										if (isSubset(chunksSet, set)) {											array.push(set);										}									}								}							}						}						return array;					};					/**					 * @typedef {Object} SelectedChunksResult					 * @property {Chunk[]} chunks the list of chunks					 * @property {string} key a key of the list					 */					/**					 * @typedef {function(Chunk): boolean} ChunkFilterFunction					 */					/** @type {WeakMap<Set<Chunk>, WeakMap<ChunkFilterFunction, SelectedChunksResult>>} */					const selectedChunksCacheByChunksSet = new WeakMap();					/**					 * get list and key by applying the filter function to the list					 * It is cached for performance reasons					 * @param {Set<Chunk>} chunks list of chunks					 * @param {ChunkFilterFunction} chunkFilter filter function for chunks					 * @returns {SelectedChunksResult} list and key					 */					const getSelectedChunks = (chunks, chunkFilter) => {						let entry = selectedChunksCacheByChunksSet.get(chunks);						if (entry === undefined) {							entry = new WeakMap();							selectedChunksCacheByChunksSet.set(chunks, entry);						}						/** @type {SelectedChunksResult} */						let entry2 = entry.get(chunkFilter);						if (entry2 === undefined) {							/** @type {Chunk[]} */							const selectedChunks = [];							for (const chunk of chunks) {								if (chunkFilter(chunk)) selectedChunks.push(chunk);							}							entry2 = {								chunks: selectedChunks,								key: getKey(selectedChunks)							};							entry.set(chunkFilter, entry2);						}						return entry2;					};					/**					 * @typedef {Object} ChunksInfoItem					 * @property {SortableSet} modules					 * @property {TODO} cacheGroup					 * @property {number} cacheGroupIndex					 * @property {string} name					 * @property {number} size					 * @property {Set<Chunk>} chunks					 * @property {Set<Chunk>} reuseableChunks					 * @property {Set<string>} chunksKeys					 */					// 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					/** @type {Map<string, ChunksInfoItem>} */					const chunksInfoMap = new Map();					/**					 * @param {TODO} cacheGroup the current cache group					 * @param {number} cacheGroupIndex the index of the cache group of ordering					 * @param {Chunk[]} selectedChunks chunks selected for this module					 * @param {string} selectedChunksKey a key of selectedChunks					 * @param {Module} module the current module					 * @returns {void}					 */					const addModuleToChunksInfoMap = (						cacheGroup,						cacheGroupIndex,						selectedChunks,						selectedChunksKey,						module					) => {						// Break if minimum number of chunks is not reached						if (selectedChunks.length < cacheGroup.minChunks) return;						// 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 key =							cacheGroup.key +							(name ? ` name:${name}` : ` chunks:${selectedChunksKey}`);						// Add module to maps						let info = chunksInfoMap.get(key);						if (info === undefined) {							chunksInfoMap.set(								key,								(info = {									modules: new SortableSet(undefined, sortByIdentifier),									cacheGroup,									cacheGroupIndex,									name,									size: 0,									chunks: new Set(),									reuseableChunks: new Set(),									chunksKeys: new Set()								})							);						}						const oldSize = info.modules.size;						info.modules.add(module);						if (info.modules.size !== oldSize) {							info.size += module.size();						}						const oldChunksKeysSize = info.chunksKeys.size;						info.chunksKeys.add(selectedChunksKey);						if (oldChunksKeysSize !== info.chunksKeys.size) {							for (const chunk of selectedChunks) {								info.chunks.add(chunk);							}						}					};					// Walk through all modules					for (const module of compilation.modules) {						// Get cache group						let cacheGroups = this.options.getCacheGroups(module);						if (!Array.isArray(cacheGroups) || cacheGroups.length === 0) {							continue;						}						// Prepare some values						const chunksKey = getKey(module.chunksIterable);						let combs = combinationsCache.get(chunksKey);						if (combs === undefined) {							combs = getCombinations(chunksKey);							combinationsCache.set(chunksKey, combs);						}						let cacheGroupIndex = 0;						for (const cacheGroupSource of cacheGroups) {							const minSize =								cacheGroupSource.minSize !== undefined									? cacheGroupSource.minSize									: cacheGroupSource.enforce									? 0									: this.options.minSize;							const enforceSizeThreshold =								cacheGroupSource.enforceSizeThreshold !== undefined									? cacheGroupSource.enforceSizeThreshold									: cacheGroupSource.enforce									? 0									: this.options.enforceSizeThreshold;							const cacheGroup = {								key: cacheGroupSource.key,								priority: cacheGroupSource.priority || 0,								chunksFilter:									cacheGroupSource.chunksFilter || this.options.chunksFilter,								minSize,								minSizeForMaxSize:									cacheGroupSource.minSize !== undefined										? cacheGroupSource.minSize										: this.options.minSize,								enforceSizeThreshold,								maxSize:									cacheGroupSource.maxSize !== undefined										? cacheGroupSource.maxSize										: cacheGroupSource.enforce										? 0										: this.options.maxSize,								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,								filename:									cacheGroupSource.filename !== undefined										? cacheGroupSource.filename										: this.options.filename,								automaticNameDelimiter:									cacheGroupSource.automaticNameDelimiter !== undefined										? cacheGroupSource.automaticNameDelimiter										: this.options.automaticNameDelimiter,								reuseExistingChunk: cacheGroupSource.reuseExistingChunk,								_validateSize: minSize > 0,								_conditionalEnforce: enforceSizeThreshold > 0							};							// For all combination of chunk selection							for (const chunkCombination of combs) {								// Break if minimum number of chunks is not reached								if (chunkCombination.size < cacheGroup.minChunks) continue;								// Select chunks by configuration								const {									chunks: selectedChunks,									key: selectedChunksKey								} = getSelectedChunks(									chunkCombination,									cacheGroup.chunksFilter								);								addModuleToChunksInfoMap(									cacheGroup,									cacheGroupIndex,									selectedChunks,									selectedChunksKey,									module								);							}							cacheGroupIndex++;						}					}					// Filter items were size < minSize					for (const pair of chunksInfoMap) {						const info = pair[1];						if (							info.cacheGroup._validateSize &&							info.size < info.cacheGroup.minSize						) {							chunksInfoMap.delete(pair[0]);						}					}					/** @type {Map<Chunk, {minSize: number, maxSize: number, automaticNameDelimiter: string, keys: string[]}>} */					const maxSizeQueueMap = new Map();					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;							}						}						const item = bestEntry;						chunksInfoMap.delete(bestEntryKey);						let chunkName = item.name;						// Variable for the new chunk (lazy created)						/** @type {Chunk} */						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) {							outer: for (const chunk of item.chunks) {								if (chunk.getNumberOfModules() !== item.modules.size) continue;								if (chunk.hasEntryModule()) continue;								for (const module of item.modules) {									if (!chunk.containsModule(module)) continue outer;								}								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;							}						}						// Check if maxRequests condition can be fulfilled						const selectedChunks = Array.from(item.chunks).filter(chunk => {							// skip if we address ourself							return (								(!chunkName || chunk.name !== chunkName) && chunk !== newChunk							);						});						const enforced =							item.cacheGroup._conditionalEnforce &&							item.size >= item.cacheGroup.enforceSizeThreshold;						// Skip when no chunk selected						if (selectedChunks.length === 0) continue;						const usedChunks = new Set(selectedChunks);						// Check if maxRequests condition can be fulfilled						if (							!enforced &&							(Number.isFinite(item.cacheGroup.maxInitialRequests) ||								Number.isFinite(item.cacheGroup.maxAsyncRequests))						) {							for (const chunk of usedChunks) {								// respect max requests								const maxRequests = chunk.isOnlyInitial()									? item.cacheGroup.maxInitialRequests									: chunk.canBeInitial()									? Math.min(											item.cacheGroup.maxInitialRequests,											item.cacheGroup.maxAsyncRequests									  )									: item.cacheGroup.maxAsyncRequests;								if (									isFinite(maxRequests) &&									getRequests(chunk) >= maxRequests								) {									usedChunks.delete(chunk);								}							}						}						outer: for (const chunk of usedChunks) {							for (const module of item.modules) {								if (chunk.containsModule(module)) continue outer;							}							usedChunks.delete(chunk);						}						// Were some (invalid) chunks removed from usedChunks?						// => readd all modules to the queue, as things could have been changed						if (usedChunks.size < selectedChunks.length) {							if (usedChunks.size >= item.cacheGroup.minChunks) {								const chunksArr = Array.from(usedChunks);								for (const module of item.modules) {									addModuleToChunksInfoMap(										item.cacheGroup,										item.cacheGroupIndex,										chunksArr,										getKey(usedChunks),										module									);								}							}							continue;						}						// Create the new chunk if not reusing one						if (!isReused) {							newChunk = compilation.addChunk(chunkName);						}						// Walk through all chunks						for (const chunk of usedChunks) {							// Add graph connections for splitted chunk							chunk.split(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})`;							// If the chosen name is already an entry point we remove the entry point							const entrypoint = compilation.entrypoints.get(chunkName);							if (entrypoint) {								compilation.entrypoints.delete(chunkName);								entrypoint.remove();								newChunk.entryModule = undefined;							}						}						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;						}						if (!isReused) {							// Add all modules to the new chunk							for (const module of item.modules) {								if (typeof module.chunkCondition === "function") {									if (!module.chunkCondition(newChunk)) continue;								}								// Add module to new chunk								GraphHelpers.connectChunkAndModule(newChunk, module);								// Remove module from used chunks								for (const chunk of usedChunks) {									chunk.removeModule(module);									module.rewriteChunkInReasons(chunk, [newChunk]);								}							}						} else {							// Remove all modules from used chunks							for (const module of item.modules) {								for (const chunk of usedChunks) {									chunk.removeModule(module);									module.rewriteChunkInReasons(chunk, [newChunk]);								}							}						}						if (item.cacheGroup.maxSize > 0) {							const oldMaxSizeSettings = maxSizeQueueMap.get(newChunk);							maxSizeQueueMap.set(newChunk, {								minSize: Math.max(									oldMaxSizeSettings ? oldMaxSizeSettings.minSize : 0,									item.cacheGroup.minSizeForMaxSize								),								maxSize: Math.min(									oldMaxSizeSettings ? oldMaxSizeSettings.maxSize : Infinity,									item.cacheGroup.maxSize								),								automaticNameDelimiter: item.cacheGroup.automaticNameDelimiter,								keys: oldMaxSizeSettings									? oldMaxSizeSettings.keys.concat(item.cacheGroup.key)									: [item.cacheGroup.key]							});						}						// remove all modules from other entries and update size						for (const [key, info] of chunksInfoMap) {							if (isOverlap(info.chunks, usedChunks)) {								// update modules and total size								// may remove it from the map when < minSize								const oldSize = info.modules.size;								for (const module of item.modules) {									info.modules.delete(module);								}								if (info.modules.size !== oldSize) {									if (info.modules.size === 0) {										chunksInfoMap.delete(key);										continue;									}									info.size = getModulesSize(info.modules);									if (										info.cacheGroup._validateSize &&										info.size < info.cacheGroup.minSize									) {										chunksInfoMap.delete(key);									}									if (info.modules.size === 0) {										chunksInfoMap.delete(key);									}								}							}						}					}					const incorrectMinMaxSizeSet = new Set();					// Make sure that maxSize is fulfilled					for (const chunk of compilation.chunks.slice()) {						const { minSize, maxSize, automaticNameDelimiter, keys } =							maxSizeQueueMap.get(chunk) || this.options.fallbackCacheGroup;						if (!maxSize) continue;						if (minSize > maxSize) {							const warningKey = `${keys && keys.join()} ${minSize} ${maxSize}`;							if (!incorrectMinMaxSizeSet.has(warningKey)) {								incorrectMinMaxSizeSet.add(warningKey);								compilation.warnings.push(									new MinMaxSizeWarning(keys, minSize, maxSize)								);							}						}						const results = deterministicGroupingForModules({							maxSize: Math.max(minSize, maxSize),							minSize,							items: chunk.modulesIterable,							getKey(module) {								const ident = contextify(									compilation.options.context,									module.identifier()								);								const name = module.nameForCondition									? contextify(											compilation.options.context,											module.nameForCondition()									  )									: ident.replace(/^.*!|\?[^?!]*$/g, "");								const fullKey =									name + automaticNameDelimiter + hashFilename(ident);								return fullKey.replace(/[\\/?]/g, "_");							},							getSize(module) {								return module.size();							}						});						results.sort((a, b) => {							if (a.key < b.key) return -1;							if (a.key > b.key) return 1;							return 0;						});						for (let i = 0; i < results.length; i++) {							const group = results[i];							const key = this.options.hidePathInfo								? hashFilename(group.key)								: group.key;							let name = chunk.name								? chunk.name + automaticNameDelimiter + key								: null;							if (name && name.length > 100) {								name =									name.slice(0, 100) +									automaticNameDelimiter +									hashFilename(name);							}							let newPart;							if (i !== results.length - 1) {								newPart = compilation.addChunk(name);								chunk.split(newPart);								newPart.chunkReason = chunk.chunkReason;								// Add all modules to the new chunk								for (const module of group.items) {									if (typeof module.chunkCondition === "function") {										if (!module.chunkCondition(newPart)) continue;									}									// Add module to new chunk									GraphHelpers.connectChunkAndModule(newPart, module);									// Remove module from used chunks									chunk.removeModule(module);									module.rewriteChunkInReasons(chunk, [newPart]);								}							} else {								// change the chunk to be a part								newPart = chunk;								chunk.name = name;							}						}					}				}			);		});	}};
 |