@atlaspack/bundler-default 2.14.5-canary.3 → 2.14.5-canary.300
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 +564 -0
- package/dist/DefaultBundler.js +84 -0
- package/dist/MonolithicBundler.js +68 -0
- package/dist/bundleMerge.js +137 -0
- package/dist/bundlerConfig.js +223 -0
- package/dist/decorateLegacyGraph.js +189 -0
- package/dist/idealGraph.js +1471 -0
- package/dist/memoize.js +31 -0
- package/dist/stats.js +69 -0
- package/lib/DefaultBundler.js +6 -1
- package/lib/MonolithicBundler.js +11 -3
- package/lib/bundleMerge.js +160 -0
- package/lib/bundlerConfig.js +76 -9
- package/lib/decorateLegacyGraph.js +24 -3
- package/lib/idealGraph.js +453 -36
- package/lib/memoize.js +39 -0
- package/lib/stats.js +85 -0
- package/lib/types/DefaultBundler.d.ts +18 -0
- package/lib/types/MonolithicBundler.d.ts +2 -0
- package/lib/types/bundleMerge.d.ts +9 -0
- package/lib/types/bundlerConfig.d.ts +36 -0
- package/lib/types/decorateLegacyGraph.d.ts +3 -0
- package/lib/types/idealGraph.d.ts +40 -0
- package/lib/types/memoize.d.ts +2 -0
- package/lib/types/stats.d.ts +16 -0
- package/package.json +20 -12
- package/src/{DefaultBundler.js → DefaultBundler.ts} +21 -6
- package/src/{MonolithicBundler.js → MonolithicBundler.ts} +17 -5
- package/src/bundleMerge.ts +250 -0
- package/src/{bundlerConfig.js → bundlerConfig.ts} +124 -44
- package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +26 -7
- package/src/{idealGraph.js → idealGraph.ts} +802 -116
- package/src/memoize.ts +32 -0
- package/src/stats.ts +97 -0
- package/tsconfig.json +30 -0
- package/tsconfig.tsbuildinfo +1 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
// @flow strict-local
|
|
2
|
-
|
|
3
1
|
import path from 'path';
|
|
2
|
+
import sortedArray from 'sorted-array-functions';
|
|
4
3
|
|
|
5
4
|
import {getFeatureFlag} from '@atlaspack/feature-flags';
|
|
6
5
|
import {
|
|
@@ -8,7 +7,7 @@ import {
|
|
|
8
7
|
BitSet,
|
|
9
8
|
ContentGraph,
|
|
10
9
|
Graph,
|
|
11
|
-
|
|
10
|
+
NodeId,
|
|
12
11
|
} from '@atlaspack/graph';
|
|
13
12
|
import type {
|
|
14
13
|
Asset,
|
|
@@ -18,41 +17,50 @@ import type {
|
|
|
18
17
|
MutableBundleGraph,
|
|
19
18
|
Target,
|
|
20
19
|
PluginLogger,
|
|
20
|
+
BundleGraphTraversable,
|
|
21
|
+
TraversalActions,
|
|
21
22
|
} from '@atlaspack/types';
|
|
22
23
|
import {DefaultMap, globToRegex} from '@atlaspack/utils';
|
|
23
24
|
import invariant from 'assert';
|
|
24
25
|
import nullthrows from 'nullthrows';
|
|
25
26
|
|
|
26
|
-
import
|
|
27
|
+
import {findMergeCandidates, MergeGroup} from './bundleMerge';
|
|
28
|
+
import type {
|
|
29
|
+
ResolvedBundlerConfig,
|
|
30
|
+
SharedBundleMergeCandidates,
|
|
31
|
+
AsyncBundleMerge,
|
|
32
|
+
} from './bundlerConfig';
|
|
33
|
+
import {Stats} from './stats';
|
|
27
34
|
|
|
28
35
|
/* BundleRoot - An asset that is the main entry of a Bundle. */
|
|
29
36
|
type BundleRoot = Asset;
|
|
30
37
|
|
|
31
|
-
export type Bundle = {
|
|
32
|
-
uniqueKey:
|
|
33
|
-
assets: Set<Asset
|
|
34
|
-
internalizedAssets?: BitSet
|
|
35
|
-
bundleBehavior?:
|
|
36
|
-
needsStableName: boolean
|
|
37
|
-
mainEntryAsset:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
|
38
|
+
export type Bundle = {
|
|
39
|
+
uniqueKey: string | null | undefined;
|
|
40
|
+
assets: Set<Asset>;
|
|
41
|
+
internalizedAssets?: BitSet;
|
|
42
|
+
bundleBehavior?: BundleBehavior | null | undefined;
|
|
43
|
+
needsStableName: boolean;
|
|
44
|
+
mainEntryAsset: Asset | null | undefined;
|
|
45
|
+
bundleRoots: Set<Asset>;
|
|
46
|
+
size: number;
|
|
47
|
+
sourceBundles: Set<NodeId>;
|
|
48
|
+
target: Target;
|
|
49
|
+
env: Environment;
|
|
50
|
+
type: string;
|
|
51
|
+
manualSharedBundle: string | null | undefined; // for naming purposes;
|
|
52
|
+
};
|
|
45
53
|
|
|
46
54
|
export type DependencyBundleGraph = ContentGraph<
|
|
47
|
-
| {
|
|
48
|
-
value: Bundle
|
|
49
|
-
type: 'bundle'
|
|
50
|
-
|
|
51
|
-
| {
|
|
52
|
-
value: Dependency
|
|
53
|
-
type: 'dependency'
|
|
54
|
-
|
|
55
|
-
number
|
|
55
|
+
| {
|
|
56
|
+
value: Bundle;
|
|
57
|
+
type: 'bundle';
|
|
58
|
+
}
|
|
59
|
+
| {
|
|
60
|
+
value: Dependency;
|
|
61
|
+
type: 'dependency';
|
|
62
|
+
},
|
|
63
|
+
number
|
|
56
64
|
>;
|
|
57
65
|
|
|
58
66
|
const dependencyPriorityEdges = {
|
|
@@ -60,29 +68,39 @@ const dependencyPriorityEdges = {
|
|
|
60
68
|
parallel: 2,
|
|
61
69
|
lazy: 3,
|
|
62
70
|
conditional: 4,
|
|
63
|
-
};
|
|
71
|
+
} as const;
|
|
64
72
|
|
|
65
73
|
export const idealBundleGraphEdges = Object.freeze({
|
|
66
74
|
default: 1,
|
|
67
75
|
conditional: 2,
|
|
68
76
|
});
|
|
69
77
|
|
|
70
|
-
type IdealBundleGraph = Graph<
|
|
78
|
+
export type IdealBundleGraph = Graph<
|
|
71
79
|
Bundle | 'root',
|
|
72
|
-
|
|
80
|
+
(typeof idealBundleGraphEdges)[keyof typeof idealBundleGraphEdges]
|
|
73
81
|
>;
|
|
74
82
|
|
|
75
83
|
// IdealGraph is the structure we will pass to decorate,
|
|
76
84
|
// which mutates the assetGraph into the bundleGraph we would
|
|
77
85
|
// expect from default bundler
|
|
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
|
+
export type IdealGraph = {
|
|
87
|
+
assetReference: DefaultMap<Asset, Array<[Dependency, Bundle]>>;
|
|
88
|
+
assets: Array<Asset>;
|
|
89
|
+
bundleGraph: IdealBundleGraph;
|
|
90
|
+
bundleGroupBundleIds: Set<NodeId>;
|
|
91
|
+
dependencyBundleGraph: DependencyBundleGraph;
|
|
92
|
+
manualAssetToBundle: Map<Asset, NodeId>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function isNonRootBundle(
|
|
96
|
+
bundle?: Bundle | 'root' | null,
|
|
97
|
+
message?: string,
|
|
98
|
+
): Bundle {
|
|
99
|
+
let existingBundle = nullthrows(bundle, message);
|
|
100
|
+
invariant(existingBundle !== 'root', "Bundle cannot be 'root'");
|
|
101
|
+
|
|
102
|
+
return existingBundle;
|
|
103
|
+
}
|
|
86
104
|
|
|
87
105
|
export function createIdealGraph(
|
|
88
106
|
assetGraph: MutableBundleGraph,
|
|
@@ -96,8 +114,9 @@ export function createIdealGraph(
|
|
|
96
114
|
let dependencyBundleGraph: DependencyBundleGraph = new ContentGraph();
|
|
97
115
|
let assetReference: DefaultMap<
|
|
98
116
|
Asset,
|
|
99
|
-
Array<[Dependency, Bundle]
|
|
117
|
+
Array<[Dependency, Bundle]>
|
|
100
118
|
> = new DefaultMap(() => []);
|
|
119
|
+
let stats = new Stats(config.projectRoot);
|
|
101
120
|
|
|
102
121
|
// A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
|
|
103
122
|
// referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role.
|
|
@@ -110,8 +129,9 @@ export function createIdealGraph(
|
|
|
110
129
|
};
|
|
111
130
|
// Graph that models bundleRoots, with parallel & async deps only to inform reachability
|
|
112
131
|
let bundleRootGraph: Graph<
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
// asset index
|
|
133
|
+
number,
|
|
134
|
+
(typeof bundleRootEdgeTypes)[keyof typeof bundleRootEdgeTypes]
|
|
115
135
|
> = new Graph();
|
|
116
136
|
let assetToBundleRootNodeId = new Map<BundleRoot, number>();
|
|
117
137
|
|
|
@@ -145,26 +165,47 @@ export function createIdealGraph(
|
|
|
145
165
|
bundleGroupBundleIds.add(nodeId);
|
|
146
166
|
}
|
|
147
167
|
|
|
148
|
-
let assets = [];
|
|
168
|
+
let assets: Array<Asset> = [];
|
|
149
169
|
let assetToIndex = new Map<Asset, number>();
|
|
150
170
|
|
|
151
|
-
function makeManualAssetToConfigLookup() {
|
|
152
|
-
|
|
153
|
-
|
|
171
|
+
function makeManualAssetToConfigLookup(): {
|
|
172
|
+
manualAssetToConfig: Map<
|
|
173
|
+
Asset,
|
|
174
|
+
ResolvedBundlerConfig['manualSharedBundles'][number]
|
|
175
|
+
>;
|
|
176
|
+
constantModuleToMSB: DefaultMap<
|
|
177
|
+
Asset,
|
|
178
|
+
Array<ResolvedBundlerConfig['manualSharedBundles'][number]>
|
|
179
|
+
>;
|
|
180
|
+
} {
|
|
181
|
+
let manualAssetToConfig = new Map<
|
|
182
|
+
Asset,
|
|
183
|
+
ResolvedBundlerConfig['manualSharedBundles'][number]
|
|
184
|
+
>();
|
|
185
|
+
let constantModuleToMSB = new DefaultMap<
|
|
186
|
+
Asset,
|
|
187
|
+
Array<ResolvedBundlerConfig['manualSharedBundles'][number]>
|
|
188
|
+
>(() => []);
|
|
154
189
|
|
|
155
190
|
if (config.manualSharedBundles.length === 0) {
|
|
156
191
|
return {manualAssetToConfig, constantModuleToMSB};
|
|
157
192
|
}
|
|
158
193
|
|
|
159
|
-
let parentsToConfig = new DefaultMap
|
|
194
|
+
let parentsToConfig = new DefaultMap<
|
|
195
|
+
string,
|
|
196
|
+
Array<ResolvedBundlerConfig['manualSharedBundles'][number]>
|
|
197
|
+
>(() => []);
|
|
160
198
|
|
|
161
199
|
for (let c of config.manualSharedBundles) {
|
|
162
200
|
if (c.root != null) {
|
|
163
201
|
parentsToConfig.get(path.join(config.projectRoot, c.root)).push(c);
|
|
164
202
|
}
|
|
165
203
|
}
|
|
166
|
-
let numParentsToFind = parentsToConfig.size;
|
|
167
|
-
let configToParentAsset = new Map
|
|
204
|
+
let numParentsToFind: number = parentsToConfig.size;
|
|
205
|
+
let configToParentAsset = new Map<
|
|
206
|
+
ResolvedBundlerConfig['manualSharedBundles'][number],
|
|
207
|
+
Asset
|
|
208
|
+
>();
|
|
168
209
|
|
|
169
210
|
assetGraph.traverse((node, _, actions) => {
|
|
170
211
|
if (node.type === 'asset' && parentsToConfig.has(node.value.filePath)) {
|
|
@@ -191,8 +232,10 @@ export function createIdealGraph(
|
|
|
191
232
|
continue;
|
|
192
233
|
}
|
|
193
234
|
|
|
194
|
-
let parentAsset = configToParentAsset.get(c);
|
|
195
|
-
let assetRegexes = c.assets.map((glob) =>
|
|
235
|
+
let parentAsset: Asset | undefined = configToParentAsset.get(c);
|
|
236
|
+
let assetRegexes: Array<RegExp> = c.assets.map((glob) =>
|
|
237
|
+
globToRegex(glob),
|
|
238
|
+
);
|
|
196
239
|
|
|
197
240
|
assetGraph.traverse((node, _, actions) => {
|
|
198
241
|
if (
|
|
@@ -203,17 +246,16 @@ export function createIdealGraph(
|
|
|
203
246
|
config.projectRoot,
|
|
204
247
|
node.value.filePath,
|
|
205
248
|
);
|
|
206
|
-
if (!assetRegexes.some((regex) => regex.test(projectRelativePath))) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
249
|
|
|
210
250
|
// We track all matching MSB's for constant modules as they are never duplicated
|
|
211
251
|
// and need to be assigned to all matching bundles
|
|
212
252
|
if (node.value.meta.isConstantModule === true) {
|
|
213
253
|
constantModuleToMSB.get(node.value).push(c);
|
|
214
254
|
}
|
|
215
|
-
|
|
216
|
-
|
|
255
|
+
|
|
256
|
+
if (assetRegexes.some((regex) => regex.test(projectRelativePath))) {
|
|
257
|
+
manualAssetToConfig.set(node.value, c);
|
|
258
|
+
}
|
|
217
259
|
}
|
|
218
260
|
|
|
219
261
|
if (
|
|
@@ -241,9 +283,19 @@ export function createIdealGraph(
|
|
|
241
283
|
makeManualAssetToConfigLookup();
|
|
242
284
|
let manualBundleToInternalizedAsset: DefaultMap<
|
|
243
285
|
NodeId,
|
|
244
|
-
Array<Asset
|
|
286
|
+
Array<Asset>
|
|
245
287
|
> = new DefaultMap(() => []);
|
|
246
288
|
|
|
289
|
+
let mergeSourceBundleLookup = new Map<string, NodeId>();
|
|
290
|
+
let mergeSourceBundleAssets: Set<string> = new Set(
|
|
291
|
+
config.sharedBundleMerge?.flatMap(
|
|
292
|
+
(c) =>
|
|
293
|
+
c.sourceBundles?.map((assetMatch: string) =>
|
|
294
|
+
path.join(config.projectRoot, assetMatch),
|
|
295
|
+
) ?? [],
|
|
296
|
+
),
|
|
297
|
+
);
|
|
298
|
+
|
|
247
299
|
/**
|
|
248
300
|
* Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
|
|
249
301
|
* for asset type changes, parallel, inline, and async or lazy dependencies,
|
|
@@ -251,7 +303,26 @@ export function createIdealGraph(
|
|
|
251
303
|
*/
|
|
252
304
|
assetGraph.traverse(
|
|
253
305
|
{
|
|
254
|
-
enter(
|
|
306
|
+
enter(
|
|
307
|
+
node:
|
|
308
|
+
| BundleGraphTraversable
|
|
309
|
+
| {
|
|
310
|
+
readonly type: 'dependency';
|
|
311
|
+
value: Dependency;
|
|
312
|
+
},
|
|
313
|
+
context:
|
|
314
|
+
| {
|
|
315
|
+
readonly type: 'asset';
|
|
316
|
+
value: Asset;
|
|
317
|
+
}
|
|
318
|
+
| null
|
|
319
|
+
| undefined
|
|
320
|
+
| {
|
|
321
|
+
readonly type: 'dependency';
|
|
322
|
+
value: Dependency;
|
|
323
|
+
},
|
|
324
|
+
actions: TraversalActions,
|
|
325
|
+
) {
|
|
255
326
|
if (node.type === 'asset') {
|
|
256
327
|
if (
|
|
257
328
|
context?.type === 'dependency' &&
|
|
@@ -287,12 +358,16 @@ export function createIdealGraph(
|
|
|
287
358
|
}
|
|
288
359
|
|
|
289
360
|
for (let childAsset of assets) {
|
|
290
|
-
let bundleId = bundles.get(
|
|
291
|
-
|
|
361
|
+
let bundleId: number | undefined | null = bundles.get(
|
|
362
|
+
childAsset.id,
|
|
363
|
+
);
|
|
364
|
+
let bundle: Bundle | 'root' | undefined;
|
|
292
365
|
|
|
293
366
|
// MSB Step 1: Match glob on filepath and type for any asset
|
|
294
|
-
let manualSharedBundleKey;
|
|
295
|
-
let manualSharedObject
|
|
367
|
+
let manualSharedBundleKey: string | undefined;
|
|
368
|
+
let manualSharedObject:
|
|
369
|
+
| ResolvedBundlerConfig['manualSharedBundles'][number]
|
|
370
|
+
| undefined = manualAssetToConfig.get(childAsset);
|
|
296
371
|
|
|
297
372
|
if (manualSharedObject) {
|
|
298
373
|
// MSB Step 2: Generate a key for which to look up this manual bundle with
|
|
@@ -311,7 +386,8 @@ export function createIdealGraph(
|
|
|
311
386
|
dependency.priority === 'lazy' ||
|
|
312
387
|
(getFeatureFlag('conditionalBundlingApi') &&
|
|
313
388
|
node.value.priority === 'conditional') ||
|
|
314
|
-
childAsset.bundleBehavior === 'isolated' // An isolated Dependency, or Bundle must contain all assets it needs to load.
|
|
389
|
+
childAsset.bundleBehavior === 'isolated' || // An isolated Dependency, or Bundle must contain all assets it needs to load.
|
|
390
|
+
childAsset.bundleBehavior === 'inlineIsolated'
|
|
315
391
|
) {
|
|
316
392
|
if (bundleId == null) {
|
|
317
393
|
let firstBundleGroup = nullthrows(
|
|
@@ -324,7 +400,9 @@ export function createIdealGraph(
|
|
|
324
400
|
dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
325
401
|
needsStableName:
|
|
326
402
|
dependency.bundleBehavior === 'inline' ||
|
|
327
|
-
childAsset.bundleBehavior === 'inline'
|
|
403
|
+
childAsset.bundleBehavior === 'inline' ||
|
|
404
|
+
dependency.bundleBehavior === 'inlineIsolated' ||
|
|
405
|
+
childAsset.bundleBehavior === 'inlineIsolated'
|
|
328
406
|
? false
|
|
329
407
|
: dependency.isEntry || dependency.needsStableName,
|
|
330
408
|
target: firstBundleGroup.target,
|
|
@@ -334,6 +412,14 @@ export function createIdealGraph(
|
|
|
334
412
|
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
335
413
|
bundleGroupBundleIds.add(bundleId);
|
|
336
414
|
bundleGraph.addEdge(bundleGraphRootNodeId, bundleId);
|
|
415
|
+
// If this asset is relevant for merging then track it's source
|
|
416
|
+
// bundle id for later
|
|
417
|
+
if (mergeSourceBundleAssets.has(childAsset.filePath)) {
|
|
418
|
+
mergeSourceBundleLookup.set(
|
|
419
|
+
path.relative(config.projectRoot, childAsset.filePath),
|
|
420
|
+
bundleId,
|
|
421
|
+
);
|
|
422
|
+
}
|
|
337
423
|
if (manualSharedObject) {
|
|
338
424
|
// MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
|
|
339
425
|
// since MSBs should not have main entry assets
|
|
@@ -348,7 +434,8 @@ export function createIdealGraph(
|
|
|
348
434
|
if (
|
|
349
435
|
// If this dependency requests isolated, but the bundle is not,
|
|
350
436
|
// make the bundle isolated for all uses.
|
|
351
|
-
dependency.bundleBehavior === 'isolated'
|
|
437
|
+
(dependency.bundleBehavior === 'isolated' ||
|
|
438
|
+
dependency.bundleBehavior === 'inlineIsolated') &&
|
|
352
439
|
bundle.bundleBehavior == null
|
|
353
440
|
) {
|
|
354
441
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
@@ -370,7 +457,9 @@ export function createIdealGraph(
|
|
|
370
457
|
type: 'bundle',
|
|
371
458
|
},
|
|
372
459
|
),
|
|
373
|
-
dependencyPriorityEdges[
|
|
460
|
+
dependencyPriorityEdges[
|
|
461
|
+
dependency.priority as keyof typeof dependencyPriorityEdges
|
|
462
|
+
],
|
|
374
463
|
);
|
|
375
464
|
|
|
376
465
|
if (
|
|
@@ -431,6 +520,7 @@ export function createIdealGraph(
|
|
|
431
520
|
needsStableName:
|
|
432
521
|
childAsset.bundleBehavior === 'inline' ||
|
|
433
522
|
dependency.bundleBehavior === 'inline' ||
|
|
523
|
+
dependency.bundleBehavior === 'inlineIsolated' ||
|
|
434
524
|
(dependency.priority === 'parallel' &&
|
|
435
525
|
!dependency.needsStableName)
|
|
436
526
|
? false
|
|
@@ -438,13 +528,15 @@ export function createIdealGraph(
|
|
|
438
528
|
});
|
|
439
529
|
bundleId = bundleGraph.addNode(bundle);
|
|
440
530
|
} else {
|
|
441
|
-
|
|
442
|
-
invariant(
|
|
531
|
+
let bundleNode = bundleGraph.getNode(bundleId);
|
|
532
|
+
invariant(bundleNode != null && bundleNode !== 'root');
|
|
443
533
|
|
|
534
|
+
bundle = bundleNode;
|
|
444
535
|
if (
|
|
445
536
|
// If this dependency requests isolated, but the bundle is not,
|
|
446
537
|
// make the bundle isolated for all uses.
|
|
447
|
-
dependency.bundleBehavior === 'isolated'
|
|
538
|
+
(dependency.bundleBehavior === 'isolated' ||
|
|
539
|
+
dependency.bundleBehavior === 'inlineIsolated') &&
|
|
448
540
|
bundle.bundleBehavior == null
|
|
449
541
|
) {
|
|
450
542
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
@@ -514,7 +606,7 @@ export function createIdealGraph(
|
|
|
514
606
|
}
|
|
515
607
|
return node;
|
|
516
608
|
},
|
|
517
|
-
exit(node) {
|
|
609
|
+
exit(node: BundleGraphTraversable): undefined {
|
|
518
610
|
if (stack[stack.length - 1]?.[0] === node.value) {
|
|
519
611
|
stack.pop();
|
|
520
612
|
}
|
|
@@ -563,22 +655,22 @@ export function createIdealGraph(
|
|
|
563
655
|
|
|
564
656
|
// reachableRoots is an array of bit sets for each asset. Each bit set
|
|
565
657
|
// indicates which bundle roots are reachable from that asset synchronously.
|
|
566
|
-
let reachableRoots = [];
|
|
658
|
+
let reachableRoots: Array<BitSet> = [];
|
|
567
659
|
for (let i = 0; i < assets.length; i++) {
|
|
568
660
|
reachableRoots.push(new BitSet(bundleRootGraph.nodes.length));
|
|
569
661
|
}
|
|
570
662
|
|
|
571
663
|
// reachableAssets is the inverse mapping of reachableRoots. For each bundle root,
|
|
572
664
|
// it contains a bit set that indicates which assets are reachable from it.
|
|
573
|
-
let reachableAssets = [];
|
|
665
|
+
let reachableAssets: Array<BitSet> = [];
|
|
574
666
|
|
|
575
667
|
// ancestorAssets maps bundle roots to the set of all assets available to it at runtime,
|
|
576
668
|
// including in earlier parallel bundles. These are intersected through all paths to
|
|
577
669
|
// the bundle to ensure that the available assets are always present no matter in which
|
|
578
670
|
// order the bundles are loaded.
|
|
579
|
-
let ancestorAssets = [];
|
|
671
|
+
let ancestorAssets: Array<null | BitSet> = [];
|
|
580
672
|
|
|
581
|
-
let inlineConstantDeps = new DefaultMap(() => new Set());
|
|
673
|
+
let inlineConstantDeps = new DefaultMap<Asset, Set<Asset>>(() => new Set());
|
|
582
674
|
|
|
583
675
|
for (let [bundleRootId, assetId] of bundleRootGraph.nodes.entries()) {
|
|
584
676
|
let reachable = new BitSet(assets.length);
|
|
@@ -687,7 +779,10 @@ export function createIdealGraph(
|
|
|
687
779
|
// not true that a bundle's available assets = all assets of all the bundleGroups
|
|
688
780
|
// it belongs to. It's the intersection of those sets.
|
|
689
781
|
let available;
|
|
690
|
-
if (
|
|
782
|
+
if (
|
|
783
|
+
bundleRoot.bundleBehavior === 'isolated' ||
|
|
784
|
+
bundleRoot.bundleBehavior === 'inlineIsolated'
|
|
785
|
+
) {
|
|
691
786
|
available = new BitSet(assets.length);
|
|
692
787
|
} else {
|
|
693
788
|
available = nullthrows(ancestorAssets[nodeId]).clone();
|
|
@@ -771,7 +866,8 @@ export function createIdealGraph(
|
|
|
771
866
|
|
|
772
867
|
let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, ALL_EDGE_TYPES);
|
|
773
868
|
let canDelete =
|
|
774
|
-
getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated'
|
|
869
|
+
getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated' &&
|
|
870
|
+
getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'inlineIsolated';
|
|
775
871
|
if (parentRoots.length === 0) continue;
|
|
776
872
|
for (let parentId of parentRoots) {
|
|
777
873
|
if (parentId === rootNodeId) {
|
|
@@ -819,8 +915,10 @@ export function createIdealGraph(
|
|
|
819
915
|
let reachableNonEntries = new BitSet(assets.length);
|
|
820
916
|
let reachableIntersection = new BitSet(assets.length);
|
|
821
917
|
for (let i = 0; i < assets.length; i++) {
|
|
822
|
-
let asset = assets[i];
|
|
823
|
-
let manualSharedObject
|
|
918
|
+
let asset: Asset = assets[i];
|
|
919
|
+
let manualSharedObject:
|
|
920
|
+
| ResolvedBundlerConfig['manualSharedBundles'][number]
|
|
921
|
+
| undefined = manualAssetToConfig.get(asset);
|
|
824
922
|
|
|
825
923
|
if (bundleRoots.has(asset) && inlineConstantDeps.get(asset).size > 0) {
|
|
826
924
|
let entryBundleId = nullthrows(bundleRoots.get(asset))[0];
|
|
@@ -848,7 +946,8 @@ export function createIdealGraph(
|
|
|
848
946
|
!a.isBundleSplittable ||
|
|
849
947
|
(bundleRoots.get(a) &&
|
|
850
948
|
(getBundleFromBundleRoot(a).needsStableName ||
|
|
851
|
-
getBundleFromBundleRoot(a).bundleBehavior === 'isolated'
|
|
949
|
+
getBundleFromBundleRoot(a).bundleBehavior === 'isolated' ||
|
|
950
|
+
getBundleFromBundleRoot(a).bundleBehavior === 'inlineIsolated'))
|
|
852
951
|
) {
|
|
853
952
|
// Add asset to non-splittable bundles.
|
|
854
953
|
addAssetToBundleRoot(asset, a);
|
|
@@ -863,10 +962,11 @@ export function createIdealGraph(
|
|
|
863
962
|
|
|
864
963
|
// If we encounter a "manual" asset, draw an edge from reachable to its MSB
|
|
865
964
|
if (manualSharedObject && !reachable.empty()) {
|
|
866
|
-
let bundle;
|
|
867
|
-
let bundleId;
|
|
868
|
-
let manualSharedBundleKey =
|
|
869
|
-
|
|
965
|
+
let bundle: Bundle | undefined;
|
|
966
|
+
let bundleId: NodeId | undefined;
|
|
967
|
+
let manualSharedBundleKey: string =
|
|
968
|
+
manualSharedObject.name + ',' + asset.type;
|
|
969
|
+
let sourceBundles: Array<NodeId> = [];
|
|
870
970
|
reachable.forEach((id) => {
|
|
871
971
|
sourceBundles.push(nullthrows(bundleRoots.get(assets[id]))[0]);
|
|
872
972
|
});
|
|
@@ -890,12 +990,14 @@ export function createIdealGraph(
|
|
|
890
990
|
manualSharedMap.set(manualSharedBundleKey, bundleId);
|
|
891
991
|
} else {
|
|
892
992
|
bundleId = nullthrows(manualSharedMap.get(manualSharedBundleKey));
|
|
893
|
-
|
|
993
|
+
let bundleNode = nullthrows(bundleGraph.getNode(bundleId));
|
|
894
994
|
invariant(
|
|
895
|
-
|
|
995
|
+
bundleNode != null && bundleNode !== 'root',
|
|
896
996
|
'We tried to use the root incorrectly',
|
|
897
997
|
);
|
|
898
998
|
|
|
999
|
+
bundle = bundleNode;
|
|
1000
|
+
|
|
899
1001
|
if (!bundle.assets.has(asset)) {
|
|
900
1002
|
bundle.assets.add(asset);
|
|
901
1003
|
bundle.size += asset.stats.size;
|
|
@@ -981,7 +1083,7 @@ export function createIdealGraph(
|
|
|
981
1083
|
});
|
|
982
1084
|
}
|
|
983
1085
|
|
|
984
|
-
let reachableArray = [];
|
|
1086
|
+
let reachableArray: Array<Asset> = [];
|
|
985
1087
|
reachable.forEach((id) => {
|
|
986
1088
|
reachableArray.push(assets[id]);
|
|
987
1089
|
});
|
|
@@ -991,12 +1093,13 @@ export function createIdealGraph(
|
|
|
991
1093
|
config.disableSharedBundles === false &&
|
|
992
1094
|
reachableArray.length > config.minBundles
|
|
993
1095
|
) {
|
|
994
|
-
let sourceBundles = reachableArray.map(
|
|
1096
|
+
let sourceBundles: Array<NodeId> = reachableArray.map(
|
|
995
1097
|
(a) => nullthrows(bundleRoots.get(a))[0],
|
|
996
1098
|
);
|
|
997
|
-
let key =
|
|
998
|
-
|
|
999
|
-
let
|
|
1099
|
+
let key: string =
|
|
1100
|
+
reachableArray.map((a) => a.id).join(',') + '.' + asset.type;
|
|
1101
|
+
let bundleId: NodeId | undefined = bundles.get(key);
|
|
1102
|
+
let bundle: Bundle | undefined;
|
|
1000
1103
|
if (bundleId == null) {
|
|
1001
1104
|
let firstSourceBundle = nullthrows(
|
|
1002
1105
|
bundleGraph.getNode(sourceBundles[0]),
|
|
@@ -1027,8 +1130,9 @@ export function createIdealGraph(
|
|
|
1027
1130
|
bundleId = bundleGraph.addNode(bundle);
|
|
1028
1131
|
bundles.set(key, bundleId);
|
|
1029
1132
|
} else {
|
|
1030
|
-
|
|
1031
|
-
invariant(
|
|
1133
|
+
let bundleNode = nullthrows(bundleGraph.getNode(bundleId));
|
|
1134
|
+
invariant(bundleNode !== 'root');
|
|
1135
|
+
bundle = bundleNode;
|
|
1032
1136
|
}
|
|
1033
1137
|
bundle.assets.add(asset);
|
|
1034
1138
|
bundle.size += asset.stats.size;
|
|
@@ -1056,25 +1160,31 @@ export function createIdealGraph(
|
|
|
1056
1160
|
}
|
|
1057
1161
|
|
|
1058
1162
|
let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
|
|
1163
|
+
let modifiedSourceBundles = new Set<Bundle>();
|
|
1164
|
+
|
|
1059
1165
|
// Step split manual shared bundles for those that have the "split" property set
|
|
1060
|
-
let remainderMap = new DefaultMap(() => []);
|
|
1166
|
+
let remainderMap = new DefaultMap<number, Array<Asset>>(() => []);
|
|
1061
1167
|
for (let id of manualSharedMap.values()) {
|
|
1062
1168
|
let manualBundle = bundleGraph.getNode(id);
|
|
1063
1169
|
invariant(manualBundle !== 'root' && manualBundle != null);
|
|
1064
1170
|
|
|
1065
1171
|
if (manualBundle.sourceBundles.size > 0) {
|
|
1066
|
-
let
|
|
1172
|
+
let firstSourceBundleNode: Bundle | 'root' = nullthrows(
|
|
1067
1173
|
bundleGraph.getNode([...manualBundle.sourceBundles][0]),
|
|
1068
1174
|
);
|
|
1069
|
-
invariant(
|
|
1070
|
-
let
|
|
1071
|
-
|
|
1175
|
+
invariant(firstSourceBundleNode !== 'root');
|
|
1176
|
+
let firstSourceBundle = firstSourceBundleNode;
|
|
1177
|
+
|
|
1178
|
+
let firstAsset: Asset = [...manualBundle.assets][0];
|
|
1179
|
+
let manualSharedObject:
|
|
1180
|
+
| ResolvedBundlerConfig['manualSharedBundles'][number]
|
|
1181
|
+
| undefined = manualAssetToConfig.get(firstAsset);
|
|
1072
1182
|
invariant(manualSharedObject != null);
|
|
1073
|
-
let modNum =
|
|
1183
|
+
let modNum: number | undefined =
|
|
1184
|
+
manualAssetToConfig.get(firstAsset)?.split;
|
|
1074
1185
|
if (modNum != null) {
|
|
1075
1186
|
for (let a of [...manualBundle.assets]) {
|
|
1076
1187
|
let numRep = getBigIntFromContentKey(a.id);
|
|
1077
|
-
// $FlowFixMe Flow doesn't know about BigInt
|
|
1078
1188
|
let r = Number(numRep % BigInt(modNum));
|
|
1079
1189
|
|
|
1080
1190
|
remainderMap.get(r).push(a);
|
|
@@ -1128,6 +1238,89 @@ export function createIdealGraph(
|
|
|
1128
1238
|
}
|
|
1129
1239
|
}
|
|
1130
1240
|
|
|
1241
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1242
|
+
// Merge webpack chunk name bundles
|
|
1243
|
+
let chunkNameBundles = new DefaultMap<string, Set<NodeId>>(() => new Set());
|
|
1244
|
+
for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
|
|
1245
|
+
// meta.chunkName is set by the Rust transformer, so we just need to find
|
|
1246
|
+
// bundles that have a chunkName set.
|
|
1247
|
+
if (
|
|
1248
|
+
!node ||
|
|
1249
|
+
node.type !== 'dependency' ||
|
|
1250
|
+
typeof node.value.meta.chunkName !== 'string'
|
|
1251
|
+
) {
|
|
1252
|
+
continue;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(
|
|
1256
|
+
nodeId,
|
|
1257
|
+
dependencyPriorityEdges[node.value.priority],
|
|
1258
|
+
);
|
|
1259
|
+
|
|
1260
|
+
if (connectedBundles.length === 0) {
|
|
1261
|
+
continue;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
invariant(
|
|
1265
|
+
connectedBundles.length === 1,
|
|
1266
|
+
'Expected webpackChunkName dependency to be connected to no more than one bundle',
|
|
1267
|
+
);
|
|
1268
|
+
|
|
1269
|
+
let bundleId = connectedBundles[0];
|
|
1270
|
+
let bundleNode = dependencyBundleGraph.getNode(bundleId);
|
|
1271
|
+
invariant(bundleNode != null && bundleNode.type === 'bundle');
|
|
1272
|
+
|
|
1273
|
+
// If a bundle does not have a main entry asset, it's somehow just a
|
|
1274
|
+
// shared bundle, and will be merged/deleted by other means.
|
|
1275
|
+
if (bundleNode.value.mainEntryAsset == null) {
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
let bundleNodeId = null;
|
|
1280
|
+
let mainEntryAssetId = bundleNode.value.mainEntryAsset?.id;
|
|
1281
|
+
|
|
1282
|
+
if (mainEntryAssetId != null) {
|
|
1283
|
+
bundleNodeId = bundles.get(mainEntryAssetId);
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
if (bundleNodeId == null) {
|
|
1287
|
+
continue;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
chunkNameBundles
|
|
1291
|
+
.get(node.value.meta.chunkName)
|
|
1292
|
+
// DependencyBundleGraph uses content keys as node ids, so we can use that
|
|
1293
|
+
// to get the bundle id.
|
|
1294
|
+
.add(bundleNodeId);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
|
|
1298
|
+
// The `[request]` placeholder is not yet supported
|
|
1299
|
+
if (
|
|
1300
|
+
bundleIds.size <= 1 ||
|
|
1301
|
+
(typeof chunkName === 'string' && chunkName.includes('[request]'))
|
|
1302
|
+
) {
|
|
1303
|
+
continue; // Nothing to merge
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// Merge all bundles with the same chunk name into the first one.
|
|
1307
|
+
let [firstBundleId, ...rest] = Array.from(bundleIds);
|
|
1308
|
+
for (let bundleId of rest) {
|
|
1309
|
+
mergeBundles(firstBundleId, bundleId, 'webpack-chunk-name');
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// Step merge async bundles that meet the configured params
|
|
1315
|
+
if (config.asyncBundleMerge) {
|
|
1316
|
+
mergeAsyncBundles(config.asyncBundleMerge);
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// Step merge shared bundles that meet the configured params
|
|
1320
|
+
if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
|
|
1321
|
+
mergeSharedBundles(config.sharedBundleMerge);
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1131
1324
|
// Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
|
|
1132
1325
|
// their source bundles, and remove the bundle.
|
|
1133
1326
|
// We should include "bundle reuse" as shared bundles that may be removed but the bundle itself would have to be retained
|
|
@@ -1143,9 +1336,8 @@ export function createIdealGraph(
|
|
|
1143
1336
|
}
|
|
1144
1337
|
}
|
|
1145
1338
|
|
|
1146
|
-
let modifiedSourceBundles = new Set();
|
|
1147
|
-
|
|
1148
1339
|
// Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
|
|
1340
|
+
|
|
1149
1341
|
if (config.disableSharedBundles === false) {
|
|
1150
1342
|
for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
|
|
1151
1343
|
// Find shared bundles in this bundle group.
|
|
@@ -1158,7 +1350,13 @@ export function createIdealGraph(
|
|
|
1158
1350
|
let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
|
|
1159
1351
|
let bundle = nullthrows(bundleGraph.getNode(b));
|
|
1160
1352
|
invariant(bundle !== 'root');
|
|
1161
|
-
return
|
|
1353
|
+
return (
|
|
1354
|
+
count +
|
|
1355
|
+
Number(
|
|
1356
|
+
bundle.bundleBehavior !== 'inline' &&
|
|
1357
|
+
bundle.bundleBehavior !== 'inlineIsolated',
|
|
1358
|
+
)
|
|
1359
|
+
);
|
|
1162
1360
|
}, 0);
|
|
1163
1361
|
|
|
1164
1362
|
if (numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
@@ -1193,6 +1391,7 @@ export function createIdealGraph(
|
|
|
1193
1391
|
numBundlesContributingToPRL > config.maxParallelRequests
|
|
1194
1392
|
) {
|
|
1195
1393
|
let bundleTuple = sharedBundlesInGroup.pop();
|
|
1394
|
+
if (!bundleTuple) break;
|
|
1196
1395
|
let bundleToRemove = bundleTuple.bundle;
|
|
1197
1396
|
let bundleIdToRemove = bundleTuple.id;
|
|
1198
1397
|
//TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
|
|
@@ -1249,10 +1448,417 @@ export function createIdealGraph(
|
|
|
1249
1448
|
}
|
|
1250
1449
|
}
|
|
1251
1450
|
|
|
1252
|
-
function
|
|
1451
|
+
function mergeBundles(
|
|
1452
|
+
bundleToKeepId: NodeId,
|
|
1453
|
+
bundleToRemoveId: NodeId,
|
|
1454
|
+
reason: string,
|
|
1455
|
+
) {
|
|
1456
|
+
stats.trackMerge(bundleToKeepId, bundleToRemoveId, reason);
|
|
1457
|
+
let bundleToKeep = isNonRootBundle(
|
|
1458
|
+
bundleGraph.getNode(bundleToKeepId),
|
|
1459
|
+
`Bundle ${bundleToKeepId} not found`,
|
|
1460
|
+
);
|
|
1461
|
+
let bundleToRemove = isNonRootBundle(
|
|
1462
|
+
bundleGraph.getNode(bundleToRemoveId),
|
|
1463
|
+
`Bundle ${bundleToRemoveId} not found`,
|
|
1464
|
+
);
|
|
1465
|
+
modifiedSourceBundles.add(bundleToKeep);
|
|
1466
|
+
for (let asset of bundleToRemove.assets) {
|
|
1467
|
+
bundleToKeep.assets.add(asset);
|
|
1468
|
+
bundleToKeep.size += asset.stats.size;
|
|
1469
|
+
|
|
1470
|
+
let newAssetReference = assetReference
|
|
1471
|
+
.get(asset)
|
|
1472
|
+
.map(([dep, bundle]): [Dependency, Bundle] =>
|
|
1473
|
+
bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle],
|
|
1474
|
+
);
|
|
1475
|
+
|
|
1476
|
+
assetReference.set(asset, newAssetReference);
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// Merge any internalized assets
|
|
1480
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1481
|
+
if (bundleToKeep.internalizedAssets != null) {
|
|
1482
|
+
if (bundleToRemove.internalizedAssets != null) {
|
|
1483
|
+
bundleToKeep.internalizedAssets.intersect(
|
|
1484
|
+
bundleToRemove.internalizedAssets,
|
|
1485
|
+
);
|
|
1486
|
+
} else {
|
|
1487
|
+
bundleToKeep.internalizedAssets.clear();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1490
|
+
} else {
|
|
1491
|
+
invariant(
|
|
1492
|
+
bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets,
|
|
1493
|
+
'All shared bundles should have internalized assets',
|
|
1494
|
+
);
|
|
1495
|
+
bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// Merge and clean up source bundles
|
|
1499
|
+
for (let sourceBundleId of bundleToRemove.sourceBundles) {
|
|
1500
|
+
if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
if (sourceBundleId !== bundleToKeepId) {
|
|
1505
|
+
bundleToKeep.sourceBundles.add(sourceBundleId);
|
|
1506
|
+
bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1511
|
+
bundleToKeep.sourceBundles.delete(bundleToRemoveId);
|
|
1512
|
+
|
|
1513
|
+
for (let bundle of bundleGraph.getNodeIdsConnectedFrom(
|
|
1514
|
+
bundleToRemoveId,
|
|
1515
|
+
)) {
|
|
1516
|
+
let bundleNode = nullthrows(bundleGraph.getNode(bundle));
|
|
1517
|
+
if (bundleNode === 'root') {
|
|
1518
|
+
continue;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// If the bundle is a source bundle, add it to the bundle to keep
|
|
1522
|
+
if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
|
|
1523
|
+
bundleNode.sourceBundles.delete(bundleToRemoveId);
|
|
1524
|
+
if (bundle !== bundleToKeepId) {
|
|
1525
|
+
bundleNode.sourceBundles.add(bundleToKeepId);
|
|
1526
|
+
bundleGraph.addEdge(bundleToKeepId, bundle);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// Merge bundle roots
|
|
1532
|
+
for (let bundleRoot of bundleToRemove.bundleRoots) {
|
|
1533
|
+
bundleToKeep.bundleRoots.add(bundleRoot);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
if (bundleToRemove.mainEntryAsset != null) {
|
|
1537
|
+
invariant(bundleToKeep.mainEntryAsset != null);
|
|
1538
|
+
|
|
1539
|
+
// Merge the bundles in bundle group
|
|
1540
|
+
let bundlesInRemoveBundleGroup =
|
|
1541
|
+
getBundlesForBundleGroup(bundleToRemoveId);
|
|
1542
|
+
|
|
1543
|
+
let removedBundleSharedBundles = new Set<NodeId>();
|
|
1544
|
+
for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
|
|
1545
|
+
if (bundleIdInGroup === bundleToRemoveId) {
|
|
1546
|
+
continue;
|
|
1547
|
+
}
|
|
1548
|
+
bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
|
|
1549
|
+
removedBundleSharedBundles.add(bundleIdInGroup);
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (getFeatureFlag('removeRedundantSharedBundles')) {
|
|
1553
|
+
// Merge any shared bundles that now have the same source bundles due to
|
|
1554
|
+
// the current bundle merge
|
|
1555
|
+
let sharedBundles = new DefaultMap<string, Array<NodeId>>(() => []);
|
|
1556
|
+
for (let bundleId of removedBundleSharedBundles) {
|
|
1557
|
+
let bundleNode = nullthrows(bundleGraph.getNode(bundleId));
|
|
1558
|
+
invariant(bundleNode !== 'root');
|
|
1559
|
+
if (
|
|
1560
|
+
bundleNode.mainEntryAsset != null ||
|
|
1561
|
+
bundleNode.manualSharedBundle != null
|
|
1562
|
+
) {
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
let key =
|
|
1567
|
+
Array.from(bundleNode.sourceBundles)
|
|
1568
|
+
.filter((sourceBundle) => sourceBundle !== bundleToRemoveId)
|
|
1569
|
+
.sort()
|
|
1570
|
+
.join(',') +
|
|
1571
|
+
'.' +
|
|
1572
|
+
bundleNode.type;
|
|
1573
|
+
|
|
1574
|
+
sharedBundles.get(key).push(bundleId);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
for (let sharedBundlesToMerge of sharedBundles.values()) {
|
|
1578
|
+
if (sharedBundlesToMerge.length > 1) {
|
|
1579
|
+
let [firstBundleId, ...rest] = sharedBundlesToMerge;
|
|
1580
|
+
for (let bundleId of rest) {
|
|
1581
|
+
mergeBundles(firstBundleId, bundleId, 'redundant-shared');
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
// Remove old bundle group
|
|
1588
|
+
bundleGroupBundleIds.delete(bundleToRemoveId);
|
|
1589
|
+
|
|
1590
|
+
// Clean up bundle roots
|
|
1591
|
+
let bundleRootToRemoveNodeId = nullthrows(
|
|
1592
|
+
assetToBundleRootNodeId.get(
|
|
1593
|
+
nullthrows(bundleToRemove.mainEntryAsset),
|
|
1594
|
+
),
|
|
1595
|
+
);
|
|
1596
|
+
let bundleRootToKeepNodeId = nullthrows(
|
|
1597
|
+
assetToBundleRootNodeId.get(nullthrows(bundleToKeep.mainEntryAsset)),
|
|
1598
|
+
);
|
|
1599
|
+
|
|
1600
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(
|
|
1601
|
+
bundleRootToRemoveNodeId,
|
|
1602
|
+
)) {
|
|
1603
|
+
bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
|
|
1604
|
+
bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(
|
|
1608
|
+
bundleRootToRemoveNodeId,
|
|
1609
|
+
)) {
|
|
1610
|
+
bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
|
|
1611
|
+
bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
bundleRoots.set(nullthrows(bundleToRemove.mainEntryAsset), [
|
|
1615
|
+
bundleToKeepId,
|
|
1616
|
+
bundleToKeepId,
|
|
1617
|
+
]);
|
|
1618
|
+
|
|
1619
|
+
// Merge dependency bundle graph
|
|
1620
|
+
for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(
|
|
1621
|
+
dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)),
|
|
1622
|
+
ALL_EDGE_TYPES,
|
|
1623
|
+
)) {
|
|
1624
|
+
let dependencyNode = nullthrows(
|
|
1625
|
+
dependencyBundleGraph.getNode(dependencyNodeId),
|
|
1626
|
+
);
|
|
1627
|
+
invariant(dependencyNode.type === 'dependency');
|
|
1628
|
+
|
|
1629
|
+
// Add dependency to the bundle to keep
|
|
1630
|
+
dependencyBundleGraph.addEdge(
|
|
1631
|
+
dependencyNodeId,
|
|
1632
|
+
dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)),
|
|
1633
|
+
dependencyPriorityEdges[dependencyNode.value.priority],
|
|
1634
|
+
);
|
|
1635
|
+
// Remove dependency from the bundle to remove
|
|
1636
|
+
dependencyBundleGraph.removeEdge(
|
|
1637
|
+
dependencyNodeId,
|
|
1638
|
+
dependencyBundleGraph.getNodeIdByContentKey(
|
|
1639
|
+
String(bundleToRemoveId),
|
|
1640
|
+
),
|
|
1641
|
+
dependencyPriorityEdges[dependencyNode.value.priority],
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
bundleGraph.removeNode(bundleToRemoveId);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
function mergeSharedBundles(mergeConfig: SharedBundleMergeCandidates) {
|
|
1651
|
+
// Find all shared bundles
|
|
1652
|
+
let sharedBundles = new Set<NodeId>();
|
|
1653
|
+
bundleGraph.traverse((nodeId) => {
|
|
1654
|
+
let bundle = bundleGraph.getNode(nodeId);
|
|
1655
|
+
|
|
1656
|
+
if (!bundle) {
|
|
1657
|
+
throw new Error(`Unable to find bundle ${nodeId} in bundle graph`);
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
if (bundle === 'root') {
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// Only consider JS shared bundles and non-reused bundles.
|
|
1665
|
+
// These count potentially be considered for merging in future but they're
|
|
1666
|
+
// more complicated to merge
|
|
1667
|
+
if (
|
|
1668
|
+
bundle.sourceBundles.size > 0 &&
|
|
1669
|
+
bundle.manualSharedBundle == null &&
|
|
1670
|
+
!bundle.mainEntryAsset &&
|
|
1671
|
+
bundle.type === 'js'
|
|
1672
|
+
) {
|
|
1673
|
+
sharedBundles.add(nodeId);
|
|
1674
|
+
}
|
|
1675
|
+
});
|
|
1676
|
+
|
|
1677
|
+
let clusters = findMergeCandidates(
|
|
1678
|
+
bundleGraph,
|
|
1679
|
+
Array.from(sharedBundles),
|
|
1680
|
+
mergeConfig.map(
|
|
1681
|
+
(config): MergeGroup => ({
|
|
1682
|
+
...config,
|
|
1683
|
+
sourceBundles: config.sourceBundles?.map((assetMatch: string) => {
|
|
1684
|
+
let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
|
|
1685
|
+
|
|
1686
|
+
if (sourceBundleNodeId == null) {
|
|
1687
|
+
throw new Error(
|
|
1688
|
+
`Source bundle ${assetMatch} not found in merge source bundle lookup`,
|
|
1689
|
+
);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
return sourceBundleNodeId;
|
|
1693
|
+
}),
|
|
1694
|
+
}),
|
|
1695
|
+
),
|
|
1696
|
+
);
|
|
1697
|
+
|
|
1698
|
+
let mergedBundles = new Set();
|
|
1699
|
+
|
|
1700
|
+
for (let cluster of clusters) {
|
|
1701
|
+
let [mergeTarget, ...rest] = cluster;
|
|
1702
|
+
|
|
1703
|
+
for (let bundleIdToMerge of rest) {
|
|
1704
|
+
mergeBundles(mergeTarget, bundleIdToMerge, 'shared-merge');
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
mergedBundles.add(mergeTarget);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
if (getFeatureFlag('supportWebpackChunkName')) {
|
|
1711
|
+
return mergedBundles;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
function mergeAsyncBundles({
|
|
1716
|
+
bundleSize,
|
|
1717
|
+
maxOverfetchSize,
|
|
1718
|
+
ignore,
|
|
1719
|
+
}: AsyncBundleMerge) {
|
|
1720
|
+
let mergeCandidates = [];
|
|
1721
|
+
let ignoreRegexes = ignore?.map((glob) => globToRegex(glob)) ?? [];
|
|
1722
|
+
|
|
1723
|
+
let isIgnored = (bundle: Bundle) => {
|
|
1724
|
+
if (!bundle.mainEntryAsset) {
|
|
1725
|
+
return false;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
let mainEntryFilePath = path.relative(
|
|
1729
|
+
config.projectRoot,
|
|
1730
|
+
nullthrows(bundle.mainEntryAsset).filePath,
|
|
1731
|
+
);
|
|
1732
|
+
|
|
1733
|
+
return ignoreRegexes.some((regex) => regex.test(mainEntryFilePath));
|
|
1734
|
+
};
|
|
1735
|
+
|
|
1736
|
+
for (let [_bundleRootAsset, [bundleRootBundleId]] of bundleRoots) {
|
|
1737
|
+
let bundleRootBundle = nullthrows(
|
|
1738
|
+
bundleGraph.getNode(bundleRootBundleId),
|
|
1739
|
+
);
|
|
1740
|
+
invariant(bundleRootBundle !== 'root');
|
|
1741
|
+
|
|
1742
|
+
if (
|
|
1743
|
+
bundleRootBundle.type === 'js' &&
|
|
1744
|
+
bundleRootBundle.bundleBehavior !== 'inline' &&
|
|
1745
|
+
bundleRootBundle.bundleBehavior !== 'inlineIsolated' &&
|
|
1746
|
+
bundleRootBundle.size <= bundleSize &&
|
|
1747
|
+
!isIgnored(bundleRootBundle)
|
|
1748
|
+
) {
|
|
1749
|
+
mergeCandidates.push(bundleRootBundleId);
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
let candidates = [];
|
|
1754
|
+
for (let i = 0; i < mergeCandidates.length; i++) {
|
|
1755
|
+
for (let j = i + 1; j < mergeCandidates.length; j++) {
|
|
1756
|
+
let a = mergeCandidates[i];
|
|
1757
|
+
let b = mergeCandidates[j];
|
|
1758
|
+
if (a === b) continue; // Skip self-comparison
|
|
1759
|
+
|
|
1760
|
+
candidates.push(scoreAsyncMerge(a, b, maxOverfetchSize));
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
let sortByScore = (
|
|
1765
|
+
a: AsyncBundleMergeCandidate,
|
|
1766
|
+
b: AsyncBundleMergeCandidate,
|
|
1767
|
+
) => {
|
|
1768
|
+
let diff = a.score - b.score;
|
|
1769
|
+
if (diff > 0) {
|
|
1770
|
+
return 1;
|
|
1771
|
+
} else if (diff < 0) {
|
|
1772
|
+
return -1;
|
|
1773
|
+
}
|
|
1774
|
+
return 0;
|
|
1775
|
+
};
|
|
1776
|
+
|
|
1777
|
+
candidates = candidates
|
|
1778
|
+
.filter(
|
|
1779
|
+
({overfetchSize, score}) =>
|
|
1780
|
+
overfetchSize <= maxOverfetchSize && score > 0,
|
|
1781
|
+
)
|
|
1782
|
+
.sort(sortByScore);
|
|
1783
|
+
|
|
1784
|
+
// Tracks the bundles that have been merged
|
|
1785
|
+
let merged = new Set<NodeId>();
|
|
1786
|
+
// Tracks the deleted bundles to the bundle they were merged into.
|
|
1787
|
+
let mergeRemap = new Map<NodeId, NodeId>();
|
|
1788
|
+
// Tracks the bundles that have been rescored and added back into the
|
|
1789
|
+
// candidates.
|
|
1790
|
+
let rescored = new DefaultMap<NodeId, Set<NodeId>>(() => new Set());
|
|
1791
|
+
|
|
1792
|
+
do {
|
|
1793
|
+
let [a, b] = nullthrows(candidates.pop()).bundleIds;
|
|
1794
|
+
|
|
1795
|
+
if (
|
|
1796
|
+
bundleGraph.hasNode(a) &&
|
|
1797
|
+
bundleGraph.hasNode(b) &&
|
|
1798
|
+
((!merged.has(a) && !merged.has(b)) || rescored.get(a).has(b))
|
|
1799
|
+
) {
|
|
1800
|
+
mergeRemap.set(b, a);
|
|
1801
|
+
merged.add(a);
|
|
1802
|
+
rescored.get(a).clear();
|
|
1803
|
+
|
|
1804
|
+
mergeBundles(a, b, 'async-merge');
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// One or both of the bundles have been previously merged, so we'll
|
|
1809
|
+
// rescore and add the result back into the list of candidates.
|
|
1810
|
+
let getMergedBundleId = (bundleId: NodeId): NodeId | undefined => {
|
|
1811
|
+
let seen = new Set<NodeId>();
|
|
1812
|
+
while (!bundleGraph.hasNode(bundleId) && !seen.has(bundleId)) {
|
|
1813
|
+
seen.add(bundleId);
|
|
1814
|
+
bundleId = nullthrows(mergeRemap.get(bundleId));
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
if (!bundleGraph.hasNode(bundleId)) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
return bundleId;
|
|
1822
|
+
};
|
|
1823
|
+
|
|
1824
|
+
// Map a and b to their merged bundle ids if they've already been merged
|
|
1825
|
+
let currentA = getMergedBundleId(a);
|
|
1826
|
+
let currentB = getMergedBundleId(b);
|
|
1827
|
+
|
|
1828
|
+
if (
|
|
1829
|
+
!currentA ||
|
|
1830
|
+
!currentB ||
|
|
1831
|
+
// Bundles are already merged
|
|
1832
|
+
currentA === currentB
|
|
1833
|
+
) {
|
|
1834
|
+
// This combiniation is not valid, so we skip it.
|
|
1835
|
+
continue;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
let candidate = scoreAsyncMerge(currentA, currentB, maxOverfetchSize);
|
|
1839
|
+
|
|
1840
|
+
if (candidate.overfetchSize <= maxOverfetchSize && candidate.score > 0) {
|
|
1841
|
+
sortedArray.add(candidates, candidate, sortByScore);
|
|
1842
|
+
rescored.get(currentA).add(currentB);
|
|
1843
|
+
}
|
|
1844
|
+
} while (candidates.length > 0);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
function getBundle(bundleId: NodeId): Bundle {
|
|
1848
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1849
|
+
if (bundle === 'root') {
|
|
1850
|
+
throw new Error(`Cannot access root bundle`);
|
|
1851
|
+
}
|
|
1852
|
+
if (bundle == null) {
|
|
1853
|
+
throw new Error(`Bundle ${bundleId} not found in bundle graph`);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
return bundle;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
function getBigIntFromContentKey(contentKey: string) {
|
|
1253
1860
|
let b = Buffer.alloc(64);
|
|
1254
1861
|
b.write(contentKey);
|
|
1255
|
-
// $FlowFixMe Flow doesn't have BigInt types in this version
|
|
1256
1862
|
return b.readBigInt64BE();
|
|
1257
1863
|
}
|
|
1258
1864
|
// Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
|
|
@@ -1279,14 +1885,86 @@ export function createIdealGraph(
|
|
|
1279
1885
|
bundleRootGraph.removeNode(bundleRootId);
|
|
1280
1886
|
}
|
|
1281
1887
|
}
|
|
1282
|
-
function getBundlesForBundleGroup(bundleGroupId) {
|
|
1283
|
-
let bundlesInABundleGroup = [];
|
|
1888
|
+
function getBundlesForBundleGroup(bundleGroupId: NodeId) {
|
|
1889
|
+
let bundlesInABundleGroup: Array<NodeId> = [];
|
|
1284
1890
|
bundleGraph.traverse((nodeId) => {
|
|
1285
1891
|
bundlesInABundleGroup.push(nodeId);
|
|
1286
1892
|
}, bundleGroupId);
|
|
1287
1893
|
return bundlesInABundleGroup;
|
|
1288
1894
|
}
|
|
1289
1895
|
|
|
1896
|
+
interface AsyncBundleMergeCandidate {
|
|
1897
|
+
overfetchSize: number;
|
|
1898
|
+
score: number;
|
|
1899
|
+
bundleIds: number[];
|
|
1900
|
+
}
|
|
1901
|
+
function scoreAsyncMerge(
|
|
1902
|
+
bundleAId: NodeId,
|
|
1903
|
+
bundleBId: NodeId,
|
|
1904
|
+
maxOverfetchSize: number,
|
|
1905
|
+
): AsyncBundleMergeCandidate {
|
|
1906
|
+
let bundleGroupA = new Set(getBundlesForBundleGroup(bundleAId));
|
|
1907
|
+
let bundleGroupB = new Set(getBundlesForBundleGroup(bundleBId));
|
|
1908
|
+
|
|
1909
|
+
let overlapSize = 0;
|
|
1910
|
+
let overfetchSize = 0;
|
|
1911
|
+
|
|
1912
|
+
for (let bundleId of new Set([...bundleGroupA, ...bundleGroupB])) {
|
|
1913
|
+
let bundle = getBundle(bundleId);
|
|
1914
|
+
|
|
1915
|
+
if (bundleGroupA.has(bundleId) && bundleGroupB.has(bundleId)) {
|
|
1916
|
+
overlapSize += bundle.size;
|
|
1917
|
+
} else {
|
|
1918
|
+
overfetchSize += bundle.size;
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
let overlapPercent = overlapSize / (overfetchSize + overlapSize);
|
|
1923
|
+
|
|
1924
|
+
let bundleAParents = getBundleParents(bundleAId);
|
|
1925
|
+
let bundleBParents = getBundleParents(bundleBId);
|
|
1926
|
+
|
|
1927
|
+
let sharedParentOverlap = 0;
|
|
1928
|
+
let sharedParentMismatch = 0;
|
|
1929
|
+
|
|
1930
|
+
for (let bundleId of new Set([...bundleAParents, ...bundleBParents])) {
|
|
1931
|
+
if (bundleAParents.has(bundleId) && bundleBParents.has(bundleId)) {
|
|
1932
|
+
sharedParentOverlap++;
|
|
1933
|
+
} else {
|
|
1934
|
+
sharedParentMismatch++;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
let overfetchScore = overfetchSize / maxOverfetchSize;
|
|
1939
|
+
let sharedParentPercent =
|
|
1940
|
+
sharedParentOverlap / (sharedParentOverlap + sharedParentMismatch);
|
|
1941
|
+
let score = sharedParentPercent + overlapPercent + overfetchScore;
|
|
1942
|
+
|
|
1943
|
+
return {
|
|
1944
|
+
overfetchSize,
|
|
1945
|
+
score,
|
|
1946
|
+
bundleIds: [bundleAId, bundleBId],
|
|
1947
|
+
};
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
function getBundleParents(bundleId: NodeId): Set<NodeId> {
|
|
1951
|
+
let parents = new Set<NodeId>();
|
|
1952
|
+
let {bundleRoots} = getBundle(bundleId);
|
|
1953
|
+
|
|
1954
|
+
for (let bundleRoot of bundleRoots) {
|
|
1955
|
+
let bundleRootNodeId = nullthrows(
|
|
1956
|
+
assetToBundleRootNodeId.get(bundleRoot),
|
|
1957
|
+
);
|
|
1958
|
+
for (let parentId of bundleRootGraph.getNodeIdsConnectedTo(
|
|
1959
|
+
bundleRootNodeId,
|
|
1960
|
+
ALL_EDGE_TYPES,
|
|
1961
|
+
)) {
|
|
1962
|
+
parents.add(parentId);
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
return parents;
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1290
1968
|
function getBundleFromBundleRoot(bundleRoot: BundleRoot): Bundle {
|
|
1291
1969
|
let bundle = bundleGraph.getNode(
|
|
1292
1970
|
nullthrows(bundleRoots.get(bundleRoot))[0],
|
|
@@ -1351,6 +2029,12 @@ export function createIdealGraph(
|
|
|
1351
2029
|
bundleGraph.removeNode(bundleId);
|
|
1352
2030
|
}
|
|
1353
2031
|
|
|
2032
|
+
stats.report((bundleId) => {
|
|
2033
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
2034
|
+
invariant(bundle !== 'root');
|
|
2035
|
+
return bundle;
|
|
2036
|
+
});
|
|
2037
|
+
|
|
1354
2038
|
return {
|
|
1355
2039
|
assets,
|
|
1356
2040
|
bundleGraph,
|
|
@@ -1361,23 +2045,24 @@ export function createIdealGraph(
|
|
|
1361
2045
|
};
|
|
1362
2046
|
}
|
|
1363
2047
|
|
|
1364
|
-
function createBundle(opts: {
|
|
1365
|
-
asset?: Asset
|
|
1366
|
-
bundleBehavior?:
|
|
1367
|
-
env?: Environment
|
|
1368
|
-
manualSharedBundle?:
|
|
1369
|
-
needsStableName?: boolean
|
|
1370
|
-
sourceBundles?: Set<NodeId
|
|
1371
|
-
target: Target
|
|
1372
|
-
type?: string
|
|
1373
|
-
uniqueKey?: string
|
|
1374
|
-
|
|
2048
|
+
function createBundle(opts: {
|
|
2049
|
+
asset?: Asset;
|
|
2050
|
+
bundleBehavior?: BundleBehavior | null | undefined;
|
|
2051
|
+
env?: Environment;
|
|
2052
|
+
manualSharedBundle?: string | null | undefined;
|
|
2053
|
+
needsStableName?: boolean;
|
|
2054
|
+
sourceBundles?: Set<NodeId>;
|
|
2055
|
+
target: Target;
|
|
2056
|
+
type?: string;
|
|
2057
|
+
uniqueKey?: string;
|
|
2058
|
+
}): Bundle {
|
|
1375
2059
|
if (opts.asset == null) {
|
|
1376
2060
|
return {
|
|
1377
2061
|
assets: new Set(),
|
|
1378
2062
|
bundleBehavior: opts.bundleBehavior,
|
|
1379
2063
|
env: nullthrows(opts.env),
|
|
1380
2064
|
mainEntryAsset: null,
|
|
2065
|
+
bundleRoots: new Set(),
|
|
1381
2066
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1382
2067
|
needsStableName: Boolean(opts.needsStableName),
|
|
1383
2068
|
size: 0,
|
|
@@ -1394,6 +2079,7 @@ function createBundle(opts: {|
|
|
|
1394
2079
|
bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
|
|
1395
2080
|
env: opts.env ?? asset.env,
|
|
1396
2081
|
mainEntryAsset: asset,
|
|
2082
|
+
bundleRoots: new Set([asset]),
|
|
1397
2083
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1398
2084
|
needsStableName: Boolean(opts.needsStableName),
|
|
1399
2085
|
size: asset.stats.size,
|