webpack 4.15.0 → 4.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +761 -758
  3. package/SECURITY.md +9 -9
  4. package/buildin/amd-define.js +3 -3
  5. package/buildin/amd-options.js +2 -2
  6. package/buildin/global.js +20 -20
  7. package/buildin/harmony-module.js +24 -24
  8. package/buildin/module.js +22 -22
  9. package/buildin/system.js +7 -7
  10. package/hot/dev-server.js +61 -61
  11. package/hot/emitter.js +2 -2
  12. package/hot/log-apply-result.js +44 -44
  13. package/hot/log.js +45 -45
  14. package/hot/only-dev-server.js +105 -105
  15. package/hot/poll.js +40 -37
  16. package/hot/signal.js +62 -62
  17. package/lib/APIPlugin.js +84 -84
  18. package/lib/AmdMainTemplatePlugin.js +87 -87
  19. package/lib/AsyncDependencyToInitialChunkError.js +31 -31
  20. package/lib/AutomaticPrefetchPlugin.js +1 -1
  21. package/lib/BannerPlugin.js +117 -117
  22. package/lib/BasicEvaluatedExpression.js +211 -211
  23. package/lib/CachePlugin.js +102 -102
  24. package/lib/CaseSensitiveModulesWarning.js +67 -67
  25. package/lib/Chunk.js +833 -811
  26. package/lib/ChunkGroup.js +4 -4
  27. package/lib/ChunkRenderError.js +32 -32
  28. package/lib/CommentCompilationWarning.js +2 -2
  29. package/lib/CompatibilityPlugin.js +70 -70
  30. package/lib/Compilation.js +46 -15
  31. package/lib/ConcurrentCompilationError.js +19 -19
  32. package/lib/ConstPlugin.js +258 -258
  33. package/lib/ContextExclusionPlugin.js +28 -17
  34. package/lib/ContextModule.js +844 -739
  35. package/lib/ContextModuleFactory.js +262 -256
  36. package/lib/ContextReplacementPlugin.js +133 -133
  37. package/lib/DefinePlugin.js +49 -0
  38. package/lib/DelegatedModule.js +5 -0
  39. package/lib/DelegatedModuleFactoryPlugin.js +95 -89
  40. package/lib/DelegatedPlugin.js +39 -39
  41. package/lib/DependenciesBlock.js +1 -1
  42. package/lib/Dependency.js +10 -4
  43. package/lib/DllModule.js +60 -54
  44. package/lib/DllModuleFactory.js +29 -29
  45. package/lib/DllPlugin.js +44 -44
  46. package/lib/DllReferencePlugin.js +132 -84
  47. package/lib/EntryModuleNotFoundError.js +21 -21
  48. package/lib/Entrypoint.js +54 -54
  49. package/lib/EnvironmentPlugin.js +72 -65
  50. package/lib/ErrorHelpers.js +60 -60
  51. package/lib/EvalDevToolModulePlugin.js +27 -27
  52. package/lib/EvalSourceMapDevToolModuleTemplatePlugin.js +115 -115
  53. package/lib/EvalSourceMapDevToolPlugin.js +41 -41
  54. package/lib/ExportPropertyMainTemplatePlugin.js +53 -53
  55. package/lib/ExternalModule.js +165 -159
  56. package/lib/ExternalsPlugin.js +23 -23
  57. package/lib/FlagDependencyExportsPlugin.js +146 -146
  58. package/lib/FlagInitialModulesAsUsedPlugin.js +36 -36
  59. package/lib/FunctionModuleTemplatePlugin.js +100 -100
  60. package/lib/Generator.js +60 -52
  61. package/lib/HarmonyLinkingError.js +17 -17
  62. package/lib/HashedModuleIdsPlugin.js +53 -53
  63. package/lib/HotModuleReplacementPlugin.js +411 -413
  64. package/lib/IgnorePlugin.js +90 -90
  65. package/lib/JavascriptGenerator.js +229 -229
  66. package/lib/JavascriptModulesPlugin.js +179 -179
  67. package/lib/JsonGenerator.js +55 -55
  68. package/lib/JsonModulesPlugin.js +30 -30
  69. package/lib/JsonParser.js +27 -27
  70. package/lib/LibManifestPlugin.js +86 -86
  71. package/lib/LibraryTemplatePlugin.js +153 -153
  72. package/lib/LoaderOptionsPlugin.js +53 -53
  73. package/lib/LoaderTargetPlugin.js +24 -24
  74. package/lib/MemoryOutputFileSystem.js +5 -5
  75. package/lib/Module.js +431 -391
  76. package/lib/ModuleBuildError.js +52 -52
  77. package/lib/ModuleDependencyError.js +35 -35
  78. package/lib/ModuleDependencyWarning.js +25 -25
  79. package/lib/ModuleError.js +36 -36
  80. package/lib/ModuleFilenameHelpers.js +178 -178
  81. package/lib/ModuleNotFoundError.js +23 -23
  82. package/lib/ModuleParseError.js +57 -57
  83. package/lib/ModuleTemplate.js +93 -93
  84. package/lib/ModuleWarning.js +36 -36
  85. package/lib/MultiCompiler.js +283 -283
  86. package/lib/MultiModule.js +87 -81
  87. package/lib/MultiModuleFactory.js +23 -23
  88. package/lib/MultiStats.js +92 -92
  89. package/lib/MultiWatching.js +38 -38
  90. package/lib/NamedChunksPlugin.js +29 -29
  91. package/lib/NamedModulesPlugin.js +57 -57
  92. package/lib/NoEmitOnErrorsPlugin.js +20 -20
  93. package/lib/NoModeWarning.js +23 -23
  94. package/lib/NodeStuffPlugin.js +197 -179
  95. package/lib/NormalModule.js +542 -536
  96. package/lib/NormalModuleFactory.js +526 -526
  97. package/lib/NormalModuleReplacementPlugin.js +51 -51
  98. package/lib/NullFactory.js +12 -12
  99. package/lib/OptionsApply.js +10 -10
  100. package/lib/OptionsDefaulter.js +84 -84
  101. package/lib/Parser.js +2202 -2193
  102. package/lib/ParserHelpers.js +103 -103
  103. package/lib/PrefetchPlugin.js +37 -37
  104. package/lib/ProgressPlugin.js +246 -246
  105. package/lib/ProvidePlugin.js +86 -86
  106. package/lib/RawModule.js +56 -56
  107. package/lib/RecordIdsPlugin.js +230 -230
  108. package/lib/RemovedPluginError.js +11 -11
  109. package/lib/RequestShortener.js +83 -83
  110. package/lib/RequireJsStuffPlugin.js +69 -69
  111. package/lib/ResolverFactory.js +64 -64
  112. package/lib/RuntimeTemplate.js +12 -0
  113. package/lib/SetVarMainTemplatePlugin.js +69 -69
  114. package/lib/SingleEntryPlugin.js +6 -1
  115. package/lib/SizeFormatHelpers.js +24 -24
  116. package/lib/SourceMapDevToolModuleOptionsPlugin.js +49 -49
  117. package/lib/SourceMapDevToolPlugin.js +301 -301
  118. package/lib/Stats.js +28 -5
  119. package/lib/TemplatedPathPlugin.js +173 -173
  120. package/lib/UnsupportedFeatureWarning.js +22 -22
  121. package/lib/UseStrictPlugin.js +54 -54
  122. package/lib/WarnCaseSensitiveModulesPlugin.js +37 -37
  123. package/lib/WarnNoModeSetPlugin.js +17 -17
  124. package/lib/WatchIgnorePlugin.js +100 -100
  125. package/lib/Watching.js +194 -194
  126. package/lib/WebpackOptionsApply.js +92 -10
  127. package/lib/WebpackOptionsDefaulter.js +368 -354
  128. package/lib/debug/ProfilingPlugin.js +430 -430
  129. package/lib/dependencies/AMDPlugin.js +250 -250
  130. package/lib/dependencies/AMDRequireArrayDependency.js +49 -49
  131. package/lib/dependencies/AMDRequireContextDependency.js +20 -20
  132. package/lib/dependencies/AMDRequireDependency.js +135 -135
  133. package/lib/dependencies/AMDRequireItemDependency.js +22 -22
  134. package/lib/dependencies/CommonJsPlugin.js +161 -161
  135. package/lib/dependencies/CommonJsRequireContextDependency.js +23 -23
  136. package/lib/dependencies/CommonJsRequireDependency.js +22 -22
  137. package/lib/dependencies/CommonJsRequireDependencyParserPlugin.js +6 -0
  138. package/lib/dependencies/ConstDependency.js +33 -33
  139. package/lib/dependencies/ContextDependency.js +68 -68
  140. package/lib/dependencies/ContextDependencyHelpers.js +142 -142
  141. package/lib/dependencies/ContextDependencyTemplateAsId.js +42 -42
  142. package/lib/dependencies/ContextDependencyTemplateAsRequireCall.js +38 -38
  143. package/lib/dependencies/ContextElementDependency.js +21 -21
  144. package/lib/dependencies/CriticalDependencyWarning.js +20 -20
  145. package/lib/dependencies/DelegatedSourceDependency.js +18 -18
  146. package/lib/dependencies/DllEntryDependency.js +20 -20
  147. package/lib/dependencies/HarmonyAcceptDependency.js +45 -45
  148. package/lib/dependencies/HarmonyCompatibilityDependency.js +31 -31
  149. package/lib/dependencies/HarmonyDetectionParserPlugin.js +92 -92
  150. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +139 -139
  151. package/lib/dependencies/HarmonyExportHeaderDependency.js +30 -30
  152. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +2 -1
  153. package/lib/dependencies/HarmonyImportDependency.js +109 -109
  154. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +222 -222
  155. package/lib/dependencies/HarmonyImportSideEffectDependency.js +31 -31
  156. package/lib/dependencies/HarmonyImportSpecifierDependency.js +167 -167
  157. package/lib/dependencies/HarmonyInitDependency.js +60 -60
  158. package/lib/dependencies/HarmonyModulesPlugin.js +146 -146
  159. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +26 -26
  160. package/lib/dependencies/ImportContextDependency.js +23 -23
  161. package/lib/dependencies/ImportDependenciesBlock.js +18 -18
  162. package/lib/dependencies/ImportDependency.js +34 -34
  163. package/lib/dependencies/ImportEagerDependency.js +32 -32
  164. package/lib/dependencies/ImportParserPlugin.js +263 -263
  165. package/lib/dependencies/ImportPlugin.js +82 -82
  166. package/lib/dependencies/ImportWeakDependency.js +34 -34
  167. package/lib/dependencies/LoaderPlugin.js +18 -1
  168. package/lib/dependencies/LocalModule.js +23 -23
  169. package/lib/dependencies/LocalModulesHelpers.js +52 -52
  170. package/lib/dependencies/ModuleDependencyTemplateAsId.js +17 -17
  171. package/lib/dependencies/ModuleDependencyTemplateAsRequireId.js +17 -17
  172. package/lib/dependencies/ModuleHotAcceptDependency.js +23 -23
  173. package/lib/dependencies/ModuleHotDeclineDependency.js +23 -23
  174. package/lib/dependencies/NullDependency.js +20 -20
  175. package/lib/dependencies/PrefetchDependency.js +18 -18
  176. package/lib/dependencies/RequireContextDependency.js +22 -22
  177. package/lib/dependencies/RequireContextDependencyParserPlugin.js +56 -56
  178. package/lib/dependencies/RequireContextPlugin.js +143 -143
  179. package/lib/dependencies/RequireEnsureDependenciesBlock.js +33 -33
  180. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +116 -116
  181. package/lib/dependencies/RequireEnsureDependency.js +58 -58
  182. package/lib/dependencies/RequireEnsureItemDependency.js +21 -21
  183. package/lib/dependencies/RequireEnsurePlugin.js +74 -74
  184. package/lib/dependencies/RequireHeaderDependency.js +26 -26
  185. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +23 -23
  186. package/lib/dependencies/RequireIncludePlugin.js +61 -61
  187. package/lib/dependencies/RequireResolveContextDependency.js +23 -23
  188. package/lib/dependencies/RequireResolveDependency.js +22 -22
  189. package/lib/dependencies/RequireResolveDependencyParserPlugin.js +85 -85
  190. package/lib/dependencies/RequireResolveHeaderDependency.js +26 -26
  191. package/lib/dependencies/SystemPlugin.js +125 -125
  192. package/lib/dependencies/UnsupportedDependency.js +27 -27
  193. package/lib/dependencies/WebAssemblyExportImportedDependency.js +29 -29
  194. package/lib/dependencies/WebAssemblyImportDependency.js +48 -48
  195. package/lib/dependencies/WebpackMissingModule.js +20 -20
  196. package/lib/formatLocation.js +75 -61
  197. package/lib/node/NodeChunkTemplatePlugin.js +31 -31
  198. package/lib/node/NodeEnvironmentPlugin.js +28 -28
  199. package/lib/node/NodeHotUpdateChunkTemplatePlugin.js +36 -36
  200. package/lib/node/NodeMainTemplate.runtime.js +27 -27
  201. package/lib/node/NodeMainTemplatePlugin.js +323 -323
  202. package/lib/node/NodeOutputFileSystem.js +22 -22
  203. package/lib/node/NodeSourcePlugin.js +144 -144
  204. package/lib/node/NodeTargetPlugin.js +18 -18
  205. package/lib/node/NodeTemplatePlugin.js +31 -31
  206. package/lib/node/NodeWatchFileSystem.js +99 -99
  207. package/lib/node/ReadFileCompileWasmTemplatePlugin.js +61 -61
  208. package/lib/optimize/AggressiveMergingPlugin.js +87 -87
  209. package/lib/optimize/AggressiveSplittingPlugin.js +287 -287
  210. package/lib/optimize/ConcatenatedModule.js +5 -0
  211. package/lib/optimize/EnsureChunkConditionsPlugin.js +70 -70
  212. package/lib/optimize/FlagIncludedChunksPlugin.js +99 -99
  213. package/lib/optimize/LimitChunkCountPlugin.js +66 -66
  214. package/lib/optimize/MergeDuplicateChunksPlugin.js +78 -78
  215. package/lib/optimize/MinChunkSizePlugin.js +77 -77
  216. package/lib/optimize/NaturalChunkOrderPlugin.js +41 -0
  217. package/lib/optimize/OccurrenceChunkOrderPlugin.js +61 -0
  218. package/lib/optimize/OccurrenceModuleOrderPlugin.js +103 -0
  219. package/lib/optimize/OccurrenceOrderPlugin.js +135 -133
  220. package/lib/optimize/RemoveEmptyChunksPlugin.js +42 -42
  221. package/lib/optimize/RemoveParentModulesPlugin.js +127 -127
  222. package/lib/optimize/RuntimeChunkPlugin.js +41 -41
  223. package/lib/optimize/SideEffectsFlagPlugin.js +168 -168
  224. package/lib/optimize/SplitChunksPlugin.js +866 -850
  225. package/lib/performance/AssetsOverSizeLimitWarning.js +30 -30
  226. package/lib/performance/EntrypointsOverSizeLimitWarning.js +30 -30
  227. package/lib/performance/NoAsyncChunksWarning.js +21 -21
  228. package/lib/performance/SizeLimitsPlugin.js +105 -105
  229. package/lib/util/SortableSet.js +1 -0
  230. package/lib/util/StackedSetMap.js +144 -135
  231. package/lib/util/TrackingSet.js +35 -35
  232. package/lib/util/cachedMerge.js +35 -35
  233. package/lib/util/deterministicGrouping.js +251 -251
  234. package/lib/util/identifier.js +103 -103
  235. package/lib/util/objectToMap.js +16 -16
  236. package/lib/validateSchema.js +67 -67
  237. package/lib/wasm/UnsupportedWebAssemblyFeatureError.js +17 -17
  238. package/lib/wasm/WasmFinalizeExportsPlugin.js +1 -1
  239. package/lib/wasm/WebAssemblyGenerator.js +16 -2
  240. package/lib/wasm/WebAssemblyJavascriptGenerator.js +147 -133
  241. package/lib/wasm/WebAssemblyParser.js +174 -174
  242. package/lib/wasm/WebAssemblyUtils.js +59 -59
  243. package/lib/web/FetchCompileWasmTemplatePlugin.js +37 -37
  244. package/lib/web/JsonpExportMainTemplatePlugin.js +47 -47
  245. package/lib/web/JsonpHotUpdateChunkTemplatePlugin.js +39 -39
  246. package/lib/web/JsonpMainTemplate.runtime.js +65 -65
  247. package/lib/web/JsonpMainTemplatePlugin.js +1 -1
  248. package/lib/web/JsonpTemplatePlugin.js +23 -23
  249. package/lib/web/WebEnvironmentPlugin.js +18 -18
  250. package/lib/webpack.js +5 -0
  251. package/lib/webworker/WebWorkerChunkTemplatePlugin.js +35 -35
  252. package/lib/webworker/WebWorkerHotUpdateChunkTemplatePlugin.js +40 -40
  253. package/lib/webworker/WebWorkerMainTemplate.runtime.js +65 -65
  254. package/lib/webworker/WebWorkerMainTemplatePlugin.js +196 -196
  255. package/lib/webworker/WebWorkerTemplatePlugin.js +25 -25
  256. package/package.json +22 -13
  257. package/schemas/WebpackOptions.json +2062 -2036
  258. package/schemas/ajv.absolutePath.js +55 -55
  259. package/schemas/plugins/BannerPlugin.json +96 -96
  260. package/schemas/plugins/DllPlugin.json +32 -32
  261. package/schemas/plugins/DllReferencePlugin.json +99 -99
  262. package/schemas/plugins/HashedModuleIdsPlugin.json +24 -24
  263. package/schemas/plugins/LoaderOptionsPlugin.json +26 -26
  264. package/schemas/plugins/SourceMapDevToolPlugin.json +187 -187
  265. package/schemas/plugins/WatchIgnorePlugin.json +16 -16
  266. package/schemas/plugins/debug/ProfilingPlugin.json +12 -12
  267. package/schemas/plugins/optimize/AggressiveSplittingPlugin.json +22 -22
  268. package/schemas/plugins/optimize/LimitChunkCountPlugin.json +15 -15
  269. package/schemas/plugins/optimize/MinChunkSizePlugin.json +13 -13
  270. package/schemas/plugins/optimize/OccurrenceOrderChunkIdsPlugin.json +10 -0
  271. package/schemas/plugins/optimize/OccurrenceOrderModuleIdsPlugin.json +10 -0
