@atlaspack/bundler-default 2.14.5-canary.35 → 2.14.5-canary.351

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 (37) hide show
  1. package/CHANGELOG.md +589 -0
  2. package/dist/DefaultBundler.js +84 -0
  3. package/dist/MonolithicBundler.js +68 -0
  4. package/dist/bundleMerge.js +137 -0
  5. package/dist/bundlerConfig.js +223 -0
  6. package/dist/decorateLegacyGraph.js +189 -0
  7. package/dist/idealGraph.js +1471 -0
  8. package/dist/memoize.js +31 -0
  9. package/dist/stats.js +69 -0
  10. package/lib/DefaultBundler.js +6 -1
  11. package/lib/MonolithicBundler.js +11 -3
  12. package/lib/bundleMerge.js +106 -37
  13. package/lib/bundlerConfig.js +52 -6
  14. package/lib/decorateLegacyGraph.js +24 -3
  15. package/lib/idealGraph.js +410 -55
  16. package/lib/memoize.js +39 -0
  17. package/lib/stats.js +85 -0
  18. package/lib/types/DefaultBundler.d.ts +18 -0
  19. package/lib/types/MonolithicBundler.d.ts +2 -0
  20. package/lib/types/bundleMerge.d.ts +9 -0
  21. package/lib/types/bundlerConfig.d.ts +36 -0
  22. package/lib/types/decorateLegacyGraph.d.ts +3 -0
  23. package/lib/types/idealGraph.d.ts +40 -0
  24. package/lib/types/memoize.d.ts +2 -0
  25. package/lib/types/stats.d.ts +16 -0
  26. package/package.json +20 -12
  27. package/src/{DefaultBundler.js → DefaultBundler.ts} +21 -6
  28. package/src/{MonolithicBundler.js → MonolithicBundler.ts} +17 -5
  29. package/src/bundleMerge.ts +250 -0
  30. package/src/{bundlerConfig.js → bundlerConfig.ts} +106 -45
  31. package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +26 -7
  32. package/src/{idealGraph.js → idealGraph.ts} +729 -137
  33. package/src/memoize.ts +32 -0
  34. package/src/stats.ts +97 -0
  35. package/tsconfig.json +30 -0
  36. package/tsconfig.tsbuildinfo +1 -0
  37. package/src/bundleMerge.js +0 -103
package/lib/idealGraph.js CHANGED
@@ -12,6 +12,13 @@ function _path() {
12
12
  };
13
13
  return data;
14
14
  }
