@depup/webpack 5.109.0-depup.0 → 5.109.2-depup.0

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 (62) hide show
  1. package/README.md +4 -4
  2. package/bin/webpack.js +12 -0
  3. package/changes.json +4 -4
  4. package/lib/ChunkGraph.js +17 -15
  5. package/lib/CleanPlugin.js +11 -9
  6. package/lib/CompatibilityPlugin.js +31 -0
  7. package/lib/Compilation.js +21 -5
  8. package/lib/Compiler.js +16 -4
  9. package/lib/DefinePlugin.js +47 -20
  10. package/lib/ExportsInfo.js +22 -5
  11. package/lib/ExternalModule.js +10 -7
  12. package/lib/FileSystemInfo.js +126 -26
  13. package/lib/FlagDependencyUsagePlugin.js +33 -12
  14. package/lib/InitFragment.js +20 -32
  15. package/lib/ModuleFilenameHelpers.js +13 -2
  16. package/lib/NormalModule.js +80 -71
  17. package/lib/NormalModuleFactory.js +8 -2
  18. package/lib/RuntimePlugin.js +40 -113
  19. package/lib/Template.js +23 -3
  20. package/lib/cache/PackFileCacheStrategy.js +259 -5
  21. package/lib/config/defaults.js +21 -2
  22. package/lib/container/HoistContainerReferencesPlugin.js +47 -55
  23. package/lib/container/ModuleFederationPlugin.js +15 -9
  24. package/lib/css/CssGenerator.js +8 -2
  25. package/lib/css/CssLoadingRuntimeModule.js +25 -12
  26. package/lib/css/CssModulesPlugin.js +29 -18
  27. package/lib/css/CssParser.js +135 -50
  28. package/lib/css/syntax.js +862 -765
  29. package/lib/dependencies/CommonJsExportRequireDependency.js +19 -0
  30. package/lib/dependencies/CommonJsFullRequireDependency.js +33 -4
  31. package/lib/dependencies/CommonJsImportsParserPlugin.js +2 -0
  32. package/lib/dependencies/HarmonyEvaluatedImportSpecifierDependency.js +2 -1
  33. package/lib/dependencies/HarmonyExportImportedSpecifierDependency.js +17 -9
  34. package/lib/dependencies/HarmonyImportDependency.js +1 -1
  35. package/lib/dependencies/HarmonyImportGuard.js +2 -1
  36. package/lib/dependencies/HarmonyImportSpecifierDependency.js +10 -7
  37. package/lib/esm/ModuleChunkLoadingRuntimeModule.js +15 -9
  38. package/lib/hmr/lazyCompilationBackend.js +19 -4
  39. package/lib/html/HtmlGenerator.js +23 -3
  40. package/lib/html/HtmlModulesPlugin.js +30 -12
  41. package/lib/html/syntax.js +2485 -2399
  42. package/lib/ids/IdHelpers.js +24 -10
  43. package/lib/javascript/JavascriptModulesPlugin.js +83 -49
  44. package/lib/javascript/JavascriptParser.js +49 -13
  45. package/lib/javascript/syntax.js +700 -43
  46. package/lib/node/ReadFileCompileAsyncWasmPlugin.js +10 -2
  47. package/lib/node/ReadFileCompileWasmPlugin.js +10 -2
  48. package/lib/optimize/AggressiveMergingPlugin.js +19 -15
  49. package/lib/optimize/ConcatenatedModule.js +19 -15
  50. package/lib/optimize/ModuleConcatenationPlugin.js +73 -24
  51. package/lib/optimize/RealContentHashPlugin.js +10 -7
  52. package/lib/optimize/SplitChunksPlugin.js +146 -44
  53. package/lib/prefetch/ResourceHintPlugin.js +96 -72
  54. package/lib/serialization/BinaryMiddleware.js +270 -938
  55. package/lib/serialization/FileMiddleware.js +299 -60
  56. package/lib/stats/DefaultStatsFactoryPlugin.js +204 -198
  57. package/lib/stats/DefaultStatsPrinterPlugin.js +24 -166
  58. package/lib/stats/StatsFactory.js +31 -9
  59. package/lib/util/identifier.js +21 -1
  60. package/package.json +10 -10
  61. package/schemas/WebpackOptions.json +110 -55
  62. package/types.d.ts +832 -247
@@ -77,7 +77,11 @@ class ReadFileCompileAsyncWasmPlugin {
77
77
  "",
78
78
  "// Fake fetch response",
79
79
  "resolve({",
80
- Template.indent(["arrayBuffer() { return buffer; }"]),
80
+ Template.indent([
81
+ // Return a real ArrayBuffer: some runtimes (e.g. Deno)
82
+ // reject a Node Buffer view here as "not a buffer source".
83
+ "arrayBuffer() { return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); }"
84
+ ]),
81
85
  "});"
82
86
  ]),
