diff options
Diffstat (limited to 'node_modules/webpack/lib/optimize/SplitChunksPlugin.js')
| -rw-r--r-- | node_modules/webpack/lib/optimize/SplitChunksPlugin.js | 1763 |
1 files changed, 0 insertions, 1763 deletions
diff --git a/node_modules/webpack/lib/optimize/SplitChunksPlugin.js b/node_modules/webpack/lib/optimize/SplitChunksPlugin.js deleted file mode 100644 index 2e79ab6a..00000000 --- a/node_modules/webpack/lib/optimize/SplitChunksPlugin.js +++ /dev/null @@ -1,1763 +0,0 @@ -/* - 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<Module>} DeterministicGroupingGroupedItemsForModule */ -/** @typedef {import("../util/deterministicGrouping").Options<Module>} DeterministicGroupingOptionsForModule */ - -/** @typedef {Record<string, number>} 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<Module>} modules - * @property {CacheGroup} cacheGroup - * @property {number} cacheGroupIndex - * @property {string} name - * @property {Record<string, number>} sizes - * @property {Set<Chunk>} chunks - * @property {Set<Chunk>} reuseableChunks - * @property {Set<bigint | Chunk>} chunksKeys - */ - -const defaultGetName = /** @type {GetName} */ (() => {}); - -const deterministicGroupingForModules = - /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[]} */ ( - deterministicGrouping - ); - -/** @type {WeakMap<Module, string>} */ -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<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 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<string, number>} */ - 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<string, false|string|RegExp|OptimizationSplitChunksGetCacheGroups|OptimizationSplitChunksCacheGroup>} 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<CacheGroupSource, CacheGroup>} */ - 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<Chunk, bigint>} */ - 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<Chunk>} 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<bigint, Set<Chunk>>} */ - const chunkSetsInGraph = new Map(); - /** @type {Set<Chunk>} */ - 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<Chunk[]>} 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<Module, Iterable<Chunk[]>>} */ - const groupedByExportsMap = new Map(); - - const getExportsChunkSetsInGraph = memoize(() => { - /** @type {Map<bigint, Set<Chunk>>} */ - const chunkSetsInGraph = new Map(); - /** @type {Set<Chunk>} */ - 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<Set<Chunk>>} chunkSets set of sets of chunks - * @returns {Map<number, Array<Set<Chunk>>>} map of sets of chunks by count - */ - const groupChunkSetsByCount = chunkSets => { - /** @type {Map<number, Array<Set<Chunk>>>} */ - 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<bigint | Chunk, (Set<Chunk> | 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> | 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<Set<Chunk> | 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> | 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<string, boolean>} */ - const alreadyValidatedParents = new Map(); - /** @type {Set<string>} */ - 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<string, ChunksInfoItem>} */ - 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<ChunkGroup>} */ - 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<Set<Chunk> | Chunk>} */ - const set = new Set(); - const groupedByUsedExports = - /** @type {Iterable<Chunk[]>} */ - (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<Chunk, MaxSizeQueueItem>} */ - 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<string>} */ - 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"); - } - ); - }); - } -}; |
