webpack 5.108.4 → 5.109.1

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 (200) hide show
  1. package/bin/webpack.js +12 -0
  2. package/lib/APIPlugin.js +48 -29
  3. package/lib/CacheFacade.js +8 -0
  4. package/lib/ChunkGraph.js +17 -15
  5. package/lib/ChunkGroup.js +1 -0
  6. package/lib/CompatibilityPlugin.js +62 -29
  7. package/lib/Compilation.js +58 -6
  8. package/lib/ConstPlugin.js +11 -11
  9. package/lib/ContextModule.js +239 -6
  10. package/lib/ContextModuleFactory.js +139 -14
  11. package/lib/DefinePlugin.js +433 -112
  12. package/lib/Entrypoint.js +1 -1
  13. package/lib/ExportsInfo.js +22 -5
  14. package/lib/ExportsInfoApiPlugin.js +2 -2
  15. package/lib/ExternalModule.js +107 -5
  16. package/lib/ExternalModuleFactoryPlugin.js +20 -1
  17. package/lib/FileSystemInfo.js +256 -100
  18. package/lib/FlagDependencyUsagePlugin.js +33 -12
  19. package/lib/HotModuleReplacementPlugin.js +18 -15
  20. package/lib/InitFragment.js +20 -32
  21. package/lib/Module.js +3 -0
  22. package/lib/ModuleFilenameHelpers.js +2 -2
  23. package/lib/ModuleTypeConstants.js +8 -1
  24. package/lib/MultiCompiler.js +13 -2
  25. package/lib/MultiWatching.js +1 -0
  26. package/lib/NodeStuffPlugin.js +51 -33
  27. package/lib/NormalModule.js +175 -401
  28. package/lib/NormalModuleFactory.js +76 -2
  29. package/lib/ProgressPlugin.js +211 -119
  30. package/lib/ProvidePlugin.js +2 -2
  31. package/lib/RuntimeGlobals.js +47 -0
  32. package/lib/RuntimePlugin.js +67 -107
  33. package/lib/RuntimeTemplate.js +304 -16
  34. package/lib/Template.js +23 -3
  35. package/lib/UseStrictPlugin.js +1 -1
  36. package/lib/WebpackIsIncludedPlugin.js +1 -1
  37. package/lib/WebpackOptionsApply.js +24 -19
  38. package/lib/asset/AssetGenerator.js +110 -28
  39. package/lib/asset/AssetModulesPlugin.js +92 -2
  40. package/lib/asset/WebManifestGenerator.js +164 -0
  41. package/lib/asset/WebManifestParser.js +130 -0
  42. package/lib/async-modules/AwaitDependenciesInitFragment.js +14 -4
  43. package/lib/async-modules/isGeneratorLowered.js +26 -0
  44. package/lib/cache/PackFileCacheStrategy.js +9 -3
  45. package/lib/config/browserslistTargetHandler.js +40 -0
  46. package/lib/config/defaults.js +219 -29
  47. package/lib/config/normalization.js +26 -0
  48. package/lib/config/target.js +9 -1
  49. package/lib/container/HoistContainerReferencesPlugin.js +47 -55
  50. package/lib/css/CssGenerator.js +40 -31
  51. package/lib/css/CssLoadingRuntimeModule.js +5 -1
  52. package/lib/css/CssModulesPlugin.js +13 -0
  53. package/lib/css/CssParser.js +729 -313
  54. package/lib/css/syntax.js +1371 -861
  55. package/lib/dependencies/AMDDefineDependencyParserPlugin.js +6 -6
  56. package/lib/dependencies/AMDPlugin.js +2 -2
  57. package/lib/dependencies/AMDRequireDependenciesBlockParserPlugin.js +13 -13
  58. package/lib/dependencies/CommonJsDependencyHelpers.js +39 -0
  59. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
  60. package/lib/dependencies/CommonJsExportsDependency.js +47 -2
  61. package/lib/dependencies/CommonJsExportsParserPlugin.js +10 -10
  62. package/lib/dependencies/CommonJsImportsParserPlugin.js +18 -18
  63. package/lib/dependencies/CommonJsPlugin.js +2 -2
  64. package/lib/dependencies/CommonJsRequireDependency.js +26 -1
  65. package/lib/dependencies/CommonJsSelfReferenceDependency.js +32 -1
  66. package/lib/dependencies/ContextDependencyHelpers.js +3 -3
  67. package/lib/dependencies/CreateRequireParserPlugin.js +6 -6
  68. package/lib/dependencies/CssIcssExportDependency.js +162 -71
  69. package/lib/dependencies/CssUrlDependency.js +35 -4
  70. package/lib/dependencies/ExternalModuleInitFragmentDependency.js +2 -2
  71. package/lib/dependencies/HarmonyCompatibilityDependency.js +32 -14
  72. package/lib/dependencies/HarmonyDetectionParserPlugin.js +43 -13
  73. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
  74. package/lib/dependencies/HarmonyExportDependencyParserPlugin.js +7 -19
  75. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
  76. package/lib/dependencies/HarmonyImportDependency.js +13 -2
  77. package/lib/dependencies/HarmonyImportDependencyParserPlugin.js +11 -11
  78. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
  79. package/lib/dependencies/HarmonyModulesPlugin.js +6 -0
  80. package/lib/dependencies/HarmonyTopLevelThisParserPlugin.js +1 -1
  81. package/lib/dependencies/HtmlEntryDependency.js +797 -172
  82. package/lib/dependencies/HtmlSourceDependency.js +26 -0
  83. package/lib/dependencies/ImportDependency.js +43 -1
  84. package/lib/dependencies/ImportMetaContextDependencyParserPlugin.js +31 -18
  85. package/lib/dependencies/ImportMetaContextPlugin.js +38 -7
  86. package/lib/dependencies/ImportMetaGlobDependency.js +81 -0
  87. package/lib/dependencies/ImportMetaGlobDependencyParserPlugin.js +78 -0
  88. package/lib/dependencies/ImportMetaGlobHelpers.js +660 -0
  89. package/lib/dependencies/ImportMetaPlugin.js +532 -180
  90. package/lib/dependencies/ImportMetaResolveDependency.js +90 -0
  91. package/lib/dependencies/ImportParserPlugin.js +80 -17
  92. package/lib/dependencies/ImportPhase.js +10 -8
  93. package/lib/dependencies/LoaderPlugin.js +5 -1
  94. package/lib/dependencies/RequireContextDependencyParserPlugin.js +1 -1
  95. package/lib/dependencies/RequireEnsureDependenciesBlockParserPlugin.js +3 -6
  96. package/lib/dependencies/RequireIncludeDependencyParserPlugin.js +4 -11
  97. package/lib/dependencies/SystemPlugin.js +2 -4
  98. package/lib/dependencies/TopLevelAwaitDependency.js +93 -0
  99. package/lib/dependencies/URLDependency.js +141 -21
  100. package/lib/dependencies/{WorkerPlugin.js → WorkerAndWorkletPlugin.js} +324 -112
  101. package/lib/dependencies/WorkerDependency.js +86 -18
  102. package/lib/dependencies/WorkletDependency.js +278 -0
  103. package/lib/dll/DelegatedModuleFactoryPlugin.js +19 -0
  104. package/lib/errors/EnvironmentNotSupportAsyncWarning.js +1 -1
  105. package/lib/errors/LoaderLoadingError.js +20 -0
  106. package/lib/errors/ModuleParseError.js +39 -22
  107. package/lib/errors/NodeStuffInWebError.js +1 -1
  108. package/lib/esm/ModuleChunkFormatPlugin.js +47 -14
  109. package/lib/esm/ModuleChunkLoadingPlugin.js +8 -1
  110. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +29 -1
  111. package/lib/hmr/lazyCompilationBackend.js +51 -12
  112. package/lib/html/HtmlGenerator.js +907 -15
  113. package/lib/html/HtmlModule.js +2 -2
  114. package/lib/html/HtmlModulesPlugin.js +610 -166
  115. package/lib/html/HtmlParser.js +603 -247
  116. package/lib/html/favicon.svg +1 -0
  117. package/lib/html/syntax.js +3451 -2762
  118. package/lib/ids/IdHelpers.js +25 -11
  119. package/lib/index.js +6 -0
  120. package/lib/javascript/ArrayPushCallbackChunkFormatPlugin.js +4 -5
  121. package/lib/javascript/BasicEvaluatedExpression.js +366 -48
  122. package/lib/javascript/JavascriptGenerator.js +197 -49
  123. package/lib/javascript/JavascriptModulesPlugin.js +178 -24
  124. package/lib/javascript/JavascriptParser.js +704 -355
  125. package/lib/javascript/JavascriptParserHelpers.js +3 -6
  126. package/lib/javascript/syntax.js +5037 -0
  127. package/lib/library/ModuleLibraryPlugin.js +38 -1
  128. package/lib/loaders/LoaderRunner.js +714 -0
  129. package/lib/loaders/loadLoader.js +100 -0
  130. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +29 -2
  131. package/lib/node/ReadFileCompileWasmPlugin.js +24 -2
  132. package/lib/optimize/AggressiveMergingPlugin.js +19 -15
  133. package/lib/optimize/ConcatenatedModule.js +139 -31
  134. package/lib/optimize/FlagIncludedChunksPlugin.js +13 -3
  135. package/lib/optimize/InnerGraphPlugin.js +50 -30
  136. package/lib/optimize/ModuleConcatenationPlugin.js +139 -24
  137. package/lib/optimize/SideEffectsFlagPlugin.js +86 -43
  138. package/lib/optimize/SplitChunksPlugin.js +146 -44
  139. package/lib/prefetch/ChunkPrefetchPreloadPlugin.js +15 -0
  140. package/lib/prefetch/ChunkPreloadTriggerRuntimeModule.js +24 -13
  141. package/lib/prefetch/ResourceHintPlugin.js +687 -0
  142. package/lib/prefetch/ResourceHintRuntimeModule.js +149 -0
  143. package/lib/prefetch/StartupAssetHintRuntimeModule.js +210 -0
  144. package/lib/prefetch/parseResourceHintOptions.js +103 -0
  145. package/lib/rules/RuleSetCompiler.js +114 -0
  146. package/lib/runtime/AsyncModuleGeneratorRuntimeModule.js +56 -0
  147. package/lib/runtime/AsyncModuleRuntimeModule.js +24 -8
  148. package/lib/runtime/AutoPublicPathRuntimeModule.js +7 -1
  149. package/lib/runtime/CommonJsWrapRuntimeModule.js +37 -0
  150. package/lib/runtime/GetWorkletBootstrapRuntimeModule.js +59 -0
  151. package/lib/runtime/MakeDeferredNamespaceObjectRuntime.js +204 -8
  152. package/lib/runtime/SetAnonymousDefaultNameRuntimeModule.js +4 -1
  153. package/lib/schemes/HttpUriPlugin.js +40 -1
  154. package/lib/schemes/VirtualUrlPlugin.js +1 -2
  155. package/lib/serialization/BinaryMiddleware.js +270 -938
  156. package/lib/serialization/FileMiddleware.js +29 -3
  157. package/lib/stats/DefaultStatsFactoryPlugin.js +229 -194
  158. package/lib/stats/DefaultStatsPresetPlugin.js +2 -0
  159. package/lib/stats/DefaultStatsPrinterPlugin.js +36 -168
  160. package/lib/stats/StatsFactory.js +46 -17
  161. package/lib/url/URLParserPlugin.js +66 -13
  162. package/lib/util/AsyncQueue.js +122 -72
  163. package/lib/util/SetHelpers.js +1 -1
  164. package/lib/util/StackedMap.js +122 -48
  165. package/lib/util/binarySearchBounds.js +2 -2
  166. package/lib/util/concatenate.js +40 -14
  167. package/lib/util/createHooksRegistry.js +13 -5
  168. package/lib/util/dataURL.js +2 -2
  169. package/lib/util/deterministicGrouping.js +7 -5
  170. package/lib/util/formatSize.js +6 -2
  171. package/lib/util/globUtils.js +710 -0
  172. package/lib/util/identifier.js +23 -6
  173. package/lib/util/implicitTypeLoaderFallback.js +62 -0
  174. package/lib/util/internalSerializables.js +88 -71
  175. package/lib/util/magicComment.js +121 -25
  176. package/lib/util/numberHash.js +2 -2
  177. package/lib/wasm-async/AsyncWasmCompileRuntimeModule.js +24 -18
  178. package/lib/wasm-async/AsyncWasmLoadingRuntimeModule.js +33 -22
  179. package/lib/wasm-sync/WasmChunkLoadingRuntimeModule.js +79 -16
  180. package/lib/web/FetchCompileAsyncWasmPlugin.js +19 -0
  181. package/lib/web/FetchCompileWasmPlugin.js +14 -0
  182. package/lib/web/JsonpChunkLoadingRuntimeModule.js +2 -0
  183. package/lib/webpack.js +65 -0
  184. package/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +3 -0
  185. package/module.d.ts +21 -0
  186. package/package.json +12 -11
  187. package/schemas/WebpackOptions.check.js +1 -1
  188. package/schemas/WebpackOptions.json +759 -36
  189. package/schemas/plugins/HtmlParserOptions.check.js +1 -1
  190. package/schemas/plugins/ProgressPlugin.check.js +1 -1
  191. package/schemas/plugins/ProgressPlugin.json +17 -1
  192. package/schemas/plugins/container/ContainerReferencePlugin.check.js +1 -1
  193. package/schemas/plugins/container/ContainerReferencePlugin.json +1 -0
  194. package/schemas/plugins/container/ExternalsType.check.js +1 -1
  195. package/schemas/plugins/container/ModuleFederationPlugin.check.js +1 -1
  196. package/schemas/plugins/container/ModuleFederationPlugin.json +1 -0
  197. package/schemas/plugins/css/CssAutoOrModuleParserOptions.check.js +1 -1
  198. package/schemas/plugins/css/CssModuleParserOptions.check.js +1 -1
  199. package/schemas/plugins/css/CssParserOptions.check.js +1 -1
  200. package/types.d.ts +1212 -99