83
87
  "});"
@@ -99,7 +103,11 @@ class ReadFileCompileAsyncWasmPlugin {
99
103
  "",
100
104
  "// Fake fetch response",
101
105
  "resolve({",
102
- Template.indent(["arrayBuffer() { return buffer; }"]),
106
+ Template.indent([
107
+ // Return a real ArrayBuffer: some runtimes (e.g. Deno)
108
+ // reject a Node Buffer view here as "not a buffer source".
109
+ "arrayBuffer() { return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); }"
110
+ ]),
103
111
  "});"
104
112
  ]),
105
113
  "});"
@@ -77,7 +77,11 @@ class ReadFileCompileWasmPlugin {
77
77
  "",
78
78
  "// Fake fetch response",
79
79
  "resolve({",
80
- Template.indent(["arrayBuffer() { return buffer; }"]),
80
+ Template.indent([
81
+ // Return a real ArrayBuffer: some runtimes (e.g. Deno)
82
+ // reject a Node Buffer view here as "not a buffer source".
83
+ "arrayBuffer() { return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); }"
84
+ ]),
81
85
  "});"
82
86
  ]),
83
87
  "});"
@@ -99,7 +103,11 @@ class ReadFileCompileWasmPlugin {
99
103
  "",
100
104
  "// Fake fetch response",
101
105
  "resolve({",
102
- Template.indent(["arrayBuffer() { return buffer; }"]),
106
+ Template.indent([
107
+ // Return a real ArrayBuffer: some runtimes (e.g. Deno)
108
+ // reject a Node Buffer view here as "not a buffer source".
109
+ "arrayBuffer() { return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); }"
110
+ ]),
103
111
  "});"
104
112
  ]),
105
113
  "});"
@@ -53,8 +53,16 @@ class AggressiveMergingPlugin {
53
53
  },
54
54
  (chunks) => {
55
55
  const chunkGraph = compilation.chunkGraph;
56
- /** @type {{ a: Chunk, b: Chunk, improvement: number }[]} */
57
- const combinations = [];
56
+ // Only the single best pair is merged per pass (the hook re-runs
57
+ // after each merge), so track the max directly instead of building
58
+ // and sorting the full O(chunks²) list of pairs every pass. A strict
59
+ // `>` keeps the first-encountered best, matching the previous stable
60
+ // descending sort's `[0]`.
61
+ /** @type {Chunk | undefined} */
62
+ let bestA;
63
+ /** @type {Chunk | undefined} */
64
+ let bestB;
65
+ let bestImprovement = -Infinity;
58
66
  for (const a of chunks) {
59
67
  if (a.canBeInitial()) continue;
60
68
  for (const b of chunks) {
@@ -73,23 +81,19 @@ class AggressiveMergingPlugin {
73
81
  chunkOverhead: 0
74
82
  });
75
83
  const improvement = (aSize + bSize) / abSize;
76
- combinations.push({
77
- a,
78
- b,
79
- improvement
80
- });
84
+ if (improvement > bestImprovement) {
85
+ bestImprovement = improvement;
86
+ bestA = a;
87
+ bestB = b;
88
+ }
81
89
  }
82
90
  }
83
91
 
84
- combinations.sort((a, b) => b.improvement - a.improvement);
85
-
86
- const pair = combinations[0];
87
-
88
- if (!pair) return;
89
- if (pair.improvement < minSizeReduce) return;
92
+ if (bestA === undefined) return;
93
+ if (bestImprovement < minSizeReduce) return;
90
94
 
91
- chunkGraph.integrateChunks(pair.b, pair.a);
92
- compilation.chunks.delete(pair.a);
95
+ chunkGraph.integrateChunks(/** @type {Chunk} */ (bestB), bestA);
96
+ compilation.chunks.delete(bestA);
93
97
  return true;
94
98
  }
95
99
  );
@@ -99,6 +99,7 @@ const { InlinedUsedName } = require("./InlineExports");
99
99
  /** @typedef {import("../javascript/JavascriptModulesPlugin").Variable} Variable */
100
100
  /** @typedef {import("../javascript/JavascriptParser").Program} Program */
101
101
  /** @typedef {import("../javascript/JavascriptParser").Range} Range */
102
+ /** @typedef {import("estree").Identifier} Identifier */
102
103
  /** @typedef {import("../serialization/ObjectMiddleware").ObjectDeserializerContext} ObjectDeserializerContext */
103
104
  /** @typedef {import("../util/Hash")} Hash */
104
105
  /** @typedef {import("../util/Hash").HashFunction} HashFunction */
@@ -1011,16 +1012,17 @@ class ConcatenatedModule extends Module {
1011
1012
  }