@@ -1,850 +1,866 @@
1
- /*
2
- MIT License http://www.opensource.org/licenses/mit-license.php
3
- Author Tobias Koppers @sokra
4
- */
5
- "use strict";
6
-
7
- const crypto = require("crypto");
8
- const SortableSet = require("../util/SortableSet");
9
- const GraphHelpers = require("../GraphHelpers");
10
- const { isSubset } = require("../util/SetHelpers");
11
- const deterministicGrouping = require("../util/deterministicGrouping");
12
- const contextify = require("../util/identifier").contextify;
13
-
14
- /** @typedef {import("../Compiler")} Compiler */
15
- /** @typedef {import("../Chunk")} Chunk */
16
- /** @typedef {import("../Module")} Module */
17
- /** @typedef {import("../util/deterministicGrouping").Options<Module>} DeterministicGroupingOptionsForModule */
18
- /** @typedef {import("../util/deterministicGrouping").GroupedItems<Module>} DeterministicGroupingGroupedItemsForModule */
19
-
20
- const deterministicGroupingForModules = /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[]} */ (deterministicGrouping);
21
-
22
- const hashFilename = name => {
23
- return crypto
24
- .createHash("md4")
25
- .update(name)
26
- .digest("hex")
27
- .slice(0, 8);
28
- };
29
-
30
- const sortByIdentifier = (a, b) => {
31
- if (a.identifier() > b.identifier()) return 1;
32
- if (a.identifier() < b.identifier()) return -1;
33
- return 0;
34
- };
35
-
36
- const getRequests = chunk => {
37
- let requests = 0;
38
- for (const chunkGroup of chunk.groupsIterable) {
39
- requests = Math.max(requests, chunkGroup.chunks.length);
40
- }
41
- return requests;
42
- };
43
-
44
- const getModulesSize = modules => {
45
- let sum = 0;
46
- for (const m of modules) {
47
- sum += m.size();
48
- }
49
- return sum;
50
- };
51
-
52
- /**
53
- * @template T
54
- * @param {Set<T>} a set
55
- * @param {Set<T>} b other set
56
- * @returns {boolean} true if at least one item of a is in b
57
- */
58
- const isOverlap = (a, b) => {
59
- for (const item of a) {
60
- if (b.has(item)) return true;
61
- }
62
- return false;
63
- };
64
-
65
- const compareEntries = (a, b) => {
66
- // 1. by priority
67
- const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority;
68
- if (diffPriority) return diffPriority;
69
- // 2. by number of chunks
70
- const diffCount = a.chunks.size - b.chunks.size;
71
- if (diffCount) return diffCount;
72
- // 3. by size reduction
73
- const aSizeReduce = a.size * (a.chunks.size - 1);
74
- const bSizeReduce = b.size * (b.chunks.size - 1);
75
- const diffSizeReduce = aSizeReduce - bSizeReduce;
76
- if (diffSizeReduce) return diffSizeReduce;
77
- // 4. by number of modules (to be able to compare by identifier)
78
- const modulesA = a.modules;
79
- const modulesB = b.modules;
80
- const diff = modulesA.size - modulesB.size;
81
- if (diff) return diff;
82
- // 5. by module identifiers
83
- modulesA.sort();
84
- modulesB.sort();
85
- const aI = modulesA[Symbol.iterator]();
86
- const bI = modulesB[Symbol.iterator]();
87
- // eslint-disable-next-line no-constant-condition
88
- while (true) {
89
- const aItem = aI.next();
90
- const bItem = bI.next();
91
- if (aItem.done) return 0;
92
- const aModuleIdentifier = aItem.value.identifier();
93
- const bModuleIdentifier = bItem.value.identifier();
94
- if (aModuleIdentifier > bModuleIdentifier) return -1;
95
- if (aModuleIdentifier < bModuleIdentifier) return 1;
96
- }
97
- };
98
-
99
- const INITIAL_CHUNK_FILTER = chunk => chunk.canBeInitial();
100
- const ASYNC_CHUNK_FILTER = chunk => !chunk.canBeInitial();
101
- const ALL_CHUNK_FILTER = chunk => true;
102
-
103
- module.exports = class SplitChunksPlugin {
104
- constructor(options) {
105
- this.options = SplitChunksPlugin.normalizeOptions(options);
106
- }
107
-
108
- static normalizeOptions(options = {}) {
109
- return {
110
- chunksFilter: SplitChunksPlugin.normalizeChunksFilter(
111
- options.chunks || "all"
112
- ),
113
- minSize: options.minSize || 0,
114
- maxSize: options.maxSize || 0,
115
- minChunks: options.minChunks || 1,
116
- maxAsyncRequests: options.maxAsyncRequests || 1,
117
- maxInitialRequests: options.maxInitialRequests || 1,
118
- getName:
119
- SplitChunksPlugin.normalizeName({
120
- name: options.name,
121
- automaticNameDelimiter: options.automaticNameDelimiter
122
- }) || (() => {}),
123
- hidePathInfo: options.hidePathInfo || false,
124
- filename: options.filename || undefined,
125
- getCacheGroups: SplitChunksPlugin.normalizeCacheGroups({
126
- cacheGroups: options.cacheGroups,
127
- automaticNameDelimiter: options.automaticNameDelimiter
128
- }),
129
- fallbackCacheGroup: SplitChunksPlugin.normalizeFallbackCacheGroup(
130
- options.fallbackCacheGroup || {},
131
- options
132
- )
133
- };
134
- }
135
-
136
- static normalizeName({ name, automaticNameDelimiter }) {
137
- if (name === true) {
138
- const cache = new Map();
139
- const fn = (module, chunks, cacheGroup) => {
140
- let cacheEntry = cache.get(chunks);
141
- if (cacheEntry === undefined) {
142
- cacheEntry = {};
143
- cache.set(chunks, cacheEntry);
144
- } else if (cacheGroup in cacheEntry) {
145
- return cacheEntry[cacheGroup];
146
- }
147
- const names = chunks.map(c => c.name);
148
- if (!names.every(Boolean)) {
149
- cacheEntry[cacheGroup] = undefined;
150
- return;
151
- }
152
- names.sort();
153
- let name =
154
- (cacheGroup && cacheGroup !== "default"
155
- ? cacheGroup + automaticNameDelimiter
156
- : "") + names.join(automaticNameDelimiter);
157
- // Filenames and paths can't be too long otherwise an
158
- // ENAMETOOLONG error is raised. If the generated name if too
159
- // long, it is truncated and a hash is appended. The limit has
160
- // been set to 100 to prevent `[name].[chunkhash].[ext]` from
161
- // generating a 256+ character string.
162
- if (name.length > 100) {
163
- name =
164
- name.slice(0, 100) + automaticNameDelimiter + hashFilename(name);
165
- }
166
- cacheEntry[cacheGroup] = name;
167
- return name;
168
- };
169
- return fn;
170
- }
171
- if (typeof name === "string") {
172
- const fn = () => {
173
- return name;
174
- };
175
- return fn;
176
- }
177
- if (typeof name === "function") return name;
178
- }
179
-
180
- static normalizeChunksFilter(chunks) {
181
- if (chunks === "initial") {
182
- return INITIAL_CHUNK_FILTER;
183
- }
184
- if (chunks === "async") {
185
- return ASYNC_CHUNK_FILTER;
186
- }
187
- if (chunks === "all") {
188
- return ALL_CHUNK_FILTER;
189
- }
190
- if (typeof chunks === "function") return chunks;
191
- }
192
-
193
- static normalizeFallbackCacheGroup(
194
- {
195
- minSize = undefined,
196
- maxSize = undefined,
197
- automaticNameDelimiter = undefined
198
- },
199
- {
200
- minSize: defaultMinSize = undefined,
201
- maxSize: defaultMaxSize = undefined,
202
- automaticNameDelimiter: defaultAutomaticNameDelimiter = undefined
203
- }
204
- ) {
205
- return {
206
- minSize: typeof minSize === "number" ? minSize : defaultMinSize || 0,
207
- maxSize: typeof maxSize === "number" ? maxSize : defaultMaxSize || 0,
208
- automaticNameDelimiter:
209
- automaticNameDelimiter || defaultAutomaticNameDelimiter || "~"
210
- };
211
- }
212
-
213
- static normalizeCacheGroups({ cacheGroups, automaticNameDelimiter }) {
214
- if (typeof cacheGroups === "function") {
215
- // TODO webpack 5 remove this
216
- if (cacheGroups.length !== 1) {
217
- return module => cacheGroups(module, module.getChunks());
218
- }
219
- return cacheGroups;
220
- }
221
- if (cacheGroups && typeof cacheGroups === "object") {
222
- const fn = module => {
223
- let results;
224
- for (const key of Object.keys(cacheGroups)) {
225
- let option = cacheGroups[key];
226
- if (option === false) continue;
227
- if (option instanceof RegExp || typeof option === "string") {
228
- option = {
229
- test: option
230
- };
231
- }
232
- if (typeof option === "function") {
233
- let result = option(module);
234
- if (result) {
235
- if (results === undefined) results = [];
236
- for (const r of Array.isArray(result) ? result : [result]) {
237
- const result = Object.assign({ key }, r);
238
- if (result.name) result.getName = () => result.name;
239
- if (result.chunks) {
240
- result.chunksFilter = SplitChunksPlugin.normalizeChunksFilter(
241
- result.chunks
242
- );
243
- }
244
- results.push(result);
245
- }
246
- }
247
- } else if (SplitChunksPlugin.checkTest(option.test, module)) {
248
- if (results === undefined) results = [];
249
- results.push({
250
- key: key,
251
- priority: option.priority,
252
- getName: SplitChunksPlugin.normalizeName({
253
- name: option.name,
254
- automaticNameDelimiter
255
- }),
256
- chunksFilter: SplitChunksPlugin.normalizeChunksFilter(
257
- option.chunks
258
- ),
259
- enforce: option.enforce,
260
- minSize: option.minSize,
261
- maxSize: option.maxSize,
262
- minChunks: option.minChunks,
263
- maxAsyncRequests: option.maxAsyncRequests,
264
- maxInitialRequests: option.maxInitialRequests,
265
- filename: option.filename,
266
- reuseExistingChunk: option.reuseExistingChunk
267
- });
268
- }
269
- }
270
- return results;
271
- };
272
- return fn;
273
- }
274
- const fn = () => {};
275
- return fn;
276
- }
277
-
278
- static checkTest(test, module) {
279
- if (test === undefined) return true;
280
- if (typeof test === "function") {
281
- if (test.length !== 1) {
282
- return test(module, module.getChunks());
283
- }
284
- return test(module);
285
- }
286
- if (typeof test === "boolean") return test;
287
- if (typeof test === "string") {
288
- if (
289
- module.nameForCondition &&
290
- module.nameForCondition().startsWith(test)
291
- ) {
292
- return true;
293
- }
294
- for (const chunk of module.chunksIterable) {
295
- if (chunk.name && chunk.name.startsWith(test)) {
296
- return true;
297
- }
298
- }
299
- return false;
300
- }
301
- if (test instanceof RegExp) {
302
- if (module.nameForCondition && test.test(module.nameForCondition())) {
303
- return true;
304
- }
305
- for (const chunk of module.chunksIterable) {
306
- if (chunk.name && test.test(chunk.name)) {
307
- return true;
308
- }
309
- }
310
- return false;
311
- }
312
- return false;
313
- }
314
-
315
- /**
316
- * @param {Compiler} compiler webpack compiler
317
- * @returns {void}
318
- */
319
- apply(compiler) {
320
- compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => {
321
- let alreadyOptimized = false;
322
- compilation.hooks.unseal.tap("SplitChunksPlugin", () => {
323
- alreadyOptimized = false;
324
- });
325
- compilation.hooks.optimizeChunksAdvanced.tap(
326
- "SplitChunksPlugin",
327
- chunks => {
328
- if (alreadyOptimized) return;
329
- alreadyOptimized = true;
330
- // Give each selected chunk an index (to create strings from chunks)
331
- const indexMap = new Map();
332
- let index = 1;
333
- for (const chunk of chunks) {
334
- indexMap.set(chunk, index++);
335
- }
336
- const getKey = chunks => {
337
- return Array.from(chunks, c => indexMap.get(c))
338
- .sort()
339
- .join();
340
- };
341
- /** @type {Map<string, Set<Chunk>>} */
342
- const chunkSetsInGraph = new Map();
343
- for (const module of compilation.modules) {
344
- const chunksKey = getKey(module.chunksIterable);
345
- if (!chunkSetsInGraph.has(chunksKey)) {
346
- chunkSetsInGraph.set(chunksKey, new Set(module.chunksIterable));
347
- }
348
- }
349
-
350
- // group these set of chunks by count
351
- // to allow to check less sets via isSubset
352
- // (only smaller sets can be subset)
353
- /** @type {Map<number, Array<Set<Chunk>>>} */
354
- const chunkSetsByCount = new Map();
355
- for (const chunksSet of chunkSetsInGraph.values()) {
356
- const count = chunksSet.size;
357
- let array = chunkSetsByCount.get(count);
358
- if (array === undefined) {
359
- array = [];
360
- chunkSetsByCount.set(count, array);
361
- }
362
- array.push(chunksSet);
363
- }
364
-
365
- // Create a list of possible combinations
366
- const combinationsCache = new Map(); // Map<string, Set<Chunk>[]>
367
-
368
- const getCombinations = key => {
369
- const chunksSet = chunkSetsInGraph.get(key);
370
- var array = [chunksSet];
371
- if (chunksSet.size > 1) {
372
- for (const [count, setArray] of chunkSetsByCount) {
373
- // "equal" is not needed because they would have been merge in the first step
374
- if (count < chunksSet.size) {
375
- for (const set of setArray) {
376
- if (isSubset(chunksSet, set)) {
377
- array.push(set);
378
- }
379
- }
380
- }
381
- }
382
- }
383
- return array;
384
- };
385
-
386
- /**
387
- * @typedef {Object} SelectedChunksResult
388
- * @property {Chunk[]} chunks the list of chunks
389
- * @property {string} key a key of the list
390
- */
391
-
392
- /**
393
- * @typedef {function(Chunk): boolean} ChunkFilterFunction
394
- */
395
-
396
- /** @type {WeakMap<Set<Chunk>, WeakMap<ChunkFilterFunction, SelectedChunksResult>>} */
397
- const selectedChunksCacheByChunksSet = new WeakMap();
398
-
399
- /**
400
- * get list and key by applying the filter function to the list
401
- * It is cached for performance reasons
402
- * @param {Set<Chunk>} chunks list of chunks
403
- * @param {ChunkFilterFunction} chunkFilter filter function for chunks
404
- * @returns {SelectedChunksResult} list and key
405
- */
406
- const getSelectedChunks = (chunks, chunkFilter) => {
407
- let entry = selectedChunksCacheByChunksSet.get(chunks);
408
- if (entry === undefined) {
409
- entry = new WeakMap();
410
- selectedChunksCacheByChunksSet.set(chunks, entry);
411
- }
412
- /** @type {SelectedChunksResult} */
413
- let entry2 = entry.get(chunkFilter);
414
- if (entry2 === undefined) {
415
- /** @type {Chunk[]} */
416
- const selectedChunks = [];
417
- for (const chunk of chunks) {
418
- if (chunkFilter(chunk)) selectedChunks.push(chunk);
419
- }
420
- entry2 = {
421
- chunks: selectedChunks,
422
- key: getKey(selectedChunks)
423
- };
424
- entry.set(chunkFilter, entry2);
425
- }
426
- return entry2;
427
- };
428
-
429
- /**
430
- * @typedef {Object} ChunksInfoItem
431
- * @property {SortableSet} modules
432
- * @property {TODO} cacheGroup
433
- * @property {string} name
434
- * @property {number} size
435
- * @property {Set<Chunk>} chunks
436
- * @property {Set<Chunk>} reuseableChunks
437
- * @property {Set<string>} chunksKeys
438
- */
439
-
440
- // Map a list of chunks to a list of modules
441
- // For the key the chunk "index" is used, the value is a SortableSet of modules
442
- /** @type {Map<string, ChunksInfoItem>} */
443
- const chunksInfoMap = new Map();
444
-
445
- /**
446
- * @param {TODO} cacheGroup the current cache group
447
- * @param {Chunk[]} selectedChunks chunks selected for this module
448
- * @param {string} selectedChunksKey a key of selectedChunks
449
- * @param {Module} module the current module
450
- * @returns {void}
451
- */
452
- const addModuleToChunksInfoMap = (
453
- cacheGroup,
454
- selectedChunks,
455
- selectedChunksKey,
456
- module
457
- ) => {
458
- // Break if minimum number of chunks is not reached
459
- if (selectedChunks.length < cacheGroup.minChunks) return;
460
- // Determine name for split chunk
461
- const name = cacheGroup.getName(
462
- module,
463
- selectedChunks,
464
- cacheGroup.key
465
- );
466
- // Create key for maps
467
- // When it has a name we use the name as key
468
- // Elsewise we create the key from chunks and cache group key
469
- // This automatically merges equal names
470
- const key =
471
- (name && `name:${name}`) ||
472
- `chunks:${selectedChunksKey} key:${cacheGroup.key}`;
473
- // Add module to maps
474
- let info = chunksInfoMap.get(key);
475
- if (info === undefined) {
476
- chunksInfoMap.set(
477
- key,
478
- (info = {
479
- modules: new SortableSet(undefined, sortByIdentifier),
480
- cacheGroup,
481
- name,
482
- size: 0,
483
- chunks: new Set(),
484
- reuseableChunks: new Set(),
485
- chunksKeys: new Set()
486
- })
487
- );
488
- }
489
- info.modules.add(module);
490
- info.size += module.size();
491
- if (!info.chunksKeys.has(selectedChunksKey)) {
492
- info.chunksKeys.add(selectedChunksKey);
493
- for (const chunk of selectedChunks) {
494
- info.chunks.add(chunk);
495
- }
496
- }
497
- };
498
-
499
- // Walk through all modules
500
- for (const module of compilation.modules) {
501
- // Get cache group
502
- let cacheGroups = this.options.getCacheGroups(module);
503
- if (!Array.isArray(cacheGroups) || cacheGroups.length === 0) {
504
- continue;
505
- }
506
-
507
- // Prepare some values
508
- const chunksKey = getKey(module.chunksIterable);
509
- let combs = combinationsCache.get(chunksKey);
510
- if (combs === undefined) {
511
- combs = getCombinations(chunksKey);
512
- combinationsCache.set(chunksKey, combs);
513
- }
514
-
515
- for (const cacheGroupSource of cacheGroups) {
516
- const cacheGroup = {
517
- key: cacheGroupSource.key,
518
- priority: cacheGroupSource.priority || 0,
519
- chunksFilter:
520
- cacheGroupSource.chunksFilter || this.options.chunksFilter,
521
- minSize:
522
- cacheGroupSource.minSize !== undefined
523
- ? cacheGroupSource.minSize
524
- : cacheGroupSource.enforce
525
- ? 0
526
- : this.options.minSize,
527
- maxSize:
528
- cacheGroupSource.maxSize !== undefined
529
- ? cacheGroupSource.maxSize
530
- : cacheGroupSource.enforce
531
- ? 0
532
- : this.options.maxSize,
533
- minChunks:
534
- cacheGroupSource.minChunks !== undefined
535
- ? cacheGroupSource.minChunks
536
- : cacheGroupSource.enforce
537
- ? 1
538
- : this.options.minChunks,
539
- maxAsyncRequests:
540
- cacheGroupSource.maxAsyncRequests !== undefined
541
- ? cacheGroupSource.maxAsyncRequests
542
- : cacheGroupSource.enforce
543
- ? Infinity
544
- : this.options.maxAsyncRequests,
545
- maxInitialRequests:
546
- cacheGroupSource.maxInitialRequests !== undefined
547
- ? cacheGroupSource.maxInitialRequests
548
- : cacheGroupSource.enforce
549
- ? Infinity
550
- : this.options.maxInitialRequests,
551
- getName:
552
- cacheGroupSource.getName !== undefined
553
- ? cacheGroupSource.getName
554
- : this.options.getName,
555
- filename:
556
- cacheGroupSource.filename !== undefined
557
- ? cacheGroupSource.filename
558
- : this.options.filename,
559
- reuseExistingChunk: cacheGroupSource.reuseExistingChunk
560
- };
561
- // For all combination of chunk selection
562
- for (const chunkCombination of combs) {
563
- // Break if minimum number of chunks is not reached
564
- if (chunkCombination.size < cacheGroup.minChunks) continue;
565
- // Select chunks by configuration
566
- const {
567
- chunks: selectedChunks,
568
- key: selectedChunksKey
569
- } = getSelectedChunks(
570
- chunkCombination,
571
- cacheGroup.chunksFilter
572
- );
573
-
574
- addModuleToChunksInfoMap(
575
- cacheGroup,
576
- selectedChunks,
577
- selectedChunksKey,
578
- module
579
- );
580
- }
581
- }
582
- }
583
-
584
- /** @type {Map<Chunk, {minSize: number, maxSize: number, automaticNameDelimiter: string}>} */
585
- const maxSizeQueueMap = new Map();
586
-
587
- while (chunksInfoMap.size > 0) {
588
- // Find best matching entry
589
- let bestEntryKey;
590
- let bestEntry;
591
- for (const pair of chunksInfoMap) {
592
- const key = pair[0];
593
- const info = pair[1];
594
- if (info.size >= info.cacheGroup.minSize) {
595
- if (bestEntry === undefined) {
596
- bestEntry = info;
597
- bestEntryKey = key;
598
- } else if (compareEntries(bestEntry, info) < 0) {
599
- bestEntry = info;
600
- bestEntryKey = key;
601
- }
602
- }
603
- }
604
-
605
- // No suitable item left
606
- if (bestEntry === undefined) break;
607
-
608
- const item = bestEntry;
609
- chunksInfoMap.delete(bestEntryKey);
610
-
611
- let chunkName = item.name;
612
- // Variable for the new chunk (lazy created)
613
- /** @type {Chunk} */
614
- let newChunk;
615
- // When no chunk name, check if we can reuse a chunk instead of creating a new one
616
- let isReused = false;
617
- if (item.cacheGroup.reuseExistingChunk) {
618
- outer: for (const chunk of item.chunks) {
619
- if (chunk.getNumberOfModules() !== item.modules.size) continue;
620
- if (chunk.hasEntryModule()) continue;
621
- for (const module of item.modules) {
622
- if (!chunk.containsModule(module)) continue outer;
623
- }
624
- if (!newChunk || !newChunk.name) {
625
- newChunk = chunk;
626
- } else if (
627
- chunk.name &&
628
- chunk.name.length < newChunk.name.length
629
- ) {
630
- newChunk = chunk;
631
- } else if (
632
- chunk.name &&
633
- chunk.name.length === newChunk.name.length &&
634
- chunk.name < newChunk.name
635
- ) {
636
- newChunk = chunk;
637
- }
638
- chunkName = undefined;
639
- isReused = true;
640
- }
641
- }
642
- // Check if maxRequests condition can be fullfilled
643
-
644
- const usedChunks = Array.from(item.chunks).filter(chunk => {
645
- // skip if we address ourself
646
- return (
647
- (!chunkName || chunk.name !== chunkName) && chunk !== newChunk
648
- );
649
- });
650
-
651
- // Skip when no chunk selected
652
- if (usedChunks.length === 0) continue;
653
-
654
- const chunkInLimit = usedChunks.filter(chunk => {
655
- // respect max requests when not enforced
656
- const maxRequests = chunk.isOnlyInitial()
657
- ? item.cacheGroup.maxInitialRequests
658
- : chunk.canBeInitial()
659
- ? Math.min(
660
- item.cacheGroup.maxInitialRequests,
661
- item.cacheGroup.maxAsyncRequests
662
- )
663
- : item.cacheGroup.maxAsyncRequests;
664
- return !isFinite(maxRequests) || getRequests(chunk) < maxRequests;
665
- });
666
-
667
- if (chunkInLimit.length < usedChunks.length) {
668
- for (const module of item.modules) {
669
- addModuleToChunksInfoMap(
670
- item.cacheGroup,
671
- chunkInLimit,
672
- getKey(chunkInLimit),
673
- module
674
- );
675
- }
676
- continue;
677
- }
678
-
679
- // Create the new chunk if not reusing one
680
- if (!isReused) {
681
- newChunk = compilation.addChunk(chunkName);
682
- }
683
- // Walk through all chunks
684
- for (const chunk of usedChunks) {
685
- // Add graph connections for splitted chunk
686
- chunk.split(newChunk);
687
- }
688
-
689
- // Add a note to the chunk
690
- newChunk.chunkReason = isReused
691
- ? "reused as split chunk"
692
- : "split chunk";
693
- if (item.cacheGroup.key) {
694
- newChunk.chunkReason += ` (cache group: ${item.cacheGroup.key})`;
695
- }
696
- if (chunkName) {
697
- newChunk.chunkReason += ` (name: ${chunkName})`;
698
- // If the chosen name is already an entry point we remove the entry point
699
- const entrypoint = compilation.entrypoints.get(chunkName);
700
- if (entrypoint) {
701
- compilation.entrypoints.delete(chunkName);
702
- entrypoint.remove();
703
- newChunk.entryModule = undefined;
704
- }
705
- }
706
- if (item.cacheGroup.filename) {
707
- if (!newChunk.isOnlyInitial()) {
708
- throw new Error(
709
- "SplitChunksPlugin: You are trying to set a filename for a chunk which is (also) loaded on demand. " +
710
- "The runtime can only handle loading of chunks which match the chunkFilename schema. " +
711
- "Using a custom filename would fail at runtime. " +
712
- `(cache group: ${item.cacheGroup.key})`
713
- );
714
- }
715
- newChunk.filenameTemplate = item.cacheGroup.filename;
716
- }
717
- if (!isReused) {
718
- // Add all modules to the new chunk
719
- for (const module of item.modules) {
720
- if (typeof module.chunkCondition === "function") {
721
- if (!module.chunkCondition(newChunk)) continue;
722
- }
723
- // Add module to new chunk
724
- GraphHelpers.connectChunkAndModule(newChunk, module);
725
- // Remove module from used chunks
726
- for (const chunk of usedChunks) {
727
- chunk.removeModule(module);
728
- module.rewriteChunkInReasons(chunk, [newChunk]);
729
- }
730
- }
731
- } else {
732
- // Remove all modules from used chunks
733
- for (const module of item.modules) {
734
- for (const chunk of usedChunks) {
735
- chunk.removeModule(module);
736
- module.rewriteChunkInReasons(chunk, [newChunk]);
737
- }
738
- }
739
- }
740
-
741
- if (item.cacheGroup.maxSize > 0) {
742
- const oldMaxSizeSettings = maxSizeQueueMap.get(newChunk);
743
- maxSizeQueueMap.set(newChunk, {
744
- minSize: Math.max(
745
- oldMaxSizeSettings ? oldMaxSizeSettings.minSize : 0,
746
- item.cacheGroup.minSize
747
- ),
748
- maxSize: Math.min(
749
- oldMaxSizeSettings ? oldMaxSizeSettings.maxSize : Infinity,
750
- item.cacheGroup.maxSize
751
- ),
752
- automaticNameDelimiter: item.cacheGroup.automaticNameDelimiter
753
- });
754
- }
755
-
756
- // remove all modules from other entries and update size
757
- for (const [key, info] of chunksInfoMap) {
758
- if (isOverlap(info.chunks, item.chunks)) {
759
- const oldSize = info.modules.size;
760
- for (const module of item.modules) {
761
- info.modules.delete(module);
762
- }
763
- if (info.modules.size === 0) {
764
- chunksInfoMap.delete(key);
765
- continue;
766
- }
767
- if (info.modules.size !== oldSize) {
768
- info.size = getModulesSize(info.modules);
769
- if (info.size < info.cacheGroup.minSize) {
770
- chunksInfoMap.delete(key);
771
- }
772
- }
773
- }
774
- }
775
- }
776
-
777
- // Make sure that maxSize is fulfilled
778
- for (const chunk of compilation.chunks.slice()) {
779
- const { minSize, maxSize, automaticNameDelimiter } =
780
- maxSizeQueueMap.get(chunk) || this.options.fallbackCacheGroup;
781
- if (!maxSize) continue;
782
- const results = deterministicGroupingForModules({
783
- maxSize,
784
- minSize,
785
- items: chunk.modulesIterable,
786
- getKey(module) {
787
- const ident = contextify(
788
- compilation.options.context,
789
- module.identifier()
790
- );
791
- const name = module.nameForCondition
792
- ? contextify(
793
- compilation.options.context,
794
- module.nameForCondition()
795
- )
796
- : ident.replace(/^.*!|\?[^?!]*$/g, "");
797
- const fullKey =
798
- name + automaticNameDelimiter + hashFilename(ident);
799
- return fullKey.replace(/[\\/?]/g, "_");
800
- },
801
- getSize(module) {
802
- return module.size();
803
- }
804
- });
805
- results.sort((a, b) => {
806
- if (a.key < b.key) return -1;
807
- if (a.key > b.key) return 1;
808
- return 0;
809
- });
810
- for (let i = 0; i < results.length; i++) {
811
- const group = results[i];
812
- const key = this.options.hidePathInfo
813
- ? hashFilename(group.key)
814
- : group.key;
815
- let name = chunk.name
816
- ? chunk.name + automaticNameDelimiter + key
817
- : null;
818
- if (name && name.length > 100) {
819
- name =
820
- name.slice(0, 100) +
821
- automaticNameDelimiter +
822
- hashFilename(name);
823
- }
824
- let newPart;
825
- if (i !== results.length - 1) {
826
- newPart = compilation.addChunk(name);
827
- chunk.split(newPart);
828
- // Add all modules to the new chunk
829
- for (const module of group.items) {
830
- if (typeof module.chunkCondition === "function") {
831
- if (!module.chunkCondition(newPart)) continue;
832
- }
833
- // Add module to new chunk
834
- GraphHelpers.connectChunkAndModule(newPart, module);
835
- // Remove module from used chunks
836
- chunk.removeModule(module);
837
- module.rewriteChunkInReasons(chunk, [newPart]);
838
- }
839
- } else {
840
- // change the chunk to be a part
841
- newPart = chunk;
842
- chunk.name = name;
843
- }
844
- }
845
- }
846
- }
847
- );
848
- });
849
- }
850
- };
1
+ /*
2
+ MIT License http://www.opensource.org/licenses/mit-license.php
3
+ Author Tobias Koppers @sokra
4
+ */
5
+ "use strict";
6
+
7
+ const crypto = require("crypto");
8
+ const SortableSet = require("../util/SortableSet");
9
+ const GraphHelpers = require("../GraphHelpers");
10
+ const { isSubset } = require("../util/SetHelpers");
11
+ const deterministicGrouping = require("../util/deterministicGrouping");
12
+ const contextify = require("../util/identifier").contextify;
13
+
14
+ /** @typedef {import("../Compiler")} Compiler */
15
+ /** @typedef {import("../Chunk")} Chunk */
16
+ /** @typedef {import("../Module")} Module */
17
+ /** @typedef {import("../util/deterministicGrouping").Options<Module>} DeterministicGroupingOptionsForModule */
18
+ /** @typedef {import("../util/deterministicGrouping").GroupedItems<Module>} DeterministicGroupingGroupedItemsForModule */
19
+
20
+ const deterministicGroupingForModules = /** @type {function(DeterministicGroupingOptionsForModule): DeterministicGroupingGroupedItemsForModule[]} */ (deterministicGrouping);
21
+
22
+ const hashFilename = name => {
23
+ return crypto
24
+ .createHash("md4")
25
+ .update(name)
26
+ .digest("hex")
27
+ .slice(0, 8);
28
+ };
29
+
30
+ const sortByIdentifier = (a, b) => {
31
+ if (a.identifier() > b.identifier()) return 1;
32
+ if (a.identifier() < b.identifier()) return -1;
33
+ return 0;
34
+ };
35
+
36
+ const getRequests = chunk => {
37
+ let requests = 0;
38
+ for (const chunkGroup of chunk.groupsIterable) {
39
+ requests = Math.max(requests, chunkGroup.chunks.length);
40
+ }
41
+ return requests;
42
+ };
43
+
44
+ const getModulesSize = modules => {
45
+ let sum = 0;
46
+ for (const m of modules) {
47
+ sum += m.size();
48
+ }
49
+ return sum;
50
+ };
51
+
52
+ /**
53
+ * @template T
54
+ * @param {Set<T>} a set
55
+ * @param {Set<T>} b other set
56
+ * @returns {boolean} true if at least one item of a is in b
57
+ */
58
+ const isOverlap = (a, b) => {
59
+ for (const item of a) {
60
+ if (b.has(item)) return true;
61
+ }
62
+ return false;
63
+ };
64
+
65
+ const compareEntries = (a, b) => {
66
+ // 1. by priority
67
+ const diffPriority = a.cacheGroup.priority - b.cacheGroup.priority;
68
+ if (diffPriority) return diffPriority;
69
+ // 2. by number of chunks
70
+ const diffCount = a.chunks.size - b.chunks.size;
71
+ if (diffCount) return diffCount;
72
+ // 3. by size reduction
73
+ const aSizeReduce = a.size * (a.chunks.size - 1);
74
+ const bSizeReduce = b.size * (b.chunks.size - 1);
75
+ const diffSizeReduce = aSizeReduce - bSizeReduce;
76
+ if (diffSizeReduce) return diffSizeReduce;
77
+ // 4. by number of modules (to be able to compare by identifier)
78
+ const modulesA = a.modules;
79
+ const modulesB = b.modules;
80
+ const diff = modulesA.size - modulesB.size;
81
+ if (diff) return diff;
82
+ // 5. by module identifiers
83
+ modulesA.sort();
84
+ modulesB.sort();
85
+ const aI = modulesA[Symbol.iterator]();
86
+ const bI = modulesB[Symbol.iterator]();
87
+ // eslint-disable-next-line no-constant-condition
88
+ while (true) {
89
+ const aItem = aI.next();
90
+ const bItem = bI.next();
91
+ if (aItem.done) return 0;
92
+ const aModuleIdentifier = aItem.value.identifier();
93
+ const bModuleIdentifier = bItem.value.identifier();
94
+ if (aModuleIdentifier > bModuleIdentifier) return -1;
95
+ if (aModuleIdentifier < bModuleIdentifier) return 1;
96
+ }
97
+ };
98
+
99
+ const INITIAL_CHUNK_FILTER = chunk => chunk.canBeInitial();
100
+ const ASYNC_CHUNK_FILTER = chunk => !chunk.canBeInitial();
101
+ const ALL_CHUNK_FILTER = chunk => true;
102
+
103
+ module.exports = class SplitChunksPlugin {
104
+ constructor(options) {
105
+ this.options = SplitChunksPlugin.normalizeOptions(options);
106
+ }
107
+
108
+ static normalizeOptions(options = {}) {
109
+ return {
110
+ chunksFilter: SplitChunksPlugin.normalizeChunksFilter(
111
+ options.chunks || "all"
112
+ ),
113
+ minSize: options.minSize || 0,
114
+ maxSize: options.maxSize || 0,
115
+ minChunks: options.minChunks || 1,
116
+ maxAsyncRequests: options.maxAsyncRequests || 1,
117
+ maxInitialRequests: options.maxInitialRequests || 1,
118
+ hidePathInfo: options.hidePathInfo || false,
119
+ filename: options.filename || undefined,
120
+ getCacheGroups: SplitChunksPlugin.normalizeCacheGroups({
121
+ cacheGroups: options.cacheGroups,
122
+ name: options.name,
123
+ automaticNameDelimiter: options.automaticNameDelimiter
124
+ }),
125
+ automaticNameDelimiter: options.automaticNameDelimiter,
126
+ fallbackCacheGroup: SplitChunksPlugin.normalizeFallbackCacheGroup(
127
+ options.fallbackCacheGroup || {},
128
+ options
129
+ )
130
+ };
131
+ }
132
+
133
+ static normalizeName({ name, automaticNameDelimiter, automaticNamePrefix }) {
134
+ if (name === true) {
135
+ /** @type {WeakMap<Chunk[], Record<string, string>>} */
136
+ const cache = new WeakMap();
137
+ const fn = (module, chunks, cacheGroup) => {
138
+ let cacheEntry = cache.get(chunks);
139
+ if (cacheEntry === undefined) {
140
+ cacheEntry = {};
141
+ cache.set(chunks, cacheEntry);
142
+ } else if (cacheGroup in cacheEntry) {
143
+ return cacheEntry[cacheGroup];
144
+ }
145
+ const names = chunks.map(c => c.name);
146
+ if (!names.every(Boolean)) {
147
+ cacheEntry[cacheGroup] = undefined;
148
+ return;
149
+ }
150
+ names.sort();
151
+ const prefix =
152
+ typeof automaticNamePrefix === "string"
153
+ ? automaticNamePrefix
154
+ : cacheGroup;
155
+ const namePrefix = prefix ? prefix + automaticNameDelimiter : "";
156
+ let name = namePrefix + names.join(automaticNameDelimiter);
157
+ // Filenames and paths can't be too long otherwise an
158
+ // ENAMETOOLONG error is raised. If the generated name if too
159
+ // long, it is truncated and a hash is appended. The limit has
160
+ // been set to 100 to prevent `[name].[chunkhash].[ext]` from
161
+ // generating a 256+ character string.
162
+ if (name.length > 100) {
163
+ name =
164
+ name.slice(0, 100) + automaticNameDelimiter + hashFilename(name);
165
+ }
166
+ cacheEntry[cacheGroup] = name;
167
+ return name;
168
+ };
169
+ return fn;
170
+ }
171
+ if (typeof name === "string") {
172
+ const fn = () => {
173
+ return name;
174
+ };
175
+ return fn;
176
+ }
177
+ if (typeof name === "function") return name;
178
+ }
179
+
180
+ static normalizeChunksFilter(chunks) {
181
+ if (chunks === "initial") {
182
+ return INITIAL_CHUNK_FILTER;
183
+ }
184
+ if (chunks === "async") {
185
+ return ASYNC_CHUNK_FILTER;
186
+ }
187
+ if (chunks === "all") {
188
+ return ALL_CHUNK_FILTER;
189
+ }
190
+ if (typeof chunks === "function") return chunks;
191
+ }
192
+
193
+ static normalizeFallbackCacheGroup(
194
+ {
195
+ minSize = undefined,
196
+ maxSize = undefined,
197
+ automaticNameDelimiter = undefined
198
+ },
199
+ {
200
+ minSize: defaultMinSize = undefined,
201
+ maxSize: defaultMaxSize = undefined,
202
+ automaticNameDelimiter: defaultAutomaticNameDelimiter = undefined
203
+ }
204
+ ) {
205
+ return {
206
+ minSize: typeof minSize === "number" ? minSize : defaultMinSize || 0,
207
+ maxSize: typeof maxSize === "number" ? maxSize : defaultMaxSize || 0,
208
+ automaticNameDelimiter:
209
+ automaticNameDelimiter || defaultAutomaticNameDelimiter || "~"
210
+ };
211
+ }
212
+
213
+ static normalizeCacheGroups({ cacheGroups, name, automaticNameDelimiter }) {
214
+ if (typeof cacheGroups === "function") {
215
+ // TODO webpack 5 remove this
216
+ if (cacheGroups.length !== 1) {
217
+ return module => cacheGroups(module, module.getChunks());
218
+ }
219
+ return cacheGroups;
220
+ }
221
+ if (cacheGroups && typeof cacheGroups === "object") {
222
+ const fn = module => {
223
+ let results;
224
+ for (const key of Object.keys(cacheGroups)) {
225
+ let option = cacheGroups[key];
226
+ if (option === false) continue;
227
+ if (option instanceof RegExp || typeof option === "string") {
228
+ option = {
229
+ test: option
230
+ };
231
+ }
232
+ if (typeof option === "function") {
233
+ let result = option(module);
234
+ if (result) {
235
+ if (results === undefined) results = [];
236
+ for (const r of Array.isArray(result) ? result : [result]) {
237
+ const result = Object.assign({ key }, r);
238
+ if (result.name) result.getName = () => result.name;
239
+ if (result.chunks) {
240
+ result.chunksFilter = SplitChunksPlugin.normalizeChunksFilter(
241
+ result.chunks
242
+ );
243
+ }
244
+ results.push(result);
245
+ }
246
+ }
247
+ } else if (SplitChunksPlugin.checkTest(option.test, module)) {
248
+ if (results === undefined) results = [];
249
+ results.push({
250
+ key: key,
251
+ priority: option.priority,
252
+ getName:
253
+ SplitChunksPlugin.normalizeName({
254
+ name: option.name || name,
255
+ automaticNameDelimiter:
256
+ typeof option.automaticNameDelimiter === "string"
257
+ ? option.automaticNameDelimiter
258
+ : automaticNameDelimiter,
259
+ automaticNamePrefix: option.automaticNamePrefix
260
+ }) || (() => {}),
261
+ chunksFilter: SplitChunksPlugin.normalizeChunksFilter(
262
+ option.chunks
263
+ ),
264
+ enforce: option.enforce,
265
+ minSize: option.minSize,
266
+ maxSize: option.maxSize,
267
+ minChunks: option.minChunks,
268
+ maxAsyncRequests: option.maxAsyncRequests,
269
+ maxInitialRequests: option.maxInitialRequests,
270
+ filename: option.filename,
271
+ reuseExistingChunk: option.reuseExistingChunk
272
+ });
273
+ }
274
+ }
275
+ return results;
276
+ };
277
+ return fn;
278
+ }
279
+ const fn = () => {};
280
+ return fn;
281
+ }
282
+
283
+ static checkTest(test, module) {
284
+ if (test === undefined) return true;
285
+ if (typeof test === "function") {
286
+ if (test.length !== 1) {
287
+ return test(module, module.getChunks());
288
+ }
289
+ return test(module);
290
+ }
291
+ if (typeof test === "boolean") return test;
292
+ if (typeof test === "string") {
293
+ if (
294
+ module.nameForCondition &&
295
+ module.nameForCondition().startsWith(test)
296
+ ) {
297
+ return true;
298
+ }
299
+ for (const chunk of module.chunksIterable) {
300
+ if (chunk.name && chunk.name.startsWith(test)) {
301
+ return true;
302
+ }
303
+ }
304
+ return false;
305
+ }
306
+ if (test instanceof RegExp) {
307
+ if (module.nameForCondition && test.test(module.nameForCondition())) {
308
+ return true;
309
+ }
310
+ for (const chunk of module.chunksIterable) {
311
+ if (chunk.name && test.test(chunk.name)) {
312
+ return true;
313
+ }
314
+ }
315
+ return false;
316
+ }
317
+ return false;
318
+ }
319
+
320
+ /**
321
+ * @param {Compiler} compiler webpack compiler
322
+ * @returns {void}
323
+ */
324
+ apply(compiler) {
325
+ compiler.hooks.thisCompilation.tap("SplitChunksPlugin", compilation => {
326
+ let alreadyOptimized = false;
327
+ compilation.hooks.unseal.tap("SplitChunksPlugin", () => {
328
+ alreadyOptimized = false;
329
+ });
330
+ compilation.hooks.optimizeChunksAdvanced.tap(
331
+ "SplitChunksPlugin",
332
+ chunks => {
333
+ if (alreadyOptimized) return;
334
+ alreadyOptimized = true;
335
+ // Give each selected chunk an index (to create strings from chunks)
336
+ const indexMap = new Map();
337
+ let index = 1;
338
+ for (const chunk of chunks) {
339
+ indexMap.set(chunk, index++);
340
+ }
341
+ const getKey = chunks => {
342
+ return Array.from(chunks, c => indexMap.get(c))
343
+ .sort()
344
+ .join();
345
+ };
346
+ /** @type {Map<string, Set<Chunk>>} */
347
+ const chunkSetsInGraph = new Map();
348
+ for (const module of compilation.modules) {
349
+ const chunksKey = getKey(module.chunksIterable);
350
+ if (!chunkSetsInGraph.has(chunksKey)) {
351
+ chunkSetsInGraph.set(chunksKey, new Set(module.chunksIterable));
352
+ }
353
+ }
354
+
355
+ // group these set of chunks by count
356
+ // to allow to check less sets via isSubset
357
+ // (only smaller sets can be subset)
358
+ /** @type {Map<number, Array<Set<Chunk>>>} */
359
+ const chunkSetsByCount = new Map();
360
+ for (const chunksSet of chunkSetsInGraph.values()) {
361
+ const count = chunksSet.size;
362
+ let array = chunkSetsByCount.get(count);
363
+ if (array === undefined) {
364
+ array = [];
365
+ chunkSetsByCount.set(count, array);
366
+ }
367
+ array.push(chunksSet);
368
+ }
369
+
370
+ // Create a list of possible combinations
371
+ const combinationsCache = new Map(); // Map<string, Set<Chunk>[]>
372
+
373
+ const getCombinations = key => {
374
+ const chunksSet = chunkSetsInGraph.get(key);
375
+ var array = [chunksSet];
376
+ if (chunksSet.size > 1) {
377
+ for (const [count, setArray] of chunkSetsByCount) {
378
+ // "equal" is not needed because they would have been merge in the first step
379
+ if (count < chunksSet.size) {
380
+ for (const set of setArray) {
381
+ if (isSubset(chunksSet, set)) {
382
+ array.push(set);
383
+ }
384
+ }
385
+ }
386
+ }
387
+ }
388
+ return array;
389
+ };
390
+
391
+ /**
392
+ * @typedef {Object} SelectedChunksResult
393
+ * @property {Chunk[]} chunks the list of chunks
394
+ * @property {string} key a key of the list
395
+ */
396
+
397
+ /**
398
+ * @typedef {function(Chunk): boolean} ChunkFilterFunction
399
+ */
400
+
401
+ /** @type {WeakMap<Set<Chunk>, WeakMap<ChunkFilterFunction, SelectedChunksResult>>} */
402
+ const selectedChunksCacheByChunksSet = new WeakMap();
403
+
404
+ /**
405
+ * get list and key by applying the filter function to the list
406
+ * It is cached for performance reasons
407
+ * @param {Set<Chunk>} chunks list of chunks
408
+ * @param {ChunkFilterFunction} chunkFilter filter function for chunks
409
+ * @returns {SelectedChunksResult} list and key
410
+ */
411
+ const getSelectedChunks = (chunks, chunkFilter) => {
412
+ let entry = selectedChunksCacheByChunksSet.get(chunks);
413
+ if (entry === undefined) {
414
+ entry = new WeakMap();
415
+ selectedChunksCacheByChunksSet.set(chunks, entry);
416
+ }
417
+ /** @type {SelectedChunksResult} */
418
+ let entry2 = entry.get(chunkFilter);
419
+ if (entry2 === undefined) {
420
+ /** @type {Chunk[]} */
421
+ const selectedChunks = [];
422
+ for (const chunk of chunks) {
423
+ if (chunkFilter(chunk)) selectedChunks.push(chunk);
424
+ }
425
+ entry2 = {
426
+ chunks: selectedChunks,
427
+ key: getKey(selectedChunks)
428
+ };
429
+ entry.set(chunkFilter, entry2);
430
+ }
431
+ return entry2;
432
+ };
433
+
434
+ /**
435
+ * @typedef {Object} ChunksInfoItem
436
+ * @property {SortableSet} modules
437
+ * @property {TODO} cacheGroup
438
+ * @property {string} name
439
+ * @property {number} size
440
+ * @property {Set<Chunk>} chunks
441
+ * @property {Set<Chunk>} reuseableChunks
442
+ * @property {Set<string>} chunksKeys
443
+ */
444
+
445
+ // Map a list of chunks to a list of modules
446
+ // For the key the chunk "index" is used, the value is a SortableSet of modules
447
+ /** @type {Map<string, ChunksInfoItem>} */
448
+ const chunksInfoMap = new Map();
449
+
450
+ /**
451
+ * @param {TODO} cacheGroup the current cache group
452
+ * @param {Chunk[]} selectedChunks chunks selected for this module
453
+ * @param {string} selectedChunksKey a key of selectedChunks
454
+ * @param {Module} module the current module
455
+ * @returns {void}
456
+ */
457
+ const addModuleToChunksInfoMap = (
458
+ cacheGroup,
459
+ selectedChunks,
460
+ selectedChunksKey,
461
+ module
462
+ ) => {
463
+ // Break if minimum number of chunks is not reached
464
+ if (selectedChunks.length < cacheGroup.minChunks) return;
465
+ // Determine name for split chunk
466
+ const name = cacheGroup.getName(
467
+ module,
468
+ selectedChunks,
469
+ cacheGroup.key
470
+ );
471
+ // Create key for maps
472
+ // When it has a name we use the name as key
473
+ // Elsewise we create the key from chunks and cache group key
474
+ // This automatically merges equal names
475
+ const key =
476
+ (name && `name:${name}`) ||
477
+ `chunks:${selectedChunksKey} key:${cacheGroup.key}`;
478
+ // Add module to maps
479
+ let info = chunksInfoMap.get(key);
480
+ if (info === undefined) {
481
+ chunksInfoMap.set(
482
+ key,
483
+ (info = {
484
+ modules: new SortableSet(undefined, sortByIdentifier),
485
+ cacheGroup,
486
+ name,
487
+ size: 0,
488
+ chunks: new Set(),
489
+ reuseableChunks: new Set(),
490
+ chunksKeys: new Set()
491
+ })
492
+ );
493
+ } else {
494
+ if (info.cacheGroup !== cacheGroup) {
495
+ if (info.cacheGroup.priority < cacheGroup.priority) {
496
+ info.cacheGroup = cacheGroup;
497
+ }
498
+ }
499
+ }
500
+ info.modules.add(module);
501
+ info.size += module.size();
502
+ if (!info.chunksKeys.has(selectedChunksKey)) {
503
+ info.chunksKeys.add(selectedChunksKey);
504
+ for (const chunk of selectedChunks) {
505
+ info.chunks.add(chunk);
506
+ }
507
+ }
508
+ };
509
+
510
+ // Walk through all modules
511
+ for (const module of compilation.modules) {
512
+ // Get cache group
513
+ let cacheGroups = this.options.getCacheGroups(module);
514
+ if (!Array.isArray(cacheGroups) || cacheGroups.length === 0) {
515
+ continue;
516
+ }
517
+
518
+ // Prepare some values
519
+ const chunksKey = getKey(module.chunksIterable);
520
+ let combs = combinationsCache.get(chunksKey);
521
+ if (combs === undefined) {
522
+ combs = getCombinations(chunksKey);
523
+ combinationsCache.set(chunksKey, combs);
524
+ }
525
+
526
+ for (const cacheGroupSource of cacheGroups) {
527
+ const cacheGroup = {
528
+ key: cacheGroupSource.key,
529
+ priority: cacheGroupSource.priority || 0,
530
+ chunksFilter:
531
+ cacheGroupSource.chunksFilter || this.options.chunksFilter,
532
+ minSize:
533
+ cacheGroupSource.minSize !== undefined
534
+ ? cacheGroupSource.minSize
535
+ : cacheGroupSource.enforce
536
+ ? 0
537
+ : this.options.minSize,
538
+ maxSize:
539
+ cacheGroupSource.maxSize !== undefined
540
+ ? cacheGroupSource.maxSize
541
+ : cacheGroupSource.enforce
542
+ ? 0
543
+ : this.options.maxSize,
544
+ minChunks:
545
+ cacheGroupSource.minChunks !== undefined
546
+ ? cacheGroupSource.minChunks
547
+ : cacheGroupSource.enforce
548
+ ? 1
549
+ : this.options.minChunks,
550
+ maxAsyncRequests:
551
+ cacheGroupSource.maxAsyncRequests !== undefined
552
+ ? cacheGroupSource.maxAsyncRequests
553
+ : cacheGroupSource.enforce
554
+ ? Infinity
555
+ : this.options.maxAsyncRequests,
556
+ maxInitialRequests:
557
+ cacheGroupSource.maxInitialRequests !== undefined
558
+ ? cacheGroupSource.maxInitialRequests
559
+ : cacheGroupSource.enforce
560
+ ? Infinity
561
+ : this.options.maxInitialRequests,
562
+ getName:
563
+ cacheGroupSource.getName !== undefined
564
+ ? cacheGroupSource.getName
565
+ : this.options.getName,
566
+ filename:
567
+ cacheGroupSource.filename !== undefined
568
+ ? cacheGroupSource.filename
569
+ : this.options.filename,
570
+ automaticNameDelimiter:
571
+ cacheGroupSource.automaticNameDelimiter !== undefined
572
+ ? cacheGroupSource.automaticNameDelimiter
573
+ : this.options.automaticNameDelimiter,
574
+ reuseExistingChunk: cacheGroupSource.reuseExistingChunk
575
+ };
576
+ // For all combination of chunk selection
577
+ for (const chunkCombination of combs) {
578
+ // Break if minimum number of chunks is not reached
579
+ if (chunkCombination.size < cacheGroup.minChunks) continue;
580
+ // Select chunks by configuration
581
+ const {
582
+ chunks: selectedChunks,
583
+ key: selectedChunksKey
584
+ } = getSelectedChunks(
585
+ chunkCombination,
586
+ cacheGroup.chunksFilter
587
+ );
588
+
589
+ addModuleToChunksInfoMap(
590
+ cacheGroup,
591
+ selectedChunks,
592
+ selectedChunksKey,
593
+ module
594
+ );
595
+ }
596
+ }
597
+ }
598
+
599
+ /** @type {Map<Chunk, {minSize: number, maxSize: number, automaticNameDelimiter: string}>} */
600
+ const maxSizeQueueMap = new Map();
601
+
602
+ while (chunksInfoMap.size > 0) {
603
+ // Find best matching entry
604
+ let bestEntryKey;
605
+ let bestEntry;
606
+ for (const pair of chunksInfoMap) {
607
+ const key = pair[0];
608
+ const info = pair[1];
609
+ if (info.size >= info.cacheGroup.minSize) {
610
+ if (bestEntry === undefined) {
611
+ bestEntry = info;
612
+ bestEntryKey = key;
613
+ } else if (compareEntries(bestEntry, info) < 0) {
614
+ bestEntry = info;
615
+ bestEntryKey = key;
616
+ }
617
+ }
618
+ }
619
+
620
+ // No suitable item left
621
+ if (bestEntry === undefined) break;
622
+
623
+ const item = bestEntry;
624
+ chunksInfoMap.delete(bestEntryKey);
625
+
626
+ let chunkName = item.name;
627
+ // Variable for the new chunk (lazy created)
628
+ /** @type {Chunk} */
629
+ let newChunk;
630
+ // When no chunk name, check if we can reuse a chunk instead of creating a new one
631
+ let isReused = false;
632
+ if (item.cacheGroup.reuseExistingChunk) {
633
+ outer: for (const chunk of item.chunks) {
634
+ if (chunk.getNumberOfModules() !== item.modules.size) continue;
635
+ if (chunk.hasEntryModule()) continue;
636
+ for (const module of item.modules) {
637
+ if (!chunk.containsModule(module)) continue outer;
638
+ }
639
+ if (!newChunk || !newChunk.name) {
640
+ newChunk = chunk;
641
+ } else if (
642
+ chunk.name &&
643
+ chunk.name.length < newChunk.name.length
644
+ ) {
645
+ newChunk = chunk;
646
+ } else if (
647
+ chunk.name &&
648
+ chunk.name.length === newChunk.name.length &&
649
+ chunk.name < newChunk.name
650
+ ) {
651
+ newChunk = chunk;
652
+ }
653
+ chunkName = undefined;
654
+ isReused = true;
655
+ }
656
+ }
657
+ // Check if maxRequests condition can be fulfilled
658
+
659
+ const usedChunks = Array.from(item.chunks).filter(chunk => {
660
+ // skip if we address ourself
661
+ return (
662
+ (!chunkName || chunk.name !== chunkName) && chunk !== newChunk
663
+ );
664
+ });
665
+
666
+ // Skip when no chunk selected
667
+ if (usedChunks.length === 0) continue;
668
+
669
+ const chunkInLimit = usedChunks.filter(chunk => {
670
+ // respect max requests when not enforced
671
+ const maxRequests = chunk.isOnlyInitial()
672
+ ? item.cacheGroup.maxInitialRequests
673
+ : chunk.canBeInitial()
674
+ ? Math.min(
675
+ item.cacheGroup.maxInitialRequests,
676
+ item.cacheGroup.maxAsyncRequests
677
+ )
678
+ : item.cacheGroup.maxAsyncRequests;
679
+ return !isFinite(maxRequests) || getRequests(chunk) < maxRequests;
680
+ });
681
+
682
+ if (chunkInLimit.length < usedChunks.length) {
683
+ for (const module of item.modules) {
684
+ addModuleToChunksInfoMap(
685
+ item.cacheGroup,
686
+ chunkInLimit,
687
+ getKey(chunkInLimit),
688
+ module
689
+ );
690
+ }
691
+ continue;
692
+ }
693
+
694
+ // Create the new chunk if not reusing one
695
+ if (!isReused) {
696
+ newChunk = compilation.addChunk(chunkName);
697
+ }
698
+ // Walk through all chunks
699
+ for (const chunk of usedChunks) {
700
+ // Add graph connections for splitted chunk
701
+ chunk.split(newChunk);
702
+ }
703
+
704
+ // Add a note to the chunk
705
+ newChunk.chunkReason = isReused
706
+ ? "reused as split chunk"
707
+ : "split chunk";
708
+ if (item.cacheGroup.key) {
709
+ newChunk.chunkReason += ` (cache group: ${item.cacheGroup.key})`;
710
+ }
711
+ if (chunkName) {
712
+ newChunk.chunkReason += ` (name: ${chunkName})`;
713
+ // If the chosen name is already an entry point we remove the entry point
714
+ const entrypoint = compilation.entrypoints.get(chunkName);
715
+ if (entrypoint) {
716
+ compilation.entrypoints.delete(chunkName);
717
+ entrypoint.remove();
718
+ newChunk.entryModule = undefined;
719
+ }
720
+ }
721
+ if (item.cacheGroup.filename) {
722
+ if (!newChunk.isOnlyInitial()) {
723
+ throw new Error(
724
+ "SplitChunksPlugin: You are trying to set a filename for a chunk which is (also) loaded on demand. " +
725
+ "The runtime can only handle loading of chunks which match the chunkFilename schema. " +
726
+ "Using a custom filename would fail at runtime. " +
727
+ `(cache group: ${item.cacheGroup.key})`
728
+ );
729
+ }
730
+ newChunk.filenameTemplate = item.cacheGroup.filename;
731
+ }
732
+ if (!isReused) {
733
+ // Add all modules to the new chunk
734
+ for (const module of item.modules) {
735
+ if (typeof module.chunkCondition === "function") {
736
+ if (!module.chunkCondition(newChunk)) continue;
737
+ }
738
+ // Add module to new chunk
739
+ GraphHelpers.connectChunkAndModule(newChunk, module);
740
+ // Remove module from used chunks
741
+ for (const chunk of usedChunks) {
742
+ chunk.removeModule(module);
743
+ module.rewriteChunkInReasons(chunk, [newChunk]);
744
+ }
745
+ }
746
+ } else {
747
+ // Remove all modules from used chunks
748
+ for (const module of item.modules) {
749
+ for (const chunk of usedChunks) {
750
+ chunk.removeModule(module);
751
+ module.rewriteChunkInReasons(chunk, [newChunk]);
752
+ }
753
+ }
754
+ }
755
+
756
+ if (item.cacheGroup.maxSize > 0) {
757
+ const oldMaxSizeSettings = maxSizeQueueMap.get(newChunk);
758
+ maxSizeQueueMap.set(newChunk, {
759
+ minSize: Math.max(
760
+ oldMaxSizeSettings ? oldMaxSizeSettings.minSize : 0,
761
+ item.cacheGroup.minSize
762
+ ),
763
+ maxSize: Math.min(
764
+ oldMaxSizeSettings ? oldMaxSizeSettings.maxSize : Infinity,
765
+ item.cacheGroup.maxSize
766
+ ),
767
+ automaticNameDelimiter: item.cacheGroup.automaticNameDelimiter
768
+ });
769
+ }
770
+
771
+ // remove all modules from other entries and update size
772
+ for (const [key, info] of chunksInfoMap) {
773
+ if (isOverlap(info.chunks, item.chunks)) {
774
+ const oldSize = info.modules.size;
775
+ for (const module of item.modules) {
776
+ info.modules.delete(module);
777
+ }
778
+ if (info.modules.size === 0) {
779
+ chunksInfoMap.delete(key);
780
+ continue;
781
+ }
782
+ if (info.modules.size !== oldSize) {
783
+ info.size = getModulesSize(info.modules);
784
+ if (info.size < info.cacheGroup.minSize) {
785
+ chunksInfoMap.delete(key);
786
+ }
787
+ }
788
+ }
789
+ }
790
+ }
791
+
792
+ // Make sure that maxSize is fulfilled
793
+ for (const chunk of compilation.chunks.slice()) {
794
+ const { minSize, maxSize, automaticNameDelimiter } =
795
+ maxSizeQueueMap.get(chunk) || this.options.fallbackCacheGroup;
796
+ if (!maxSize) continue;
797
+ const results = deterministicGroupingForModules({
798
+ maxSize,
799
+ minSize,
800
+ items: chunk.modulesIterable,
801
+ getKey(module) {
802
+ const ident = contextify(
803
+ compilation.options.context,
804
+ module.identifier()
805
+ );
806
+ const name = module.nameForCondition
807
+ ? contextify(
808
+ compilation.options.context,
809
+ module.nameForCondition()
810
+ )
811
+ : ident.replace(/^.*!|\?[^?!]*$/g, "");
812
+ const fullKey =
813
+ name + automaticNameDelimiter + hashFilename(ident);
814
+ return fullKey.replace(/[\\/?]/g, "_");
815
+ },
816
+ getSize(module) {
817
+ return module.size();
818
+ }
819
+ });
820
+ results.sort((a, b) => {
821
+ if (a.key < b.key) return -1;
822
+ if (a.key > b.key) return 1;
823
+ return 0;
824
+ });
825
+ for (let i = 0; i < results.length; i++) {
826
+ const group = results[i];
827
+ const key = this.options.hidePathInfo
828
+ ? hashFilename(group.key)
829
+ : group.key;
830
+ let name = chunk.name
831
+ ? chunk.name + automaticNameDelimiter + key
832
+ : null;
833
+ if (name && name.length > 100) {
834
+ name =
835
+ name.slice(0, 100) +
836
+ automaticNameDelimiter +
837
+ hashFilename(name);
838
+ }
839
+ let newPart;
840
+ if (i !== results.length - 1) {
841
+ newPart = compilation.addChunk(name);
842
+ chunk.split(newPart);
843
+ newPart.chunkReason = chunk.chunkReason;
844
+ // Add all modules to the new chunk
845
+ for (const module of group.items) {
846
+ if (typeof module.chunkCondition === "function") {
847
+ if (!module.chunkCondition(newPart)) continue;
848
+ }
849
+ // Add module to new chunk
850
+ GraphHelpers.connectChunkAndModule(newPart, module);
851
+ // Remove module from used chunks
852
+ chunk.removeModule(module);
853
+ module.rewriteChunkInReasons(chunk, [newPart]);
854
+ }
855
+ } else {
856
+ // change the chunk to be a part
857
+ newPart = chunk;
858
+ chunk.name = name;
859
+ }
860
+ }
861
+ }
862
+ }
863
+ );
864
+ });
865
+ }
866
+ };