@@ -182,7 +182,6 @@ const MinMaxSizeWarning = require("./MinMaxSizeWarning");
182
182
  * @property {string=} name
183
183
  * @property {SplitChunksSizes} sizes
184
184
  * @property {ChunkSet} chunks
185
- * @property {ChunkSet} reusableChunks
186
185
  * @property {Set<bigint | Chunk>} chunksKeys
187
186
  */
188
187
 
@@ -229,7 +228,6 @@ const getRequests = (chunk) => {
229
228
  /**
230
229
  * Returns result.
231
230
  * @template {object} T
232
- * @template {object} R
233
231
  * @param {T} obj obj an object
234
232
  * @param {(obj: T[keyof T], key: keyof T) => T[keyof T]} fn fn
235
233
  * @returns {T} result
@@ -314,6 +312,9 @@ const ASYNC_CHUNK_FILTER = (chunk) => !chunk.canBeInitial();
314
312
  */
315
313
  const ALL_CHUNK_FILTER = (_chunk) => true;
316
314
 
315
+ /** Placeholder until a chunk set group is filled and its signatures are written. */
316
+ const EMPTY_SIGNATURES = new Uint32Array(0);
317
+
317
318
  /**
318
319
  * Returns normalized representation.
319
320
  * @param {OptimizationSplitChunksSizes | undefined} value the sizes
@@ -902,15 +903,21 @@ module.exports = class SplitChunksPlugin {
902
903
  // Give each selected chunk an index (to create strings from chunks)
903
904
  /** @type {Map<Chunk, bigint>} */
904
905
  const chunkIndexMap = new Map();
906
+ // Dense per-chunk index, folded into the 64 bit membership
907
+ // signatures that reject non-subsets in getCombinations
908
+ /** @type {Map<Chunk, number>} */
909
+ const chunkSignatureBitMap = new Map();
905
910
  const ZERO = BigInt("0");
906
911
  const ONE = BigInt("1");
907
912
  const START = ONE << BigInt("31");
908
913
  let index = START;
914
+ let denseIndex = 0;
909
915
  for (const chunk of chunks) {
910
916
  chunkIndexMap.set(
911
917
  chunk,
912
918
  index | BigInt((Math.random() * 0x7fffffff) | 0)
913
919
  );
920
+ chunkSignatureBitMap.set(chunk, denseIndex++ & 63);
914
921
  index <<= ONE;
915
922
  }
916
923
  /**
@@ -1011,7 +1018,36 @@ module.exports = class SplitChunksPlugin {
1011
1018
  return { chunkSetsInGraph, singleChunkSets };
1012
1019
  });
1013
1020
 
1014
- /** @typedef {Map<CountOfChunk, ChunkSet[]>} ChunkSetsByCount */
1021
+ /**
1022
+ * Chunk sets of equal size, with a 64 bit membership signature
1023
+ * per set stored as two words at `2 * i` in `signatures`.
1024
+ * @typedef {object} ChunkSetGroup
1025
+ * @property {ChunkSet[]} sets the chunk sets
1026
+ * @property {Uint32Array} signatures two signature words per set
1027
+ */
1028
+
1029
+ /** @typedef {Map<CountOfChunk, ChunkSetGroup>} ChunkSetsByCount */
1030
+
1031
+ // holds the signature of the set currently being combined; transient
1032
+ const signatureScratch = new Uint32Array(2);
1033
+
1034
+ /**
1035
+ * Folds a chunk set into a 64 bit membership signature.
1036
+ * @param {ChunkSet} chunksSet set of chunks
1037
+ * @param {Uint32Array} out target array
1038
+ * @param {number} offset index of the first of the two words
1039
+ * @returns {void}
1040
+ */
1041
+ const writeSignature = (chunksSet, out, offset) => {
1042
+ out[offset] = 0;
1043
+ out[offset + 1] = 0;
1044
+ for (const chunk of chunksSet) {
1045
+ const bit = /** @type {number} */ (
1046
+ chunkSignatureBitMap.get(chunk)
1047
+ );
1048
+ out[offset + (bit >> 5)] |= 1 << (bit & 31);
1049
+ }
1050
+ };
1015
1051
 
1016
1052
  // group these set of chunks by count
1017
1053
  // to allow to check less sets via isSubset
@@ -1026,12 +1062,20 @@ module.exports = class SplitChunksPlugin {
1026
1062
  const chunkSetsByCount = new Map();
1027
1063
  for (const chunksSet of chunkSets) {
1028
1064
  const count = chunksSet.size;
1029
- let array = chunkSetsByCount.get(count);
1030
- if (array === undefined) {
1031
- array = [];
1032
- chunkSetsByCount.set(count, array);
1065
+ let group = chunkSetsByCount.get(count);
1066
+ if (group === undefined) {
1067
+ group = { sets: [], signatures: EMPTY_SIGNATURES };
1068
+ chunkSetsByCount.set(count, group);
1033
1069
  }
1034
- array.push(chunksSet);
1070
+ group.sets.push(chunksSet);
1071
+ }
1072
+ for (const group of chunkSetsByCount.values()) {
1073
+ const { sets } = group;
1074
+ const signatures = new Uint32Array(sets.length * 2);
1075
+ for (let i = 0; i < sets.length; i++) {
1076
+ writeSignature(sets[i], signatures, i * 2);
1077
+ }
1078
+ group.signatures = signatures;
1035
1079
  }
1036
1080
  return chunkSetsByCount;
1037
1081
  };
@@ -1077,10 +1121,24 @@ module.exports = class SplitChunksPlugin {
1077
1121
  (chunkSets.get(key));
1078
1122
  /** @type {Combinations} */
1079
1123
  const array = [chunksSet];
1080
- for (const [count, setArray] of chunkSetsByCount) {
1124
+ writeSignature(chunksSet, signatureScratch, 0);
1125
+ const selfLow = signatureScratch[0];
1126
+ const selfHigh = signatureScratch[1];
1127
+ for (const [count, group] of chunkSetsByCount) {
1081
1128
  // "equal" is not needed because they would have been merge in the first step
1082
1129
  if (count < chunksSet.size) {
1083
- for (const set of setArray) {
1130
+ const { sets, signatures } = group;
1131
+ for (let i = 0; i < sets.length; i++) {
1132
+ // a subset cannot hold a signature bit the superset lacks,
1133
+ // so this rejects nearly every candidate without touching the Set
1134
+ const offset = i * 2;
1135
+ if (
1136
+ (signatures[offset] & ~selfLow) !== 0 ||
1137
+ (signatures[offset + 1] & ~selfHigh) !== 0
1138
+ ) {
1139
+ continue;
1140
+ }
1141
+ const set = sets[i];
1084
1142
  if (isSubset(chunksSet, set)) {
1085
1143
  array.push(set);
1086
1144
  }
@@ -1187,6 +1245,31 @@ module.exports = class SplitChunksPlugin {
1187
1245
  /** @type {Map<string, ChunksInfoItem>} */
1188
1246
  const chunksInfoMap = new Map();
1189
1247
 
1248
+ // The unnamed key is derived only from cache group and chunk set, so it
1249
+ // repeats for every module sharing that pair — build the string once
1250
+ /** @type {Map<CacheGroup, Map<bigint | Chunk, string>>} */
1251
+ const unnamedKeyCache = new Map();
1252
+
1253
+ /**
1254
+ * Returns the chunksInfoMap key for a cache group and chunk set.
1255
+ * @param {CacheGroup} cacheGroup the current cache group
1256
+ * @param {bigint | Chunk} selectedChunksKey a key of selectedChunks
1257
+ * @returns {string} key into chunksInfoMap
1258
+ */
1259
+ const getUnnamedKey = (cacheGroup, selectedChunksKey) => {
1260
+ let byChunksKey = unnamedKeyCache.get(cacheGroup);
1261
+ if (byChunksKey === undefined) {
1262
+ byChunksKey = new Map();
1263
+ unnamedKeyCache.set(cacheGroup, byChunksKey);
1264
+ }
1265
+ let key = byChunksKey.get(selectedChunksKey);
1266
+ if (key === undefined) {
1267
+ key = `${cacheGroup.key} chunks:${keyToString(selectedChunksKey)}`;
1268
+ byChunksKey.set(selectedChunksKey, key);
1269
+ }
1270
+ return key;
1271
+ };
1272
+
1190
1273
  /**
1191
1274
  * Adds module to chunks info map.
1192
1275
  * @param {CacheGroup} cacheGroup the current cache group
@@ -1270,11 +1353,9 @@ module.exports = class SplitChunksPlugin {
1270
1353
  // When it has a name we use the name as key
1271
1354
  // Otherwise we create the key from chunks and cache group key
1272
1355
  // This automatically merges equal names
1273
- const key =
1274
- cacheGroup.key +
1275
- (name
1276
- ? ` name:${name}`
1277
- : ` chunks:${keyToString(selectedChunksKey)}`);
1356
+ const key = name
1357
+ ? `${cacheGroup.key} name:${name}`
1358
+ : getUnnamedKey(cacheGroup, selectedChunksKey);
1278
1359
  // Add module to maps
1279
1360
  let info = chunksInfoMap.get(key);
1280
1361
  if (info === undefined) {
@@ -1290,7 +1371,6 @@ module.exports = class SplitChunksPlugin {
1290
1371
  name,
1291
1372
  sizes: {},
1292
1373
  chunks: new Set(),
1293
- reusableChunks: new Set(),
1294
1374
  chunksKeys: new Set()
1295
1375
  })
1296
1376
  );
@@ -1316,6 +1396,35 @@ module.exports = class SplitChunksPlugin {
1316
1396
  chunkGraph
1317
1397
  };
1318
1398
 
1399
+ /**
1400
+ * Returns combinations of the chunks the module is in.
1401
+ * @param {Module} module the module
1402
+ * @returns {Combinations} chunk combinations
1403
+ */
1404
+ const computeCombinations = (module) =>
1405
+ getCombinations(getKey(chunkGraph.getModuleChunksIterable(module)));
1406
+
1407
+ /**
1408
+ * Returns combinations of the module's chunks grouped by used exports.
1409
+ * @param {Module} module the module
1410
+ * @returns {Set<ChunkSet | Chunk>} chunk combinations
1411
+ */
1412
+ const computeCombinationsByUsedExports = (module) => {
1413
+ // fill the groupedByExportsMap
1414
+ getExportsChunkSetsInGraph();
1415
+ /** @type {Set<ChunkSet | Chunk>} */
1416
+ const set = new Set();
1417
+ const groupedByUsedExports =
1418
+ /** @type {Iterable<Chunk[]>} */
1419
+ (groupedByExportsMap.get(module));
1420
+ for (const chunks of groupedByUsedExports) {
1421
+ for (const comb of getExportsCombinations(getKey(chunks))) {
1422
+ set.add(comb);
1423
+ }
1424
+ }
1425
+ return set;
1426
+ };
1427
+
1319
1428
  logger.timeEnd("prepare");
1320
1429
 
1321
1430
  logger.time("modules");
@@ -1328,38 +1437,29 @@ module.exports = class SplitChunksPlugin {
1328
1437
  continue;
1329
1438
  }
1330
1439
 
1331
- // Prepare some values (usedExports = false)
1332
- const getCombs = memoize(() => {
1333
- const chunks = chunkGraph.getModuleChunksIterable(module);
1334
- const chunksKey = getKey(chunks);
1335
- return getCombinations(chunksKey);
1336
- });
1337
-
1338
- // Prepare some values (usedExports = true)
1339
- const getCombsByUsedExports = memoize(() => {
1340
- // fill the groupedByExportsMap
1341
- getExportsChunkSetsInGraph();
1342
- /** @type {Set<ChunkSet | Chunk>} */
1343
- const set = new Set();
1344
- const groupedByUsedExports =
1345
- /** @type {Iterable<Chunk[]>} */
1346
- (groupedByExportsMap.get(module));
1347
- for (const chunks of groupedByUsedExports) {
1348
- const chunksKey = getKey(chunks);
1349
- for (const comb of getExportsCombinations(chunksKey)) {
1350
- set.add(comb);
1351
- }
1352
- }
1353
- return set;
1354
- });
1440
+ // Computed at most once per module, on first use by a cache group
1441
+ /** @type {Combinations | undefined} */
1442
+ let combsCache;
1443
+ /** @type {Set<ChunkSet | Chunk> | undefined} */
1444
+ let combsByUsedExportsCache;
1355
1445
 
1356
1446
  let cacheGroupIndex = 0;
1357
1447
  for (const cacheGroupSource of cacheGroups) {
1358
1448
  const cacheGroup = this._getCacheGroup(cacheGroupSource);
1359
1449
 
1360
- const combs = cacheGroup.usedExports
1361
- ? getCombsByUsedExports()
1362
- : getCombs();
1450
+ let combs;
1451
+ if (cacheGroup.usedExports) {
1452
+ if (combsByUsedExportsCache === undefined) {
1453
+ combsByUsedExportsCache =
1454
+ computeCombinationsByUsedExports(module);
1455
+ }
1456
+ combs = combsByUsedExportsCache;
1457
+ } else {
1458
+ if (combsCache === undefined) {
1459
+ combsCache = computeCombinations(module);
1460
+ }
1461
+ combs = combsCache;
1462
+ }
1363
1463
  // For all combination of chunk selection
1364
1464
  for (const chunkCombination of combs) {
1365
1465
  // Break if minimum number of chunks is not reached
@@ -1577,12 +1677,14 @@ module.exports = class SplitChunksPlugin {
1577
1677
  }
1578
1678
  if (usedChunks.size >= item.cacheGroup.minChunks) {
1579
1679
  const chunksArr = [...usedChunks];
1680
+ // invariant across the module loop below
1681
+ const usedChunksKey = getKey(usedChunks);
1580
1682
  for (const module of item.modules) {
1581
1683
  addModuleToChunksInfoMap(
1582
1684
  item.cacheGroup,
1583
1685
  item.cacheGroupIndex,
1584
1686
  chunksArr,
1585
- getKey(usedChunks),
1687
+ usedChunksKey,
1586
1688
  module
1587
1689
  );
1588
1690
  }
@@ -66,6 +66,21 @@ class ChunkPrefetchPreloadPlugin {
66
66
  new ChunkPreloadTriggerRuntimeModule(chunkMap.preload)
67
67
  );
68
68
  }
69
+ if (chunkMap.cssPreload) {
70
+ // CSS-only preload (`parser.javascript.dynamicImportCssPreload`):
71
+ // reuse `preloadChunk` — with no JS `preload` order the JS
72
+ // handler is never registered, so only the CSS `.s` handler
73
+ // fires (`<link rel="preload" as="style">`).
74
+ set.add(RuntimeGlobals.preloadChunk);
75
+ set.add(RuntimeGlobals.preloadChunkHandlers);
76
+ compilation.addRuntimeModule(
77
+ chunk,
78
+ new ChunkPreloadTriggerRuntimeModule(
79
+ chunkMap.cssPreload,
80
+ "cssPreload"
81
+ )
82
+ );
83
+ }
69
84
  }
70
85
  );
71
86
  compilation.hooks.runtimeRequirementInTree
@@ -14,11 +14,14 @@ const Template = require("../Template");
14
14
  class ChunkPreloadTriggerRuntimeModule extends RuntimeModule {
15
15
  /**
16
16
  * @param {ChunkChildIdsByOrdersMap} chunkMap map from chunk to chunks
17
+ * @param {string=} handlerKey the `ensureChunkHandlers` key to install under (default `"preload"`); `"cssPreload"` preloads only the CSS handler so it stays CSS-only even when the build also registers a JS preload handler
17
18
  */
18
- constructor(chunkMap) {
19
- super("chunk preload trigger", RuntimeModule.STAGE_TRIGGER);
19
+ constructor(chunkMap, handlerKey = "preload") {
20
+ super(`chunk ${handlerKey} trigger`, RuntimeModule.STAGE_TRIGGER);
20
21
  /** @type {ChunkChildIdsByOrdersMap} */
21
22
  this.chunkMap = chunkMap;
23
+ /** @type {string} */
24
+ this.handlerKey = handlerKey;
22
25
  }
23
26
 
24
27
  /**
@@ -26,20 +29,28 @@ class ChunkPreloadTriggerRuntimeModule extends RuntimeModule {
26
29
  * @returns {string | null} runtime code
27
30
  */
28
31
  generate() {
29
- const { chunkMap } = this;
32
+ const { chunkMap, handlerKey } = this;
30
33
  const compilation = /** @type {Compilation} */ (this.compilation);
31
34
  const { runtimeTemplate } = compilation;
32
- const body = [
33
- "var chunks = chunkToChildrenMap[chunkId];",
34
- `Array.isArray(chunks) && chunks.map(${RuntimeGlobals.preloadChunk});`
35
- ];
35
+ // `cssPreload` calls only the CSS preload handler (`preloadChunkHandlers.s`)
36
+ // so it stays CSS-only regardless of a JS preload handler registered
37
+ // elsewhere; plain `preload` fans out to every handler via `preloadChunk`.
38
+ const body =
39
+ handlerKey === "cssPreload"
40
+ ? [
41
+ `var handler = ${RuntimeGlobals.preloadChunkHandlers}.s;`,
42
+ "var chunks = chunkToChildrenMap[chunkId];",
43
+ "if (handler && Array.isArray(chunks)) chunks.forEach(handler);"
44
+ ]
45
+ : [
46
+ "var chunks = chunkToChildrenMap[chunkId];",
47
+ `Array.isArray(chunks) && chunks.map(${RuntimeGlobals.preloadChunk});`
48
+ ];
36
49
  return Template.asString([
37
- Template.asString([
38
- `var chunkToChildrenMap = ${JSON.stringify(chunkMap, null, "\t")};`,
39
- `${
40
- RuntimeGlobals.ensureChunkHandlers
41
- }.preload = ${runtimeTemplate.basicFunction("chunkId", body)};`
42
- ])
50
+ `var chunkToChildrenMap = ${JSON.stringify(chunkMap, null, "\t")};`,
51
+ `${
52
+ RuntimeGlobals.ensureChunkHandlers
53
+ }.${handlerKey} = ${runtimeTemplate.basicFunction("chunkId", body)};`
43
54
  ]);
44
55
  }
45
56
  }