1012
1013
  }
1013
1014
 
1014
- // populate dependencies
1015
- for (const d of m.dependencies.filter(
1016
- (dep) =>
1017
- !Dependency.canConcatenate(dep) ||
1015
+ // populate dependencies — keep only deps that leave the concat set
1016
+ for (const d of m.dependencies) {
1017
+ if (
1018
+ !Dependency.canConcatenate(d) ||
1018
1019
  !this._modules.has(
1019
1020
  /** @type {Module} */
1020
- (compilation.moduleGraph.getModule(dep))
1021
+ (compilation.moduleGraph.getModule(d))
1021
1022
  )
1022
- )) {
1023
- this.dependencies.push(d);
1023
+ ) {
1024
+ this.dependencies.push(d);
1025
+ }
1024
1026
  }
1025
1027
  // populate codeGenerationDependencies — the inner modules'
1026
1028
  // templates are applied during ConcatenatedModule.codeGeneration,
@@ -1753,10 +1755,12 @@ class ConcatenatedModule extends Module {
1753
1755
  const newName = _findNewName(name, info, references);
1754
1756
  if (newName) {
1755
1757
  const source = /** @type {ReplaceSource} */ (info.source);
1756
- const allIdentifiers = new Set([
1757
- ...references.map((r) => r.identifier),
1758
- ...variable.identifiers
1759
- ]);
1758
+ /** @type {Set<Identifier>} */
1759
+ const allIdentifiers = new Set();
1760
+ for (const r of references) allIdentifiers.add(r.identifier);
1761
+ for (const identifier of variable.identifiers) {
1762
+ allIdentifiers.add(identifier);
1763
+ }
1760
1764
  for (const identifier of allIdentifiers) {
1761
1765
  const r = /** @type {Range} */ (identifier.range);
1762
1766
  const path = getPathInAst(
@@ -1889,11 +1893,11 @@ class ConcatenatedModule extends Module {
1889
1893
  const referencesByName = new Map();
1890
1894
  for (const reference of globalScope.through) {
1891
1895
  const name = reference.identifier.name;
1892
- if (!referencesByName.has(name)) {
1893
- referencesByName.set(name, []);
1896
+ let references = referencesByName.get(name);
1897
+ if (references === undefined) {
1898
+ referencesByName.set(name, (references = []));
1894
1899
  }
1895
- /** @type {Reference[]} */
1896
- (referencesByName.get(name)).push(reference);
1900
+ references.push(reference);
1897
1901
  }
1898
1902
  for (const [name, references] of referencesByName) {
1899
1903
  const match = ConcatenationScope.matchModuleReference(name);
@@ -70,6 +70,23 @@ const getMergedModuleRuntime = (chunkGraph, cache, module) => {
70
70
  * @property {number} added
71
71
  */
72
72
 
73
+ /**
74
+ * Record why `module` can't be added (so retried lookups hit the cache), bump
75
+ * the matching statistic, and return the problem to bubble up the recursion.
76
+ * Only reached on failure, so it stays off the hot success path.
77
+ * @param {Map<Module, Problem>} failureCache per-module failure cache
78
+ * @param {Statistics} statistics running statistics
79
+ * @param {keyof Statistics} statKey statistic to increment
80
+ * @param {Module} module the module that couldn't be added
81
+ * @param {Problem} problem the failure to cache and return
82
+ * @returns {Problem} the same problem
83
+ */
84
+ const cacheFailure = (failureCache, statistics, statKey, module, problem) => {
85
+ statistics[statKey]++;
86
+ failureCache.set(module, problem);
87
+ return problem;
88
+ };
89
+
73
90
  /**
74
91
  * Format bailout reason.
75
92
  * @param {string} msg message
@@ -687,9 +704,13 @@ class ModuleConcatenationPlugin {
687
704
 
688
705
  // Not possible to add?
689
706
  if (!possibleModules.has(module)) {
690
- statistics.invalidModule++;
691
- failureCache.set(module, module); // cache failures for performance
692
- return module;
707
+ return cacheFailure(
708
+ failureCache,
709
+ statistics,
710
+ "invalidModule",
711
+ module,
712
+ module
713
+ );
693
714
  }
694
715
 
695
716
  // A deferred import must stay a concatenation boundary: concatenating a
@@ -707,9 +728,13 @@ class ModuleConcatenationPlugin {
707
728
  `Module ${module.readableIdentifier(
708
729
  requestShortener
709
730
  )} imports a module in this configuration via import defer`;
710
- statistics.incorrectDependency++;
711
- failureCache.set(module, problem); // cache failures for performance
712
- return problem;
731
+ return cacheFailure(
732
+ failureCache,
733
+ statistics,
734
+ "incorrectDependency",
735
+ module,
736
+ problem
737
+ );
713
738
  }
714
739
 
715
740
  // Module must be in the correct chunks
@@ -741,9 +766,13 @@ class ModuleConcatenationPlugin {
741
766
  ", "
742
767
  )}, module is in chunk(s) ${chunks.join(", ")})`;
743
768
  };
744
- statistics.incorrectChunks++;
745
- failureCache.set(module, problem); // cache failures for performance
746
- return problem;
769
+ return cacheFailure(
770
+ failureCache,
771
+ statistics,
772
+ "incorrectChunks",
773
+ module,
774
+ problem
775
+ );
747
776
  }
748
777
 
749
778
  const moduleGraph = compilation.moduleGraph;
@@ -782,9 +811,13 @@ class ModuleConcatenationPlugin {
782
811
  : "in an unsupported way"
783
812
  }`;
784
813
  };
785
- statistics.incorrectDependency++;
786
- failureCache.set(module, problem); // cache failures for performance
787
- return problem;
814
+ return cacheFailure(
815
+ failureCache,
816
+ statistics,
817
+ "incorrectDependency",
818
+ module,
819
+ problem
820
+ );
788
821
  }
789
822
  }
790
823
 
@@ -841,9 +874,13 @@ class ModuleConcatenationPlugin {
841
874
  ", "
842
875
  )}`;
843
876
  };
844
- statistics.incorrectChunksOfImporter++;
845
- failureCache.set(module, problem); // cache failures for performance
846
- return problem;
877
+ return cacheFailure(
878
+ failureCache,
879
+ statistics,
880
+ "incorrectChunksOfImporter",
881
+ module,
882
+ problem
883
+ );
847
884
  }
848
885
 
849
886
  /** @type {Map<Module, ReadonlyArray<ModuleGraph.ModuleGraphConnection>>} */
@@ -893,9 +930,13 @@ class ModuleConcatenationPlugin {
893
930
  ", "
894
931
  )}`;
895
932
  };
896
- statistics.incorrectModuleDependency++;
897
- failureCache.set(module, problem); // cache failures for performance
898
- return problem;
933
+ return cacheFailure(
934
+ failureCache,
935
+ statistics,
936
+ "incorrectModuleDependency",
937
+ module,
938
+ problem
939
+ );
899
940
  }
900
941
 
901
942
  if (runtime !== undefined && typeof runtime !== "string") {
@@ -946,9 +987,13 @@ class ModuleConcatenationPlugin {
946
987
  /** @type {RuntimeSpec} */ (runtimeCondition)
947
988
  )})`
948
989
  ).join(", ")}`;
949
- statistics.incorrectRuntimeCondition++;
950
- failureCache.set(module, problem); // cache failures for performance
951
- return problem;
990
+ return cacheFailure(
991
+ failureCache,
992
+ statistics,
993
+ "incorrectRuntimeCondition",
994
+ module,
995
+ problem
996
+ );
952
997
  }
953
998
  }
954
999
 
@@ -981,9 +1026,13 @@ class ModuleConcatenationPlugin {
981
1026
  );
982
1027
  if (problem) {
983
1028
  if (backup !== undefined) config.rollback(backup);
984
- statistics.importerFailed++;
985
- failureCache.set(module, problem); // cache failures for performance
986
- return problem;
1029
+ return cacheFailure(
1030
+ failureCache,
1031
+ statistics,
1032
+ "importerFailed",
1033
+ module,
1034
+ problem
1035
+ );
987
1036
  }
988
1037
  }
989
1038
 
@@ -166,10 +166,16 @@ const toCachedSource = (source) => {
166
166
  * @property {Hashes} hashes
167
167
  */
168
168
 
169
+ const createCompilationHooks = () => ({
170
+ /**
171
+ * @type {SyncBailHook<[Buffer[], string], string | void>}
172
+ * @since 5.8.0
173
+ */
174
+ updateHash: new SyncBailHook(["content", "oldHash"])
175
+ });
176
+
169
177
  /**
170
- * Defines the compilation hooks type used by this module.
171
- * @typedef {object} CompilationHooks
172
- * @property {SyncBailHook<[Buffer[], string], string | void>} updateHash
178
+ * @typedef {ReturnType<typeof createCompilationHooks>} CompilationHooks
173
179
  */
174
180
 
175
181
  /**
@@ -554,10 +560,7 @@ ${referencingAssets
554
560
  }
555
561
 
556
562
  RealContentHashPlugin.getCompilationHooks = createHooksRegistry(
557
- () =>
558
- /** @type {CompilationHooks} */ ({
559
- updateHash: new SyncBailHook(["content", "oldHash"])
560
- })
563
+ createCompilationHooks
561
564
  );
562
565
 
563
566
  module.exports = RealContentHashPlugin;
@@ -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
  }