@atlaspack/bundler-default 3.1.3-typescript-5b4d3ad41.0 → 3.2.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.
@@ -1,3 +1,5 @@
1
+ // @flow strict-local
2
+
1
3
  import path from 'path';
2
4
 
3
5
  import {getFeatureFlag} from '@atlaspack/feature-flags';
@@ -6,7 +8,7 @@ import {
6
8
  BitSet,
7
9
  ContentGraph,
8
10
  Graph,
9
- NodeId,
11
+ type NodeId,
10
12
  } from '@atlaspack/graph';
11
13
  import type {
12
14
  Asset,
@@ -21,37 +23,38 @@ import {DefaultMap, globToRegex} from '@atlaspack/utils';
21
23
  import invariant from 'assert';
22
24
  import nullthrows from 'nullthrows';
23
25
 
24
- import {findMergeCandidates, MergeGroup} from './bundleMerge';
26
+ import {findMergeCandidates, type MergeGroup} from './bundleMerge';
25
27
  import type {ResolvedBundlerConfig, MergeCandidates} from './bundlerConfig';
26
28
 
27
29
  /* BundleRoot - An asset that is the main entry of a Bundle. */
28
30
  type BundleRoot = Asset;
29
31
 
30
- export type Bundle = {
31
- uniqueKey: string | null | undefined;
32
- assets: Set<Asset>;
33
- internalizedAssets?: BitSet;
34
- bundleBehavior?: BundleBehavior | null | undefined;
35
- needsStableName: boolean;
36
- mainEntryAsset: Asset | null | undefined;
37
- size: number;
38
- sourceBundles: Set<NodeId>;
39
- target: Target;
40
- env: Environment;
41
- type: string;
42
- manualSharedBundle: string | null | undefined; // for naming purposes;
43
- };
32
+ export type Bundle = {|
33
+ uniqueKey: ?string,
34
+ assets: Set<Asset>,
35
+ internalizedAssets?: BitSet,
36
+ bundleBehavior?: ?BundleBehavior,
37
+ needsStableName: boolean,
38
+ mainEntryAsset: ?Asset,
39
+ bundleRoots: Set<Asset>,
40
+ size: number,
41
+ sourceBundles: Set<NodeId>,
42
+ target: Target,
43
+ env: Environment,
44
+ type: string,
45
+ manualSharedBundle: ?string, // for naming purposes
46
+ |};
44
47
 
45
48
  export type DependencyBundleGraph = ContentGraph<
46
- | {
47
- value: Bundle;
48
- type: 'bundle';
49
- }
50
- | {
51
- value: Dependency;
52
- type: 'dependency';
53
- },
54
- number
49
+ | {|
50
+ value: Bundle,
51
+ type: 'bundle',
52
+ |}
53
+ | {|
54
+ value: Dependency,
55
+ type: 'dependency',
56
+ |},
57
+ number,
55
58
  >;
56
59
 
57
60
  const dependencyPriorityEdges = {
@@ -59,7 +62,7 @@ const dependencyPriorityEdges = {
59
62
  parallel: 2,
60
63
  lazy: 3,
61
64
  conditional: 4,
62
- } as const;
65
+ };
63
66
 
64
67
  export const idealBundleGraphEdges = Object.freeze({
65
68
  default: 1,
@@ -68,20 +71,30 @@ export const idealBundleGraphEdges = Object.freeze({
68
71
 
69
72
  export type IdealBundleGraph = Graph<
70
73
  Bundle | 'root',
71
- (typeof idealBundleGraphEdges)[keyof typeof idealBundleGraphEdges]
74
+ $Values<typeof idealBundleGraphEdges>,
72
75
  >;
73
76
 
74
77
  // IdealGraph is the structure we will pass to decorate,
75
78
  // which mutates the assetGraph into the bundleGraph we would
76
79
  // expect from default bundler
77
- export type IdealGraph = {
78
- assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>;
79
- assets: Array<Asset>;
80
- bundleGraph: IdealBundleGraph;
81
- bundleGroupBundleIds: Set<NodeId>;
82
- dependencyBundleGraph: DependencyBundleGraph;
83
- manualAssetToBundle: Map<Asset, NodeId>;
84
- };
80
+ export type IdealGraph = {|
81
+ assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>,
82
+ assets: Array<Asset>,
83
+ bundleGraph: IdealBundleGraph,
84
+ bundleGroupBundleIds: Set<NodeId>,
85
+ dependencyBundleGraph: DependencyBundleGraph,
86
+ manualAssetToBundle: Map<Asset, NodeId>,
87
+ |};
88
+
89
+ function isNonRootBundle(
90
+ bundle?: Bundle | 'root' | null,
91
+ message?: string,
92
+ ): Bundle {
93
+ let existingBundle = nullthrows(bundle, message);
94
+ invariant(existingBundle !== 'root', "Bundle cannot be 'root'");
95
+
96
+ return existingBundle;
97
+ }
85
98
 
86
99
  export function createIdealGraph(
87
100
  assetGraph: MutableBundleGraph,
@@ -95,7 +108,7 @@ export function createIdealGraph(
95
108
  let dependencyBundleGraph: DependencyBundleGraph = new ContentGraph();
96
109
  let assetReference: DefaultMap<
97
110
  Asset,
98
- Array<[Dependency, Bundle]>
111
+ Array<[Dependency, Bundle]>,
99
112
  > = new DefaultMap(() => []);
100
113
 
101
114
  // A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
@@ -109,9 +122,8 @@ export function createIdealGraph(
109
122
  };
110
123
  // Graph that models bundleRoots, with parallel & async deps only to inform reachability
111
124
  let bundleRootGraph: Graph<
112
- // asset index
113
- number,
114
- (typeof bundleRootEdgeTypes)[keyof typeof bundleRootEdgeTypes]
125
+ number, // asset index
126
+ $Values<typeof bundleRootEdgeTypes>,
115
127
  > = new Graph();
116
128
  let assetToBundleRootNodeId = new Map<BundleRoot, number>();
117
129
 
@@ -145,7 +157,7 @@ export function createIdealGraph(
145
157
  bundleGroupBundleIds.add(nodeId);
146
158
  }
147
159
 
148
- let assets: Array<Asset> = [];
160
+ let assets = [];
149
161
  let assetToIndex = new Map<Asset, number>();
150
162
 
151
163
  function makeManualAssetToConfigLookup() {
@@ -160,7 +172,6 @@ export function createIdealGraph(
160
172
 
161
173
  for (let c of config.manualSharedBundles) {
162
174
  if (c.root != null) {
163
- // @ts-expect-error TS2345
164
175
  parentsToConfig.get(path.join(config.projectRoot, c.root)).push(c);
165
176
  }
166
177
  }
@@ -208,7 +219,6 @@ export function createIdealGraph(
208
219
  // We track all matching MSB's for constant modules as they are never duplicated
209
220
  // and need to be assigned to all matching bundles
210
221
  if (node.value.meta.isConstantModule === true) {
211
- // @ts-expect-error TS2345
212
222
  constantModuleToMSB.get(node.value).push(c);
213
223
  }
214
224
 
@@ -242,14 +252,14 @@ export function createIdealGraph(
242
252
  makeManualAssetToConfigLookup();
243
253
  let manualBundleToInternalizedAsset: DefaultMap<
244
254
  NodeId,
245
- Array<Asset>
255
+ Array<Asset>,
246
256
  > = new DefaultMap(() => []);
247
257
 
248
258
  let mergeSourceBundleLookup = new Map<string, NodeId>();
249
259
  let mergeSourceBundleAssets = new Set(
250
260
  config.sharedBundleMerge?.flatMap(
251
261
  (c) =>
252
- c.sourceBundles?.map((assetMatch: string) =>
262
+ c.sourceBundles?.map((assetMatch) =>
253
263
  path.join(config.projectRoot, assetMatch),
254
264
  ) ?? [],
255
265
  ),
@@ -262,27 +272,7 @@ export function createIdealGraph(
262
272
  */
263
273
  assetGraph.traverse(
264
274
  {
265
- enter(
266
- node: // @ts-expect-error TS2304
267
- | BundleGraphTraversable
268
- | {
269
- readonly type: 'dependency';
270
- value: Dependency;
271
- },
272
- context:
273
- | {
274
- readonly type: 'asset';
275
- value: Asset;
276
- }
277
- | null
278
- | undefined
279
- | {
280
- readonly type: 'dependency';
281
- value: Dependency;
282
- },
283
- // @ts-expect-error TS2304
284
- actions: TraversalActions,
285
- ) {
275
+ enter(node, context, actions) {
286
276
  if (node.type === 'asset') {
287
277
  if (
288
278
  context?.type === 'dependency' &&
@@ -409,7 +399,6 @@ export function createIdealGraph(
409
399
  type: 'bundle',
410
400
  },
411
401
  ),
412
- // @ts-expect-error TS7053
413
402
  dependencyPriorityEdges[dependency.priority],
414
403
  );
415
404
 
@@ -521,7 +510,6 @@ export function createIdealGraph(
521
510
 
522
511
  assetReference.get(childAsset).push([dependency, bundle]);
523
512
  } else {
524
- // @ts-expect-error TS2322
525
513
  bundleId = null;
526
514
  }
527
515
  if (manualSharedObject && bundleId != null) {
@@ -529,7 +517,6 @@ export function createIdealGraph(
529
517
  // add the asset if it doesn't already have it and set key
530
518
 
531
519
  invariant(
532
- // @ts-expect-error TS2367
533
520
  bundle !== 'root' && bundle != null && bundleId != null,
534
521
  );
535
522
 
@@ -556,8 +543,7 @@ export function createIdealGraph(
556
543
  }
557
544
  return node;
558
545
  },
559
- // @ts-expect-error TS2322
560
- exit(node: BundleGraphTraversable) {
546
+ exit(node) {
561
547
  if (stack[stack.length - 1]?.[0] === node.value) {
562
548
  stack.pop();
563
549
  }
@@ -606,20 +592,20 @@ export function createIdealGraph(
606
592
 
607
593
  // reachableRoots is an array of bit sets for each asset. Each bit set
608
594
  // indicates which bundle roots are reachable from that asset synchronously.
609
- let reachableRoots: Array<BitSet> = [];
595
+ let reachableRoots = [];
610
596
  for (let i = 0; i < assets.length; i++) {
611
597
  reachableRoots.push(new BitSet(bundleRootGraph.nodes.length));
612
598
  }
613
599
 
614
600
  // reachableAssets is the inverse mapping of reachableRoots. For each bundle root,
615
601
  // it contains a bit set that indicates which assets are reachable from it.
616
- let reachableAssets: Array<BitSet> = [];
602
+ let reachableAssets = [];
617
603
 
618
604
  // ancestorAssets maps bundle roots to the set of all assets available to it at runtime,
619
605
  // including in earlier parallel bundles. These are intersected through all paths to
620
606
  // the bundle to ensure that the available assets are always present no matter in which
621
607
  // order the bundles are loaded.
622
- let ancestorAssets: Array<null | BitSet> = [];
608
+ let ancestorAssets = [];
623
609
 
624
610
  let inlineConstantDeps = new DefaultMap(() => new Set());
625
611
 
@@ -848,11 +834,8 @@ export function createIdealGraph(
848
834
 
849
835
  function assignInlineConstants(parentAsset: Asset, bundle: Bundle) {
850
836
  for (let inlineConstant of inlineConstantDeps.get(parentAsset)) {
851
- // @ts-expect-error TS2345
852
837
  if (!bundle.assets.has(inlineConstant)) {
853
- // @ts-expect-error TS2345
854
838
  bundle.assets.add(inlineConstant);
855
- // @ts-expect-error TS18046
856
839
  bundle.size += inlineConstant.stats.size;
857
840
  }
858
841
  }
@@ -912,7 +895,7 @@ export function createIdealGraph(
912
895
  let bundle;
913
896
  let bundleId;
914
897
  let manualSharedBundleKey = manualSharedObject.name + ',' + asset.type;
915
- let sourceBundles: Array<NodeId> = [];
898
+ let sourceBundles = [];
916
899
  reachable.forEach((id) => {
917
900
  sourceBundles.push(nullthrows(bundleRoots.get(assets[id]))[0]);
918
901
  });
@@ -1027,7 +1010,7 @@ export function createIdealGraph(
1027
1010
  });
1028
1011
  }
1029
1012
 
1030
- let reachableArray: Array<Asset> = [];
1013
+ let reachableArray = [];
1031
1014
  reachable.forEach((id) => {
1032
1015
  reachableArray.push(assets[id]);
1033
1016
  });
@@ -1102,6 +1085,8 @@ export function createIdealGraph(
1102
1085
  }
1103
1086
 
1104
1087
  let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
1088
+ let modifiedSourceBundles = new Set<Bundle>();
1089
+
1105
1090
  // Step split manual shared bundles for those that have the "split" property set
1106
1091
  let remainderMap = new DefaultMap(() => []);
1107
1092
  for (let id of manualSharedMap.values()) {
@@ -1120,9 +1105,9 @@ export function createIdealGraph(
1120
1105
  if (modNum != null) {
1121
1106
  for (let a of [...manualBundle.assets]) {
1122
1107
  let numRep = getBigIntFromContentKey(a.id);
1108
+ // $FlowFixMe Flow doesn't know about BigInt
1123
1109
  let r = Number(numRep % BigInt(modNum));
1124
1110
 
1125
- // @ts-expect-error TS2345
1126
1111
  remainderMap.get(r).push(a);
1127
1112
  }
1128
1113
 
@@ -1145,10 +1130,8 @@ export function createIdealGraph(
1145
1130
  }
1146
1131
  for (let sp of remainderMap.get(i)) {
1147
1132
  bundle.assets.add(sp);
1148
- // @ts-expect-error TS2339
1149
1133
  bundle.size += sp.stats.size;
1150
1134
  manualBundle.assets.delete(sp);
1151
- // @ts-expect-error TS2339
1152
1135
  manualBundle.size -= sp.stats.size;
1153
1136
  }
1154
1137
  }
@@ -1161,7 +1144,6 @@ export function createIdealGraph(
1161
1144
  // match multiple MSB's
1162
1145
  for (let [asset, msbs] of constantModuleToMSB.entries()) {
1163
1146
  for (let manualSharedObject of msbs) {
1164
- // @ts-expect-error TS2339
1165
1147
  let bundleId = manualSharedMap.get(manualSharedObject.name + ',js');
1166
1148
  if (bundleId == null) continue;
1167
1149
  let bundle = nullthrows(bundleGraph.getNode(bundleId));
@@ -1170,16 +1152,86 @@ export function createIdealGraph(
1170
1152
  'We tried to use the root incorrectly',
1171
1153
  );
1172
1154
 
1173
- // @ts-expect-error TS2345
1174
1155
  if (!bundle.assets.has(asset)) {
1175
- // @ts-expect-error TS2345
1176
1156
  bundle.assets.add(asset);
1177
- // @ts-expect-error TS18046
1178
1157
  bundle.size += asset.stats.size;
1179
1158
  }
1180
1159
  }
1181
1160
  }
1182
1161
 
1162
+ if (getFeatureFlag('supportWebpackChunkName')) {
1163
+ // Merge webpack chunk name bundles
1164
+ let chunkNameBundles = new DefaultMap(() => new Set());
1165
+ for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
1166
+ // meta.chunkName is set by the Rust transformer, so we just need to find
1167
+ // bundles that have a chunkName set.
1168
+ if (
1169
+ !node ||
1170
+ node.type !== 'dependency' ||
1171
+ node.value.meta.chunkName == null
1172
+ ) {
1173
+ continue;
1174
+ }
1175
+
1176
+ let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(
1177
+ nodeId,
1178
+ dependencyPriorityEdges[node.value.priority],
1179
+ );
1180
+
1181
+ if (connectedBundles.length === 0) {
1182
+ continue;
1183
+ }
1184
+
1185
+ invariant(
1186
+ connectedBundles.length === 1,
1187
+ 'Expected webpackChunkName dependency to be connected to no more than one bundle',
1188
+ );
1189
+
1190
+ let bundleId = connectedBundles[0];
1191
+ let bundleNode = dependencyBundleGraph.getNode(bundleId);
1192
+ invariant(bundleNode != null && bundleNode.type === 'bundle');
1193
+
1194
+ // If a bundle does not have a main entry asset, it's somehow just a
1195
+ // shared bundle, and will be merged/deleted by other means.
1196
+ if (bundleNode.value.mainEntryAsset == null) {
1197
+ continue;
1198
+ }
1199
+
1200
+ let bundleNodeId = null;
1201
+ let mainEntryAssetId = bundleNode.value.mainEntryAsset?.id;
1202
+
1203
+ if (mainEntryAssetId != null) {
1204
+ bundleNodeId = bundles.get(mainEntryAssetId);
1205
+ }
1206
+
1207
+ if (bundleNodeId == null) {
1208
+ continue;
1209
+ }
1210
+
1211
+ chunkNameBundles
1212
+ .get(node.value.meta.chunkName)
1213
+ // DependencyBundleGraph uses content keys as node ids, so we can use that
1214
+ // to get the bundle id.
1215
+ .add(bundleNodeId);
1216
+ }
1217
+
1218
+ for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
1219
+ // The `[request]` placeholder is not yet supported
1220
+ if (
1221
+ bundleIds.size <= 1 ||
1222
+ (typeof chunkName === 'string' && chunkName.includes('[request]'))
1223
+ ) {
1224
+ continue; // Nothing to merge
1225
+ }
1226
+
1227
+ // Merge all bundles with the same chunk name into the first one.
1228
+ let [firstBundleId, ...rest] = Array.from(bundleIds);
1229
+ for (let bundleId of rest) {
1230
+ mergeBundles(firstBundleId, bundleId);
1231
+ }
1232
+ }
1233
+ }
1234
+
1183
1235
  // Step merge shared bundles that meet the overlap threshold
1184
1236
  // This step is skipped by default as the threshold defaults to 1
1185
1237
  if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
@@ -1202,7 +1254,6 @@ export function createIdealGraph(
1202
1254
  }
1203
1255
 
1204
1256
  // Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
1205
- let modifiedSourceBundles = new Set();
1206
1257
 
1207
1258
  if (config.disableSharedBundles === false) {
1208
1259
  for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
@@ -1216,7 +1267,6 @@ export function createIdealGraph(
1216
1267
  let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
1217
1268
  let bundle = nullthrows(bundleGraph.getNode(b));
1218
1269
  invariant(bundle !== 'root');
1219
- // @ts-expect-error TS2365
1220
1270
  return count + (bundle.bundleBehavior !== 'inline');
1221
1271
  }, 0);
1222
1272
 
@@ -1252,9 +1302,7 @@ export function createIdealGraph(
1252
1302
  numBundlesContributingToPRL > config.maxParallelRequests
1253
1303
  ) {
1254
1304
  let bundleTuple = sharedBundlesInGroup.pop();
1255
- // @ts-expect-error TS18048
1256
1305
  let bundleToRemove = bundleTuple.bundle;
1257
- // @ts-expect-error TS18048
1258
1306
  let bundleIdToRemove = bundleTuple.id;
1259
1307
  //TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
1260
1308
  // but total # bundles still exceeds limit due to non shared bundles
@@ -1310,36 +1358,49 @@ export function createIdealGraph(
1310
1358
  }
1311
1359
  }
1312
1360
 
1313
- function mergeBundles(
1314
- bundleGraph: IdealBundleGraph,
1315
- bundleToKeepId: NodeId,
1316
- bundleToRemoveId: NodeId,
1317
- assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>,
1318
- ) {
1319
- let bundleToKeep = nullthrows(bundleGraph.getNode(bundleToKeepId));
1320
- let bundleToRemove = nullthrows(bundleGraph.getNode(bundleToRemoveId));
1321
- invariant(bundleToKeep !== 'root' && bundleToRemove !== 'root');
1361
+ function mergeBundles(bundleToKeepId: NodeId, bundleToRemoveId: NodeId) {
1362
+ let bundleToKeep = isNonRootBundle(
1363
+ bundleGraph.getNode(bundleToKeepId),
1364
+ `Bundle ${bundleToKeepId} not found`,
1365
+ );
1366
+ let bundleToRemove = isNonRootBundle(
1367
+ bundleGraph.getNode(bundleToRemoveId),
1368
+ `Bundle ${bundleToRemoveId} not found`,
1369
+ );
1370
+ modifiedSourceBundles.add(bundleToKeep);
1322
1371
  for (let asset of bundleToRemove.assets) {
1323
1372
  bundleToKeep.assets.add(asset);
1324
1373
  bundleToKeep.size += asset.stats.size;
1325
1374
 
1326
1375
  let newAssetReference = assetReference
1327
1376
  .get(asset)
1328
- .map(([dep, bundle]: [any, any]) =>
1377
+ .map(([dep, bundle]) =>
1329
1378
  bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle],
1330
1379
  );
1331
1380
 
1332
- // @ts-expect-error TS2345
1333
1381
  assetReference.set(asset, newAssetReference);
1334
1382
  }
1335
1383
 
1336
1384
  // Merge any internalized assets
1337
- invariant(
1338
- bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
1339
- 'All shared bundles should have internalized assets',
1340
- );
1341
- bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1385
+ if (getFeatureFlag('supportWebpackChunkName')) {
1386
+ if (bundleToKeep.internalizedAssets != null) {
1387
+ if (bundleToRemove.internalizedAssets != null) {
1388
+ bundleToKeep.internalizedAssets.intersect(
1389
+ bundleToRemove.internalizedAssets,
1390
+ );
1391
+ } else {
1392
+ bundleToKeep.internalizedAssets.clear();
1393
+ }
1394
+ }
1395
+ } else {
1396
+ invariant(
1397
+ bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
1398
+ 'All shared bundles should have internalized assets',
1399
+ );
1400
+ bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
1401
+ }
1342
1402
 
1403
+ // Merge and clean up source bundles
1343
1404
  for (let sourceBundleId of bundleToRemove.sourceBundles) {
1344
1405
  if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
1345
1406
  continue;
@@ -1349,6 +1410,104 @@ export function createIdealGraph(
1349
1410
  bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
1350
1411
  }
1351
1412
 
1413
+ if (getFeatureFlag('supportWebpackChunkName')) {
1414
+ bundleToKeep.sourceBundles.delete(bundleToRemoveId);
1415
+
1416
+ for (let bundle of bundleGraph.getNodeIdsConnectedFrom(
1417
+ bundleToRemoveId,
1418
+ )) {
1419
+ let bundleNode = nullthrows(bundleGraph.getNode(bundle));
1420
+ if (bundleNode === 'root') {
1421
+ continue;
1422
+ }
1423
+
1424
+ // If the bundle is a source bundle, add it to the bundle to keep
1425
+ if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
1426
+ bundleNode.sourceBundles.add(bundleToKeepId);
1427
+ bundleNode.sourceBundles.delete(bundleToRemoveId);
1428
+ bundleGraph.addEdge(bundleToKeepId, bundle);
1429
+ }
1430
+ }
1431
+
1432
+ // Merge bundle roots
1433
+ for (let bundleRoot of bundleToRemove.bundleRoots) {
1434
+ bundleToKeep.bundleRoots.add(bundleRoot);
1435
+ }
1436
+
1437
+ if (bundleToRemove.mainEntryAsset != null) {
1438
+ invariant(bundleToKeep.mainEntryAsset != null);
1439
+
1440
+ // Merge the bundles in bundle group
1441
+ let bundlesInRemoveBundleGroup =
1442
+ getBundlesForBundleGroup(bundleToRemoveId);
1443
+
1444
+ for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
1445
+ if (bundleIdInGroup === bundleToRemoveId) {
1446
+ continue;
1447
+ }
1448
+ bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
1449
+ }
1450
+
1451
+ // Remove old bundle group
1452
+ bundleGroupBundleIds.delete(bundleToRemoveId);
1453
+
1454
+ // Clean up bundle roots
1455
+ let bundleRootToRemoveNodeId = nullthrows(
1456
+ assetToBundleRootNodeId.get(
1457
+ nullthrows(bundleToRemove.mainEntryAsset),
1458
+ ),
1459
+ );
1460
+ let bundleRootToKeepNodeId = nullthrows(
1461
+ assetToBundleRootNodeId.get(nullthrows(bundleToKeep.mainEntryAsset)),
1462
+ );
1463
+
1464
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(
1465
+ bundleRootToRemoveNodeId,
1466
+ )) {
1467
+ bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
1468
+ bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
1469
+ }
1470
+
1471
+ for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(
1472
+ bundleRootToRemoveNodeId,
1473
+ )) {
1474
+ bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
1475
+ bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
1476
+ }
1477
+
1478
+ bundleRoots.set(nullthrows(bundleToRemove.mainEntryAsset), [
1479
+ bundleToKeepId,
1480
+ bundleToKeepId,
1481
+ ]);
1482
+
1483
+ // Merge dependency bundle graph
1484
+ for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(
1485
+ dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)),
1486
+ ALL_EDGE_TYPES,
1487
+ )) {
1488
+ let dependencyNode = nullthrows(
1489
+ dependencyBundleGraph.getNode(dependencyNodeId),
1490
+ );
1491
+ invariant(dependencyNode.type === 'dependency');
1492
+
1493
+ // Add dependency to the bundle to keep
1494
+ dependencyBundleGraph.addEdge(
1495
+ dependencyNodeId,
1496
+ dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)),
1497
+ dependencyPriorityEdges[dependencyNode.value.priority],
1498
+ );
1499
+ // Remove dependency from the bundle to remove
1500
+ dependencyBundleGraph.removeEdge(
1501
+ dependencyNodeId,
1502
+ dependencyBundleGraph.getNodeIdByContentKey(
1503
+ String(bundleToRemoveId),
1504
+ ),
1505
+ dependencyPriorityEdges[dependencyNode.value.priority],
1506
+ );
1507
+ }
1508
+ }
1509
+ }
1510
+
1352
1511
  bundleGraph.removeNode(bundleToRemoveId);
1353
1512
  }
1354
1513
 
@@ -1382,36 +1541,43 @@ export function createIdealGraph(
1382
1541
  let clusters = findMergeCandidates(
1383
1542
  bundleGraph,
1384
1543
  Array.from(sharedBundles),
1385
- mergeConfig.map(
1386
- (config): MergeGroup => ({
1387
- ...config,
1388
- sourceBundles: config.sourceBundles?.map((assetMatch: string) => {
1389
- let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
1390
-
1391
- if (sourceBundleNodeId == null) {
1392
- throw new Error(
1393
- `Source bundle ${assetMatch} not found in merge source bundle lookup`,
1394
- );
1395
- }
1544
+ mergeConfig.map((config): MergeGroup => ({
1545
+ ...config,
1546
+ sourceBundles: config.sourceBundles?.map((assetMatch) => {
1547
+ let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
1548
+
1549
+ if (sourceBundleNodeId == null) {
1550
+ throw new Error(
1551
+ `Source bundle ${assetMatch} not found in merge source bundle lookup`,
1552
+ );
1553
+ }
1396
1554
 
1397
- return sourceBundleNodeId;
1398
- }),
1555
+ return sourceBundleNodeId;
1399
1556
  }),
1400
- ),
1557
+ })),
1401
1558
  );
1402
1559
 
1560
+ let mergedBundles = new Set();
1561
+
1403
1562
  for (let cluster of clusters) {
1404
1563
  let [mergeTarget, ...rest] = cluster;
1405
1564
 
1406
1565
  for (let bundleIdToMerge of rest) {
1407
- mergeBundles(bundleGraph, mergeTarget, bundleIdToMerge, assetReference);
1566
+ mergeBundles(mergeTarget, bundleIdToMerge);
1408
1567
  }
1568
+
1569
+ mergedBundles.add(mergeTarget);
1570
+ }
1571
+
1572
+ if (getFeatureFlag('supportWebpackChunkName')) {
1573
+ return mergedBundles;
1409
1574
  }
1410
1575
  }
1411
1576
 
1412
- function getBigIntFromContentKey(contentKey: string) {
1577
+ function getBigIntFromContentKey(contentKey) {
1413
1578
  let b = Buffer.alloc(64);
1414
1579
  b.write(contentKey);
1580
+ // $FlowFixMe Flow doesn't have BigInt types in this version
1415
1581
  return b.readBigInt64BE();
1416
1582
  }
1417
1583
  // Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
@@ -1419,9 +1585,7 @@ export function createIdealGraph(
1419
1585
  let assetOrderMap = new Map(assets.map((a, index) => [a, index]));
1420
1586
 
1421
1587
  for (let bundle of modifiedSourceBundles) {
1422
- // @ts-expect-error TS18046
1423
1588
  bundle.assets = new Set(
1424
- // @ts-expect-error TS18046
1425
1589
  [...bundle.assets].sort((a, b) => {
1426
1590
  let aIndex = nullthrows(assetOrderMap.get(a));
1427
1591
  let bIndex = nullthrows(assetOrderMap.get(b));
@@ -1440,8 +1604,8 @@ export function createIdealGraph(
1440
1604
  bundleRootGraph.removeNode(bundleRootId);
1441
1605
  }
1442
1606
  }
1443
- function getBundlesForBundleGroup(bundleGroupId: NodeId) {
1444
- let bundlesInABundleGroup: Array<NodeId> = [];
1607
+ function getBundlesForBundleGroup(bundleGroupId) {
1608
+ let bundlesInABundleGroup = [];
1445
1609
  bundleGraph.traverse((nodeId) => {
1446
1610
  bundlesInABundleGroup.push(nodeId);
1447
1611
  }, bundleGroupId);
@@ -1522,23 +1686,24 @@ export function createIdealGraph(
1522
1686
  };
1523
1687
  }
1524
1688
 
1525
- function createBundle(opts: {
1526
- asset?: Asset;
1527
- bundleBehavior?: BundleBehavior | null | undefined;
1528
- env?: Environment;
1529
- manualSharedBundle?: string | null | undefined;
1530
- needsStableName?: boolean;
1531
- sourceBundles?: Set<NodeId>;
1532
- target: Target;
1533
- type?: string;
1534
- uniqueKey?: string;
1535
- }): Bundle {
1689
+ function createBundle(opts: {|
1690
+ asset?: Asset,
1691
+ bundleBehavior?: ?BundleBehavior,
1692
+ env?: Environment,
1693
+ manualSharedBundle?: ?string,
1694
+ needsStableName?: boolean,
1695
+ sourceBundles?: Set<NodeId>,
1696
+ target: Target,
1697
+ type?: string,
1698
+ uniqueKey?: string,
1699
+ |}): Bundle {
1536
1700
  if (opts.asset == null) {
1537
1701
  return {
1538
1702
  assets: new Set(),
1539
1703
  bundleBehavior: opts.bundleBehavior,
1540
1704
  env: nullthrows(opts.env),
1541
1705
  mainEntryAsset: null,
1706
+ bundleRoots: new Set(),
1542
1707
  manualSharedBundle: opts.manualSharedBundle,
1543
1708
  needsStableName: Boolean(opts.needsStableName),
1544
1709
  size: 0,
@@ -1555,6 +1720,7 @@ function createBundle(opts: {
1555
1720
  bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
1556
1721
  env: opts.env ?? asset.env,
1557
1722
  mainEntryAsset: asset,
1723
+ bundleRoots: new Set([asset]),
1558
1724
  manualSharedBundle: opts.manualSharedBundle,
1559
1725
  needsStableName: Boolean(opts.needsStableName),
1560
1726
  size: asset.stats.size,