@atlaspack/bundler-default 2.14.5-canary.14 → 2.14.5-canary.141
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.
- package/CHANGELOG.md +302 -0
- package/lib/DefaultBundler.d.ts +18 -0
- package/lib/DefaultBundler.js +6 -1
- package/lib/MonolithicBundler.d.ts +2 -0
- package/lib/bundleMerge.d.ts +9 -0
- package/lib/bundleMerge.js +107 -37
- package/lib/bundlerConfig.d.ts +27 -0
- package/lib/bundlerConfig.js +49 -9
- package/lib/decorateLegacyGraph.d.ts +3 -0
- package/lib/decorateLegacyGraph.js +24 -3
- package/lib/idealGraph.d.ts +40 -0
- package/lib/idealGraph.js +217 -36
- package/lib/memoize.d.ts +2 -0
- package/lib/memoize.js +39 -0
- package/package.json +16 -11
- package/src/{DefaultBundler.js → DefaultBundler.ts} +21 -6
- package/src/{MonolithicBundler.js → MonolithicBundler.ts} +0 -1
- package/src/bundleMerge.ts +245 -0
- package/src/{bundlerConfig.js → bundlerConfig.ts} +87 -49
- package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +25 -6
- package/src/{idealGraph.js → idealGraph.ts} +367 -94
- package/src/memoize.ts +32 -0
- package/tsconfig.json +4 -0
- package/src/bundleMerge.js +0 -103
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// @flow strict-local
|
|
2
|
-
|
|
3
1
|
import path from 'path';
|
|
4
2
|
|
|
5
3
|
import {getFeatureFlag} from '@atlaspack/feature-flags';
|
|
@@ -8,7 +6,7 @@ import {
|
|
|
8
6
|
BitSet,
|
|
9
7
|
ContentGraph,
|
|
10
8
|
Graph,
|
|
11
|
-
|
|
9
|
+
NodeId,
|
|
12
10
|
} from '@atlaspack/graph';
|
|
13
11
|
import type {
|
|
14
12
|
Asset,
|
|
@@ -23,37 +21,38 @@ import {DefaultMap, globToRegex} from '@atlaspack/utils';
|
|
|
23
21
|
import invariant from 'assert';
|
|
24
22
|
import nullthrows from 'nullthrows';
|
|
25
23
|
|
|
26
|
-
import {findMergeCandidates} from './bundleMerge';
|
|
27
|
-
import type {ResolvedBundlerConfig} from './bundlerConfig';
|
|
24
|
+
import {findMergeCandidates, MergeGroup} from './bundleMerge';
|
|
25
|
+
import type {ResolvedBundlerConfig, MergeCandidates} from './bundlerConfig';
|
|
28
26
|
|
|
29
27
|
/* BundleRoot - An asset that is the main entry of a Bundle. */
|
|
30
28
|
type BundleRoot = Asset;
|
|
31
29
|
|
|
32
|
-
export type Bundle = {
|
|
33
|
-
uniqueKey:
|
|
34
|
-
assets: Set<Asset
|
|
35
|
-
internalizedAssets?: BitSet
|
|
36
|
-
bundleBehavior?:
|
|
37
|
-
needsStableName: boolean
|
|
38
|
-
mainEntryAsset:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
|
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
|
+
bundleRoots: Set<Asset>;
|
|
38
|
+
size: number;
|
|
39
|
+
sourceBundles: Set<NodeId>;
|
|
40
|
+
target: Target;
|
|
41
|
+
env: Environment;
|
|
42
|
+
type: string;
|
|
43
|
+
manualSharedBundle: string | null | undefined; // for naming purposes;
|
|
44
|
+
};
|
|
46
45
|
|
|
47
46
|
export type DependencyBundleGraph = ContentGraph<
|
|
48
|
-
| {
|
|
49
|
-
value: Bundle
|
|
50
|
-
type: 'bundle'
|
|
51
|
-
|
|
52
|
-
| {
|
|
53
|
-
value: Dependency
|
|
54
|
-
type: 'dependency'
|
|
55
|
-
|
|
56
|
-
number
|
|
47
|
+
| {
|
|
48
|
+
value: Bundle;
|
|
49
|
+
type: 'bundle';
|
|
50
|
+
}
|
|
51
|
+
| {
|
|
52
|
+
value: Dependency;
|
|
53
|
+
type: 'dependency';
|
|
54
|
+
},
|
|
55
|
+
number
|
|
57
56
|
>;
|
|
58
57
|
|
|
59
58
|
const dependencyPriorityEdges = {
|
|
@@ -61,7 +60,7 @@ const dependencyPriorityEdges = {
|
|
|
61
60
|
parallel: 2,
|
|
62
61
|
lazy: 3,
|
|
63
62
|
conditional: 4,
|
|
64
|
-
};
|
|
63
|
+
} as const;
|
|
65
64
|
|
|
66
65
|
export const idealBundleGraphEdges = Object.freeze({
|
|
67
66
|
default: 1,
|
|
@@ -70,20 +69,30 @@ export const idealBundleGraphEdges = Object.freeze({
|
|
|
70
69
|
|
|
71
70
|
export type IdealBundleGraph = Graph<
|
|
72
71
|
Bundle | 'root',
|
|
73
|
-
|
|
72
|
+
(typeof idealBundleGraphEdges)[keyof typeof idealBundleGraphEdges]
|
|
74
73
|
>;
|
|
75
74
|
|
|
76
75
|
// IdealGraph is the structure we will pass to decorate,
|
|
77
76
|
// which mutates the assetGraph into the bundleGraph we would
|
|
78
77
|
// expect from default bundler
|
|
79
|
-
export type IdealGraph = {
|
|
80
|
-
assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]
|
|
81
|
-
assets: Array<Asset
|
|
82
|
-
bundleGraph: IdealBundleGraph
|
|
83
|
-
bundleGroupBundleIds: Set<NodeId
|
|
84
|
-
dependencyBundleGraph: DependencyBundleGraph
|
|
85
|
-
manualAssetToBundle: Map<Asset, NodeId
|
|
86
|
-
|
|
78
|
+
export type IdealGraph = {
|
|
79
|
+
assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>;
|
|
80
|
+
assets: Array<Asset>;
|
|
81
|
+
bundleGraph: IdealBundleGraph;
|
|
82
|
+
bundleGroupBundleIds: Set<NodeId>;
|
|
83
|
+
dependencyBundleGraph: DependencyBundleGraph;
|
|
84
|
+
manualAssetToBundle: Map<Asset, NodeId>;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function isNonRootBundle(
|
|
88
|
+
bundle?: Bundle | 'root' | null,
|
|
89
|
+
message?: string,
|
|
90
|
+
): Bundle {
|
|
91
|
+
let existingBundle = nullthrows(bundle, message);
|
|
92
|
+
invariant(existingBundle !== 'root', "Bundle cannot be 'root'");
|
|
93
|
+
|
|
94
|
+
return existingBundle;
|
|
95
|
+
}
|
|
87
96
|
|
|
88
97
|
export function createIdealGraph(
|
|
89
98
|
assetGraph: MutableBundleGraph,
|
|
@@ -97,7 +106,7 @@ export function createIdealGraph(
|
|
|
97
106
|
let dependencyBundleGraph: DependencyBundleGraph = new ContentGraph();
|
|
98
107
|
let assetReference: DefaultMap<
|
|
99
108
|
Asset,
|
|
100
|
-
Array<[Dependency, Bundle]
|
|
109
|
+
Array<[Dependency, Bundle]>
|
|
101
110
|
> = new DefaultMap(() => []);
|
|
102
111
|
|
|
103
112
|
// A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
|
|
@@ -111,8 +120,9 @@ export function createIdealGraph(
|
|
|
111
120
|
};
|
|
112
121
|
// Graph that models bundleRoots, with parallel & async deps only to inform reachability
|
|
113
122
|
let bundleRootGraph: Graph<
|
|
114
|
-
|
|
115
|
-
|
|
123
|
+
// asset index
|
|
124
|
+
number,
|
|
125
|
+
(typeof bundleRootEdgeTypes)[keyof typeof bundleRootEdgeTypes]
|
|
116
126
|
> = new Graph();
|
|
117
127
|
let assetToBundleRootNodeId = new Map<BundleRoot, number>();
|
|
118
128
|
|
|
@@ -146,7 +156,7 @@ export function createIdealGraph(
|
|
|
146
156
|
bundleGroupBundleIds.add(nodeId);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
let assets = [];
|
|
159
|
+
let assets: Array<Asset> = [];
|
|
150
160
|
let assetToIndex = new Map<Asset, number>();
|
|
151
161
|
|
|
152
162
|
function makeManualAssetToConfigLookup() {
|
|
@@ -161,6 +171,7 @@ export function createIdealGraph(
|
|
|
161
171
|
|
|
162
172
|
for (let c of config.manualSharedBundles) {
|
|
163
173
|
if (c.root != null) {
|
|
174
|
+
// @ts-expect-error TS2345
|
|
164
175
|
parentsToConfig.get(path.join(config.projectRoot, c.root)).push(c);
|
|
165
176
|
}
|
|
166
177
|
}
|
|
@@ -204,17 +215,17 @@ export function createIdealGraph(
|
|
|
204
215
|
config.projectRoot,
|
|
205
216
|
node.value.filePath,
|
|
206
217
|
);
|
|
207
|
-
if (!assetRegexes.some((regex) => regex.test(projectRelativePath))) {
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
218
|
|
|
211
219
|
// We track all matching MSB's for constant modules as they are never duplicated
|
|
212
220
|
// and need to be assigned to all matching bundles
|
|
213
221
|
if (node.value.meta.isConstantModule === true) {
|
|
222
|
+
// @ts-expect-error TS2345
|
|
214
223
|
constantModuleToMSB.get(node.value).push(c);
|
|
215
224
|
}
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
|
|
226
|
+
if (assetRegexes.some((regex) => regex.test(projectRelativePath))) {
|
|
227
|
+
manualAssetToConfig.set(node.value, c);
|
|
228
|
+
}
|
|
218
229
|
}
|
|
219
230
|
|
|
220
231
|
if (
|
|
@@ -242,9 +253,19 @@ export function createIdealGraph(
|
|
|
242
253
|
makeManualAssetToConfigLookup();
|
|
243
254
|
let manualBundleToInternalizedAsset: DefaultMap<
|
|
244
255
|
NodeId,
|
|
245
|
-
Array<Asset
|
|
256
|
+
Array<Asset>
|
|
246
257
|
> = new DefaultMap(() => []);
|
|
247
258
|
|
|
259
|
+
let mergeSourceBundleLookup = new Map<string, NodeId>();
|
|
260
|
+
let mergeSourceBundleAssets = new Set(
|
|
261
|
+
config.sharedBundleMerge?.flatMap(
|
|
262
|
+
(c) =>
|
|
263
|
+
c.sourceBundles?.map((assetMatch: string) =>
|
|
264
|
+
path.join(config.projectRoot, assetMatch),
|
|
265
|
+
) ?? [],
|
|
266
|
+
),
|
|
267
|
+
);
|
|
268
|
+
|
|
248
269
|
/**
|
|
249
270
|
* Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
|
|
250
271
|
* for asset type changes, parallel, inline, and async or lazy dependencies,
|
|
@@ -252,7 +273,27 @@ export function createIdealGraph(
|
|
|
252
273
|
*/
|
|
253
274
|
assetGraph.traverse(
|
|
254
275
|
{
|
|
255
|
-
enter(
|
|
276
|
+
enter(
|
|
277
|
+
node: // @ts-expect-error TS2304
|
|
278
|
+
| BundleGraphTraversable
|
|
279
|
+
| {
|
|
280
|
+
readonly type: 'dependency';
|
|
281
|
+
value: Dependency;
|
|
282
|
+
},
|
|
283
|
+
context:
|
|
284
|
+
| {
|
|
285
|
+
readonly type: 'asset';
|
|
286
|
+
value: Asset;
|
|
287
|
+
}
|
|
288
|
+
| null
|
|
289
|
+
| undefined
|
|
290
|
+
| {
|
|
291
|
+
readonly type: 'dependency';
|
|
292
|
+
value: Dependency;
|
|
293
|
+
},
|
|
294
|
+
// @ts-expect-error TS2304
|
|
295
|
+
actions: TraversalActions,
|
|
296
|
+
) {
|
|
256
297
|
if (node.type === 'asset') {
|
|
257
298
|
if (
|
|
258
299
|
context?.type === 'dependency' &&
|
|
@@ -335,6 +376,14 @@ export function createIdealGraph(
|
|
|
335
376
|
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
336
377
|
bundleGroupBundleIds.add(bundleId);
|
|
337
378
|
bundleGraph.addEdge(bundleGraphRootNodeId, bundleId);
|
|
379
|
+
// If this asset is relevant for merging then track it's source
|
|
380
|
+
// bundle id for later
|
|
381
|
+
if (mergeSourceBundleAssets.has(childAsset.filePath)) {
|
|
382
|
+
mergeSourceBundleLookup.set(
|
|
383
|
+
path.relative(config.projectRoot, childAsset.filePath),
|
|
384
|
+
bundleId,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
338
387
|
if (manualSharedObject) {
|
|
339
388
|
// MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
|
|
340
389
|
// since MSBs should not have main entry assets
|
|
@@ -371,6 +420,7 @@ export function createIdealGraph(
|
|
|
371
420
|
type: 'bundle',
|
|
372
421
|
},
|
|
373
422
|
),
|
|
423
|
+
// @ts-expect-error TS7053
|
|
374
424
|
dependencyPriorityEdges[dependency.priority],
|
|
375
425
|
);
|
|
376
426
|
|
|
@@ -482,6 +532,7 @@ export function createIdealGraph(
|
|
|
482
532
|
|
|
483
533
|
assetReference.get(childAsset).push([dependency, bundle]);
|
|
484
534
|
} else {
|
|
535
|
+
// @ts-expect-error TS2322
|
|
485
536
|
bundleId = null;
|
|
486
537
|
}
|
|
487
538
|
if (manualSharedObject && bundleId != null) {
|
|
@@ -489,6 +540,7 @@ export function createIdealGraph(
|
|
|
489
540
|
// add the asset if it doesn't already have it and set key
|
|
490
541
|
|
|
491
542
|
invariant(
|
|
543
|
+
// @ts-expect-error TS2367
|
|
492
544
|
bundle !== 'root' && bundle != null && bundleId != null,
|
|
493
545
|
);
|
|
494
546
|
|
|
@@ -515,7 +567,8 @@ export function createIdealGraph(
|
|
|
515
567
|
}
|
|
516
568
|
return node;
|
|
517
569
|
},
|
|
518
|
-
|
|
570
|
+
// @ts-expect-error TS2322
|
|
571
|
+
exit(node: BundleGraphTraversable) {
|
|
519
572
|
if (stack[stack.length - 1]?.[0] === node.value) {
|
|
520
573
|
stack.pop();
|
|
521
574
|
}
|
|
@@ -564,20 +617,20 @@ export function createIdealGraph(
|
|
|
564
617
|
|
|
565
618
|
// reachableRoots is an array of bit sets for each asset. Each bit set
|
|
566
619
|
// indicates which bundle roots are reachable from that asset synchronously.
|
|
567
|
-
let reachableRoots = [];
|
|
620
|
+
let reachableRoots: Array<BitSet> = [];
|
|
568
621
|
for (let i = 0; i < assets.length; i++) {
|
|
569
622
|
reachableRoots.push(new BitSet(bundleRootGraph.nodes.length));
|
|
570
623
|
}
|
|
571
624
|
|
|
572
625
|
// reachableAssets is the inverse mapping of reachableRoots. For each bundle root,
|
|
573
626
|
// it contains a bit set that indicates which assets are reachable from it.
|
|
574
|
-
let reachableAssets = [];
|
|
627
|
+
let reachableAssets: Array<BitSet> = [];
|
|
575
628
|
|
|
576
629
|
// ancestorAssets maps bundle roots to the set of all assets available to it at runtime,
|
|
577
630
|
// including in earlier parallel bundles. These are intersected through all paths to
|
|
578
631
|
// the bundle to ensure that the available assets are always present no matter in which
|
|
579
632
|
// order the bundles are loaded.
|
|
580
|
-
let ancestorAssets = [];
|
|
633
|
+
let ancestorAssets: Array<null | BitSet> = [];
|
|
581
634
|
|
|
582
635
|
let inlineConstantDeps = new DefaultMap(() => new Set());
|
|
583
636
|
|
|
@@ -806,8 +859,11 @@ export function createIdealGraph(
|
|
|
806
859
|
|
|
807
860
|
function assignInlineConstants(parentAsset: Asset, bundle: Bundle) {
|
|
808
861
|
for (let inlineConstant of inlineConstantDeps.get(parentAsset)) {
|
|
862
|
+
// @ts-expect-error TS2345
|
|
809
863
|
if (!bundle.assets.has(inlineConstant)) {
|
|
864
|
+
// @ts-expect-error TS2345
|
|
810
865
|
bundle.assets.add(inlineConstant);
|
|
866
|
+
// @ts-expect-error TS18046
|
|
811
867
|
bundle.size += inlineConstant.stats.size;
|
|
812
868
|
}
|
|
813
869
|
}
|
|
@@ -867,7 +923,7 @@ export function createIdealGraph(
|
|
|
867
923
|
let bundle;
|
|
868
924
|
let bundleId;
|
|
869
925
|
let manualSharedBundleKey = manualSharedObject.name + ',' + asset.type;
|
|
870
|
-
let sourceBundles = [];
|
|
926
|
+
let sourceBundles: Array<NodeId> = [];
|
|
871
927
|
reachable.forEach((id) => {
|
|
872
928
|
sourceBundles.push(nullthrows(bundleRoots.get(assets[id]))[0]);
|
|
873
929
|
});
|
|
@@ -982,7 +1038,7 @@ export function createIdealGraph(
|
|
|
982
1038
|
});
|
|
983
1039
|
}
|
|
984
1040
|
|
|
985
|
-
let reachableArray = [];
|
|
1041
|
+
let reachableArray: Array<Asset> = [];
|
|
986
1042
|
reachable.forEach((id) => {
|
|
987
1043
|
reachableArray.push(assets[id]);
|
|
988
1044
|
});
|
|
@@ -1057,6 +1113,8 @@ export function createIdealGraph(
|
|
|
1057
1113
|
}
|
|
1058
1114
|
|
|
1059
1115
|
let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
|
|
1116
|
+
let modifiedSourceBundles = new Set<Bundle>();
|
|
1117
|
+
|
|
1060
1118
|
// Step split manual shared bundles for those that have the "split" property set
|
|
1061
1119
|
let remainderMap = new DefaultMap(() => []);
|
|
1062
1120
|
for (let id of manualSharedMap.values()) {
|
|
@@ -1075,9 +1133,9 @@ export function createIdealGraph(
|
|
|
1075
1133
|
if (modNum != null) {
|
|
1076
1134
|
for (let a of [...manualBundle.assets]) {
|
|
1077
1135
|
let numRep = getBigIntFromContentKey(a.id);
|
|
1078
|
-
// $FlowFixMe Flow doesn't know about BigInt
|
|
1079
1136
|
let r = Number(numRep % BigInt(modNum));
|
|
1080
1137
|
|
|
1138
|
+
// @ts-expect-error TS2345
|
|
1081
1139
|
remainderMap.get(r).push(a);
|
|
1082
1140
|
}
|
|
1083
1141
|
|
|
@@ -1100,8 +1158,10 @@ export function createIdealGraph(
|
|
|
1100
1158
|
}
|
|
1101
1159
|
for (let sp of remainderMap.get(i)) {
|
|
1102
1160
|
bundle.assets.add(sp);
|
|
1161
|
+
// @ts-expect-error TS2339
|
|
1103
1162
|
bundle.size += sp.stats.size;
|
|
1104
1163
|
manualBundle.assets.delete(sp);
|
|
1164
|
+
// @ts-expect-error TS2339
|
|
1105
1165
|
manualBundle.size -= sp.stats.size;
|
|
1106
1166
|
}
|
|
1107
1167
|
}
|
|
@@ -1114,6 +1174,7 @@ export function createIdealGraph(
|
|
|
1114
1174
|
// match multiple MSB's
|
|
1115
1175
|
for (let [asset, msbs] of constantModuleToMSB.entries()) {
|
|
1116
1176
|
for (let manualSharedObject of msbs) {
|
|
1177
|
+
// @ts-expect-error TS2339
|
|
1117
1178
|
let bundleId = manualSharedMap.get(manualSharedObject.name + ',js');
|
|
1118
1179
|
if (bundleId == null) continue;
|
|
1119
1180
|
let bundle = nullthrows(bundleGraph.getNode(bundleId));
|
|
@@ -1122,17 +1183,94 @@ export function createIdealGraph(
|
|
|
1122
1183
|
'We tried to use the root incorrectly',
|
|
1123
1184
|
);
|
|
1124
1185
|
|
|
1186
|
+
// @ts-expect-error TS2345
|
|
1125
1187
|
if (!bundle.assets.has(asset)) {
|
|
1188
|
+
// @ts-expect-error TS2345
|
|
1126
1189
|
bundle.assets.add(asset);
|
|
1190
|
+
// @ts-expect-error TS18046
|
|
1127
1191
|
bundle.size += asset.stats.size;
|
|
1128
1192
|
}
|
|
1129
1193
|
}
|
|
1130
1194
|
}
|
|
1131
1195
|
|
|
1196
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1197
|
+
// Merge webpack chunk name bundles
|
|
1198
|
+
let chunkNameBundles = new DefaultMap(() => new Set());
|
|
1199
|
+
for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
|
|
1200
|
+
// meta.chunkName is set by the Rust transformer, so we just need to find
|
|
1201
|
+
// bundles that have a chunkName set.
|
|
1202
|
+
if (
|
|
1203
|
+
!node ||
|
|
1204
|
+
node.type !== 'dependency' ||
|
|
1205
|
+
node.value.meta.chunkName == null
|
|
1206
|
+
) {
|
|
1207
|
+
continue;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(
|
|
1211
|
+
nodeId,
|
|
1212
|
+
dependencyPriorityEdges[node.value.priority],
|
|
1213
|
+
);
|
|
1214
|
+
|
|
1215
|
+
if (connectedBundles.length === 0) {
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
invariant(
|
|
1220
|
+
connectedBundles.length === 1,
|
|
1221
|
+
'Expected webpackChunkName dependency to be connected to no more than one bundle',
|
|
1222
|
+
);
|
|
1223
|
+
|
|
1224
|
+
let bundleId = connectedBundles[0];
|
|
1225
|
+
let bundleNode = dependencyBundleGraph.getNode(bundleId);
|
|
1226
|
+
invariant(bundleNode != null && bundleNode.type === 'bundle');
|
|
1227
|
+
|
|
1228
|
+
// If a bundle does not have a main entry asset, it's somehow just a
|
|
1229
|
+
// shared bundle, and will be merged/deleted by other means.
|
|
1230
|
+
if (bundleNode.value.mainEntryAsset == null) {
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
let bundleNodeId = null;
|
|
1235
|
+
let mainEntryAssetId = bundleNode.value.mainEntryAsset?.id;
|
|
1236
|
+
|
|
1237
|
+
if (mainEntryAssetId != null) {
|
|
1238
|
+
bundleNodeId = bundles.get(mainEntryAssetId);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if (bundleNodeId == null) {
|
|
1242
|
+
continue;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
chunkNameBundles
|
|
1246
|
+
.get(node.value.meta.chunkName)
|
|
1247
|
+
// DependencyBundleGraph uses content keys as node ids, so we can use that
|
|
1248
|
+
// to get the bundle id.
|
|
1249
|
+
.add(bundleNodeId);
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
|
|
1253
|
+
// The `[request]` placeholder is not yet supported
|
|
1254
|
+
if (
|
|
1255
|
+
bundleIds.size <= 1 ||
|
|
1256
|
+
(typeof chunkName === 'string' && chunkName.includes('[request]'))
|
|
1257
|
+
) {
|
|
1258
|
+
continue; // Nothing to merge
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// Merge all bundles with the same chunk name into the first one.
|
|
1262
|
+
let [firstBundleId, ...rest] = Array.from(bundleIds);
|
|
1263
|
+
for (let bundleId of rest) {
|
|
1264
|
+
// @ts-expect-error TS2345
|
|
1265
|
+
mergeBundles(firstBundleId, bundleId);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1132
1270
|
// Step merge shared bundles that meet the overlap threshold
|
|
1133
1271
|
// This step is skipped by default as the threshold defaults to 1
|
|
1134
|
-
if (config.
|
|
1135
|
-
mergeOverlapBundles();
|
|
1272
|
+
if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
|
|
1273
|
+
mergeOverlapBundles(config.sharedBundleMerge);
|
|
1136
1274
|
}
|
|
1137
1275
|
|
|
1138
1276
|
// Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
|
|
@@ -1151,7 +1289,6 @@ export function createIdealGraph(
|
|
|
1151
1289
|
}
|
|
1152
1290
|
|
|
1153
1291
|
// Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
|
|
1154
|
-
let modifiedSourceBundles = new Set();
|
|
1155
1292
|
|
|
1156
1293
|
if (config.disableSharedBundles === false) {
|
|
1157
1294
|
for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
|
|
@@ -1165,6 +1302,7 @@ export function createIdealGraph(
|
|
|
1165
1302
|
let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
|
|
1166
1303
|
let bundle = nullthrows(bundleGraph.getNode(b));
|
|
1167
1304
|
invariant(bundle !== 'root');
|
|
1305
|
+
// @ts-expect-error TS2365
|
|
1168
1306
|
return count + (bundle.bundleBehavior !== 'inline');
|
|
1169
1307
|
}, 0);
|
|
1170
1308
|
|
|
@@ -1200,7 +1338,9 @@ export function createIdealGraph(
|
|
|
1200
1338
|
numBundlesContributingToPRL > config.maxParallelRequests
|
|
1201
1339
|
) {
|
|
1202
1340
|
let bundleTuple = sharedBundlesInGroup.pop();
|
|
1341
|
+
// @ts-expect-error TS18048
|
|
1203
1342
|
let bundleToRemove = bundleTuple.bundle;
|
|
1343
|
+
// @ts-expect-error TS18048
|
|
1204
1344
|
let bundleIdToRemove = bundleTuple.id;
|
|
1205
1345
|
//TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
|
|
1206
1346
|
// but total # bundles still exceeds limit due to non shared bundles
|
|
@@ -1256,28 +1396,50 @@ export function createIdealGraph(
|
|
|
1256
1396
|
}
|
|
1257
1397
|
}
|
|
1258
1398
|
|
|
1259
|
-
function mergeBundles(
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1399
|
+
function mergeBundles(bundleToKeepId: NodeId, bundleToRemoveId: NodeId) {
|
|
1400
|
+
let bundleToKeep = isNonRootBundle(
|
|
1401
|
+
bundleGraph.getNode(bundleToKeepId),
|
|
1402
|
+
`Bundle ${bundleToKeepId} not found`,
|
|
1403
|
+
);
|
|
1404
|
+
let bundleToRemove = isNonRootBundle(
|
|
1405
|
+
bundleGraph.getNode(bundleToRemoveId),
|
|
1406
|
+
`Bundle ${bundleToRemoveId} not found`,
|
|
1407
|
+
);
|
|
1408
|
+
modifiedSourceBundles.add(bundleToKeep);
|
|
1268
1409
|
for (let asset of bundleToRemove.assets) {
|
|
1269
1410
|
bundleToKeep.assets.add(asset);
|
|
1270
1411
|
bundleToKeep.size += asset.stats.size;
|
|
1271
1412
|
|
|
1272
1413
|
let newAssetReference = assetReference
|
|
1273
1414
|
.get(asset)
|
|
1274
|
-
.map(([dep, bundle]) =>
|
|
1415
|
+
.map(([dep, bundle]: [any, any]) =>
|
|
1275
1416
|
bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle],
|
|
1276
1417
|
);
|
|
1277
1418
|
|
|
1419
|
+
// @ts-expect-error TS2345
|
|
1278
1420
|
assetReference.set(asset, newAssetReference);
|
|
1279
1421
|
}
|
|
1280
1422
|
|
|
1423
|
+
// Merge any internalized assets
|
|
1424
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1425
|
+
if (bundleToKeep.internalizedAssets != null) {
|
|
1426
|
+
if (bundleToRemove.internalizedAssets != null) {
|
|
1427
|
+
bundleToKeep.internalizedAssets.intersect(
|
|
1428
|
+
bundleToRemove.internalizedAssets,
|
|
1429
|
+
);
|
|
1430
|
+
} else {
|
|
1431
|
+
bundleToKeep.internalizedAssets.clear();
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
} else {
|
|
1435
|
+
invariant(
|
|
1436
|
+
bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
|
|
1437
|
+
'All shared bundles should have internalized assets',
|
|
1438
|
+
);
|
|
1439
|
+
bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// Merge and clean up source bundles
|
|
1281
1443
|
for (let sourceBundleId of bundleToRemove.sourceBundles) {
|
|
1282
1444
|
if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
|
|
1283
1445
|
continue;
|
|
@@ -1287,21 +1449,108 @@ export function createIdealGraph(
|
|
|
1287
1449
|
bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
|
|
1288
1450
|
}
|
|
1289
1451
|
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1452
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1453
|
+
bundleToKeep.sourceBundles.delete(bundleToRemoveId);
|
|
1454
|
+
|
|
1455
|
+
for (let bundle of bundleGraph.getNodeIdsConnectedFrom(
|
|
1456
|
+
bundleToRemoveId,
|
|
1457
|
+
)) {
|
|
1458
|
+
let bundleNode = nullthrows(bundleGraph.getNode(bundle));
|
|
1459
|
+
if (bundleNode === 'root') {
|
|
1460
|
+
continue;
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// If the bundle is a source bundle, add it to the bundle to keep
|
|
1464
|
+
if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
|
|
1465
|
+
bundleNode.sourceBundles.add(bundleToKeepId);
|
|
1466
|
+
bundleNode.sourceBundles.delete(bundleToRemoveId);
|
|
1467
|
+
bundleGraph.addEdge(bundleToKeepId, bundle);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
// Merge bundle roots
|
|
1472
|
+
for (let bundleRoot of bundleToRemove.bundleRoots) {
|
|
1473
|
+
bundleToKeep.bundleRoots.add(bundleRoot);
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
if (bundleToRemove.mainEntryAsset != null) {
|
|
1477
|
+
invariant(bundleToKeep.mainEntryAsset != null);
|
|
1478
|
+
|
|
1479
|
+
// Merge the bundles in bundle group
|
|
1480
|
+
let bundlesInRemoveBundleGroup =
|
|
1481
|
+
getBundlesForBundleGroup(bundleToRemoveId);
|
|
1482
|
+
|
|
1483
|
+
for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
|
|
1484
|
+
if (bundleIdInGroup === bundleToRemoveId) {
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// Remove old bundle group
|
|
1491
|
+
bundleGroupBundleIds.delete(bundleToRemoveId);
|
|
1492
|
+
|
|
1493
|
+
// Clean up bundle roots
|
|
1494
|
+
let bundleRootToRemoveNodeId = nullthrows(
|
|
1495
|
+
assetToBundleRootNodeId.get(
|
|
1496
|
+
nullthrows(bundleToRemove.mainEntryAsset),
|
|
1497
|
+
),
|
|
1295
1498
|
);
|
|
1296
|
-
|
|
1297
|
-
|
|
1499
|
+
let bundleRootToKeepNodeId = nullthrows(
|
|
1500
|
+
assetToBundleRootNodeId.get(nullthrows(bundleToKeep.mainEntryAsset)),
|
|
1501
|
+
);
|
|
1502
|
+
|
|
1503
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(
|
|
1504
|
+
bundleRootToRemoveNodeId,
|
|
1505
|
+
)) {
|
|
1506
|
+
bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
|
|
1507
|
+
bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(
|
|
1511
|
+
bundleRootToRemoveNodeId,
|
|
1512
|
+
)) {
|
|
1513
|
+
bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
|
|
1514
|
+
bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
bundleRoots.set(nullthrows(bundleToRemove.mainEntryAsset), [
|
|
1518
|
+
bundleToKeepId,
|
|
1519
|
+
bundleToKeepId,
|
|
1520
|
+
]);
|
|
1521
|
+
|
|
1522
|
+
// Merge dependency bundle graph
|
|
1523
|
+
for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(
|
|
1524
|
+
dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)),
|
|
1525
|
+
ALL_EDGE_TYPES,
|
|
1526
|
+
)) {
|
|
1527
|
+
let dependencyNode = nullthrows(
|
|
1528
|
+
dependencyBundleGraph.getNode(dependencyNodeId),
|
|
1529
|
+
);
|
|
1530
|
+
invariant(dependencyNode.type === 'dependency');
|
|
1531
|
+
|
|
1532
|
+
// Add dependency to the bundle to keep
|
|
1533
|
+
dependencyBundleGraph.addEdge(
|
|
1534
|
+
dependencyNodeId,
|
|
1535
|
+
dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)),
|
|
1536
|
+
dependencyPriorityEdges[dependencyNode.value.priority],
|
|
1537
|
+
);
|
|
1538
|
+
// Remove dependency from the bundle to remove
|
|
1539
|
+
dependencyBundleGraph.removeEdge(
|
|
1540
|
+
dependencyNodeId,
|
|
1541
|
+
dependencyBundleGraph.getNodeIdByContentKey(
|
|
1542
|
+
String(bundleToRemoveId),
|
|
1543
|
+
),
|
|
1544
|
+
dependencyPriorityEdges[dependencyNode.value.priority],
|
|
1545
|
+
);
|
|
1546
|
+
}
|
|
1298
1547
|
}
|
|
1299
1548
|
}
|
|
1300
1549
|
|
|
1301
1550
|
bundleGraph.removeNode(bundleToRemoveId);
|
|
1302
1551
|
}
|
|
1303
1552
|
|
|
1304
|
-
function mergeOverlapBundles() {
|
|
1553
|
+
function mergeOverlapBundles(mergeConfig: MergeCandidates) {
|
|
1305
1554
|
// Find all shared bundles
|
|
1306
1555
|
let sharedBundles = new Set<NodeId>();
|
|
1307
1556
|
bundleGraph.traverse((nodeId) => {
|
|
@@ -1331,22 +1580,44 @@ export function createIdealGraph(
|
|
|
1331
1580
|
let clusters = findMergeCandidates(
|
|
1332
1581
|
bundleGraph,
|
|
1333
1582
|
Array.from(sharedBundles),
|
|
1334
|
-
|
|
1583
|
+
mergeConfig.map(
|
|
1584
|
+
(config): MergeGroup => ({
|
|
1585
|
+
...config,
|
|
1586
|
+
sourceBundles: config.sourceBundles?.map((assetMatch: string) => {
|
|
1587
|
+
let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
|
|
1588
|
+
|
|
1589
|
+
if (sourceBundleNodeId == null) {
|
|
1590
|
+
throw new Error(
|
|
1591
|
+
`Source bundle ${assetMatch} not found in merge source bundle lookup`,
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
return sourceBundleNodeId;
|
|
1596
|
+
}),
|
|
1597
|
+
}),
|
|
1598
|
+
),
|
|
1335
1599
|
);
|
|
1336
1600
|
|
|
1601
|
+
let mergedBundles = new Set();
|
|
1602
|
+
|
|
1337
1603
|
for (let cluster of clusters) {
|
|
1338
1604
|
let [mergeTarget, ...rest] = cluster;
|
|
1339
1605
|
|
|
1340
1606
|
for (let bundleIdToMerge of rest) {
|
|
1341
|
-
mergeBundles(
|
|
1607
|
+
mergeBundles(mergeTarget, bundleIdToMerge);
|
|
1342
1608
|
}
|
|
1609
|
+
|
|
1610
|
+
mergedBundles.add(mergeTarget);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1614
|
+
return mergedBundles;
|
|
1343
1615
|
}
|
|
1344
1616
|
}
|
|
1345
1617
|
|
|
1346
|
-
function getBigIntFromContentKey(contentKey) {
|
|
1618
|
+
function getBigIntFromContentKey(contentKey: string) {
|
|
1347
1619
|
let b = Buffer.alloc(64);
|
|
1348
1620
|
b.write(contentKey);
|
|
1349
|
-
// $FlowFixMe Flow doesn't have BigInt types in this version
|
|
1350
1621
|
return b.readBigInt64BE();
|
|
1351
1622
|
}
|
|
1352
1623
|
// Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
|
|
@@ -1373,8 +1644,8 @@ export function createIdealGraph(
|
|
|
1373
1644
|
bundleRootGraph.removeNode(bundleRootId);
|
|
1374
1645
|
}
|
|
1375
1646
|
}
|
|
1376
|
-
function getBundlesForBundleGroup(bundleGroupId) {
|
|
1377
|
-
let bundlesInABundleGroup = [];
|
|
1647
|
+
function getBundlesForBundleGroup(bundleGroupId: NodeId) {
|
|
1648
|
+
let bundlesInABundleGroup: Array<NodeId> = [];
|
|
1378
1649
|
bundleGraph.traverse((nodeId) => {
|
|
1379
1650
|
bundlesInABundleGroup.push(nodeId);
|
|
1380
1651
|
}, bundleGroupId);
|
|
@@ -1455,23 +1726,24 @@ export function createIdealGraph(
|
|
|
1455
1726
|
};
|
|
1456
1727
|
}
|
|
1457
1728
|
|
|
1458
|
-
function createBundle(opts: {
|
|
1459
|
-
asset?: Asset
|
|
1460
|
-
bundleBehavior?:
|
|
1461
|
-
env?: Environment
|
|
1462
|
-
manualSharedBundle?:
|
|
1463
|
-
needsStableName?: boolean
|
|
1464
|
-
sourceBundles?: Set<NodeId
|
|
1465
|
-
target: Target
|
|
1466
|
-
type?: string
|
|
1467
|
-
uniqueKey?: string
|
|
1468
|
-
|
|
1729
|
+
function createBundle(opts: {
|
|
1730
|
+
asset?: Asset;
|
|
1731
|
+
bundleBehavior?: BundleBehavior | null | undefined;
|
|
1732
|
+
env?: Environment;
|
|
1733
|
+
manualSharedBundle?: string | null | undefined;
|
|
1734
|
+
needsStableName?: boolean;
|
|
1735
|
+
sourceBundles?: Set<NodeId>;
|
|
1736
|
+
target: Target;
|
|
1737
|
+
type?: string;
|
|
1738
|
+
uniqueKey?: string;
|
|
1739
|
+
}): Bundle {
|
|
1469
1740
|
if (opts.asset == null) {
|
|
1470
1741
|
return {
|
|
1471
1742
|
assets: new Set(),
|
|
1472
1743
|
bundleBehavior: opts.bundleBehavior,
|
|
1473
1744
|
env: nullthrows(opts.env),
|
|
1474
1745
|
mainEntryAsset: null,
|
|
1746
|
+
bundleRoots: new Set(),
|
|
1475
1747
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1476
1748
|
needsStableName: Boolean(opts.needsStableName),
|
|
1477
1749
|
size: 0,
|
|
@@ -1488,6 +1760,7 @@ function createBundle(opts: {|
|
|
|
1488
1760
|
bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
|
|
1489
1761
|
env: opts.env ?? asset.env,
|
|
1490
1762
|
mainEntryAsset: asset,
|
|
1763
|
+
bundleRoots: new Set([asset]),
|
|
1491
1764
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1492
1765
|
needsStableName: Boolean(opts.needsStableName),
|
|
1493
1766
|
size: asset.stats.size,
|