15
+ function _sortedArrayFunctions() {
16
+ const data = _interopRequireDefault(require("sorted-array-functions"));
17
+ _sortedArrayFunctions = function () {
18
+ return data;
19
+ };
20
+ return data;
21
+ }
15
22
  function _featureFlags() {
16
23
  const data = require("@atlaspack/feature-flags");
17
24
  _featureFlags = function () {
@@ -48,8 +55,10 @@ function _nullthrows() {
48
55
  return data;
49
56
  }
50
57
  var _bundleMerge = require("./bundleMerge");
58
+ var _stats = require("./stats");
51
59
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
52
60
  /* BundleRoot - An asset that is the main entry of a Bundle. */
61
+
53
62
  const dependencyPriorityEdges = {
54
63
  sync: 1,
55
64
  parallel: 2,
@@ -65,12 +74,19 @@ const idealBundleGraphEdges = exports.idealBundleGraphEdges = Object.freeze({
65
74
  // which mutates the assetGraph into the bundleGraph we would
66
75
  // expect from default bundler
67
76
 
77
+ function isNonRootBundle(bundle, message) {
78
+ let existingBundle = (0, _nullthrows().default)(bundle, message);
79
+ (0, _assert().default)(existingBundle !== 'root', "Bundle cannot be 'root'");
80
+ return existingBundle;
81
+ }
68
82
  function createIdealGraph(assetGraph, config, entries, logger) {
83
+ var _config$sharedBundleM;
69
84
  // Asset to the bundle and group it's an entry of
70
85
  let bundleRoots = new Map();
71
86
  let bundles = new Map();
72
87
  let dependencyBundleGraph = new (_graph().ContentGraph)();
73
88
  let assetReference = new (_utils().DefaultMap)(() => []);
89
+ let stats = new _stats.Stats(config.projectRoot);
74
90
 
75
91
  // A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
76
92
  // referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role.
@@ -108,15 +124,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
108
124
  }
109
125
  let assets = [];
110
126
  let assetToIndex = new Map();
111
- //Manual is a map of the user-given name to the bundle node Id that corresponds to ALL the assets that match any glob in that user-specified array
112
- let manualSharedMap = new Map();
113
- // May need a map to be able to look up NON- bundle root assets which need special case instructions
114
- // Use this when placing assets into bundles, to avoid duplication
115
- let manualAssetToBundle = new Map();
116
- let {
117
- manualAssetToConfig,
118
- constantModuleToMSB
119
- } = function makeManualAssetToConfigLookup() {
127
+ function makeManualAssetToConfigLookup() {
120
128
  let manualAssetToConfig = new Map();
121
129
  let constantModuleToMSB = new (_utils().DefaultMap)(() => []);
122
130
  if (config.manualSharedBundles.length === 0) {
@@ -160,17 +168,15 @@ function createIdealGraph(assetGraph, config, entries, logger) {
160
168
  assetGraph.traverse((node, _, actions) => {
161
169
  if (node.type === 'asset' && (!Array.isArray(c.types) || c.types.includes(node.value.type))) {
162
170
  let projectRelativePath = _path().default.relative(config.projectRoot, node.value.filePath);
163
- if (!assetRegexes.some(regex => regex.test(projectRelativePath))) {
164
- return;
165
- }
166
171
 
167
172
  // We track all matching MSB's for constant modules as they are never duplicated
168
173
  // and need to be assigned to all matching bundles
169
174
  if (node.value.meta.isConstantModule === true) {
170
175
  constantModuleToMSB.get(node.value).push(c);
171
176
  }
172
- manualAssetToConfig.set(node.value, c);
173
- return;
177
+ if (assetRegexes.some(regex => regex.test(projectRelativePath))) {
178
+ manualAssetToConfig.set(node.value, c);
179
+ }
174
180
  }
175
181
  if (node.type === 'dependency' && (node.value.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional') && parentAsset) {
176
182
  // Don't walk past the bundle group assets
@@ -182,8 +188,23 @@ function createIdealGraph(assetGraph, config, entries, logger) {
182
188
  manualAssetToConfig,
183
189
  constantModuleToMSB
184
190
  };
185
- }();
191
+ }
192
+
193
+ //Manual is a map of the user-given name to the bundle node Id that corresponds to ALL the assets that match any glob in that user-specified array
194
+ let manualSharedMap = new Map();
195
+ // May need a map to be able to look up NON- bundle root assets which need special case instructions
196
+ // Use this when placing assets into bundles, to avoid duplication
197
+ let manualAssetToBundle = new Map();
198
+ let {
199
+ manualAssetToConfig,
200
+ constantModuleToMSB
201
+ } = makeManualAssetToConfigLookup();
186
202
  let manualBundleToInternalizedAsset = new (_utils().DefaultMap)(() => []);
203
+ let mergeSourceBundleLookup = new Map();
204
+ let mergeSourceBundleAssets = new Set((_config$sharedBundleM = config.sharedBundleMerge) === null || _config$sharedBundleM === void 0 ? void 0 : _config$sharedBundleM.flatMap(c => {
205
+ var _c$sourceBundles;
206
+ return ((_c$sourceBundles = c.sourceBundles) === null || _c$sourceBundles === void 0 ? void 0 : _c$sourceBundles.map(assetMatch => _path().default.join(config.projectRoot, assetMatch))) ?? [];
207
+ }));
187
208
 
188
209
  /**
189
210
  * Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
@@ -234,15 +255,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
234
255
  manualSharedBundleKey != null && manualSharedMap.has(manualSharedBundleKey)) {
235
256
  bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey));
236
257
  }
237
- if (dependency.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional' || childAsset.bundleBehavior === 'isolated' // An isolated Dependency, or Bundle must contain all assets it needs to load.
238
- ) {
258
+ if (dependency.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional' || childAsset.bundleBehavior === 'isolated' ||
259
+ // An isolated Dependency, or Bundle must contain all assets it needs to load.
260
+ childAsset.bundleBehavior === 'inlineIsolated') {
239
261
  if (bundleId == null) {
240
262
  let firstBundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(stack[0][1]));
241
263
  (0, _assert().default)(firstBundleGroup !== 'root');
242
264
  bundle = createBundle({
243
265
  asset: childAsset,
244
266
  bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
245
- needsStableName: dependency.bundleBehavior === 'inline' || childAsset.bundleBehavior === 'inline' ? false : dependency.isEntry || dependency.needsStableName,
267
+ needsStableName: dependency.bundleBehavior === 'inline' || childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inlineIsolated' || childAsset.bundleBehavior === 'inlineIsolated' ? false : dependency.isEntry || dependency.needsStableName,
246
268
  target: firstBundleGroup.target
247
269
  });
248
270
  bundleId = bundleGraph.addNode(bundle);
@@ -250,6 +272,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
250
272
  bundleRoots.set(childAsset, [bundleId, bundleId]);
251
273
  bundleGroupBundleIds.add(bundleId);
252
274
  bundleGraph.addEdge(bundleGraphRootNodeId, bundleId);
275
+ // If this asset is relevant for merging then track it's source
276
+ // bundle id for later
277
+ if (mergeSourceBundleAssets.has(childAsset.filePath)) {
278
+ mergeSourceBundleLookup.set(_path().default.relative(config.projectRoot, childAsset.filePath), bundleId);
279
+ }
253
280
  if (manualSharedObject) {
254
281
  // MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
255
282
  // since MSBs should not have main entry assets
@@ -261,7 +288,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
261
288
  if (
262
289
  // If this dependency requests isolated, but the bundle is not,
263
290
  // make the bundle isolated for all uses.
264
- dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
291
+ (dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
265
292
  bundle.bundleBehavior = dependency.bundleBehavior;
266
293
  }
267
294
  }
@@ -300,16 +327,17 @@ function createIdealGraph(assetGraph, config, entries, logger) {
300
327
  env: childAsset.env,
301
328
  bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
302
329
  target: referencingBundle.target,
303
- needsStableName: childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inline' || dependency.priority === 'parallel' && !dependency.needsStableName ? false : referencingBundle.needsStableName
330
+ needsStableName: childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inlineIsolated' || dependency.priority === 'parallel' && !dependency.needsStableName ? false : referencingBundle.needsStableName
304
331
  });
305
332
  bundleId = bundleGraph.addNode(bundle);
306
333
  } else {
307
- bundle = bundleGraph.getNode(bundleId);
308
- (0, _assert().default)(bundle != null && bundle !== 'root');
334
+ let bundleNode = bundleGraph.getNode(bundleId);
335
+ (0, _assert().default)(bundleNode != null && bundleNode !== 'root');
336
+ bundle = bundleNode;
309
337
  if (
310
338
  // If this dependency requests isolated, but the bundle is not,
311
339
  // make the bundle isolated for all uses.
312
- dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
340
+ (dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
313
341
  bundle.bundleBehavior = dependency.bundleBehavior;
314
342
  }
315
343
  }
@@ -495,7 +523,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
495
523
  // not true that a bundle's available assets = all assets of all the bundleGroups
496
524
  // it belongs to. It's the intersection of those sets.
497
525
  let available;
498
- if (bundleRoot.bundleBehavior === 'isolated') {
526
+ if (bundleRoot.bundleBehavior === 'isolated' || bundleRoot.bundleBehavior === 'inlineIsolated') {
499
527
  available = new (_graph().BitSet)(assets.length);
500
528
  } else {
501
529
  available = (0, _nullthrows().default)(ancestorAssets[nodeId]).clone();
@@ -558,7 +586,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
558
586
  continue;
559
587
  }
560
588
  let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, _graph().ALL_EDGE_TYPES);
561
- let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated';
589
+ let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated' && getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'inlineIsolated';
562
590
  if (parentRoots.length === 0) continue;
563
591
  for (let parentId of parentRoots) {
564
592
  var _ancestorAssets$paren;
@@ -623,7 +651,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
623
651
  let assetId = bundleRootGraph.getNode(nodeId);
624
652
  if (assetId == null) return; // deleted
625
653
  let a = assets[assetId];
626
- if (entries.has(a) || !a.isBundleSplittable || bundleRoots.get(a) && (getBundleFromBundleRoot(a).needsStableName || getBundleFromBundleRoot(a).bundleBehavior === 'isolated')) {
654
+ if (entries.has(a) || !a.isBundleSplittable || bundleRoots.get(a) && (getBundleFromBundleRoot(a).needsStableName || getBundleFromBundleRoot(a).bundleBehavior === 'isolated' || getBundleFromBundleRoot(a).bundleBehavior === 'inlineIsolated')) {
627
655
  // Add asset to non-splittable bundles.
628
656
  addAssetToBundleRoot(asset, a);
629
657
  } else if (!((_ancestorAssets$nodeI = ancestorAssets[nodeId]) !== null && _ancestorAssets$nodeI !== void 0 && _ancestorAssets$nodeI.has(i))) {
@@ -659,8 +687,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
659
687
  manualSharedMap.set(manualSharedBundleKey, bundleId);
660
688
  } else {
661
689
  bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey));
662
- bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
663
- (0, _assert().default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly');
690
+ let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
691
+ (0, _assert().default)(bundleNode != null && bundleNode !== 'root', 'We tried to use the root incorrectly');
692
+ bundle = bundleNode;
664
693
  if (!bundle.assets.has(asset)) {
665
694
  bundle.assets.add(asset);
666
695
  bundle.size += asset.stats.size;
@@ -763,8 +792,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
763
792
  bundleId = bundleGraph.addNode(bundle);
764
793
  bundles.set(key, bundleId);
765
794
  } else {
766
- bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
767
- (0, _assert().default)(bundle !== 'root');
795
+ let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
796
+ (0, _assert().default)(bundleNode !== 'root');
797
+ bundle = bundleNode;
768
798
  }
769
799
  bundle.assets.add(asset);
770
800
  bundle.size += asset.stats.size;
@@ -785,6 +815,8 @@ function createIdealGraph(assetGraph, config, entries, logger) {
785
815
  }
786
816
  }
787
817
  let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
818
+ let modifiedSourceBundles = new Set();
819
+
788
820
  // Step split manual shared bundles for those that have the "split" property set
789
821
  let remainderMap = new (_utils().DefaultMap)(() => []);
790
822
  for (let id of manualSharedMap.values()) {
@@ -792,8 +824,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
792
824
  (0, _assert().default)(manualBundle !== 'root' && manualBundle != null);
793
825
  if (manualBundle.sourceBundles.size > 0) {
794
826
  var _manualAssetToConfig$;
795
- let firstSourceBundle = (0, _nullthrows().default)(bundleGraph.getNode([...manualBundle.sourceBundles][0]));
796
- (0, _assert().default)(firstSourceBundle !== 'root');
827
+ let firstSourceBundleNode = (0, _nullthrows().default)(bundleGraph.getNode([...manualBundle.sourceBundles][0]));
828
+ (0, _assert().default)(firstSourceBundleNode !== 'root');
829
+ let firstSourceBundle = firstSourceBundleNode;
797
830
  let firstAsset = [...manualBundle.assets][0];
798
831
  let manualSharedObject = manualAssetToConfig.get(firstAsset);
799
832
  (0, _assert().default)(manualSharedObject != null);
@@ -801,7 +834,6 @@ function createIdealGraph(assetGraph, config, entries, logger) {
801
834
  if (modNum != null) {
802
835
  for (let a of [...manualBundle.assets]) {
803
836
  let numRep = getBigIntFromContentKey(a.id);
804
- // $FlowFixMe Flow doesn't know about BigInt
805
837
  let r = Number(numRep % BigInt(modNum));
806
838
  remainderMap.get(r).push(a);
807
839
  }
@@ -848,11 +880,65 @@ function createIdealGraph(assetGraph, config, entries, logger) {
848
880
  }
849
881
  }
850
882
  }
883
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
884
+ // Merge webpack chunk name bundles
885
+ let chunkNameBundles = new (_utils().DefaultMap)(() => new Set());
886
+ for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
887
+ var _bundleNode$value$mai;
888
+ // meta.chunkName is set by the Rust transformer, so we just need to find
889
+ // bundles that have a chunkName set.
890
+ if (!node || node.type !== 'dependency' || typeof node.value.meta.chunkName !== 'string') {
891
+ continue;
892
+ }
893
+ let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(nodeId, dependencyPriorityEdges[node.value.priority]);
894
+ if (connectedBundles.length === 0) {
895
+ continue;
896
+ }
897
+ (0, _assert().default)(connectedBundles.length === 1, 'Expected webpackChunkName dependency to be connected to no more than one bundle');
898
+ let bundleId = connectedBundles[0];
899
+ let bundleNode = dependencyBundleGraph.getNode(bundleId);
900
+ (0, _assert().default)(bundleNode != null && bundleNode.type === 'bundle');
901
+
902
+ // If a bundle does not have a main entry asset, it's somehow just a
903
+ // shared bundle, and will be merged/deleted by other means.
904
+ if (bundleNode.value.mainEntryAsset == null) {
905
+ continue;
906
+ }
907
+ let bundleNodeId = null;
908
+ let mainEntryAssetId = (_bundleNode$value$mai = bundleNode.value.mainEntryAsset) === null || _bundleNode$value$mai === void 0 ? void 0 : _bundleNode$value$mai.id;
909
+ if (mainEntryAssetId != null) {
910
+ bundleNodeId = bundles.get(mainEntryAssetId);
911
+ }
912
+ if (bundleNodeId == null) {
913
+ continue;
914
+ }
915
+ chunkNameBundles.get(node.value.meta.chunkName)
916
+ // DependencyBundleGraph uses content keys as node ids, so we can use that
917
+ // to get the bundle id.
918
+ .add(bundleNodeId);
919
+ }
920
+ for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
921
+ // The `[request]` placeholder is not yet supported
922
+ if (bundleIds.size <= 1 || typeof chunkName === 'string' && chunkName.includes('[request]')) {
923
+ continue; // Nothing to merge
924
+ }
925
+
926
+ // Merge all bundles with the same chunk name into the first one.
927
+ let [firstBundleId, ...rest] = Array.from(bundleIds);
928
+ for (let bundleId of rest) {
929
+ mergeBundles(firstBundleId, bundleId, 'webpack-chunk-name');
930
+ }
931
+ }
932
+ }
933
+
934
+ // Step merge async bundles that meet the configured params
935
+ if (config.asyncBundleMerge) {
936
+ mergeAsyncBundles(config.asyncBundleMerge);
937
+ }
851
938
 
852
- // Step merge shared bundles that meet the overlap threshold
853
- // This step is skipped by default as the threshold defaults to 1
854
- if (config.sharedBundleMergeThreshold < 1) {
855
- mergeOverlapBundles();
939
+ // Step merge shared bundles that meet the configured params
940
+ if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
941
+ mergeSharedBundles(config.sharedBundleMerge);
856
942
  }
857
943
 
858
944
  // Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
@@ -866,7 +952,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
866
952
  }
867
953
 
868
954
  // Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
869
- let modifiedSourceBundles = new Set();
955
+
870
956
  if (config.disableSharedBundles === false) {
871
957
  for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
872
958
  // Find shared bundles in this bundle group.
@@ -879,7 +965,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
879
965
  let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
880
966
  let bundle = (0, _nullthrows().default)(bundleGraph.getNode(b));
881
967
  (0, _assert().default)(bundle !== 'root');
882
- return count + (bundle.bundleBehavior !== 'inline');
968
+ return count + Number(bundle.bundleBehavior !== 'inline' && bundle.bundleBehavior !== 'inlineIsolated');
883
969
  }, 0);
884
970
  if (numBundlesContributingToPRL > config.maxParallelRequests) {
885
971
  let sharedBundleIdsInBundleGroup = bundleIdsInGroup.filter(b => {
@@ -908,6 +994,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
908
994
  // Remove bundles until the bundle group is within the parallel request limit.
909
995
  while (sharedBundlesInGroup.length > 0 && numBundlesContributingToPRL > config.maxParallelRequests) {
910
996
  let bundleTuple = sharedBundlesInGroup.pop();
997
+ if (!bundleTuple) break;
911
998
  let bundleToRemove = bundleTuple.bundle;
912
999
  let bundleIdToRemove = bundleTuple.id;
913
1000
  //TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
@@ -951,35 +1038,131 @@ function createIdealGraph(assetGraph, config, entries, logger) {
951
1038
  }
952
1039
  }
953
1040
  }
954
- function mergeBundles(bundleGraph, bundleToKeepId, bundleToRemoveId, assetReference) {
955
- let bundleToKeep = (0, _nullthrows().default)(bundleGraph.getNode(bundleToKeepId));
956
- let bundleToRemove = (0, _nullthrows().default)(bundleGraph.getNode(bundleToRemoveId));
957
- (0, _assert().default)(bundleToKeep !== 'root' && bundleToRemove !== 'root');
1041
+ function mergeBundles(bundleToKeepId, bundleToRemoveId, reason) {
1042
+ stats.trackMerge(bundleToKeepId, bundleToRemoveId, reason);
1043
+ let bundleToKeep = isNonRootBundle(bundleGraph.getNode(bundleToKeepId), `Bundle ${bundleToKeepId} not found`);
1044
+ let bundleToRemove = isNonRootBundle(bundleGraph.getNode(bundleToRemoveId), `Bundle ${bundleToRemoveId} not found`);
1045
+ modifiedSourceBundles.add(bundleToKeep);
958
1046
  for (let asset of bundleToRemove.assets) {
959
1047
  bundleToKeep.assets.add(asset);
960
1048
  bundleToKeep.size += asset.stats.size;
961
1049
  let newAssetReference = assetReference.get(asset).map(([dep, bundle]) => bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle]);
962
1050
  assetReference.set(asset, newAssetReference);
963
1051
  }
1052
+
1053
+ // Merge any internalized assets
1054
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1055
+ if (bundleToKeep.internalizedAssets != null) {
1056
+ if (bundleToRemove.internalizedAssets != null) {
1057
+ bundleToKeep.internalizedAssets.intersect(bundleToRemove.internalizedAssets);
1058
+ } else {
1059
+ bundleToKeep.internalizedAssets.clear();
1060
+ }
1061
+ }
1062
+ } else {
1063
+ (0, _assert().default)(bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets, 'All shared bundles should have internalized assets');
1064
+ bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1065
+ }
1066
+
1067
+ // Merge and clean up source bundles
964
1068
  for (let sourceBundleId of bundleToRemove.sourceBundles) {
965
1069
  if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
966
1070
  continue;
967
1071
  }
968
- bundleToKeep.sourceBundles.add(sourceBundleId);
969
- bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
1072
+ if (sourceBundleId !== bundleToKeepId) {
1073
+ bundleToKeep.sourceBundles.add(sourceBundleId);
1074
+ bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
1075
+ }
970
1076
  }
1077
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1078
+ bundleToKeep.sourceBundles.delete(bundleToRemoveId);
1079
+ for (let bundle of bundleGraph.getNodeIdsConnectedFrom(bundleToRemoveId)) {
1080
+ let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundle));
1081
+ if (bundleNode === 'root') {
1082
+ continue;
1083
+ }
971
1084
 
972
- // Merge any internalized assets
973
- if (bundleToRemove.internalizedAssets) {
974
- if (bundleToKeep.internalizedAssets) {
975
- bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
976
- } else {
977
- bundleToKeep.internalizedAssets = bundleToRemove.internalizedAssets;
1085
+ // If the bundle is a source bundle, add it to the bundle to keep
1086
+ if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
1087
+ bundleNode.sourceBundles.delete(bundleToRemoveId);
1088
+ if (bundle !== bundleToKeepId) {
1089
+ bundleNode.sourceBundles.add(bundleToKeepId);
1090
+ bundleGraph.addEdge(bundleToKeepId, bundle);
1091
+ }
1092
+ }
1093
+ }
1094
+
1095
+ // Merge bundle roots
1096
+ for (let bundleRoot of bundleToRemove.bundleRoots) {
1097
+ bundleToKeep.bundleRoots.add(bundleRoot);
1098
+ }
1099
+ if (bundleToRemove.mainEntryAsset != null) {
1100
+ (0, _assert().default)(bundleToKeep.mainEntryAsset != null);
1101
+
1102
+ // Merge the bundles in bundle group
1103
+ let bundlesInRemoveBundleGroup = getBundlesForBundleGroup(bundleToRemoveId);
1104
+ let removedBundleSharedBundles = new Set();
1105
+ for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
1106
+ if (bundleIdInGroup === bundleToRemoveId) {
1107
+ continue;
1108
+ }
1109
+ bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
1110
+ removedBundleSharedBundles.add(bundleIdInGroup);
1111
+ }
1112
+ if ((0, _featureFlags().getFeatureFlag)('removeRedundantSharedBundles')) {
1113
+ // Merge any shared bundles that now have the same source bundles due to
1114
+ // the current bundle merge
1115
+ let sharedBundles = new (_utils().DefaultMap)(() => []);
1116
+ for (let bundleId of removedBundleSharedBundles) {
1117
+ let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
1118
+ (0, _assert().default)(bundleNode !== 'root');
1119
+ if (bundleNode.mainEntryAsset != null || bundleNode.manualSharedBundle != null) {
1120
+ continue;
1121
+ }
1122
+ let key = Array.from(bundleNode.sourceBundles).filter(sourceBundle => sourceBundle !== bundleToRemoveId).sort().join(',') + '.' + bundleNode.type;
1123
+ sharedBundles.get(key).push(bundleId);
1124
+ }
1125
+ for (let sharedBundlesToMerge of sharedBundles.values()) {
1126
+ if (sharedBundlesToMerge.length > 1) {
1127
+ let [firstBundleId, ...rest] = sharedBundlesToMerge;
1128
+ for (let bundleId of rest) {
1129
+ mergeBundles(firstBundleId, bundleId, 'redundant-shared');
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+
1135
+ // Remove old bundle group
1136
+ bundleGroupBundleIds.delete(bundleToRemoveId);
1137
+
1138
+ // Clean up bundle roots
1139
+ let bundleRootToRemoveNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToRemove.mainEntryAsset)));
1140
+ let bundleRootToKeepNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToKeep.mainEntryAsset)));
1141
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootToRemoveNodeId)) {
1142
+ bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
1143
+ bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
1144
+ }
1145
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(bundleRootToRemoveNodeId)) {
1146
+ bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
1147
+ bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
1148
+ }
1149
+ bundleRoots.set((0, _nullthrows().default)(bundleToRemove.mainEntryAsset), [bundleToKeepId, bundleToKeepId]);
1150
+
1151
+ // Merge dependency bundle graph
1152
+ for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), _graph().ALL_EDGE_TYPES)) {
1153
+ let dependencyNode = (0, _nullthrows().default)(dependencyBundleGraph.getNode(dependencyNodeId));
1154
+ (0, _assert().default)(dependencyNode.type === 'dependency');
1155
+
1156
+ // Add dependency to the bundle to keep
1157
+ dependencyBundleGraph.addEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)), dependencyPriorityEdges[dependencyNode.value.priority]);
1158
+ // Remove dependency from the bundle to remove
1159
+ dependencyBundleGraph.removeEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), dependencyPriorityEdges[dependencyNode.value.priority]);
1160
+ }
978
1161
  }
979
1162
  }
980
1163
  bundleGraph.removeNode(bundleToRemoveId);
981
1164
  }
982
- function mergeOverlapBundles() {
1165
+ function mergeSharedBundles(mergeConfig) {
983
1166
  // Find all shared bundles
984
1167
  let sharedBundles = new Set();
985
1168
  bundleGraph.traverse(nodeId => {
@@ -998,18 +1181,136 @@ function createIdealGraph(assetGraph, config, entries, logger) {
998
1181
  sharedBundles.add(nodeId);
999
1182
  }
1000
1183
  });
1001
- let clusters = (0, _bundleMerge.findMergeCandidates)(bundleGraph, Array.from(sharedBundles), config.sharedBundleMergeThreshold);
1184
+ let clusters = (0, _bundleMerge.findMergeCandidates)(bundleGraph, Array.from(sharedBundles), mergeConfig.map(config => {
1185
+ var _config$sourceBundles;
1186
+ return {
1187
+ ...config,
1188
+ sourceBundles: (_config$sourceBundles = config.sourceBundles) === null || _config$sourceBundles === void 0 ? void 0 : _config$sourceBundles.map(assetMatch => {
1189
+ let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
1190
+ if (sourceBundleNodeId == null) {
1191
+ throw new Error(`Source bundle ${assetMatch} not found in merge source bundle lookup`);
1192
+ }
1193
+ return sourceBundleNodeId;
1194
+ })
1195
+ };
1196
+ }));
1197
+ let mergedBundles = new Set();
1002
1198
  for (let cluster of clusters) {
1003
1199
  let [mergeTarget, ...rest] = cluster;
1004
1200
  for (let bundleIdToMerge of rest) {
1005
- mergeBundles(bundleGraph, mergeTarget, bundleIdToMerge, assetReference);
1201
+ mergeBundles(mergeTarget, bundleIdToMerge, 'shared-merge');
1006
1202
  }
1203
+ mergedBundles.add(mergeTarget);
1204
+ }
1205
+ if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
1206
+ return mergedBundles;
1007
1207
  }
1008
1208
  }
1209
+ function mergeAsyncBundles({
1210
+ bundleSize,
1211
+ maxOverfetchSize,
1212
+ ignore
1213
+ }) {
1214
+ let mergeCandidates = [];
1215
+ let ignoreRegexes = (ignore === null || ignore === void 0 ? void 0 : ignore.map(glob => (0, _utils().globToRegex)(glob))) ?? [];
1216
+ let isIgnored = bundle => {
1217
+ if (!bundle.mainEntryAsset) {
1218
+ return false;
1219
+ }
1220
+ let mainEntryFilePath = _path().default.relative(config.projectRoot, (0, _nullthrows().default)(bundle.mainEntryAsset).filePath);
1221
+ return ignoreRegexes.some(regex => regex.test(mainEntryFilePath));
1222
+ };
1223
+ for (let [_bundleRootAsset, [bundleRootBundleId]] of bundleRoots) {
1224
+ let bundleRootBundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleRootBundleId));
1225
+ (0, _assert().default)(bundleRootBundle !== 'root');
1226
+ if (bundleRootBundle.type === 'js' && bundleRootBundle.bundleBehavior !== 'inline' && bundleRootBundle.bundleBehavior !== 'inlineIsolated' && bundleRootBundle.size <= bundleSize && !isIgnored(bundleRootBundle)) {
1227
+ mergeCandidates.push(bundleRootBundleId);
1228
+ }
1229
+ }
1230
+ let candidates = [];
1231
+ for (let i = 0; i < mergeCandidates.length; i++) {
1232
+ for (let j = i + 1; j < mergeCandidates.length; j++) {
1233
+ let a = mergeCandidates[i];
1234
+ let b = mergeCandidates[j];
1235
+ if (a === b) continue; // Skip self-comparison
1236
+
1237
+ candidates.push(scoreAsyncMerge(a, b, maxOverfetchSize));
1238
+ }
1239
+ }
1240
+ let sortByScore = (a, b) => {
1241
+ let diff = a.score - b.score;
1242
+ if (diff > 0) {
1243
+ return 1;
1244
+ } else if (diff < 0) {
1245
+ return -1;
1246
+ }
1247
+ return 0;
1248
+ };
1249
+ candidates = candidates.filter(({
1250
+ overfetchSize,
1251
+ score
1252
+ }) => overfetchSize <= maxOverfetchSize && score > 0).sort(sortByScore);
1253
+
1254
+ // Tracks the bundles that have been merged
1255
+ let merged = new Set();
1256
+ // Tracks the deleted bundles to the bundle they were merged into.
1257
+ let mergeRemap = new Map();
1258
+ // Tracks the bundles that have been rescored and added back into the
1259
+ // candidates.
1260
+ let rescored = new (_utils().DefaultMap)(() => new Set());
1261
+ do {
1262
+ let [a, b] = (0, _nullthrows().default)(candidates.pop()).bundleIds;
1263
+ if (bundleGraph.hasNode(a) && bundleGraph.hasNode(b) && (!merged.has(a) && !merged.has(b) || rescored.get(a).has(b))) {
1264
+ mergeRemap.set(b, a);
1265
+ merged.add(a);
1266
+ rescored.get(a).clear();
1267
+ mergeBundles(a, b, 'async-merge');
1268
+ continue;
1269
+ }
1270
+
1271
+ // One or both of the bundles have been previously merged, so we'll
1272
+ // rescore and add the result back into the list of candidates.
1273
+ let getMergedBundleId = bundleId => {
1274
+ let seen = new Set();
1275
+ while (!bundleGraph.hasNode(bundleId) && !seen.has(bundleId)) {
1276
+ seen.add(bundleId);
1277
+ bundleId = (0, _nullthrows().default)(mergeRemap.get(bundleId));
1278
+ }
1279
+ if (!bundleGraph.hasNode(bundleId)) {
1280
+ return;
1281
+ }
1282
+ return bundleId;
1283
+ };
1284
+
1285
+ // Map a and b to their merged bundle ids if they've already been merged
1286
+ let currentA = getMergedBundleId(a);
1287
+ let currentB = getMergedBundleId(b);
1288
+ if (!currentA || !currentB ||
1289
+ // Bundles are already merged
1290
+ currentA === currentB) {
1291
+ // This combiniation is not valid, so we skip it.
1292
+ continue;
1293
+ }
1294
+ let candidate = scoreAsyncMerge(currentA, currentB, maxOverfetchSize);
1295
+ if (candidate.overfetchSize <= maxOverfetchSize && candidate.score > 0) {
1296
+ _sortedArrayFunctions().default.add(candidates, candidate, sortByScore);
1297
+ rescored.get(currentA).add(currentB);
1298
+ }
1299
+ } while (candidates.length > 0);
1300
+ }
1301
+ function getBundle(bundleId) {
1302
+ let bundle = bundleGraph.getNode(bundleId);
1303
+ if (bundle === 'root') {
1304
+ throw new Error(`Cannot access root bundle`);
1305
+ }
1306
+ if (bundle == null) {
1307
+ throw new Error(`Bundle ${bundleId} not found in bundle graph`);
1308
+ }
1309
+ return bundle;
1310
+ }
1009
1311
  function getBigIntFromContentKey(contentKey) {
1010
1312
  let b = Buffer.alloc(64);
1011
1313
  b.write(contentKey);
1012
- // $FlowFixMe Flow doesn't have BigInt types in this version
1013
1314
  return b.readBigInt64BE();
1014
1315
  }
1015
1316
  // Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
@@ -1039,6 +1340,53 @@ function createIdealGraph(assetGraph, config, entries, logger) {
1039
1340
  }, bundleGroupId);
1040
1341
  return bundlesInABundleGroup;
1041
1342
  }
1343
+ function scoreAsyncMerge(bundleAId, bundleBId, maxOverfetchSize) {
1344
+ let bundleGroupA = new Set(getBundlesForBundleGroup(bundleAId));
1345
+ let bundleGroupB = new Set(getBundlesForBundleGroup(bundleBId));
1346
+ let overlapSize = 0;
1347
+ let overfetchSize = 0;
1348
+ for (let bundleId of new Set([...bundleGroupA, ...bundleGroupB])) {
1349
+ let bundle = getBundle(bundleId);
1350
+ if (bundleGroupA.has(bundleId) && bundleGroupB.has(bundleId)) {
1351
+ overlapSize += bundle.size;
1352
+ } else {
1353
+ overfetchSize += bundle.size;
1354
+ }
1355
+ }
1356
+ let overlapPercent = overlapSize / (overfetchSize + overlapSize);
1357
+ let bundleAParents = getBundleParents(bundleAId);
1358
+ let bundleBParents = getBundleParents(bundleBId);
1359
+ let sharedParentOverlap = 0;
1360
+ let sharedParentMismatch = 0;
1361
+ for (let bundleId of new Set([...bundleAParents, ...bundleBParents])) {
1362
+ if (bundleAParents.has(bundleId) && bundleBParents.has(bundleId)) {
1363
+ sharedParentOverlap++;
1364
+ } else {
1365
+ sharedParentMismatch++;
1366
+ }
1367
+ }
1368
+ let overfetchScore = overfetchSize / maxOverfetchSize;
1369
+ let sharedParentPercent = sharedParentOverlap / (sharedParentOverlap + sharedParentMismatch);
1370
+ let score = sharedParentPercent + overlapPercent + overfetchScore;
1371
+ return {
1372
+ overfetchSize,
1373
+ score,
1374
+ bundleIds: [bundleAId, bundleBId]
1375
+ };
1376
+ }
1377
+ function getBundleParents(bundleId) {
1378
+ let parents = new Set();
1379
+ let {
1380
+ bundleRoots
1381
+ } = getBundle(bundleId);
1382
+ for (let bundleRoot of bundleRoots) {
1383
+ let bundleRootNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get(bundleRoot));
1384
+ for (let parentId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootNodeId, _graph().ALL_EDGE_TYPES)) {
1385
+ parents.add(parentId);
1386
+ }
1387
+ }
1388
+ return parents;
1389
+ }
1042
1390
  function getBundleFromBundleRoot(bundleRoot) {
1043
1391
  let bundle = bundleGraph.getNode((0, _nullthrows().default)(bundleRoots.get(bundleRoot))[0]);
1044
1392
  (0, _assert().default)(bundle !== 'root' && bundle != null);
@@ -1088,6 +1436,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
1088
1436
  }
1089
1437
  bundleGraph.removeNode(bundleId);
1090
1438
  }
1439
+ stats.report(bundleId => {
1440
+ let bundle = bundleGraph.getNode(bundleId);
1441
+ (0, _assert().default)(bundle !== 'root');
1442
+ return bundle;
1443
+ });
1091
1444
  return {
1092
1445
  assets,
1093
1446
  bundleGraph,
@@ -1104,6 +1457,7 @@ function createBundle(opts) {
1104
1457
  bundleBehavior: opts.bundleBehavior,
1105
1458
  env: (0, _nullthrows().default)(opts.env),
1106
1459
  mainEntryAsset: null,
1460
+ bundleRoots: new Set(),
1107
1461
  manualSharedBundle: opts.manualSharedBundle,
1108
1462
  needsStableName: Boolean(opts.needsStableName),
1109
1463
  size: 0,
@@ -1119,6 +1473,7 @@ function createBundle(opts) {
1119
1473
  bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
1120
1474
  env: opts.env ?? asset.env,
1121
1475
  mainEntryAsset: asset,
1476
+ bundleRoots: new Set([asset]),
1122
1477
  manualSharedBundle: opts.manualSharedBundle,
1123
1478
  needsStableName: Boolean(opts.needsStableName),
1124
1479
  size: asset.stats.size,