From 63a4145e3fa846e4b9b720436a6fddc692c51833 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 17 May 2018 17:29:07 +0200 Subject: [PATCH 01/27] clear output directory on test start --- test/ConfigTestCases.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/ConfigTestCases.test.js b/test/ConfigTestCases.test.js index b0f58ddc86b..0fa46e346b2 100644 --- a/test/ConfigTestCases.test.js +++ b/test/ConfigTestCases.test.js @@ -5,6 +5,7 @@ const path = require("path"); const fs = require("fs"); const vm = require("vm"); const mkdirp = require("mkdirp"); +const rimraf = require("rimraf"); const checkArrayExpectation = require("./checkArrayExpectation"); const Stats = require("../lib/Stats"); @@ -47,7 +48,6 @@ describe("ConfigTestCases", () => { category.name, testName ); - mkdirp.sync(outputDirectory); const exportedTests = []; const exportedBeforeEach = []; const exportedAfterEach = []; @@ -59,6 +59,8 @@ describe("ConfigTestCases", () => { if (err) return reject(err); resolve(); }; + rimraf.sync(outputDirectory); + mkdirp.sync(outputDirectory); const options = prepareOptions( require(path.join(testDirectory, "webpack.config.js")), { testPath: outputDirectory } From 75db965170335acc7b4d1be52dca033fc0f10d9b Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 17 May 2018 12:35:13 +0200 Subject: [PATCH 02/27] update combination when split fails because of max requests fixes #7323 --- lib/optimize/SplitChunksPlugin.js | 319 +++++++++++------- .../__snapshots__/StatsTestCases.test.js.snap | 198 ++++++----- .../statsCases/split-chunks/webpack.config.js | 1 + 3 files changed, 294 insertions(+), 224 deletions(-) diff --git a/lib/optimize/SplitChunksPlugin.js b/lib/optimize/SplitChunksPlugin.js index 202ec7766c5..84f1d31b63a 100644 --- a/lib/optimize/SplitChunksPlugin.js +++ b/lib/optimize/SplitChunksPlugin.js @@ -10,6 +10,7 @@ const GraphHelpers = require("../GraphHelpers"); const { isSubset } = require("../util/SetHelpers"); /** @typedef {import("../Chunk")} Chunk */ +/** @typedef {import("../Module")} Module */ const hashFilename = name => { return crypto @@ -310,7 +311,6 @@ module.exports = class SplitChunksPlugin { // Create a list of possible combinations const combinationsCache = new Map(); // Map[]> - const selectedChunksCacheByChunksSet = new WeakMap(); // WeakMap, WeakMap> const getCombinations = key => { const chunksSet = chunkSetsInGraph.get(key); @@ -330,14 +330,36 @@ module.exports = class SplitChunksPlugin { return array; }; + /** + * @typedef {Object} SelectedChunksResult + * @property {Chunk[]} chunks the list of chunks + * @property {string} key a key of the list + */ + + /** + * @typedef {function(Chunk): boolean} ChunkFilterFunction + */ + + /** @type {WeakMap, 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} 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 Map(); + entry = new WeakMap(); selectedChunksCacheByChunksSet.set(chunks, entry); } + /** @type {SelectedChunksResult} */ let entry2 = entry.get(chunkFilter); if (entry2 === undefined) { + /** @type {Chunk[]} */ const selectedChunks = []; for (const chunk of chunks) { if (chunkFilter(chunk)) selectedChunks.push(chunk); @@ -351,10 +373,76 @@ module.exports = class SplitChunksPlugin { return entry2; }; + /** + * @typedef {Object} ChunksInfoItem + * @property {SortableSet} modules + * @property {TODO} cacheGroup + * @property {string} name + * @property {number} size + * @property {Map} chunks + * @property {Set} reuseableChunks + * @property {Set} chunksKeys + */ + // Map a list of chunks to a list of modules // For the key the chunk "index" is used, the value is a SortableSet of modules + /** @type {Map} */ const chunksInfoMap = new Map(); + /** + * @param {TODO} cacheGroup the current cache group + * @param {Chunk[]} selectedChunks chunks selected for this module + * @param {string} selectedChunksKey a key of selectedChunks + * @param {Module} module the current module + * @returns {void} + */ + const addModuleToChunksInfoMap = ( + cacheGroup, + selectedChunks, + selectedChunksKey, + module + ) => { + // Break if minimum number of chunks is not reached + if (selectedChunks.length < cacheGroup.minChunks) return; + // Determine name for split chunk + const name = cacheGroup.getName( + module, + selectedChunks, + cacheGroup.key + ); + // Create key for maps + // When it has a name we use the name as key + // Elsewise we create the key from chunks and cache group key + // This automatically merges equal names + const key = + (name && `name:${name}`) || + `chunks:${selectedChunksKey} key:${cacheGroup.key}`; + // Add module to maps + let info = chunksInfoMap.get(key); + if (info === undefined) { + chunksInfoMap.set( + key, + (info = { + modules: new SortableSet(undefined, sortByIdentifier), + cacheGroup, + name, + size: 0, + chunks: new Map(), + reuseableChunks: new Set(), + chunksKeys: new Set() + }) + ); + } + info.modules.add(module); + info.size += module.size(); + if (!info.chunksKeys.has(selectedChunksKey)) { + info.chunksKeys.add(selectedChunksKey); + for (const chunk of selectedChunks) { + info.chunks.set(chunk, chunk.getNumberOfModules()); + } + } + }; + // Walk through all modules for (const module of compilation.modules) { // Get cache group @@ -422,55 +510,17 @@ module.exports = class SplitChunksPlugin { chunkCombination, cacheGroup.chunksFilter ); - // Break if minimum number of chunks is not reached - if (selectedChunks.length < cacheGroup.minChunks) continue; - // Determine name for split chunk - const name = cacheGroup.getName( - module, + + addModuleToChunksInfoMap( + cacheGroup, selectedChunks, - cacheGroup.key + selectedChunksKey, + module ); - // Create key for maps - // When it has a name we use the name as key - // Elsewise we create the key from chunks and cache group key - // This automatically merges equal names - const key = - (name && `name:${name}`) || - `chunks:${selectedChunksKey} key:${cacheGroup.key}`; - // Add module to maps - let info = chunksInfoMap.get(key); - if (info === undefined) { - chunksInfoMap.set( - key, - (info = { - modules: new SortableSet(undefined, sortByIdentifier), - cacheGroup, - name, - size: 0, - chunks: new Map(), - reusedableChunks: new Set(), - chunksKeys: new Set() - }) - ); - } - info.modules.add(module); - info.size += module.size(); - if (!info.chunksKeys.has(selectedChunksKey)) { - info.chunksKeys.add(selectedChunksKey); - for (const chunk of selectedChunks) { - info.chunks.set(chunk, chunk.getNumberOfModules()); - } - } } } } - for (const [key, info] of chunksInfoMap) { - // Get size of module lists - if (info.size < info.cacheGroup.minSize) { - chunksInfoMap.delete(key); - } - } while (chunksInfoMap.size > 0) { // Find best matching entry let bestEntryKey; @@ -478,15 +528,20 @@ module.exports = class SplitChunksPlugin { for (const pair of chunksInfoMap) { const key = pair[0]; const info = pair[1]; - if (bestEntry === undefined) { - bestEntry = info; - bestEntryKey = key; - } else if (compareEntries(bestEntry, info) < 0) { - bestEntry = info; - bestEntryKey = key; + if (info.size >= info.cacheGroup.minSize) { + if (bestEntry === undefined) { + bestEntry = info; + bestEntryKey = key; + } else if (compareEntries(bestEntry, info) < 0) { + bestEntry = info; + bestEntryKey = key; + } } } + // No suitable item left + if (bestEntry === undefined) break; + const item = bestEntry; chunksInfoMap.delete(bestEntryKey); @@ -517,12 +572,19 @@ module.exports = class SplitChunksPlugin { } } } - const usedChunks = []; - // Walk through all chunks - for (const chunk of item.chunks.keys()) { + // Check if maxRequests condition can be fullfilled + + const usedChunks = Array.from(item.chunks.keys()).filter(chunk => { // skip if we address ourself - if ((chunkName && chunk.name === chunkName) || chunk === newChunk) - continue; + return ( + (!chunkName || chunk.name !== chunkName) && chunk !== newChunk + ); + }); + + // Skip when no chunk selected + if (usedChunks.length === 0) continue; + + const chunkInLimit = usedChunks.filter(chunk => { // respect max requests when not enforced const maxRequests = chunk.isOnlyInitial() ? item.cacheGroup.maxInitialRequests @@ -532,88 +594,97 @@ module.exports = class SplitChunksPlugin { item.cacheGroup.maxAsyncRequests ) : item.cacheGroup.maxAsyncRequests; - if (isFinite(maxRequests) && getRequests(chunk) >= maxRequests) - continue; - if (newChunk === undefined) { - // Create the new chunk - newChunk = compilation.addChunk(chunkName); + return !isFinite(maxRequests) || getRequests(chunk) < maxRequests; + }); + + if (chunkInLimit.length < usedChunks.length) { + for (const module of item.modules) { + addModuleToChunksInfoMap( + item.cacheGroup, + chunkInLimit, + getKey(chunkInLimit), + module + ); } + continue; + } + + // Create the new chunk if not reusing one + if (!isReused) { + newChunk = compilation.addChunk(chunkName); + } + // Walk through all chunks + for (const chunk of usedChunks) { // Add graph connections for splitted chunk chunk.split(newChunk); - usedChunks.push(chunk); } - // If we successfully created a new chunk or reused one - if (newChunk) { - // Add a note to the chunk - newChunk.chunkReason = isReused - ? "reused as split chunk" - : "split chunk"; - if (item.cacheGroup.key) { - newChunk.chunkReason += ` (cache group: ${ - item.cacheGroup.key - })`; + // Add a note to the chunk + newChunk.chunkReason = isReused + ? "reused as split chunk" + : "split chunk"; + if (item.cacheGroup.key) { + newChunk.chunkReason += ` (cache group: ${item.cacheGroup.key})`; + } + if (chunkName) { + newChunk.chunkReason += ` (name: ${chunkName})`; + // If the chosen name is already an entry point we remove the entry point + const entrypoint = compilation.entrypoints.get(chunkName); + if (entrypoint) { + compilation.entrypoints.delete(chunkName); + entrypoint.remove(); + newChunk.entryModule = undefined; + } + } + if (item.cacheGroup.filename) { + if (!newChunk.isOnlyInitial()) { + throw new Error( + "SplitChunksPlugin: You are trying to set a filename for a chunk which is (also) loaded on demand. " + + "The runtime can only handle loading of chunks which match the chunkFilename schema. " + + "Using a custom filename would fail at runtime. " + + `(cache group: ${item.cacheGroup.key})` + ); } - if (chunkName) { - newChunk.chunkReason += ` (name: ${chunkName})`; - // If the chosen name is already an entry point we remove the entry point - const entrypoint = compilation.entrypoints.get(chunkName); - if (entrypoint) { - compilation.entrypoints.delete(chunkName); - entrypoint.remove(); - newChunk.entryModule = undefined; + newChunk.filenameTemplate = item.cacheGroup.filename; + } + if (!isReused) { + // Add all modules to the new chunk + for (const module of item.modules) { + if (typeof module.chunkCondition === "function") { + if (!module.chunkCondition(newChunk)) continue; + } + // Add module to new chunk + GraphHelpers.connectChunkAndModule(newChunk, module); + // Remove module from used chunks + for (const chunk of usedChunks) { + chunk.removeModule(module); + module.rewriteChunkInReasons(chunk, [newChunk]); } } - if (item.cacheGroup.filename) { - if (!newChunk.isOnlyInitial()) { - throw new Error( - "SplitChunksPlugin: You are trying to set a filename for a chunk which is (also) loaded on demand. " + - "The runtime can only handle loading of chunks which match the chunkFilename schema. " + - "Using a custom filename would fail at runtime. " + - `(cache group: ${item.cacheGroup.key})` - ); + } else { + // Remove all modules from used chunks + for (const module of item.modules) { + for (const chunk of usedChunks) { + chunk.removeModule(module); + module.rewriteChunkInReasons(chunk, [newChunk]); } - newChunk.filenameTemplate = item.cacheGroup.filename; } - if (!isReused) { - // Add all modules to the new chunk + } + // remove all modules from other entries and update size + for (const [key, info] of chunksInfoMap) { + if (isOverlap(info.chunks, item.chunks)) { + const oldSize = info.modules.size; for (const module of item.modules) { - if (typeof module.chunkCondition === "function") { - if (!module.chunkCondition(newChunk)) continue; - } - // Add module to new chunk - GraphHelpers.connectChunkAndModule(newChunk, module); - // Remove module from used chunks - for (const chunk of usedChunks) { - chunk.removeModule(module); - module.rewriteChunkInReasons(chunk, [newChunk]); - } + info.modules.delete(module); } - } else { - // Remove all modules from used chunks - for (const module of item.modules) { - for (const chunk of usedChunks) { - chunk.removeModule(module); - module.rewriteChunkInReasons(chunk, [newChunk]); - } + if (info.modules.size === 0) { + chunksInfoMap.delete(key); + continue; } - } - // remove all modules from other entries and update size - for (const [key, info] of chunksInfoMap) { - if (isOverlap(info.chunks, item.chunks)) { - const oldSize = info.modules.size; - for (const module of item.modules) { - info.modules.delete(module); - } - if (info.modules.size === 0) { + if (info.modules.size !== oldSize) { + info.size = getModulesSize(info.modules); + if (info.size < info.cacheGroup.minSize) chunksInfoMap.delete(key); - continue; - } - if (info.modules.size !== oldSize) { - info.size = getModulesSize(info.modules); - if (info.size < info.cacheGroup.minSize) - chunksInfoMap.delete(key); - } } } } diff --git a/test/__snapshots__/StatsTestCases.test.js.snap b/test/__snapshots__/StatsTestCases.test.js.snap index a2efbf558d7..74b351dd2ff 100644 --- a/test/__snapshots__/StatsTestCases.test.js.snap +++ b/test/__snapshots__/StatsTestCases.test.js.snap @@ -341,61 +341,61 @@ Child multiple-vendors: > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [2] ./node_modules/x.js 20 bytes {0} [built] - chunk {1} multiple-vendors/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c) + chunk {1} multiple-vendors/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c) > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - chunk {2} multiple-vendors/async-b~async-c~async-g~b~c.js (async-b~async-c~async-g~b~c) 20 bytes <{0}> <{1}> <{10}> <{3}> <{8}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={6}= ={7}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~b~c) + chunk {2} multiple-vendors/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g) > ./g [] 6:0-47 > ./g [] 6:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [1] ./f.js 20 bytes {2} {11} {12} [built] - chunk {3} multiple-vendors/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) + chunk {3} multiple-vendors/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) > ./a a > ./b b > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 [3] ./node_modules/y.js 20 bytes {3} [built] - chunk {4} multiple-vendors/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={7}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) + chunk {4} multiple-vendors/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={8}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) > ./c c > ./c [8] ./index.js 3:0-47 [7] ./node_modules/z.js 20 bytes {4} [built] - chunk {5} multiple-vendors/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{8}> ={2}= [rendered] + chunk {5} multiple-vendors/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{6}> ={2}= [rendered] > ./g [] 6:0-47 > ./g [] 6:0-47 [9] ./g.js 34 bytes {5} [built] - chunk {6} multiple-vendors/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] - > ./b [8] ./index.js 2:0-47 - [4] ./b.js 72 bytes {6} {11} [built] - chunk {7} multiple-vendors/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] - > ./c [8] ./index.js 3:0-47 - [5] ./c.js 72 bytes {7} {12} [built] - chunk {8} multiple-vendors/a~async-a.js (a~async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a) + chunk {6} multiple-vendors/async-a.js (async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] > ./a [8] ./index.js 1:0-47 - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] + chunk {7} multiple-vendors/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] + > ./b [8] ./index.js 2:0-47 + [4] ./b.js 72 bytes {7} {11} [built] + chunk {8} multiple-vendors/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] + > ./c [8] ./index.js 3:0-47 + [5] ./c.js 72 bytes {8} {12} [built] chunk {9} multiple-vendors/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered] > ./ main [8] ./index.js 147 bytes {9} [built] chunk {10} multiple-vendors/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered] > ./a a [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] chunk {11} multiple-vendors/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered] > ./b b [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [4] ./b.js 72 bytes {6} {11} [built] + [4] ./b.js 72 bytes {7} {11} [built] chunk {12} multiple-vendors/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered] > ./c c [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [5] ./c.js 72 bytes {7} {12} [built] + [5] ./c.js 72 bytes {8} {12} [built] Child all: Entrypoint main = all/main.js Entrypoint a = all/vendors~a~async-a~async-b~async-c~b~c.js all/vendors~a~async-a~async-b~b.js all/a.js @@ -409,61 +409,61 @@ Child all: > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [2] ./node_modules/x.js 20 bytes {0} [built] - chunk {1} all/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c) + chunk {1} all/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c) > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - chunk {2} all/async-b~async-c~async-g~b~c.js (async-b~async-c~async-g~b~c) 20 bytes <{0}> <{1}> <{10}> <{3}> <{8}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={6}= ={7}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~b~c) + chunk {2} all/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g) > ./g [] 6:0-47 > ./g [] 6:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [1] ./f.js 20 bytes {2} {11} {12} [built] - chunk {3} all/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) + chunk {3} all/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) > ./a a > ./b b > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 [3] ./node_modules/y.js 20 bytes {3} [built] - chunk {4} all/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={7}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) + chunk {4} all/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={8}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) > ./c c > ./c [8] ./index.js 3:0-47 [7] ./node_modules/z.js 20 bytes {4} [built] - chunk {5} all/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{8}> ={2}= [rendered] + chunk {5} all/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{6}> ={2}= [rendered] > ./g [] 6:0-47 > ./g [] 6:0-47 [9] ./g.js 34 bytes {5} [built] - chunk {6} all/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] - > ./b [8] ./index.js 2:0-47 - [4] ./b.js 72 bytes {6} {11} [built] - chunk {7} all/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] - > ./c [8] ./index.js 3:0-47 - [5] ./c.js 72 bytes {7} {12} [built] - chunk {8} all/a~async-a.js (a~async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a) + chunk {6} all/async-a.js (async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] > ./a [8] ./index.js 1:0-47 - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] + chunk {7} all/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] + > ./b [8] ./index.js 2:0-47 + [4] ./b.js 72 bytes {7} {11} [built] + chunk {8} all/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] + > ./c [8] ./index.js 3:0-47 + [5] ./c.js 72 bytes {8} {12} [built] chunk {9} all/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered] > ./ main [8] ./index.js 147 bytes {9} [built] chunk {10} all/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered] > ./a a [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] chunk {11} all/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered] > ./b b [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [4] ./b.js 72 bytes {6} {11} [built] + [4] ./b.js 72 bytes {7} {11} [built] chunk {12} all/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered] > ./c c [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [5] ./c.js 72 bytes {7} {12} [built]" + [5] ./c.js 72 bytes {8} {12} [built]" `; exports[`StatsTestCases should print correct stats for chunk-module-id-range 1`] = ` @@ -2161,61 +2161,61 @@ Child all-chunks: > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [2] ./node_modules/x.js 20 bytes {0} [built] - chunk {1} default/a~async-a~async-b~async-c~b~c.js (a~async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a~async-b~async-c~b~c) + chunk {1} default/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c) > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - chunk {2} default/async-b~async-c~async-g~b~c.js (async-b~async-c~async-g~b~c) 20 bytes <{0}> <{1}> <{10}> <{3}> <{8}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={6}= ={7}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~b~c) + chunk {2} default/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g) > ./g [] 6:0-47 > ./g [] 6:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [1] ./f.js 20 bytes {2} {11} {12} [built] - chunk {3} default/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) + chunk {3} default/vendors~a~async-a~async-b~b.js (vendors~a~async-a~async-b~b) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={7}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~a~async-a~async-b~b) > ./a a > ./b b > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 [3] ./node_modules/y.js 20 bytes {3} [built] - chunk {4} default/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={7}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) + chunk {4} default/vendors~async-c~c.js (vendors~async-c~c) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={8}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~c) > ./c c > ./c [8] ./index.js 3:0-47 [7] ./node_modules/z.js 20 bytes {4} [built] - chunk {5} default/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{8}> ={2}= [rendered] + chunk {5} default/async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{6}> ={2}= [rendered] > ./g [] 6:0-47 > ./g [] 6:0-47 [9] ./g.js 34 bytes {5} [built] - chunk {6} default/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] - > ./b [8] ./index.js 2:0-47 - [4] ./b.js 72 bytes {6} {11} [built] - chunk {7} default/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] - > ./c [8] ./index.js 3:0-47 - [5] ./c.js 72 bytes {7} {12} [built] - chunk {8} default/a~async-a.js (a~async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: a~async-a) + chunk {6} default/async-a.js (async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] > ./a [8] ./index.js 1:0-47 - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] + chunk {7} default/async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] + > ./b [8] ./index.js 2:0-47 + [4] ./b.js 72 bytes {7} {11} [built] + chunk {8} default/async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] + > ./c [8] ./index.js 3:0-47 + [5] ./c.js 72 bytes {8} {12} [built] chunk {9} default/main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered] > ./ main [8] ./index.js 147 bytes {9} [built] chunk {10} default/a.js (a) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered] > ./a a [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + [6] ./a.js + 1 modules 156 bytes {6} {10} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] chunk {11} default/b.js (b) 112 bytes ={0}= ={3}= [entry] [rendered] > ./b b [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [4] ./b.js 72 bytes {6} {11} [built] + [4] ./b.js 72 bytes {7} {11} [built] chunk {12} default/c.js (c) 112 bytes ={0}= ={4}= [entry] [rendered] > ./c c [0] ./d.js 20 bytes {1} {10} {11} {12} [built] [1] ./f.js 20 bytes {2} {11} {12} [built] - [5] ./c.js 72 bytes {7} {12} [built] + [5] ./c.js 72 bytes {8} {12} [built] Child manual: Entrypoint main = default/main.js Entrypoint a = default/vendors.js default/a.js @@ -2274,72 +2274,70 @@ Child manual: [5] ./c.js 72 bytes {4} {8} [built] Child name-too-long: Entrypoint main = main.js - Entrypoint aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.js - Entrypoint bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js - Entrypoint cccccccccccccccccccccccccccccc = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~async-c~cccccccccccccccccccccccccccccc.js cccccccccccccccccccccccccccccc.js - chunk {0} vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js (vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f) 20 bytes <{9}> ={1}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f) + Entrypoint aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.js + Entrypoint bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793.js async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc.js bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js + Entrypoint cccccccccccccccccccccccccccccc = vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js vendors~async-c~cccccccccccccccccccccccccccccc.js aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793.js async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc.js cccccccccccccccccccccccccccccc.js + chunk {0} vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f.js (vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f) 20 bytes <{9}> ={1}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={5}= ={7}= ={8}= >{2}< >{6}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccc~50ebc41f) > ./a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > ./b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb > ./c cccccccccccccccccccccccccccccc - > ./a [8] ./index.js 1:0-47 - > ./b [8] ./index.js 2:0-47 - > ./c [8] ./index.js 3:0-47 + > ./a [7] ./index.js 1:0-47 + > ./b [7] ./index.js 2:0-47 + > ./c [7] ./index.js 3:0-47 [2] ./node_modules/x.js 20 bytes {0} [built] - chunk {1} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793) - > ./a [8] ./index.js 1:0-47 - > ./b [8] ./index.js 2:0-47 - > ./c [8] ./index.js 3:0-47 - [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - chunk {2} async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc.js (async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc) 20 bytes <{0}> <{1}> <{10}> <{3}> <{8}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={6}= ={7}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc) + chunk {1} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793) 20 bytes <{9}> ={0}= ={10}= ={11}= ={12}= ={2}= ={3}= ={4}= ={5}= ={7}= ={8}= >{2}< >{6}< [initial] [rendered] split chunk (cache group: default) (name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~async-c~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccc~18066793) + > ./a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + > ./b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + > ./c cccccccccccccccccccccccccccccc + > ./a [7] ./index.js 1:0-47 + > ./b [7] ./index.js 2:0-47 + > ./c [7] ./index.js 3:0-47 + [1] ./d.js 20 bytes {1} [built] + chunk {2} async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc.js (async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc) 20 bytes <{0}> <{1}> <{10}> <{3}> <{5}> <{9}> ={0}= ={1}= ={11}= ={12}= ={3}= ={4}= ={6}= ={7}= ={8}= [initial] [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb~cccccccccccccccccccccccccccccc) > ./g [] 6:0-47 > ./g [] 6:0-47 - > ./b [8] ./index.js 2:0-47 - > ./c [8] ./index.js 3:0-47 - [1] ./f.js 20 bytes {2} {11} {12} [built] - chunk {3} vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js (vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={6}= ={8}= >{2}< >{5}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) + > ./b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + > ./c cccccccccccccccccccccccccccccc + > ./b [7] ./index.js 2:0-47 + > ./c [7] ./index.js 3:0-47 + [0] ./f.js 20 bytes {2} [built] + chunk {3} vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js (vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) 20 bytes <{9}> ={0}= ={1}= ={10}= ={11}= ={2}= ={5}= ={7}= >{2}< >{6}< [initial] [rendered] split chunk (cache group: vendors) (name: vendors~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a~async-b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) > ./a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa > ./b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - > ./a [8] ./index.js 1:0-47 - > ./b [8] ./index.js 2:0-47 + > ./a [7] ./index.js 1:0-47 + > ./b [7] ./index.js 2:0-47 [3] ./node_modules/y.js 20 bytes {3} [built] - chunk {4} vendors~async-c~cccccccccccccccccccccccccccccc.js (vendors~async-c~cccccccccccccccccccccccccccccc) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={7}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~cccccccccccccccccccccccccccccc) + chunk {4} vendors~async-c~cccccccccccccccccccccccccccccc.js (vendors~async-c~cccccccccccccccccccccccccccccc) 20 bytes <{9}> ={0}= ={1}= ={12}= ={2}= ={8}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~async-c~cccccccccccccccccccccccccccccc) > ./c cccccccccccccccccccccccccccccc - > ./c [8] ./index.js 3:0-47 - [7] ./node_modules/z.js 20 bytes {4} [built] - chunk {5} async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{8}> ={2}= [rendered] - > ./g [] 6:0-47 - > ./g [] 6:0-47 - [9] ./g.js 34 bytes {5} [built] - chunk {6} async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] - > ./b [8] ./index.js 2:0-47 - [4] ./b.js 72 bytes {6} {11} [built] - chunk {7} async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] - > ./c [8] ./index.js 3:0-47 - [5] ./c.js 72 bytes {7} {12} [built] - chunk {8} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a) 156 bytes <{9}> ={0}= ={1}= ={3}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a) - > ./a [8] ./index.js 1:0-47 - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] + > ./c [7] ./index.js 3:0-47 + [6] ./node_modules/z.js 20 bytes {4} [built] + chunk {5} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a) 156 bytes <{9}> ={0}= ={1}= ={10}= ={3}= >{2}< >{6}< [initial] [rendered] split chunk (cache group: default) (name: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa~async-a) + > ./a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + > ./a [7] ./index.js 1:0-47 + [8] ./a.js + 1 modules 156 bytes {5} [built] | ./a.js 121 bytes [built] | ./e.js 20 bytes [built] - chunk {9} main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{6}< >{7}< >{8}< [entry] [rendered] + chunk {6} async-g.js (async-g) 34 bytes <{0}> <{1}> <{10}> <{3}> <{5}> ={2}= [rendered] + > ./g [] 6:0-47 + > ./g [] 6:0-47 + [9] ./g.js 34 bytes {6} [built] + chunk {7} async-b.js (async-b) 72 bytes <{9}> ={0}= ={1}= ={2}= ={3}= [rendered] + > ./b [7] ./index.js 2:0-47 + [4] ./b.js 72 bytes {7} {11} [built] + chunk {8} async-c.js (async-c) 72 bytes <{9}> ={0}= ={1}= ={2}= ={4}= [rendered] + > ./c [7] ./index.js 3:0-47 + [5] ./c.js 72 bytes {8} {12} [built] + chunk {9} main.js (main) 147 bytes >{0}< >{1}< >{2}< >{3}< >{4}< >{5}< >{7}< >{8}< [entry] [rendered] > ./ main - [8] ./index.js 147 bytes {9} [built] - chunk {10} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) 176 bytes ={0}= ={3}= >{2}< >{5}< [entry] [rendered] + [7] ./index.js 147 bytes {9} [built] + chunk {10} aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.js (aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa) 0 bytes ={0}= ={1}= ={3}= ={5}= >{2}< >{6}< [entry] [rendered] > ./a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [6] ./a.js + 1 modules 156 bytes {8} {10} [built] - | ./a.js 121 bytes [built] - | ./e.js 20 bytes [built] - chunk {11} bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) 112 bytes ={0}= ={3}= [entry] [rendered] + chunk {11} bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.js (bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb) 72 bytes ={0}= ={1}= ={2}= ={3}= [entry] [rendered] > ./b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [1] ./f.js 20 bytes {2} {11} {12} [built] - [4] ./b.js 72 bytes {6} {11} [built] - chunk {12} cccccccccccccccccccccccccccccc.js (cccccccccccccccccccccccccccccc) 112 bytes ={0}= ={4}= [entry] [rendered] + [4] ./b.js 72 bytes {7} {11} [built] + chunk {12} cccccccccccccccccccccccccccccc.js (cccccccccccccccccccccccccccccc) 72 bytes ={0}= ={1}= ={2}= ={4}= [entry] [rendered] > ./c cccccccccccccccccccccccccccccc - [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - [1] ./f.js 20 bytes {2} {11} {12} [built] - [5] ./c.js 72 bytes {7} {12} [built] + [5] ./c.js 72 bytes {8} {12} [built] Child custom-chunks-filter: Entrypoint main = default/main.js Entrypoint a = default/a.js @@ -2352,12 +2350,12 @@ Child custom-chunks-filter: > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [2] ./node_modules/x.js 20 bytes {0} {10} [built] - chunk {1} default/async-a~async-b~async-c~b~c.js (async-a~async-b~async-c~b~c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c~b~c) + chunk {1} default/async-a~async-b~async-c.js (async-a~async-b~async-c) 20 bytes <{9}> ={0}= ={2}= ={3}= ={4}= ={6}= ={7}= ={8}= >{2}< >{5}< [rendered] split chunk (cache group: default) (name: async-a~async-b~async-c) > ./a [8] ./index.js 1:0-47 > ./b [8] ./index.js 2:0-47 > ./c [8] ./index.js 3:0-47 [0] ./d.js 20 bytes {1} {10} {11} {12} [built] - chunk {2} default/async-b~async-c~async-g~b~c.js (async-b~async-c~async-g~b~c) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g~b~c) + chunk {2} default/async-b~async-c~async-g.js (async-b~async-c~async-g) 20 bytes <{0}> <{1}> <{10}> <{3}> <{6}> <{9}> ={0}= ={1}= ={3}= ={4}= ={5}= ={7}= ={8}= [rendered] split chunk (cache group: default) (name: async-b~async-c~async-g) > ./g [] 6:0-47 > ./g [] 6:0-47 > ./b [8] ./index.js 2:0-47 diff --git a/test/statsCases/split-chunks/webpack.config.js b/test/statsCases/split-chunks/webpack.config.js index f69088f4c98..5172e72b58f 100644 --- a/test/statsCases/split-chunks/webpack.config.js +++ b/test/statsCases/split-chunks/webpack.config.js @@ -94,6 +94,7 @@ module.exports = [ optimization: { splitChunks: { minSize: 0, + maxInitialRequests: Infinity, chunks: "all" } }, From 7453db9dddabf0784f0cdf18a524037f6e4b86f2 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 17 May 2018 18:14:34 +0200 Subject: [PATCH 03/27] Reduce the travis load with build stages --- .travis.yml | 29 +++++++++++++++++++++-------- codecov.yml | 15 +++++++++++++++ package.json | 2 ++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index b1cc16a5835..88f661c3d31 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,28 +10,41 @@ branches: cache: yarn: true +stages: + - basic + - advanced + - versions + matrix: include: - os: linux node_js: "10" - env: NO_WATCH_TESTS=1 JOB_PART=lint + env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=basic + stage: basic - os: linux node_js: "10" - env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration + env: NO_WATCH_TESTS=1 JOB_PART=lint + stage: advanced - os: linux - node_js: "8" + node_js: "10" env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration + stage: advanced - os: linux node_js: "10" env: NO_WATCH_TESTS=1 JOB_PART=unit - - os: linux - node_js: "6" - env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration + stage: advanced - os: osx node_js: "10" env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration - allow_failures: - - os: osx + stage: versions + - os: linux + node_js: "8" + env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration + stage: versions + - os: linux + node_js: "6" + env: NO_WATCH_TESTS=1 JEST=--maxWorkers=2 JOB_PART=integration + stage: versions fast_finish: true install: diff --git a/codecov.yml b/codecov.yml index 9359b370b8e..0ce74197d90 100644 --- a/codecov.yml +++ b/codecov.yml @@ -7,6 +7,9 @@ coverage: status: project: default: off + basic: + flags: basic + target: auto integration: flags: integration target: auto @@ -15,6 +18,10 @@ coverage: target: 0% patch: default: off + integration: + flags: integration + target: 90% + base: pr integration: flags: integration target: 90% @@ -25,6 +32,9 @@ coverage: base: pr changes: default: off + basic: + flags: basic + target: 0% integration: flags: integration target: 0% @@ -32,3 +42,8 @@ coverage: flags: unit target: 0% comment: off +flags: + basic: + joined: false + unit: + joined: false diff --git a/package.json b/package.json index f67472e72d0..973e968cdc0 100644 --- a/package.json +++ b/package.json @@ -99,8 +99,10 @@ "setup": "node ./setup/setup.js", "test": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest", "test:integration": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/*.test.js\"", + "test:basic": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/{TestCasesNormal,ConfigTestCases}.test.js\"", "test:unit": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/*.unittest.js\"", "travis:integration": "yarn cover:init && yarn cover:integration --ci $JEST && yarn cover:report-min", + "travis:basic": "yarn test:basic --ci $JEST", "travis:unit": "yarn cover:init && yarn cover:unit --ci", "travis:lint": "yarn lint", "travis:benchmark": "yarn benchmark --ci", From 105dd779a497b46d78af80e6610ef059d5b73c43 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 17 May 2018 19:27:51 +0200 Subject: [PATCH 04/27] Include StatsTestCases --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 973e968cdc0..0550de2fb1f 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "setup": "node ./setup/setup.js", "test": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest", "test:integration": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/*.test.js\"", - "test:basic": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/{TestCasesNormal,ConfigTestCases}.test.js\"", + "test:basic": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/{TestCasesNormal,StatsTestCases,ConfigTestCases}.test.js\"", "test:unit": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"/test/*.unittest.js\"", "travis:integration": "yarn cover:init && yarn cover:integration --ci $JEST && yarn cover:report-min", "travis:basic": "yarn test:basic --ci $JEST", From 5930cf54b92e6a839bf75f42b1eb32ace793b916 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Thu, 17 May 2018 20:33:18 +0200 Subject: [PATCH 05/27] add split chunks example --- examples/many-pages/README.md | 141 +++++++++++++++++++++++++ examples/many-pages/build.js | 3 + examples/many-pages/node_modules/m1.js | 1 + examples/many-pages/node_modules/m2.js | 1 + examples/many-pages/node_modules/m3.js | 1 + examples/many-pages/node_modules/m4.js | 1 + examples/many-pages/node_modules/m5.js | 1 + examples/many-pages/node_modules/m6.js | 1 + examples/many-pages/node_modules/m7.js | 1 + examples/many-pages/node_modules/m8.js | 1 + examples/many-pages/pages/a.js | 7 ++ examples/many-pages/pages/b.js | 7 ++ examples/many-pages/pages/c.js | 7 ++ examples/many-pages/pages/d.js | 7 ++ examples/many-pages/pages/e.js | 5 + examples/many-pages/pages/f.js | 7 ++ examples/many-pages/pages/g.js | 3 + examples/many-pages/stuff/s1.js | 1 + examples/many-pages/stuff/s2.js | 1 + examples/many-pages/stuff/s3.js | 1 + examples/many-pages/stuff/s4.js | 1 + examples/many-pages/stuff/s5.js | 1 + examples/many-pages/stuff/s6.js | 1 + examples/many-pages/stuff/s7.js | 1 + examples/many-pages/stuff/s8.js | 1 + examples/many-pages/template.md | 35 ++++++ examples/many-pages/webpack.config.js | 21 ++++ 27 files changed, 259 insertions(+) create mode 100644 examples/many-pages/README.md create mode 100644 examples/many-pages/build.js create mode 100644 examples/many-pages/node_modules/m1.js create mode 100644 examples/many-pages/node_modules/m2.js create mode 100644 examples/many-pages/node_modules/m3.js create mode 100644 examples/many-pages/node_modules/m4.js create mode 100644 examples/many-pages/node_modules/m5.js create mode 100644 examples/many-pages/node_modules/m6.js create mode 100644 examples/many-pages/node_modules/m7.js create mode 100644 examples/many-pages/node_modules/m8.js create mode 100644 examples/many-pages/pages/a.js create mode 100644 examples/many-pages/pages/b.js create mode 100644 examples/many-pages/pages/c.js create mode 100644 examples/many-pages/pages/d.js create mode 100644 examples/many-pages/pages/e.js create mode 100644 examples/many-pages/pages/f.js create mode 100644 examples/many-pages/pages/g.js create mode 100644 examples/many-pages/stuff/s1.js create mode 100644 examples/many-pages/stuff/s2.js create mode 100644 examples/many-pages/stuff/s3.js create mode 100644 examples/many-pages/stuff/s4.js create mode 100644 examples/many-pages/stuff/s5.js create mode 100644 examples/many-pages/stuff/s6.js create mode 100644 examples/many-pages/stuff/s7.js create mode 100644 examples/many-pages/stuff/s8.js create mode 100644 examples/many-pages/template.md create mode 100644 examples/many-pages/webpack.config.js diff --git a/examples/many-pages/README.md b/examples/many-pages/README.md new file mode 100644 index 00000000000..00d976fbc20 --- /dev/null +++ b/examples/many-pages/README.md @@ -0,0 +1,141 @@ +# Info + +This example illustrates webpack's algorthim for automatic deduplication using `optimization.splitChunks`. + +This example application contains 7 pages, each of them importing 1-3 modules from the `node_modules` folder (vendor libs) and 0-3 modules from the `stuff` folder (application modules). In reallity an application is probably more complex, but the same mechanisms apply. + +The following configuration is used: + +* `optimization.splitChunks.chunks: "all"` - This opt-in into automatic splitting of initial chunks which is off by default +* `optimization.splitChunks.maxInitial/AsyncRequests: 20` - This opt-in into a HTTP2 optimized splitting mode by increasing the allowed amount of requests. Browser only supports 6 requests in parallel for HTTP1.1. + +# Interpreting the result + +* `pageA.js` the normal output files for the entrypoint `pageA` +* `vendors~pageD~pageE~pageF~pageG.js` vendor libs shared by these pages extracted into a separate output file when larger then the threshold in size +* `vendors~pageA.js` vendors only used by a single page but larger than the threshold in size +* `pageA~pageD~pageF.js` application modules shared by these pages and larger than the threshold in size + +The threshold is here 40 bytes, but by default (in a real application) 30kb. + +Some modules are intentially duplicated, i. e. `./stuff/s4.js` is shared by `pageA` and `pageC`, but it's the only shared module so no separate output file is created because it would be smaller than the threshold. A separate request (which comes with an overhead and worsen gzipping) is not worth the extra bytes. + +Note: decreasing `maxInitial/AsyncRequest` will increase duplication further to reduce the number of requests. Duplication doesn't affect initial page load, it only affects download size of navigations to other pages of the application. + +## webpack.config.js + +``` +module.exports = { + // mode: "development || "production", + entry: { + pageA: "./pages/a", + pageB: "./pages/b", + pageC: "./pages/c", + pageD: "./pages/d", + pageE: "./pages/e", + pageF: "./pages/f", + pageG: "./pages/g" + }, + optimization: { + splitChunks: { + chunks: "all", + maxInitialRequests: 20, // for HTTP2 + maxAsyncRequests: 20, // for HTTP2 + minSize: 40 // for example only: choosen to match 2 modules + // omit minSize in real use case to use the default of 30kb + } + } +}; +``` + +## Production mode + +``` +Hash: 0a1b2c3d4e5f6a7b8c9d +Version: webpack 4.8.3 + Asset Size Chunks Chunk Names + pageG.js 1.15 KiB 7 [emitted] pageG +vendors~pageD~pageE~pageF~pageG.js 119 bytes 0 [emitted] vendors~pageD~pageE~pageF~pageG + vendors~pageD~pageE~pageF.js 178 bytes 2 [emitted] vendors~pageD~pageE~pageF + vendors~pageA~pageB~pageC.js 180 bytes 3 [emitted] vendors~pageA~pageB~pageC + vendors~pageC.js 121 bytes 4 [emitted] vendors~pageC + vendors~pageB.js 121 bytes 5 [emitted] vendors~pageB + vendors~pageA.js 121 bytes 6 [emitted] vendors~pageA + pageA~pageD~pageF.js 156 bytes 1 [emitted] pageA~pageD~pageF + pageF.js 1.18 KiB 8 [emitted] pageF + pageE.js 1.17 KiB 9 [emitted] pageE + pageD.js 1.18 KiB 10 [emitted] pageD + pageC.js 1.27 KiB 11 [emitted] pageC + pageB.js 1.27 KiB 12 [emitted] pageB + pageA.js 1.18 KiB 13 [emitted] pageA +Entrypoint pageA = vendors~pageA~pageB~pageC.js vendors~pageA.js pageA~pageD~pageF.js pageA.js +Entrypoint pageB = vendors~pageA~pageB~pageC.js vendors~pageB.js pageB.js +Entrypoint pageC = vendors~pageA~pageB~pageC.js vendors~pageC.js pageC.js +Entrypoint pageD = vendors~pageD~pageE~pageF~pageG.js vendors~pageD~pageE~pageF.js pageA~pageD~pageF.js pageD.js +Entrypoint pageE = vendors~pageD~pageE~pageF~pageG.js vendors~pageD~pageE~pageF.js pageE.js +Entrypoint pageF = vendors~pageD~pageE~pageF~pageG.js vendors~pageD~pageE~pageF.js pageA~pageD~pageF.js pageF.js +Entrypoint pageG = vendors~pageD~pageE~pageF~pageG.js pageG.js +chunk {0} vendors~pageD~pageE~pageF~pageG.js (vendors~pageD~pageE~pageF~pageG) 43 bytes ={1}= ={10}= ={2}= ={7}= ={8}= ={9}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageD~pageE~pageF~pageG) + > ./pages/d pageD + > ./pages/e pageE + > ./pages/f pageF + > ./pages/g pageG + 1 module +chunk {1} pageA~pageD~pageF.js (pageA~pageD~pageF) 62 bytes ={0}= ={10}= ={13}= ={2}= ={3}= ={6}= ={8}= [initial] [rendered] split chunk (cache group: default) (name: pageA~pageD~pageF) + > ./pages/a pageA + > ./pages/d pageD + > ./pages/f pageF + [6] ./stuff/s3.js 31 bytes {1} [built] + [7] ./stuff/s2.js 31 bytes {1} [built] +chunk {2} vendors~pageD~pageE~pageF.js (vendors~pageD~pageE~pageF) 86 bytes ={0}= ={1}= ={10}= ={8}= ={9}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageD~pageE~pageF) + > ./pages/d pageD + > ./pages/e pageE + > ./pages/f pageF + 2 modules +chunk {3} vendors~pageA~pageB~pageC.js (vendors~pageA~pageB~pageC) 86 bytes ={1}= ={11}= ={12}= ={13}= ={4}= ={5}= ={6}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageA~pageB~pageC) + > ./pages/a pageA + > ./pages/b pageB + > ./pages/c pageC + 2 modules +chunk {4} vendors~pageC.js (vendors~pageC) 43 bytes ={11}= ={3}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageC) + > ./pages/c pageC + 1 module +chunk {5} vendors~pageB.js (vendors~pageB) 43 bytes ={12}= ={3}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageB) + > ./pages/b pageB + 1 module +chunk {6} vendors~pageA.js (vendors~pageA) 43 bytes ={1}= ={13}= ={3}= [initial] [rendered] split chunk (cache group: vendors) (name: vendors~pageA) + > ./pages/a pageA + 1 module +chunk {7} pageG.js (pageG) 70 bytes ={0}= [entry] [rendered] + > ./pages/g pageG + [0] ./stuff/s1.js 31 bytes {7} {8} {10} {12} [built] + [10] ./pages/g.js 39 bytes {7} [built] +chunk {8} pageF.js (pageF) 144 bytes ={0}= ={1}= ={2}= [entry] [rendered] + > ./pages/f pageF + [0] ./stuff/s1.js 31 bytes {7} {8} {10} {12} [built] + [11] ./pages/f.js 113 bytes {8} [built] +chunk {9} pageE.js (pageE) 98 bytes ={0}= ={2}= [entry] [rendered] + > ./pages/e pageE + [4] ./stuff/s7.js 31 bytes {9} {12} [built] + [12] ./pages/e.js 67 bytes {9} [built] +chunk {10} pageD.js (pageD) 144 bytes ={0}= ={1}= ={2}= [entry] [rendered] + > ./pages/d pageD + [0] ./stuff/s1.js 31 bytes {7} {8} {10} {12} [built] + [13] ./pages/d.js 113 bytes {10} [built] +chunk {11} pageC.js (pageC) 206 bytes ={3}= ={4}= [entry] [rendered] + > ./pages/c pageC + [5] ./stuff/s4.js 31 bytes {11} {13} [built] + [14] ./stuff/s6.js 31 bytes {11} [built] + [15] ./stuff/s5.js 31 bytes {11} [built] + [17] ./pages/c.js 113 bytes {11} [built] +chunk {12} pageB.js (pageB) 206 bytes ={3}= ={5}= [entry] [rendered] + > ./pages/b pageB + [0] ./stuff/s1.js 31 bytes {7} {8} {10} {12} [built] + [4] ./stuff/s7.js 31 bytes {9} {12} [built] + [18] ./stuff/s8.js 31 bytes {12} [built] + [20] ./pages/b.js 113 bytes {12} [built] +chunk {13} pageA.js (pageA) 144 bytes ={1}= ={3}= ={6}= [entry] [rendered] + > ./pages/a pageA + [5] ./stuff/s4.js 31 bytes {11} {13} [built] + [22] ./pages/a.js 113 bytes {13} [built] +``` \ No newline at end of file diff --git a/examples/many-pages/build.js b/examples/many-pages/build.js new file mode 100644 index 00000000000..6ae2decc566 --- /dev/null +++ b/examples/many-pages/build.js @@ -0,0 +1,3 @@ +global.NO_TARGET_ARGS = true; +global.NO_REASONS = true; +require("../build-common"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m1.js b/examples/many-pages/node_modules/m1.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m1.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m2.js b/examples/many-pages/node_modules/m2.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m2.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m3.js b/examples/many-pages/node_modules/m3.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m3.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m4.js b/examples/many-pages/node_modules/m4.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m4.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m5.js b/examples/many-pages/node_modules/m5.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m5.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m6.js b/examples/many-pages/node_modules/m6.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m6.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m7.js b/examples/many-pages/node_modules/m7.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m7.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/node_modules/m8.js b/examples/many-pages/node_modules/m8.js new file mode 100644 index 00000000000..61c8f92a5c5 --- /dev/null +++ b/examples/many-pages/node_modules/m8.js @@ -0,0 +1 @@ +console.log("a module installed from npm"); \ No newline at end of file diff --git a/examples/many-pages/pages/a.js b/examples/many-pages/pages/a.js new file mode 100644 index 00000000000..acad484d636 --- /dev/null +++ b/examples/many-pages/pages/a.js @@ -0,0 +1,7 @@ +import "m1"; +import "m2"; +import "m3"; + +import "../stuff/s2"; +import "../stuff/s3"; +import "../stuff/s4"; diff --git a/examples/many-pages/pages/b.js b/examples/many-pages/pages/b.js new file mode 100644 index 00000000000..a25a154677d --- /dev/null +++ b/examples/many-pages/pages/b.js @@ -0,0 +1,7 @@ +import "m1"; +import "m2"; +import "m4"; + +import "../stuff/s1"; +import "../stuff/s7"; +import "../stuff/s8"; diff --git a/examples/many-pages/pages/c.js b/examples/many-pages/pages/c.js new file mode 100644 index 00000000000..cca60b6112f --- /dev/null +++ b/examples/many-pages/pages/c.js @@ -0,0 +1,7 @@ +import "m1"; +import "m2"; +import "m5"; + +import "../stuff/s4"; +import "../stuff/s5"; +import "../stuff/s6"; diff --git a/examples/many-pages/pages/d.js b/examples/many-pages/pages/d.js new file mode 100644 index 00000000000..b3f8e2bcc1e --- /dev/null +++ b/examples/many-pages/pages/d.js @@ -0,0 +1,7 @@ +import "m6"; +import "m7"; +import "m8"; + +import "../stuff/s1"; +import "../stuff/s2"; +import "../stuff/s3"; diff --git a/examples/many-pages/pages/e.js b/examples/many-pages/pages/e.js new file mode 100644 index 00000000000..05da267adf5 --- /dev/null +++ b/examples/many-pages/pages/e.js @@ -0,0 +1,5 @@ +import "m6"; +import "m7"; +import "m8"; + +import "../stuff/s7"; diff --git a/examples/many-pages/pages/f.js b/examples/many-pages/pages/f.js new file mode 100644 index 00000000000..b3f8e2bcc1e --- /dev/null +++ b/examples/many-pages/pages/f.js @@ -0,0 +1,7 @@ +import "m6"; +import "m7"; +import "m8"; + +import "../stuff/s1"; +import "../stuff/s2"; +import "../stuff/s3"; diff --git a/examples/many-pages/pages/g.js b/examples/many-pages/pages/g.js new file mode 100644 index 00000000000..49f85a16b97 --- /dev/null +++ b/examples/many-pages/pages/g.js @@ -0,0 +1,3 @@ +import "m6"; + +import "../stuff/s1"; diff --git a/examples/many-pages/stuff/s1.js b/examples/many-pages/stuff/s1.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s1.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s2.js b/examples/many-pages/stuff/s2.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s2.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s3.js b/examples/many-pages/stuff/s3.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s3.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s4.js b/examples/many-pages/stuff/s4.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s4.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s5.js b/examples/many-pages/stuff/s5.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s5.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s6.js b/examples/many-pages/stuff/s6.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s6.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s7.js b/examples/many-pages/stuff/s7.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s7.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/stuff/s8.js b/examples/many-pages/stuff/s8.js new file mode 100644 index 00000000000..b5c7e15f13a --- /dev/null +++ b/examples/many-pages/stuff/s8.js @@ -0,0 +1 @@ +console.log("some own module"); \ No newline at end of file diff --git a/examples/many-pages/template.md b/examples/many-pages/template.md new file mode 100644 index 00000000000..7bbc7b0dd44 --- /dev/null +++ b/examples/many-pages/template.md @@ -0,0 +1,35 @@ +# Info + +This example illustrates webpack's algorthim for automatic deduplication using `optimization.splitChunks`. + +This example application contains 7 pages, each of them importing 1-3 modules from the `node_modules` folder (vendor libs) and 0-3 modules from the `stuff` folder (application modules). In reallity an application is probably more complex, but the same mechanisms apply. + +The following configuration is used: + +* `optimization.splitChunks.chunks: "all"` - This opt-in into automatic splitting of initial chunks which is off by default +* `optimization.splitChunks.maxInitial/AsyncRequests: 20` - This opt-in into a HTTP2 optimized splitting mode by increasing the allowed amount of requests. Browser only supports 6 requests in parallel for HTTP1.1. + +# Interpreting the result + +* `pageA.js` the normal output files for the entrypoint `pageA` +* `vendors~pageD~pageE~pageF~pageG.js` vendor libs shared by these pages extracted into a separate output file when larger then the threshold in size +* `vendors~pageA.js` vendors only used by a single page but larger than the threshold in size +* `pageA~pageD~pageF.js` application modules shared by these pages and larger than the threshold in size + +The threshold is here 40 bytes, but by default (in a real application) 30kb. + +Some modules are intentially duplicated, i. e. `./stuff/s4.js` is shared by `pageA` and `pageC`, but it's the only shared module so no separate output file is created because it would be smaller than the threshold. A separate request (which comes with an overhead and worsen gzipping) is not worth the extra bytes. + +Note: decreasing `maxInitial/AsyncRequest` will increase duplication further to reduce the number of requests. Duplication doesn't affect initial page load, it only affects download size of navigations to other pages of the application. + +## webpack.config.js + +``` +{{webpack.config.js}} +``` + +## Production mode + +``` +{{production:stdout}} +``` \ No newline at end of file diff --git a/examples/many-pages/webpack.config.js b/examples/many-pages/webpack.config.js new file mode 100644 index 00000000000..f47598f6309 --- /dev/null +++ b/examples/many-pages/webpack.config.js @@ -0,0 +1,21 @@ +module.exports = { + // mode: "development || "production", + entry: { + pageA: "./pages/a", + pageB: "./pages/b", + pageC: "./pages/c", + pageD: "./pages/d", + pageE: "./pages/e", + pageF: "./pages/f", + pageG: "./pages/g" + }, + optimization: { + splitChunks: { + chunks: "all", + maxInitialRequests: 20, // for HTTP2 + maxAsyncRequests: 20, // for HTTP2 + minSize: 40 // for example only: choosen to match 2 modules + // omit minSize in real use case to use the default of 30kb + } + } +}; From d30aef920d5a56ddbaf61d747cba515c0fc07b38 Mon Sep 17 00:00:00 2001 From: Steven Date: Thu, 17 May 2018 20:52:50 -0400 Subject: [PATCH 06/27] Add badges to display install size --- README.md | 145 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 93 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index ef71f20eb7a..cb5e32d94ad 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,9 @@ + + install size + @@ -84,25 +87,30 @@ interface](https://webpack.js.org/plugins/). Most of the features within webpack itself use this plugin interface. This makes webpack very **flexible**. -|Name|Status|Description| -|:--:|:----:|:----------| -|[extract-text-webpack-plugin][extract]|![extract-npm]|Extracts Text (CSS) from your bundles into a separate file (app.bundle.css)| -|[compression-webpack-plugin][compression]|![compression-npm]|Prepares compressed versions of assets to serve them with Content-Encoding| -|[i18n-webpack-plugin][i18n]|![i18n-npm]|Adds i18n support to your bundles| -|[html-webpack-plugin][html-plugin]|![html-plugin-npm]| Simplifies creation of HTML files (`index.html`) to serve your bundles| +|Name|Status|Install Size|Description| +|:--:|:----:|:----------:|:----------| +|[extract-text-webpack-plugin][extract]|![extract-npm]|![extract-size]|Extracts Text (CSS) from your bundles into a separate file (app.bundle.css)| +|[compression-webpack-plugin][compression]|![compression-npm]|![compression-size]|Prepares compressed versions of assets to serve them with Content-Encoding| +|[i18n-webpack-plugin][i18n]|![i18n-npm]|![i18n-size]|Adds i18n support to your bundles| +|[html-webpack-plugin][html-plugin]|![html-plugin-npm]|![html-plugin-size]| Simplifies creation of HTML files (`index.html`) to serve your bundles| [common-npm]: https://img.shields.io/npm/v/webpack.svg [extract]: https://github.com/webpack/extract-text-webpack-plugin [extract-npm]: https://img.shields.io/npm/v/extract-text-webpack-plugin.svg +[extract-size]: https://packagephobia.now.sh/badge?p=extract-text-webpack-plugin [component]: https://github.com/webpack/component-webpack-plugin [component-npm]: https://img.shields.io/npm/v/component-webpack-plugin.svg +[component-size]: https://packagephobia.now.sh/badge?p=component-webpack-plugin [compression]: https://github.com/webpack/compression-webpack-plugin [compression-npm]: https://img.shields.io/npm/v/compression-webpack-plugin.svg +[compression-size]: https://packagephobia.now.sh/badge?p=compression-webpack-plugin [i18n]: https://github.com/webpack/i18n-webpack-plugin [i18n-npm]: https://img.shields.io/npm/v/i18n-webpack-plugin.svg +[i18n-size]: https://packagephobia.now.sh/badge?p=i18n-webpack-plugin [html-plugin]: https://github.com/ampedandwired/html-webpack-plugin [html-plugin-npm]: https://img.shields.io/npm/v/html-webpack-plugin.svg +[html-plugin-size]: https://packagephobia.now.sh/badge?p=html-webpack-plugin ### [Loaders](https://webpack.js.org/loaders/) @@ -115,121 +123,154 @@ or are automatically applied via regex from your webpack configuration. #### Files -|Name|Status|Description| -|:--:|:----:|:----------| -|[raw-loader][raw]|![raw-npm]|Loads raw content of a file (utf-8)| -|[val-loader][val]|![val-npm]|Executes code as module and considers exports as JS code| -|[url-loader][url]|![url-npm]|Works like the file loader, but can return a Data Url if the file is smaller than a limit| -|[file-loader][file]|![file-npm]|Emits the file into the output folder and returns the (relative) url| +|Name|Status|Install Size|Description| +|:--:|:----:|:----------:|:----------| +|[raw-loader][raw]|![raw-npm]|![raw-size]|Loads raw content of a file (utf-8)| +|[val-loader][val]|![val-npm]|![val-size]|Executes code as module and considers exports as JS code| +|[url-loader][url]|![url-npm]|![url-size]|Works like the file loader, but can return a Data Url if the file is smaller than a limit| +|[file-loader][file]|![file-npm]|![file-size]|Emits the file into the output folder and returns the (relative) url| [raw]: https://github.com/webpack/raw-loader [raw-npm]: https://img.shields.io/npm/v/raw-loader.svg +[raw-size]: https://packagephobia.now.sh/badge?p=raw-loader [val]: https://github.com/webpack/val-loader [val-npm]: https://img.shields.io/npm/v/val-loader.svg +[val-size]: https://packagephobia.now.sh/badge?p=val-loader [url]: https://github.com/webpack/url-loader [url-npm]: https://img.shields.io/npm/v/url-loader.svg +[url-size]: https://packagephobia.now.sh/badge?p=url-loader [file]: https://github.com/webpack/file-loader [file-npm]: https://img.shields.io/npm/v/file-loader.svg +[file-size]: https://packagephobia.now.sh/badge?p=file-loader #### JSON -|Name|Status|Description| -|:--:|:----:|:----------| -||![json-npm]|Loads a JSON file (included by default)| -||![json5-npm]|Loads and transpiles a JSON 5 file| -||![cson-npm]|Loads and transpiles a CSON file| +|Name|Status|Install Size|Description| +|:--:|:----:|:----------:|:----------| +||![json-npm]|![json-size]|Loads a JSON file (included by default)| +||![json5-npm]|![json5-size]|Loads and transpiles a JSON 5 file| +||![cson-npm]|![cson-size]|Loads and transpiles a CSON file| [json-npm]: https://img.shields.io/npm/v/json-loader.svg +[json-size]: https://packagephobia.now.sh/badge?p=json-loader [json5-npm]: https://img.shields.io/npm/v/json5-loader.svg +[json5-size]: https://packagephobia.now.sh/badge?p=json5-loader [cson-npm]: https://img.shields.io/npm/v/cson-loader.svg +[cson-size]: https://packagephobia.now.sh/badge?p=cson-loader #### Transpiling -|Name|Status|Description| -|:--:|:----:|:----------| -|`