From d5f14083061f2ce6a42313188443a42e30796320 Mon Sep 17 00:00:00 2001 From: littleblack111 Date: Tue, 31 Oct 2023 15:37:35 +0800 Subject: added make browserlist(autoprefixer) work fixed --- .../webpack/lib/optimize/SplitChunksPlugin.js | 1763 ++++++++++++++++++++ 1 file changed, 1763 insertions(+) create mode 100644 node_modules/webpack/lib/optimize/SplitChunksPlugin.js (limited to 'node_modules/webpack/lib/optimize/SplitChunksPlugin.js') diff --git a/node_modules/webpack/lib/optimize/SplitChunksPlugin.js b/node_modules/webpack/lib/optimize/SplitChunksPlugin.js new file mode 100644 index 00000000..2e79ab6a --- /dev/null +++ b/node_modules/webpack/lib/optimize/SplitChunksPlugin.js @@ -0,0 +1,1763 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const Chunk = require("../Chunk"); +const { STAGE_ADVANCED } = require("../OptimizationStages"); +const WebpackError = require("../WebpackError"); +const { requestToId } = require("../ids/IdHelpers"); +const { isSubset } = require("../util/SetHelpers"); +const SortableSet = require("../util/SortableSet"); +const { + compareModulesByIdentifier, + compareIterables +} = require("../util/comparators"); +const createHash = require("../util/createHash"); +const deterministicGrouping = require("../util/deterministicGrouping"); +const { makePathsRelative } = require("../util/identifier"); +const memoize = require("../util/memoize"); +const MinMaxSizeWarning = require("./MinMaxSizeWarning"); + +/** @typedef {import("../../declarations/WebpackOptions").OptimizationSplitChunksCacheGroup} OptimizationSplitChunksCacheGroup */ +/** @typedef {import("../../declarations/WebpackOptions").OptimizationSplitChunksGetCacheGroups} OptimizationSplitChunksGetCacheGroups */ +/** @typedef {import("../../declarations/WebpackOptions").OptimizationSplitChunksOptions} OptimizationSplitChunksOptions */ +/** @typedef {import("../../declarations/WebpackOptions").OptimizationSplitChunksSizes} OptimizationSplitChunksSizes */ +/** @typedef {import("../../declarations/WebpackOptions").Output} OutputOptions */ +/** @typedef {import("../ChunkGraph")} ChunkGraph */ +/** @typedef {import("../ChunkGroup")} ChunkGroup */ +/** @typedef {import("../Compilation").AssetInfo} AssetInfo */ +/** @typedef {import("../Compilation").PathData} PathData */ +/** @typedef {import("../Compiler")} Compiler */ +/** @typedef {import("../Module")} Module */ +/** @typedef {import("../ModuleGraph")} ModuleGraph */ +/** @typedef {import("../util/deterministicGrouping").GroupedItems} DeterministicGroupingGroupedItemsForModule */ +/** @typedef {import("../util/deterministicGrouping").Options} DeterministicGroupingOptionsForModule */ + +/** @typedef {Record} SplitChunksSizes */ + +/** + * @callback ChunkFilterFunction + * @param {Chunk} chunk + * @returns {boolean | undefined} + */ + +/** + * @callback CombineSizeFunction + * @param {number} a + * @param {number} b + * @returns {number} + */ + +/** + * @typedef {Object} CacheGroupSource + * @property {string=} key + * @property {number=} priority + * @property {GetName=} getName + * @property {ChunkFilterFunction=} chunksFilter + * @property {boolean=} enforce + * @property {SplitChunksSizes} minSize + * @property {SplitChunksSizes} minSizeReduction + * @property {SplitChunksSizes} minRemainingSize + * @property {SplitChunksSizes} enforceSizeThreshold + * @property {SplitChunksSizes} maxAsyncSize + * @property {SplitChunksSizes} maxInitialSize + * @property {number=} minChunks + * @property {number=} maxAsyncRequests + * @property {number=} maxInitialRequests + * @property {(string | function(PathData, AssetInfo=): string)=} filename + * @property {string=} idHint + * @property {string=} automaticNameDelimiter + * @property {boolean=} reuseExistingChunk + * @property {boolean=} usedExports + */ + +/** + * @typedef {Object} CacheGroup + * @property {string} key + * @property {number=} priority + * @property {GetName=} getName + * @property {ChunkFilterFunction=} chunksFilter + * @property {SplitChunksSizes} minSize + * @property {SplitChunksSizes} minSizeReduction + * @property {SplitChunksSizes} minRemainingSize + * @property {SplitChunksSizes} enforceSizeThreshold + * @property {SplitChunksSizes} maxAsyncSize + * @property {SplitChunksSizes} maxInitialSize + * @property {number=} minChunks + * @property {number=} maxAsyncRequests + * @property {number=} maxInitialRequests + * @property {(string | function(PathData, AssetInfo=): string)=} filename + * @property {string=} idHint + * @property {string} automaticNameDelimiter + * @property {boolean} reuseExistingChunk + * @property {boolean} usedExports + * @property {boolean} _validateSize + * @property {boolean} _validateRemainingSize + * @property {SplitChunksSizes} _minSizeForMaxSize + * @property {boolean} _conditionalEnforce + */ + +/** + * @typedef {Object} FallbackCacheGroup + * @property {ChunkFilterFunction} chunksFilter + * @property {SplitChunksSizes} minSize + * @property {SplitChunksSizes} maxAsyncSize + * @property {SplitChunksSizes} maxInitialSize + * @property {string} automaticNameDelimiter + */ + +/** + * @typedef {Object} CacheGroupsContext + * @property {ModuleGraph} moduleGraph + * @property {ChunkGraph} chunkGraph + */ + +/** + * @callback GetCacheGroups + * @param {Module} module + * @param {CacheGroupsContext} context + * @returns {CacheGroupSource[]} + */ + +/** + * @callback GetName + * @param {Module=} module + * @param {Chunk[]=} chunks + * @param {string=} key + * @returns {string=} + */ + +/** + * @typedef {Object} SplitChunksOptions + * @property {ChunkFilterFunction} chunksFilter + * @property {string[]} defaultSizeTypes + * @property {SplitChunksSizes} minSize + * @property {SplitChunksSizes} minSizeReduction + * @property {SplitChunksSizes} minRemainingSize + * @property {SplitChunksSizes} enforceSizeThreshold + * @property {SplitChunksSizes} maxInitialSize + * @property {SplitChunksSizes} maxAsyncSize + * @property {number} minChunks + * @property {number} maxAsyncRequests + * @property {number} maxInitialRequests + * @property {boolean} hidePathInfo + * @property {string | function(PathData, AssetInfo=): string} filename + * @property {string} automaticNameDelimiter + * @property {GetCacheGroups} getCacheGroups + * @property {GetName} getName + * @property {boolean} usedExports + * @property {FallbackCacheGroup} fallbackCacheGroup + */ + +/** + * @typedef {Object} ChunksInfoItem + * @property {SortableSet} modules + * @property {CacheGroup} cacheGroup + * @property {number} cacheGroupIndex + * @property {string} name + * @property {Record} sizes + * @property {Set} chunks + * @property {Set} reuseableChunks + * @property {Set} chunksKeys + */ + +const defaultGetName = /** @type {GetName} */ (() => {}); + +const deterministicGroupingForModules = + /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[]} */ ( + deterministicGrouping + ); + +/** @type {WeakMap} */ +const getKeyCache = new WeakMap(); + +/** + * @param {string} name a filename to hash + * @param {OutputOptions} outputOptions hash function used + * @returns {string} hashed filename + */ +const hashFilename = (name, outputOptions) => { + const digest = /** @type {string} */ ( + createHash(outputOptions.hashFunction) + .update(name) + .digest(outputOptions.hashDigest) + ); + return digest.slice(0, 8); +}; + +/** + * @param {Chunk} chunk the chunk + * @returns {number} the number of requests + */ +const getRequests = chunk => { + let requests = 0; + for (const chunkGroup of chunk.groupsIterable) { + requests = Math.max(requests, chunkGroup.chunks.length); + } + return requests; +}; + +const mapObject = (obj, fn) => { + const newObj = Object.create(null); + for (const key of Object.keys(obj)) { + newObj[key] = fn(obj[key], key); + } + return newObj; +}; + +/** + * @template T + * @param {Set} a set + * @param {Set} 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 compareModuleIterables = compareIterables(compareModulesByIdentifier); + +/** + * @param {ChunksInfoItem} a item + * @param {ChunksInfoItem} b item + * @returns {number} compare result + */ +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 = totalSize(a.sizes) * (a.chunks.size - 1); + const bSizeReduce = totalSize(b.sizes) * (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(); + return compareModuleIterables(modulesA, modulesB); +}; + +/** + * @param {Chunk} chunk the chunk + * @returns {boolean} true, if the chunk is an entry chunk + */ +const INITIAL_CHUNK_FILTER = chunk => chunk.canBeInitial(); +/** + * @param {Chunk} chunk the chunk + * @returns {boolean} true, if the chunk is an async chunk + */ +const ASYNC_CHUNK_FILTER = chunk => !chunk.canBeInitial(); +/** + * @param {Chunk} chunk the chunk + * @returns {boolean} always true + */ +const ALL_CHUNK_FILTER = chunk => true; + +/** + * @param {OptimizationSplitChunksSizes | undefined} value the sizes + * @param {string[]} defaultSizeTypes the default size types + * @returns {SplitChunksSizes} normalized representation + */ +const normalizeSizes = (value, defaultSizeTypes) => { + if (typeof value === "number") { + /** @type {Record} */ + const o = {}; + for (const sizeType of defaultSizeTypes) o[sizeType] = value; + return o; + } else if (typeof value === "object" && value !== null) { + return { ...value }; + } else { + return {}; + } +}; + +/** + * @param {...SplitChunksSizes} sizes the sizes + * @returns {SplitChunksSizes} the merged sizes + */ +const mergeSizes = (...sizes) => { + /** @type {SplitChunksSizes} */ + let merged = {}; + for (let i = sizes.length - 1; i >= 0; i--) { + merged = Object.assign(merged, sizes[i]); + } + return merged; +}; + +/** + * @param {SplitChunksSizes} sizes the sizes + * @returns {boolean} true, if there are sizes > 0 + */ +const hasNonZeroSizes = sizes => { + for (const key of Object.keys(sizes)) { + if (sizes[key] > 0) return true; + } + return false; +}; + +/** + * @param {SplitChunksSizes} a first sizes + * @param {SplitChunksSizes} b second sizes + * @param {CombineSizeFunction} combine a function to combine sizes + * @returns {SplitChunksSizes} the combine sizes + */ +const combineSizes = (a, b, combine) => { + const aKeys = new Set(Object.keys(a)); + const bKeys = new Set(Object.keys(b)); + /** @type {SplitChunksSizes} */ + const result = {}; + for (const key of aKeys) { + if (bKeys.has(key)) { + result[key] = combine(a[key], b[key]); + } else { + result[key] = a[key]; + } + } + for (const key of bKeys) { + if (!aKeys.has(key)) { + result[key] = b[key]; + } + } + return result; +}; + +/** + * @param {SplitChunksSizes} sizes the sizes + * @param {SplitChunksSizes} minSize the min sizes + * @returns {boolean} true if there are sizes and all existing sizes are at least `minSize` + */ +const checkMinSize = (sizes, minSize) => { + for (const key of Object.keys(minSize)) { + const size = sizes[key]; + if (size === undefined || size === 0) continue; + if (size < minSize[key]) return false; + } + return true; +}; + +/** + * @param {SplitChunksSizes} sizes the sizes + * @param {SplitChunksSizes} minSizeReduction the min sizes + * @param {number} chunkCount number of chunks + * @returns {boolean} true if there are sizes and all existing sizes are at least `minSizeReduction` + */ +const checkMinSizeReduction = (sizes, minSizeReduction, chunkCount) => { + for (const key of Object.keys(minSizeReduction)) { + const size = sizes[key]; + if (size === undefined || size === 0) continue; + if (size * chunkCount < minSizeReduction[key]) return false; + } + return true; +}; + +/** + * @param {SplitChunksSizes} sizes the sizes + * @param {SplitChunksSizes} minSize the min sizes + * @returns {undefined | string[]} list of size types that are below min size + */ +const getViolatingMinSizes = (sizes, minSize) => { + let list; + for (const key of Object.keys(minSize)) { + const size = sizes[key]; + if (size === undefined || size === 0) continue; + if (size < minSize[key]) { + if (list === undefined) list = [key]; + else list.push(key); + } + } + return list; +}; + +/** + * @param {SplitChunksSizes} sizes the sizes + * @returns {number} the total size + */ +const totalSize = sizes => { + let size = 0; + for (const key of Object.keys(sizes)) { + size += sizes[key]; + } + return size; +}; + +/** + * @param {false|string|Function|undefined} name the chunk name + * @returns {GetName | undefined} a function to get the name of the chunk + */ +const normalizeName = name => { + if (typeof name === "string") { + return () => name; + } + if (typeof name === "function") { + return /** @type {GetName} */ (name); + } +}; + +/** + * @param {OptimizationSplitChunksCacheGroup["chunks"]} chunks the chunk filter option + * @returns {ChunkFilterFunction} the chunk filter function + */ +const normalizeChunksFilter = chunks => { + if (chunks === "initial") { + return INITIAL_CHUNK_FILTER; + } + if (chunks === "async") { + return ASYNC_CHUNK_FILTER; + } + if (chunks === "all") { + return ALL_CHUNK_FILTER; + } + if (chunks instanceof RegExp) { + return chunk => { + return chunk.name ? chunks.test(chunk.name) : false; + }; + } + if (typeof chunks === "function") { + return chunks; + } +}; + +/** + * @param {GetCacheGroups | Record} cacheGroups the cache group options + * @param {string[]} defaultSizeTypes the default size types + * @returns {GetCacheGroups} a function to get the cache groups + */ +const normalizeCacheGroups = (cacheGroups, defaultSizeTypes) => { + if (typeof cacheGroups === "function") { + return cacheGroups; + } + if (typeof cacheGroups === "object" && cacheGroups !== null) { + /** @type {(function(Module, CacheGroupsContext, CacheGroupSource[]): void)[]} */ + const handlers = []; + for (const key of Object.keys(cacheGroups)) { + const option = cacheGroups[key]; + if (option === false) { + continue; + } + if (typeof option === "string" || option instanceof RegExp) { + const source = createCacheGroupSource({}, key, defaultSizeTypes); + handlers.push((module, context, results) => { + if (checkTest(option, module, context)) { + results.push(source); + } + }); + } else if (typeof option === "function") { + const cache = new WeakMap(); + handlers.push((module, context, results) => { + const result = option(module); + if (result) { + const groups = Array.isArray(result) ? result : [result]; + for (const group of groups) { + const cachedSource = cache.get(group); + if (cachedSource !== undefined) { + results.push(cachedSource); + } else { + const source = createCacheGroupSource( + group, + key, + defaultSizeTypes + ); + cache.set(group, source); + results.push(source); + } + } + } + }); + } else { + const source = createCacheGroupSource(option, key, defaultSizeTypes); + handlers.push((module, context, results) => { + if ( + checkTest(option.test, module, context) && + checkModuleType(option.type, module) && + checkModuleLayer(option.layer, module) + ) { + results.push(source); + } + }); + } + } + /** + * @param {Module} module the current module + * @param {CacheGroupsContext} context the current context + * @returns {CacheGroupSource[]} the matching cache groups + */ + const fn = (module, context) => { + /** @type {CacheGroupSource[]} */ + let results = []; + for (const fn of handlers) { + fn(module, context, results); + } + return results; + }; + return fn; + } + return () => null; +}; + +/** + * @param {undefined|boolean|string|RegExp|Function} test test option + * @param {Module} module the module + * @param {CacheGroupsContext} context context object + * @returns {boolean} true, if the module should be selected + */ +const checkTest = (test, module, context) => { + if (test === undefined) return true; + if (typeof test === "function") { + return test(module, context); + } + if (typeof test === "boolean") return test; + if (typeof test === "string") { + const name = module.nameForCondition(); + return name && name.startsWith(test); + } + if (test instanceof RegExp) { + const name = module.nameForCondition(); + return name && test.test(name); + } + return false; +}; + +/** + * @param {undefined|string|RegExp|Function} test type option + * @param {Module} module the module + * @returns {boolean} true, if the module should be selected + */ +const checkModuleType = (test, module) => { + if (test === undefined) return true; + if (typeof test === "function") { + return test(module.type); + } + if (typeof test === "string") { + const type = module.type; + return test === type; + } + if (test instanceof RegExp) { + const type = module.type; + return test.test(type); + } + return false; +}; + +/** + * @param {undefined|string|RegExp|Function} test type option + * @param {Module} module the module + * @returns {boolean} true, if the module should be selected + */ +const checkModuleLayer = (test, module) => { + if (test === undefined) return true; + if (typeof test === "function") { + return test(module.layer); + } + if (typeof test === "string") { + const layer = module.layer; + return test === "" ? !layer : layer && layer.startsWith(test); + } + if (test instanceof RegExp) { + const layer = module.layer; + return test.test(layer); + } + return false; +}; + +/** + * @param {OptimizationSplitChunksCacheGroup} options the group options + * @param {string} key key of cache group + * @param {string[]} defaultSizeTypes the default size types + * @returns {CacheGroupSource} the normalized cached group + */ +const createCacheGroupSource = (options, key, defaultSizeTypes) => { + const minSize = normalizeSizes(options.minSize, defaultSizeTypes); + const minSizeReduction = normalizeSizes( + options.minSizeReduction, + defaultSizeTypes + ); + const maxSize = normalizeSizes(options.maxSize, defaultSizeTypes); + return { + key, + priority: options.priority, + getName: normalizeName(options.name), + chunksFilter: normalizeChunksFilter(options.chunks), + enforce: options.enforce, + minSize, + minSizeReduction, + minRemainingSize: mergeSizes( + normalizeSizes(options.minRemainingSize, defaultSizeTypes), + minSize + ), + enforceSizeThreshold: normalizeSizes( + options.enforceSizeThreshold, + defaultSizeTypes + ), + maxAsyncSize: mergeSizes( + normalizeSizes(options.maxAsyncSize, defaultSizeTypes), + maxSize + ), + maxInitialSize: mergeSizes( + normalizeSizes(options.maxInitialSize, defaultSizeTypes), + maxSize + ), + minChunks: options.minChunks, + maxAsyncRequests: options.maxAsyncRequests, + maxInitialRequests: options.maxInitialRequests, + filename: options.filename, + idHint: options.idHint, + automaticNameDelimiter: options.automaticNameDelimiter, + reuseExistingChunk: options.reuseExistingChunk, + usedExports: options.usedExports + }; +}; + +module.exports = class SplitChunksPlugin { + /** + * @param {OptimizationSplitChunksOptions=} options plugin options + */ + constructor(options = {}) { + const defaultSizeTypes = options.defaultSizeTypes || [ + "javascript", + "unknown" + ]; + const fallbackCacheGroup = options.fallbackCacheGroup || {}; + const minSize = normalizeSizes(options.minSize, defaultSizeTypes); + const minSizeReduction = normalizeSizes( + options.minSizeReduction, + defaultSizeTypes + ); + const maxSize = normalizeSizes(options.maxSize, defaultSizeTypes); + + /** @type {SplitChunksOptions} */ + this.options = { + chunksFilter: normalizeChunksFilter(options.chunks || "all"), + defaultSizeTypes, + minSize, + minSizeReduction, + minRemainingSize: mergeSizes( + normalizeSizes(options.minRemainingSize, defaultSizeTypes), + minSize + ), + enforceSizeThreshold: normalizeSizes( + options.enforceSizeThreshold, + defaultSizeTypes + ), + maxAsyncSize: mergeSizes( + normalizeSizes(options.maxAsyncSize, defaultSizeTypes), + maxSize + ), + maxInitialSize: mergeSizes( + normalizeSizes(options.maxInitialSize, defaultSizeTypes), + maxSize + ), + minChunks: options.minChunks || 1, + maxAsyncRequests: options.maxAsyncRequests || 1, + maxInitialRequests: options.maxInitialRequests || 1, + hidePathInfo: options.hidePathInfo || false, + filename: options.filename || undefined, + getCacheGroups: normalizeCacheGroups( + options.cacheGroups, + defaultSizeTypes + ), + getName: options.name ? normalizeName(options.name) : defaultGetName, + automaticNameDelimiter: options.automaticNameDelimiter, + usedExports: options.usedExports, + fallbackCacheGroup: { + chunksFilter: normalizeChunksFilter( + fallbackCacheGroup.chunks || options.chunks || "all" + ), + minSize: mergeSizes( + normalizeSizes(fallbackCacheGroup.minSize, defaultSizeTypes), + minSize + ), + maxAsyncSize: mergeSizes( + normalizeSizes(fallbackCacheGroup.maxAsyncSize, defaultSizeTypes), + normalizeSizes(fallbackCacheGroup.maxSize, defaultSizeTypes), + normalizeSizes(options.maxAsyncSize, defaultSizeTypes), + normalizeSizes(options.maxSize, defaultSizeTypes) + ), + maxInitialSize: mergeSizes( + normalizeSizes(fallbackCacheGroup.maxInitialSize, defaultSizeTypes), + normalizeSizes(fallbackCacheGroup.maxSize, defaultSizeTypes), + normalizeSizes(options.maxInitialSize, defaultSizeTypes), + normalizeSizes(options.maxSize, defaultSizeTypes) + ), + automaticNameDelimiter: + fallbackCacheGroup.automaticNameDelimiter || + options.automaticNameDelimiter || + "~" + } + }; + + /** @type {WeakMap} */ + this._cacheGroupCache = new WeakMap(); + } + + /** + * @param {CacheGroupSource} cacheGroupSource source + * @returns {CacheGroup} the cache group (cached) + */ + _getCacheGroup(cacheGroupSource) { + const cacheEntry = this._cacheGroupCache.get(cacheGroupSource); + if (cacheEntry !== undefined) return cacheEntry; + const minSize = mergeSizes( + cacheGroupSource.minSize, + cacheGroupSource.enforce ? undefined : this.options.minSize + ); + const minSizeReduction = mergeSizes( + cacheGroupSource.minSizeReduction, + cacheGroupSource.enforce ? undefined : this.options.minSizeReduction + ); + const minRemainingSize = mergeSizes( + cacheGroupSource.minRemainingSize, + cacheGroupSource.enforce ? undefined : this.options.minRemainingSize + ); + const enforceSizeThreshold = mergeSizes( + cacheGroupSource.enforceSizeThreshold, + cacheGroupSource.enforce ? undefined : this.options.enforceSizeThreshold + ); + const cacheGroup = { + key: cacheGroupSource.key, + priority: cacheGroupSource.priority || 0, + chunksFilter: cacheGroupSource.chunksFilter || this.options.chunksFilter, + minSize, + minSizeReduction, + minRemainingSize, + enforceSizeThreshold, + maxAsyncSize: mergeSizes( + cacheGroupSource.maxAsyncSize, + cacheGroupSource.enforce ? undefined : this.options.maxAsyncSize + ), + maxInitialSize: mergeSizes( + cacheGroupSource.maxInitialSize, + cacheGroupSource.enforce ? undefined : this.options.maxInitialSize + ), + 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, + usedExports: + cacheGroupSource.usedExports !== undefined + ? cacheGroupSource.usedExports + : this.options.usedExports, + filename: + cacheGroupSource.filename !== undefined + ? cacheGroupSource.filename + : this.options.filename, + automaticNameDelimiter: + cacheGroupSource.automaticNameDelimiter !== undefined + ? cacheGroupSource.automaticNameDelimiter + : this.options.automaticNameDelimiter, + idHint: + cacheGroupSource.idHint !== undefined + ? cacheGroupSource.idHint + : cacheGroupSource.key, + reuseExistingChunk: cacheGroupSource.reuseExistingChunk || false, + _validateSize: hasNonZeroSizes(minSize), + _validateRemainingSize: hasNonZeroSizes(minRemainingSize), + _minSizeForMaxSize: mergeSizes( + cacheGroupSource.minSize, + this.options.minSize + ), + _conditionalEnforce: hasNonZeroSizes(enforceSizeThreshold) + }; + this._cacheGroupCache.set(cacheGroupSource, cacheGroup); + return cacheGroup; + } + + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + const cachedMakePathsRelative = makePathsRelative.bindContextCache( + compiler.context, + compiler.root + ); + compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => { + const logger = compilation.getLogger("webpack.SplitChunksPlugin"); + let alreadyOptimized = false; + compilation.hooks.unseal.tap("SplitChunksPlugin", () => { + alreadyOptimized = false; + }); + compilation.hooks.optimizeChunks.tap( + { + name: "SplitChunksPlugin", + stage: STAGE_ADVANCED + }, + chunks => { + if (alreadyOptimized) return; + alreadyOptimized = true; + logger.time("prepare"); + const chunkGraph = compilation.chunkGraph; + const moduleGraph = compilation.moduleGraph; + // Give each selected chunk an index (to create strings from chunks) + /** @type {Map} */ + const chunkIndexMap = new Map(); + const ZERO = BigInt("0"); + const ONE = BigInt("1"); + const START = ONE << BigInt("31"); + let index = START; + for (const chunk of chunks) { + chunkIndexMap.set( + chunk, + index | BigInt((Math.random() * 0x7fffffff) | 0) + ); + index = index << ONE; + } + /** + * @param {Iterable} chunks list of chunks + * @returns {bigint | Chunk} key of the chunks + */ + const getKey = chunks => { + const iterator = chunks[Symbol.iterator](); + let result = iterator.next(); + if (result.done) return ZERO; + const first = result.value; + result = iterator.next(); + if (result.done) return first; + let key = + chunkIndexMap.get(first) | chunkIndexMap.get(result.value); + while (!(result = iterator.next()).done) { + const raw = chunkIndexMap.get(result.value); + key = key ^ raw; + } + return key; + }; + /** + * @param {bigint | Chunk} key key of the chunks + * @returns {string} stringified key + */ + const keyToString = key => { + if (typeof key === "bigint") return key.toString(16); + return chunkIndexMap.get(key).toString(16); + }; + + const getChunkSetsInGraph = memoize(() => { + /** @type {Map>} */ + const chunkSetsInGraph = new Map(); + /** @type {Set} */ + const singleChunkSets = new Set(); + for (const module of compilation.modules) { + const chunks = chunkGraph.getModuleChunksIterable(module); + const chunksKey = getKey(chunks); + if (typeof chunksKey === "bigint") { + if (!chunkSetsInGraph.has(chunksKey)) { + chunkSetsInGraph.set(chunksKey, new Set(chunks)); + } + } else { + singleChunkSets.add(chunksKey); + } + } + return { chunkSetsInGraph, singleChunkSets }; + }); + + /** + * @param {Module} module the module + * @returns {Iterable} groups of chunks with equal exports + */ + const groupChunksByExports = module => { + const exportsInfo = moduleGraph.getExportsInfo(module); + const groupedByUsedExports = new Map(); + for (const chunk of chunkGraph.getModuleChunksIterable(module)) { + const key = exportsInfo.getUsageKey(chunk.runtime); + const list = groupedByUsedExports.get(key); + if (list !== undefined) { + list.push(chunk); + } else { + groupedByUsedExports.set(key, [chunk]); + } + } + return groupedByUsedExports.values(); + }; + + /** @type {Map>} */ + const groupedByExportsMap = new Map(); + + const getExportsChunkSetsInGraph = memoize(() => { + /** @type {Map>} */ + const chunkSetsInGraph = new Map(); + /** @type {Set} */ + const singleChunkSets = new Set(); + for (const module of compilation.modules) { + const groupedChunks = Array.from(groupChunksByExports(module)); + groupedByExportsMap.set(module, groupedChunks); + for (const chunks of groupedChunks) { + if (chunks.length === 1) { + singleChunkSets.add(chunks[0]); + } else { + const chunksKey = /** @type {bigint} */ (getKey(chunks)); + if (!chunkSetsInGraph.has(chunksKey)) { + chunkSetsInGraph.set(chunksKey, new Set(chunks)); + } + } + } + } + return { chunkSetsInGraph, singleChunkSets }; + }); + + // group these set of chunks by count + // to allow to check less sets via isSubset + // (only smaller sets can be subset) + /** + * @param {IterableIterator>} chunkSets set of sets of chunks + * @returns {Map>>} map of sets of chunks by count + */ + const groupChunkSetsByCount = chunkSets => { + /** @type {Map>>} */ + const chunkSetsByCount = new Map(); + for (const chunksSet of chunkSets) { + const count = chunksSet.size; + let array = chunkSetsByCount.get(count); + if (array === undefined) { + array = []; + chunkSetsByCount.set(count, array); + } + array.push(chunksSet); + } + return chunkSetsByCount; + }; + const getChunkSetsByCount = memoize(() => + groupChunkSetsByCount( + getChunkSetsInGraph().chunkSetsInGraph.values() + ) + ); + const getExportsChunkSetsByCount = memoize(() => + groupChunkSetsByCount( + getExportsChunkSetsInGraph().chunkSetsInGraph.values() + ) + ); + + // Create a list of possible combinations + const createGetCombinations = ( + chunkSets, + singleChunkSets, + chunkSetsByCount + ) => { + /** @type {Map | Chunk)[]>} */ + const combinationsCache = new Map(); + + return key => { + const cacheEntry = combinationsCache.get(key); + if (cacheEntry !== undefined) return cacheEntry; + if (key instanceof Chunk) { + const result = [key]; + combinationsCache.set(key, result); + return result; + } + const chunksSet = chunkSets.get(key); + /** @type {(Set | Chunk)[]} */ + const array = [chunksSet]; + 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); + } + } + } + } + for (const chunk of singleChunkSets) { + if (chunksSet.has(chunk)) { + array.push(chunk); + } + } + combinationsCache.set(key, array); + return array; + }; + }; + + const getCombinationsFactory = memoize(() => { + const { chunkSetsInGraph, singleChunkSets } = getChunkSetsInGraph(); + return createGetCombinations( + chunkSetsInGraph, + singleChunkSets, + getChunkSetsByCount() + ); + }); + const getCombinations = key => getCombinationsFactory()(key); + + const getExportsCombinationsFactory = memoize(() => { + const { chunkSetsInGraph, singleChunkSets } = + getExportsChunkSetsInGraph(); + return createGetCombinations( + chunkSetsInGraph, + singleChunkSets, + getExportsChunkSetsByCount() + ); + }); + const getExportsCombinations = key => + getExportsCombinationsFactory()(key); + + /** + * @typedef {Object} SelectedChunksResult + * @property {Chunk[]} chunks the list of chunks + * @property {bigint | Chunk} key a key of the list + */ + + /** @type {WeakMap | Chunk, WeakMap>} */ + 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); + } + let entry2 = + /** @type {SelectedChunksResult} */ + (entry.get(chunkFilter)); + if (entry2 === undefined) { + /** @type {Chunk[]} */ + const selectedChunks = []; + if (chunks instanceof Chunk) { + if (chunkFilter(chunks)) selectedChunks.push(chunks); + } else { + for (const chunk of chunks) { + if (chunkFilter(chunk)) selectedChunks.push(chunk); + } + } + entry2 = { + chunks: selectedChunks, + key: getKey(selectedChunks) + }; + entry.set(chunkFilter, entry2); + } + return entry2; + }; + + /** @type {Map} */ + const alreadyValidatedParents = new Map(); + /** @type {Set} */ + const alreadyReportedErrors = new Set(); + + // 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} */ + const chunksInfoMap = new Map(); + + /** + * @param {CacheGroup} 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 {bigint | Chunk} 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 = + /** @type {string} */ + (cacheGroup.getName(module, selectedChunks, cacheGroup.key)); + // Check if the name is ok + const existingChunk = compilation.namedChunks.get(name); + if (existingChunk) { + const parentValidationKey = `${name}|${ + typeof selectedChunksKey === "bigint" + ? selectedChunksKey + : selectedChunksKey.debugId + }`; + const valid = alreadyValidatedParents.get(parentValidationKey); + if (valid === false) return; + if (valid === undefined) { + // Module can only be moved into the existing chunk if the existing chunk + // is a parent of all selected chunks + let isInAllParents = true; + /** @type {Set} */ + const queue = new Set(); + for (const chunk of selectedChunks) { + for (const group of chunk.groupsIterable) { + queue.add(group); + } + } + for (const group of queue) { + if (existingChunk.isInGroup(group)) continue; + let hasParent = false; + for (const parent of group.parentsIterable) { + hasParent = true; + queue.add(parent); + } + if (!hasParent) { + isInAllParents = false; + } + } + const valid = isInAllParents; + alreadyValidatedParents.set(parentValidationKey, valid); + if (!valid) { + if (!alreadyReportedErrors.has(name)) { + alreadyReportedErrors.add(name); + compilation.errors.push( + new WebpackError( + "SplitChunksPlugin\n" + + `Cache group "${cacheGroup.key}" conflicts with existing chunk.\n` + + `Both have the same name "${name}" and existing chunk is not a parent of the selected modules.\n` + + "Use a different name for the cache group or make sure that the existing chunk is a parent (e. g. via dependOn).\n" + + 'HINT: You can omit "name" to automatically create a name.\n' + + "BREAKING CHANGE: webpack < 5 used to allow to use an entrypoint as splitChunk. " + + "This is no longer allowed when the entrypoint is not a parent of the selected modules.\n" + + "Remove this entrypoint and add modules to cache group's 'test' instead. " + + "If you need modules to be evaluated on startup, add them to the existing entrypoints (make them arrays). " + + "See migration guide of more info." + ) + ); + } + return; + } + } + } + // Create key for maps + // When it has a name we use the name as key + // Otherwise we create the key from chunks and cache group key + // This automatically merges equal names + const key = + cacheGroup.key + + (name + ? ` name:${name}` + : ` chunks:${keyToString(selectedChunksKey)}`); + // Add module to maps + let info = /** @type {ChunksInfoItem} */ (chunksInfoMap.get(key)); + if (info === undefined) { + chunksInfoMap.set( + key, + (info = { + modules: new SortableSet( + undefined, + compareModulesByIdentifier + ), + cacheGroup, + cacheGroupIndex, + name, + sizes: {}, + chunks: new Set(), + reuseableChunks: new Set(), + chunksKeys: new Set() + }) + ); + } + const oldSize = info.modules.size; + info.modules.add(module); + if (info.modules.size !== oldSize) { + for (const type of module.getSourceTypes()) { + info.sizes[type] = (info.sizes[type] || 0) + module.size(type); + } + } + const oldChunksKeysSize = info.chunksKeys.size; + info.chunksKeys.add(selectedChunksKey); + if (oldChunksKeysSize !== info.chunksKeys.size) { + for (const chunk of selectedChunks) { + info.chunks.add(chunk); + } + } + }; + + const context = { + moduleGraph, + chunkGraph + }; + + logger.timeEnd("prepare"); + + logger.time("modules"); + + // Walk through all modules + for (const module of compilation.modules) { + // Get cache group + let cacheGroups = this.options.getCacheGroups(module, context); + if (!Array.isArray(cacheGroups) || cacheGroups.length === 0) { + continue; + } + + // Prepare some values (usedExports = false) + const getCombs = memoize(() => { + const chunks = chunkGraph.getModuleChunksIterable(module); + const chunksKey = getKey(chunks); + return getCombinations(chunksKey); + }); + + // Prepare some values (usedExports = true) + const getCombsByUsedExports = memoize(() => { + // fill the groupedByExportsMap + getExportsChunkSetsInGraph(); + /** @type {Set | Chunk>} */ + const set = new Set(); + const groupedByUsedExports = + /** @type {Iterable} */ + (groupedByExportsMap.get(module)); + for (const chunks of groupedByUsedExports) { + const chunksKey = getKey(chunks); + for (const comb of getExportsCombinations(chunksKey)) + set.add(comb); + } + return set; + }); + + let cacheGroupIndex = 0; + for (const cacheGroupSource of cacheGroups) { + const cacheGroup = this._getCacheGroup(cacheGroupSource); + + const combs = cacheGroup.usedExports + ? getCombsByUsedExports() + : getCombs(); + // For all combination of chunk selection + for (const chunkCombination of combs) { + // Break if minimum number of chunks is not reached + const count = + chunkCombination instanceof Chunk ? 1 : chunkCombination.size; + if (count < cacheGroup.minChunks) continue; + // Select chunks by configuration + const { chunks: selectedChunks, key: selectedChunksKey } = + getSelectedChunks( + chunkCombination, + /** @type {ChunkFilterFunction} */ (cacheGroup.chunksFilter) + ); + + addModuleToChunksInfoMap( + cacheGroup, + cacheGroupIndex, + selectedChunks, + selectedChunksKey, + module + ); + } + cacheGroupIndex++; + } + } + + logger.timeEnd("modules"); + + logger.time("queue"); + + /** + * @param {ChunksInfoItem} info entry + * @param {string[]} sourceTypes source types to be removed + */ + const removeModulesWithSourceType = (info, sourceTypes) => { + for (const module of info.modules) { + const types = module.getSourceTypes(); + if (sourceTypes.some(type => types.has(type))) { + info.modules.delete(module); + for (const type of types) { + info.sizes[type] -= module.size(type); + } + } + } + }; + + /** + * @param {ChunksInfoItem} info entry + * @returns {boolean} true, if entry become empty + */ + const removeMinSizeViolatingModules = info => { + if (!info.cacheGroup._validateSize) return false; + const violatingSizes = getViolatingMinSizes( + info.sizes, + info.cacheGroup.minSize + ); + if (violatingSizes === undefined) return false; + removeModulesWithSourceType(info, violatingSizes); + return info.modules.size === 0; + }; + + // Filter items were size < minSize + for (const [key, info] of chunksInfoMap) { + if (removeMinSizeViolatingModules(info)) { + chunksInfoMap.delete(key); + } else if ( + !checkMinSizeReduction( + info.sizes, + info.cacheGroup.minSizeReduction, + info.chunks.size + ) + ) { + chunksInfoMap.delete(key); + } + } + + /** + * @typedef {Object} MaxSizeQueueItem + * @property {SplitChunksSizes} minSize + * @property {SplitChunksSizes} maxAsyncSize + * @property {SplitChunksSizes} maxInitialSize + * @property {string} automaticNameDelimiter + * @property {string[]} keys + */ + + /** @type {Map} */ + 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 || + compareEntries(bestEntry, info) < 0 + ) { + bestEntry = info; + bestEntryKey = key; + } + } + + const item = /** @type {ChunksInfoItem} */ (bestEntry); + chunksInfoMap.delete(/** @type {string} */ (bestEntryKey)); + + /** @type {Chunk["name"] | undefined} */ + let chunkName = item.name; + // Variable for the new chunk (lazy created) + /** @type {Chunk | undefined} */ + let newChunk; + // When no chunk name, check if we can reuse a chunk instead of creating a new one + let isExistingChunk = false; + let isReusedWithAllModules = false; + if (chunkName) { + const chunkByName = compilation.namedChunks.get(chunkName); + if (chunkByName !== undefined) { + newChunk = chunkByName; + const oldSize = item.chunks.size; + item.chunks.delete(newChunk); + isExistingChunk = item.chunks.size !== oldSize; + } + } else if (item.cacheGroup.reuseExistingChunk) { + outer: for (const chunk of item.chunks) { + if ( + chunkGraph.getNumberOfChunkModules(chunk) !== + item.modules.size + ) { + continue; + } + if ( + item.chunks.size > 1 && + chunkGraph.getNumberOfEntryModules(chunk) > 0 + ) { + continue; + } + for (const module of item.modules) { + if (!chunkGraph.isModuleInChunk(module, chunk)) { + 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; + } + } + if (newChunk) { + item.chunks.delete(newChunk); + chunkName = undefined; + isExistingChunk = true; + isReusedWithAllModules = true; + } + } + + const enforced = + item.cacheGroup._conditionalEnforce && + checkMinSize(item.sizes, item.cacheGroup.enforceSizeThreshold); + + const usedChunks = new Set(item.chunks); + + // 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 = /** @type {number} */ ( + chunk.isOnlyInitial() + ? item.cacheGroup.maxInitialRequests + : chunk.canBeInitial() + ? Math.min( + /** @type {number} */ + (item.cacheGroup.maxInitialRequests), + /** @type {number} */ + (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 (chunkGraph.isModuleInChunk(module, chunk)) 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 < item.chunks.size) { + if (isExistingChunk) + usedChunks.add(/** @type {Chunk} */ (newChunk)); + if ( + /** @type {number} */ (usedChunks.size) >= + /** @type {number} */ (item.cacheGroup.minChunks) + ) { + const chunksArr = Array.from(usedChunks); + for (const module of item.modules) { + addModuleToChunksInfoMap( + item.cacheGroup, + item.cacheGroupIndex, + chunksArr, + getKey(usedChunks), + module + ); + } + } + continue; + } + + // Validate minRemainingSize constraint when a single chunk is left over + if ( + !enforced && + item.cacheGroup._validateRemainingSize && + usedChunks.size === 1 + ) { + const [chunk] = usedChunks; + let chunkSizes = Object.create(null); + for (const module of chunkGraph.getChunkModulesIterable(chunk)) { + if (!item.modules.has(module)) { + for (const type of module.getSourceTypes()) { + chunkSizes[type] = + (chunkSizes[type] || 0) + module.size(type); + } + } + } + const violatingSizes = getViolatingMinSizes( + chunkSizes, + item.cacheGroup.minRemainingSize + ); + if (violatingSizes !== undefined) { + const oldModulesSize = item.modules.size; + removeModulesWithSourceType(item, violatingSizes); + if ( + item.modules.size > 0 && + item.modules.size !== oldModulesSize + ) { + // queue this item again to be processed again + // without violating modules + chunksInfoMap.set(/** @type {string} */ (bestEntryKey), item); + } + continue; + } + } + + // Create the new chunk if not reusing one + if (newChunk === undefined) { + 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 = + (newChunk.chunkReason ? newChunk.chunkReason + ", " : "") + + (isReusedWithAllModules + ? "reused as split chunk" + : "split chunk"); + if (item.cacheGroup.key) { + newChunk.chunkReason += ` (cache group: ${item.cacheGroup.key})`; + } + if (chunkName) { + newChunk.chunkReason += ` (name: ${chunkName})`; + } + if (item.cacheGroup.filename) { + newChunk.filenameTemplate = item.cacheGroup.filename; + } + if (item.cacheGroup.idHint) { + newChunk.idNameHints.add(item.cacheGroup.idHint); + } + if (!isReusedWithAllModules) { + // Add all modules to the new chunk + for (const module of item.modules) { + if (!module.chunkCondition(newChunk, compilation)) continue; + // Add module to new chunk + chunkGraph.connectChunkAndModule(newChunk, module); + // Remove module from used chunks + for (const chunk of usedChunks) { + chunkGraph.disconnectChunkAndModule(chunk, module); + } + } + } else { + // Remove all modules from used chunks + for (const module of item.modules) { + for (const chunk of usedChunks) { + chunkGraph.disconnectChunkAndModule(chunk, module); + } + } + } + + if ( + Object.keys(item.cacheGroup.maxAsyncSize).length > 0 || + Object.keys(item.cacheGroup.maxInitialSize).length > 0 + ) { + const oldMaxSizeSettings = maxSizeQueueMap.get(newChunk); + maxSizeQueueMap.set(newChunk, { + minSize: oldMaxSizeSettings + ? combineSizes( + oldMaxSizeSettings.minSize, + item.cacheGroup._minSizeForMaxSize, + Math.max + ) + : item.cacheGroup.minSize, + maxAsyncSize: oldMaxSizeSettings + ? combineSizes( + oldMaxSizeSettings.maxAsyncSize, + item.cacheGroup.maxAsyncSize, + Math.min + ) + : item.cacheGroup.maxAsyncSize, + maxInitialSize: oldMaxSizeSettings + ? combineSizes( + oldMaxSizeSettings.maxInitialSize, + item.cacheGroup.maxInitialSize, + Math.min + ) + : item.cacheGroup.maxInitialSize, + 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 + let updated = false; + for (const module of item.modules) { + if (info.modules.has(module)) { + // remove module + info.modules.delete(module); + // update size + for (const key of module.getSourceTypes()) { + info.sizes[key] -= module.size(key); + } + updated = true; + } + } + if (updated) { + if (info.modules.size === 0) { + chunksInfoMap.delete(key); + continue; + } + if ( + removeMinSizeViolatingModules(info) || + !checkMinSizeReduction( + info.sizes, + info.cacheGroup.minSizeReduction, + info.chunks.size + ) + ) { + chunksInfoMap.delete(key); + continue; + } + } + } + } + } + + logger.timeEnd("queue"); + + logger.time("maxSize"); + + /** @type {Set} */ + const incorrectMinMaxSizeSet = new Set(); + + const { outputOptions } = compilation; + + // Make sure that maxSize is fulfilled + const { fallbackCacheGroup } = this.options; + for (const chunk of Array.from(compilation.chunks)) { + const chunkConfig = maxSizeQueueMap.get(chunk); + const { + minSize, + maxAsyncSize, + maxInitialSize, + automaticNameDelimiter + } = chunkConfig || fallbackCacheGroup; + if (!chunkConfig && !fallbackCacheGroup.chunksFilter(chunk)) + continue; + /** @type {SplitChunksSizes} */ + let maxSize; + if (chunk.isOnlyInitial()) { + maxSize = maxInitialSize; + } else if (chunk.canBeInitial()) { + maxSize = combineSizes(maxAsyncSize, maxInitialSize, Math.min); + } else { + maxSize = maxAsyncSize; + } + if (Object.keys(maxSize).length === 0) { + continue; + } + for (const key of Object.keys(maxSize)) { + const maxSizeValue = maxSize[key]; + const minSizeValue = minSize[key]; + if ( + typeof minSizeValue === "number" && + minSizeValue > maxSizeValue + ) { + const keys = chunkConfig && chunkConfig.keys; + const warningKey = `${ + keys && keys.join() + } ${minSizeValue} ${maxSizeValue}`; + if (!incorrectMinMaxSizeSet.has(warningKey)) { + incorrectMinMaxSizeSet.add(warningKey); + compilation.warnings.push( + new MinMaxSizeWarning(keys, minSizeValue, maxSizeValue) + ); + } + } + } + const results = deterministicGroupingForModules({ + minSize, + maxSize: mapObject(maxSize, (value, key) => { + const minSizeValue = minSize[key]; + return typeof minSizeValue === "number" + ? Math.max(value, minSizeValue) + : value; + }), + items: chunkGraph.getChunkModulesIterable(chunk), + getKey(module) { + const cache = getKeyCache.get(module); + if (cache !== undefined) return cache; + const ident = cachedMakePathsRelative(module.identifier()); + const nameForCondition = + module.nameForCondition && module.nameForCondition(); + const name = nameForCondition + ? cachedMakePathsRelative(nameForCondition) + : ident.replace(/^.*!|\?[^?!]*$/g, ""); + const fullKey = + name + + automaticNameDelimiter + + hashFilename(ident, outputOptions); + const key = requestToId(fullKey); + getKeyCache.set(module, key); + return key; + }, + getSize(module) { + const size = Object.create(null); + for (const key of module.getSourceTypes()) { + size[key] = module.size(key); + } + return size; + } + }); + if (results.length <= 1) { + continue; + } + for (let i = 0; i < results.length; i++) { + const group = results[i]; + const key = this.options.hidePathInfo + ? hashFilename(group.key, outputOptions) + : group.key; + let name = chunk.name + ? chunk.name + automaticNameDelimiter + key + : null; + if (name && name.length > 100) { + name = + name.slice(0, 100) + + automaticNameDelimiter + + hashFilename(name, outputOptions); + } + if (i !== results.length - 1) { + const newPart = compilation.addChunk( + /** @type {Chunk["name"]} */ (name) + ); + chunk.split(newPart); + newPart.chunkReason = chunk.chunkReason; + // Add all modules to the new chunk + for (const module of group.items) { + if (!module.chunkCondition(newPart, compilation)) { + continue; + } + // Add module to new chunk + chunkGraph.connectChunkAndModule(newPart, module); + // Remove module from used chunks + chunkGraph.disconnectChunkAndModule(chunk, module); + } + } else { + // change the chunk to be a part + chunk.name = /** @type {Chunk["name"]} */ (name); + } + } + } + logger.timeEnd("maxSize"); + } + ); + }); + } +}; -- cgit v1.2.3