@atlaspack/bundler-default 2.14.5-canary.2 → 2.14.5-canary.200
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 +468 -0
- package/lib/DefaultBundler.js +6 -1
- package/lib/MonolithicBundler.js +11 -3
- package/lib/bundleMerge.js +160 -0
- package/lib/bundlerConfig.js +75 -8
- package/lib/decorateLegacyGraph.js +24 -3
- package/lib/idealGraph.js +469 -31
- 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 +21 -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} +123 -43
- package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +26 -7
- package/src/{idealGraph.js → idealGraph.ts} +742 -81
- package/src/memoize.ts +32 -0
- package/src/stats.ts +97 -0
- package/tsconfig.json +4 -0
package/lib/idealGraph.js
CHANGED
|
@@ -12,6 +12,13 @@ function _path() {
|
|
|
12
12
|
};
|
|
13
13
|
return data;
|
|
14
14
|
}
|
|
15
|
+
function _sortedArrayFunctions() {
|
|
16
|
+
const data = _interopRequireDefault(require("sorted-array-functions"));
|
|
17
|
+
_sortedArrayFunctions = function () {
|
|
18
|
+
return data;
|
|
19
|
+
};
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
15
22
|
function _featureFlags() {
|
|
16
23
|
const data = require("@atlaspack/feature-flags");
|
|
17
24
|
_featureFlags = function () {
|
|
@@ -47,8 +54,11 @@ function _nullthrows() {
|
|
|
47
54
|
};
|
|
48
55
|
return data;
|
|
49
56
|
}
|
|
57
|
+
var _bundleMerge = require("./bundleMerge");
|
|
58
|
+
var _stats = require("./stats");
|
|
50
59
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
51
60
|
/* BundleRoot - An asset that is the main entry of a Bundle. */
|
|
61
|
+
|
|
52
62
|
const dependencyPriorityEdges = {
|
|
53
63
|
sync: 1,
|
|
54
64
|
parallel: 2,
|
|
@@ -64,12 +74,19 @@ const idealBundleGraphEdges = exports.idealBundleGraphEdges = Object.freeze({
|
|
|
64
74
|
// which mutates the assetGraph into the bundleGraph we would
|
|
65
75
|
// expect from default bundler
|
|
66
76
|
|
|
77
|
+
function isNonRootBundle(bundle, message) {
|
|
78
|
+
let existingBundle = (0, _nullthrows().default)(bundle, message);
|
|
79
|
+
(0, _assert().default)(existingBundle !== 'root', "Bundle cannot be 'root'");
|
|
80
|
+
return existingBundle;
|
|
81
|
+
}
|
|
67
82
|
function createIdealGraph(assetGraph, config, entries, logger) {
|
|
83
|
+
var _config$sharedBundleM;
|
|
68
84
|
// Asset to the bundle and group it's an entry of
|
|
69
85
|
let bundleRoots = new Map();
|
|
70
86
|
let bundles = new Map();
|
|
71
87
|
let dependencyBundleGraph = new (_graph().ContentGraph)();
|
|
72
88
|
let assetReference = new (_utils().DefaultMap)(() => []);
|
|
89
|
+
let stats = new _stats.Stats(config.projectRoot);
|
|
73
90
|
|
|
74
91
|
// A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
|
|
75
92
|
// referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role.
|
|
@@ -107,15 +124,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
107
124
|
}
|
|
108
125
|
let assets = [];
|
|
109
126
|
let assetToIndex = new Map();
|
|
110
|
-
|
|
111
|
-
let manualSharedMap = new Map();
|
|
112
|
-
// May need a map to be able to look up NON- bundle root assets which need special case instructions
|
|
113
|
-
// Use this when placing assets into bundles, to avoid duplication
|
|
114
|
-
let manualAssetToBundle = new Map();
|
|
115
|
-
let {
|
|
116
|
-
manualAssetToConfig,
|
|
117
|
-
constantModuleToMSB
|
|
118
|
-
} = function makeManualAssetToConfigLookup() {
|
|
127
|
+
function makeManualAssetToConfigLookup() {
|
|
119
128
|
let manualAssetToConfig = new Map();
|
|
120
129
|
let constantModuleToMSB = new (_utils().DefaultMap)(() => []);
|
|
121
130
|
if (config.manualSharedBundles.length === 0) {
|
|
@@ -127,6 +136,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
127
136
|
let parentsToConfig = new (_utils().DefaultMap)(() => []);
|
|
128
137
|
for (let c of config.manualSharedBundles) {
|
|
129
138
|
if (c.root != null) {
|
|
139
|
+
// @ts-expect-error TS2345
|
|
130
140
|
parentsToConfig.get(_path().default.join(config.projectRoot, c.root)).push(c);
|
|
131
141
|
}
|
|
132
142
|
}
|
|
@@ -159,17 +169,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
159
169
|
assetGraph.traverse((node, _, actions) => {
|
|
160
170
|
if (node.type === 'asset' && (!Array.isArray(c.types) || c.types.includes(node.value.type))) {
|
|
161
171
|
let projectRelativePath = _path().default.relative(config.projectRoot, node.value.filePath);
|
|
162
|
-
if (!assetRegexes.some(regex => regex.test(projectRelativePath))) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
172
|
|
|
166
173
|
// We track all matching MSB's for constant modules as they are never duplicated
|
|
167
174
|
// and need to be assigned to all matching bundles
|
|
168
175
|
if (node.value.meta.isConstantModule === true) {
|
|
176
|
+
// @ts-expect-error TS2345
|
|
169
177
|
constantModuleToMSB.get(node.value).push(c);
|
|
170
178
|
}
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
if (assetRegexes.some(regex => regex.test(projectRelativePath))) {
|
|
180
|
+
manualAssetToConfig.set(node.value, c);
|
|
181
|
+
}
|
|
173
182
|
}
|
|
174
183
|
if (node.type === 'dependency' && (node.value.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional') && parentAsset) {
|
|
175
184
|
// Don't walk past the bundle group assets
|
|
@@ -181,8 +190,23 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
181
190
|
manualAssetToConfig,
|
|
182
191
|
constantModuleToMSB
|
|
183
192
|
};
|
|
184
|
-
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
//Manual is a map of the user-given name to the bundle node Id that corresponds to ALL the assets that match any glob in that user-specified array
|
|
196
|
+
let manualSharedMap = new Map();
|
|
197
|
+
// May need a map to be able to look up NON- bundle root assets which need special case instructions
|
|
198
|
+
// Use this when placing assets into bundles, to avoid duplication
|
|
199
|
+
let manualAssetToBundle = new Map();
|
|
200
|
+
let {
|
|
201
|
+
manualAssetToConfig,
|
|
202
|
+
constantModuleToMSB
|
|
203
|
+
} = makeManualAssetToConfigLookup();
|
|
185
204
|
let manualBundleToInternalizedAsset = new (_utils().DefaultMap)(() => []);
|
|
205
|
+
let mergeSourceBundleLookup = new Map();
|
|
206
|
+
let mergeSourceBundleAssets = new Set((_config$sharedBundleM = config.sharedBundleMerge) === null || _config$sharedBundleM === void 0 ? void 0 : _config$sharedBundleM.flatMap(c => {
|
|
207
|
+
var _c$sourceBundles;
|
|
208
|
+
return ((_c$sourceBundles = c.sourceBundles) === null || _c$sourceBundles === void 0 ? void 0 : _c$sourceBundles.map(assetMatch => _path().default.join(config.projectRoot, assetMatch))) ?? [];
|
|
209
|
+
}));
|
|
186
210
|
|
|
187
211
|
/**
|
|
188
212
|
* Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
|
|
@@ -190,7 +214,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
190
214
|
* adding only that asset to each bundle, not its entire subgraph.
|
|
191
215
|
*/
|
|
192
216
|
assetGraph.traverse({
|
|
193
|
-
enter(node, context,
|
|
217
|
+
enter(node, context,
|
|
218
|
+
// @ts-expect-error TS2304
|
|
219
|
+
actions) {
|
|
194
220
|
if (node.type === 'asset') {
|
|
195
221
|
if ((context === null || context === void 0 ? void 0 : context.type) === 'dependency' && context !== null && context !== void 0 && context.value.isEntry && !entries.has(node.value)) {
|
|
196
222
|
// Skip whole subtrees of other targets by skipping those entries
|
|
@@ -233,15 +259,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
233
259
|
manualSharedBundleKey != null && manualSharedMap.has(manualSharedBundleKey)) {
|
|
234
260
|
bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey));
|
|
235
261
|
}
|
|
236
|
-
if (dependency.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional' || childAsset.bundleBehavior === 'isolated'
|
|
237
|
-
|
|
262
|
+
if (dependency.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional' || childAsset.bundleBehavior === 'isolated' ||
|
|
263
|
+
// An isolated Dependency, or Bundle must contain all assets it needs to load.
|
|
264
|
+
childAsset.bundleBehavior === 'inlineIsolated') {
|
|
238
265
|
if (bundleId == null) {
|
|
239
266
|
let firstBundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(stack[0][1]));
|
|
240
267
|
(0, _assert().default)(firstBundleGroup !== 'root');
|
|
241
268
|
bundle = createBundle({
|
|
242
269
|
asset: childAsset,
|
|
243
270
|
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
244
|
-
needsStableName: dependency.bundleBehavior === 'inline' || childAsset.bundleBehavior === 'inline' ? false : dependency.isEntry || dependency.needsStableName,
|
|
271
|
+
needsStableName: dependency.bundleBehavior === 'inline' || childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inlineIsolated' || childAsset.bundleBehavior === 'inlineIsolated' ? false : dependency.isEntry || dependency.needsStableName,
|
|
245
272
|
target: firstBundleGroup.target
|
|
246
273
|
});
|
|
247
274
|
bundleId = bundleGraph.addNode(bundle);
|
|
@@ -249,6 +276,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
249
276
|
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
250
277
|
bundleGroupBundleIds.add(bundleId);
|
|
251
278
|
bundleGraph.addEdge(bundleGraphRootNodeId, bundleId);
|
|
279
|
+
// If this asset is relevant for merging then track it's source
|
|
280
|
+
// bundle id for later
|
|
281
|
+
if (mergeSourceBundleAssets.has(childAsset.filePath)) {
|
|
282
|
+
mergeSourceBundleLookup.set(_path().default.relative(config.projectRoot, childAsset.filePath), bundleId);
|
|
283
|
+
}
|
|
252
284
|
if (manualSharedObject) {
|
|
253
285
|
// MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
|
|
254
286
|
// since MSBs should not have main entry assets
|
|
@@ -260,7 +292,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
260
292
|
if (
|
|
261
293
|
// If this dependency requests isolated, but the bundle is not,
|
|
262
294
|
// make the bundle isolated for all uses.
|
|
263
|
-
dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
|
|
295
|
+
(dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
|
|
264
296
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
265
297
|
}
|
|
266
298
|
}
|
|
@@ -270,7 +302,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
270
302
|
}), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
271
303
|
value: bundle,
|
|
272
304
|
type: 'bundle'
|
|
273
|
-
}),
|
|
305
|
+
}),
|
|
306
|
+
// @ts-expect-error TS7053
|
|
307
|
+
dependencyPriorityEdges[dependency.priority]);
|
|
274
308
|
if ((0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && dependency.priority === 'conditional') {
|
|
275
309
|
let [referencingBundleRoot, bundleGroupNodeId] = (0, _nullthrows().default)(stack[stack.length - 1]);
|
|
276
310
|
let referencingBundleId = (0, _nullthrows().default)(bundleRoots.get(referencingBundleRoot))[0];
|
|
@@ -299,7 +333,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
299
333
|
env: childAsset.env,
|
|
300
334
|
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
301
335
|
target: referencingBundle.target,
|
|
302
|
-
needsStableName: childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inline' || dependency.priority === 'parallel' && !dependency.needsStableName ? false : referencingBundle.needsStableName
|
|
336
|
+
needsStableName: childAsset.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inline' || dependency.bundleBehavior === 'inlineIsolated' || dependency.priority === 'parallel' && !dependency.needsStableName ? false : referencingBundle.needsStableName
|
|
303
337
|
});
|
|
304
338
|
bundleId = bundleGraph.addNode(bundle);
|
|
305
339
|
} else {
|
|
@@ -308,7 +342,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
308
342
|
if (
|
|
309
343
|
// If this dependency requests isolated, but the bundle is not,
|
|
310
344
|
// make the bundle isolated for all uses.
|
|
311
|
-
dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
|
|
345
|
+
(dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
|
|
312
346
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
313
347
|
}
|
|
314
348
|
}
|
|
@@ -330,13 +364,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
330
364
|
}
|
|
331
365
|
assetReference.get(childAsset).push([dependency, bundle]);
|
|
332
366
|
} else {
|
|
367
|
+
// @ts-expect-error TS2322
|
|
333
368
|
bundleId = null;
|
|
334
369
|
}
|
|
335
370
|
if (manualSharedObject && bundleId != null) {
|
|
336
371
|
// MSB Step 5: At this point we've either created or found an existing MSB bundle
|
|
337
372
|
// add the asset if it doesn't already have it and set key
|
|
338
373
|
|
|
339
|
-
(0, _assert().default)(
|
|
374
|
+
(0, _assert().default)(
|
|
375
|
+
// @ts-expect-error TS2367
|
|
376
|
+
bundle !== 'root' && bundle != null && bundleId != null);
|
|
340
377
|
manualAssetToBundle.set(childAsset, bundleId);
|
|
341
378
|
if (!bundle.assets.has(childAsset)) {
|
|
342
379
|
// Add asset to bundle
|
|
@@ -357,6 +394,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
357
394
|
}
|
|
358
395
|
return node;
|
|
359
396
|
},
|
|
397
|
+
// @ts-expect-error TS2322
|
|
360
398
|
exit(node) {
|
|
361
399
|
var _stack;
|
|
362
400
|
if (((_stack = stack[stack.length - 1]) === null || _stack === void 0 ? void 0 : _stack[0]) === node.value) {
|
|
@@ -494,7 +532,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
494
532
|
// not true that a bundle's available assets = all assets of all the bundleGroups
|
|
495
533
|
// it belongs to. It's the intersection of those sets.
|
|
496
534
|
let available;
|
|
497
|
-
if (bundleRoot.bundleBehavior === 'isolated') {
|
|
535
|
+
if (bundleRoot.bundleBehavior === 'isolated' || bundleRoot.bundleBehavior === 'inlineIsolated') {
|
|
498
536
|
available = new (_graph().BitSet)(assets.length);
|
|
499
537
|
} else {
|
|
500
538
|
available = (0, _nullthrows().default)(ancestorAssets[nodeId]).clone();
|
|
@@ -557,7 +595,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
557
595
|
continue;
|
|
558
596
|
}
|
|
559
597
|
let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, _graph().ALL_EDGE_TYPES);
|
|
560
|
-
let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated';
|
|
598
|
+
let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated' && getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'inlineIsolated';
|
|
561
599
|
if (parentRoots.length === 0) continue;
|
|
562
600
|
for (let parentId of parentRoots) {
|
|
563
601
|
var _ancestorAssets$paren;
|
|
@@ -585,8 +623,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
585
623
|
}
|
|
586
624
|
function assignInlineConstants(parentAsset, bundle) {
|
|
587
625
|
for (let inlineConstant of inlineConstantDeps.get(parentAsset)) {
|
|
626
|
+
// @ts-expect-error TS2345
|
|
588
627
|
if (!bundle.assets.has(inlineConstant)) {
|
|
628
|
+
// @ts-expect-error TS2345
|
|
589
629
|
bundle.assets.add(inlineConstant);
|
|
630
|
+
// @ts-expect-error TS18046
|
|
590
631
|
bundle.size += inlineConstant.stats.size;
|
|
591
632
|
}
|
|
592
633
|
}
|
|
@@ -622,7 +663,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
622
663
|
let assetId = bundleRootGraph.getNode(nodeId);
|
|
623
664
|
if (assetId == null) return; // deleted
|
|
624
665
|
let a = assets[assetId];
|
|
625
|
-
if (entries.has(a) || !a.isBundleSplittable || bundleRoots.get(a) && (getBundleFromBundleRoot(a).needsStableName || getBundleFromBundleRoot(a).bundleBehavior === 'isolated')) {
|
|
666
|
+
if (entries.has(a) || !a.isBundleSplittable || bundleRoots.get(a) && (getBundleFromBundleRoot(a).needsStableName || getBundleFromBundleRoot(a).bundleBehavior === 'isolated' || getBundleFromBundleRoot(a).bundleBehavior === 'inlineIsolated')) {
|
|
626
667
|
// Add asset to non-splittable bundles.
|
|
627
668
|
addAssetToBundleRoot(asset, a);
|
|
628
669
|
} else if (!((_ancestorAssets$nodeI = ancestorAssets[nodeId]) !== null && _ancestorAssets$nodeI !== void 0 && _ancestorAssets$nodeI.has(i))) {
|
|
@@ -784,6 +825,8 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
784
825
|
}
|
|
785
826
|
}
|
|
786
827
|
let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
|
|
828
|
+
let modifiedSourceBundles = new Set();
|
|
829
|
+
|
|
787
830
|
// Step split manual shared bundles for those that have the "split" property set
|
|
788
831
|
let remainderMap = new (_utils().DefaultMap)(() => []);
|
|
789
832
|
for (let id of manualSharedMap.values()) {
|
|
@@ -800,8 +843,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
800
843
|
if (modNum != null) {
|
|
801
844
|
for (let a of [...manualBundle.assets]) {
|
|
802
845
|
let numRep = getBigIntFromContentKey(a.id);
|
|
803
|
-
// $FlowFixMe Flow doesn't know about BigInt
|
|
804
846
|
let r = Number(numRep % BigInt(modNum));
|
|
847
|
+
|
|
848
|
+
// @ts-expect-error TS2345
|
|
805
849
|
remainderMap.get(r).push(a);
|
|
806
850
|
}
|
|
807
851
|
for (let i = 1; i < [...remainderMap.keys()].length; i++) {
|
|
@@ -823,8 +867,10 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
823
867
|
}
|
|
824
868
|
for (let sp of remainderMap.get(i)) {
|
|
825
869
|
bundle.assets.add(sp);
|
|
870
|
+
// @ts-expect-error TS2339
|
|
826
871
|
bundle.size += sp.stats.size;
|
|
827
872
|
manualBundle.assets.delete(sp);
|
|
873
|
+
// @ts-expect-error TS2339
|
|
828
874
|
manualBundle.size -= sp.stats.size;
|
|
829
875
|
}
|
|
830
876
|
}
|
|
@@ -837,16 +883,81 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
837
883
|
// match multiple MSB's
|
|
838
884
|
for (let [asset, msbs] of constantModuleToMSB.entries()) {
|
|
839
885
|
for (let manualSharedObject of msbs) {
|
|
886
|
+
// @ts-expect-error TS2339
|
|
840
887
|
let bundleId = manualSharedMap.get(manualSharedObject.name + ',js');
|
|
841
888
|
if (bundleId == null) continue;
|
|
842
889
|
let bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
|
|
843
890
|
(0, _assert().default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly');
|
|
891
|
+
|
|
892
|
+
// @ts-expect-error TS2345
|
|
844
893
|
if (!bundle.assets.has(asset)) {
|
|
894
|
+
// @ts-expect-error TS2345
|
|
845
895
|
bundle.assets.add(asset);
|
|
896
|
+
// @ts-expect-error TS18046
|
|
846
897
|
bundle.size += asset.stats.size;
|
|
847
898
|
}
|
|
848
899
|
}
|
|
849
900
|
}
|
|
901
|
+
if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
|
|
902
|
+
// Merge webpack chunk name bundles
|
|
903
|
+
let chunkNameBundles = new (_utils().DefaultMap)(() => new Set());
|
|
904
|
+
for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
|
|
905
|
+
var _bundleNode$value$mai;
|
|
906
|
+
// meta.chunkName is set by the Rust transformer, so we just need to find
|
|
907
|
+
// bundles that have a chunkName set.
|
|
908
|
+
if (!node || node.type !== 'dependency' || typeof node.value.meta.chunkName !== 'string') {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(nodeId, dependencyPriorityEdges[node.value.priority]);
|
|
912
|
+
if (connectedBundles.length === 0) {
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
(0, _assert().default)(connectedBundles.length === 1, 'Expected webpackChunkName dependency to be connected to no more than one bundle');
|
|
916
|
+
let bundleId = connectedBundles[0];
|
|
917
|
+
let bundleNode = dependencyBundleGraph.getNode(bundleId);
|
|
918
|
+
(0, _assert().default)(bundleNode != null && bundleNode.type === 'bundle');
|
|
919
|
+
|
|
920
|
+
// If a bundle does not have a main entry asset, it's somehow just a
|
|
921
|
+
// shared bundle, and will be merged/deleted by other means.
|
|
922
|
+
if (bundleNode.value.mainEntryAsset == null) {
|
|
923
|
+
continue;
|
|
924
|
+
}
|
|
925
|
+
let bundleNodeId = null;
|
|
926
|
+
let mainEntryAssetId = (_bundleNode$value$mai = bundleNode.value.mainEntryAsset) === null || _bundleNode$value$mai === void 0 ? void 0 : _bundleNode$value$mai.id;
|
|
927
|
+
if (mainEntryAssetId != null) {
|
|
928
|
+
bundleNodeId = bundles.get(mainEntryAssetId);
|
|
929
|
+
}
|
|
930
|
+
if (bundleNodeId == null) {
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
chunkNameBundles.get(node.value.meta.chunkName)
|
|
934
|
+
// DependencyBundleGraph uses content keys as node ids, so we can use that
|
|
935
|
+
// to get the bundle id.
|
|
936
|
+
.add(bundleNodeId);
|
|
937
|
+
}
|
|
938
|
+
for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
|
|
939
|
+
// The `[request]` placeholder is not yet supported
|
|
940
|
+
if (bundleIds.size <= 1 || typeof chunkName === 'string' && chunkName.includes('[request]')) {
|
|
941
|
+
continue; // Nothing to merge
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Merge all bundles with the same chunk name into the first one.
|
|
945
|
+
let [firstBundleId, ...rest] = Array.from(bundleIds);
|
|
946
|
+
for (let bundleId of rest) {
|
|
947
|
+
mergeBundles(firstBundleId, bundleId, 'webpack-chunk-name');
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// Step merge async bundles that meet the configured params
|
|
953
|
+
if (config.asyncBundleMerge) {
|
|
954
|
+
mergeAsyncBundles(config.asyncBundleMerge);
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// Step merge shared bundles that meet the configured params
|
|
958
|
+
if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
|
|
959
|
+
mergeSharedBundles(config.sharedBundleMerge);
|
|
960
|
+
}
|
|
850
961
|
|
|
851
962
|
// Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
|
|
852
963
|
// their source bundles, and remove the bundle.
|
|
@@ -857,9 +968,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
857
968
|
removeBundle(bundleGraph, bundleNodeId, assetReference);
|
|
858
969
|
}
|
|
859
970
|
}
|
|
860
|
-
let modifiedSourceBundles = new Set();
|
|
861
971
|
|
|
862
972
|
// Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
|
|
973
|
+
|
|
863
974
|
if (config.disableSharedBundles === false) {
|
|
864
975
|
for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
|
|
865
976
|
// Find shared bundles in this bundle group.
|
|
@@ -872,7 +983,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
872
983
|
let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
|
|
873
984
|
let bundle = (0, _nullthrows().default)(bundleGraph.getNode(b));
|
|
874
985
|
(0, _assert().default)(bundle !== 'root');
|
|
875
|
-
return count + (bundle.bundleBehavior !== 'inline');
|
|
986
|
+
return count + Number(bundle.bundleBehavior !== 'inline' && bundle.bundleBehavior !== 'inlineIsolated');
|
|
876
987
|
}, 0);
|
|
877
988
|
if (numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
878
989
|
let sharedBundleIdsInBundleGroup = bundleIdsInGroup.filter(b => {
|
|
@@ -901,7 +1012,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
901
1012
|
// Remove bundles until the bundle group is within the parallel request limit.
|
|
902
1013
|
while (sharedBundlesInGroup.length > 0 && numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
903
1014
|
let bundleTuple = sharedBundlesInGroup.pop();
|
|
1015
|
+
// @ts-expect-error TS18048
|
|
904
1016
|
let bundleToRemove = bundleTuple.bundle;
|
|
1017
|
+
// @ts-expect-error TS18048
|
|
905
1018
|
let bundleIdToRemove = bundleTuple.id;
|
|
906
1019
|
//TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
|
|
907
1020
|
// but total # bundles still exceeds limit due to non shared bundles
|
|
@@ -944,10 +1057,281 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
944
1057
|
}
|
|
945
1058
|
}
|
|
946
1059
|
}
|
|
1060
|
+
function mergeBundles(bundleToKeepId, bundleToRemoveId, reason) {
|
|
1061
|
+
stats.trackMerge(bundleToKeepId, bundleToRemoveId, reason);
|
|
1062
|
+
let bundleToKeep = isNonRootBundle(bundleGraph.getNode(bundleToKeepId), `Bundle ${bundleToKeepId} not found`);
|
|
1063
|
+
let bundleToRemove = isNonRootBundle(bundleGraph.getNode(bundleToRemoveId), `Bundle ${bundleToRemoveId} not found`);
|
|
1064
|
+
modifiedSourceBundles.add(bundleToKeep);
|
|
1065
|
+
for (let asset of bundleToRemove.assets) {
|
|
1066
|
+
bundleToKeep.assets.add(asset);
|
|
1067
|
+
bundleToKeep.size += asset.stats.size;
|
|
1068
|
+
let newAssetReference = assetReference.get(asset).map(([dep, bundle]) => bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle]);
|
|
1069
|
+
|
|
1070
|
+
// @ts-expect-error TS2345
|
|
1071
|
+
assetReference.set(asset, newAssetReference);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// Merge any internalized assets
|
|
1075
|
+
if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
|
|
1076
|
+
if (bundleToKeep.internalizedAssets != null) {
|
|
1077
|
+
if (bundleToRemove.internalizedAssets != null) {
|
|
1078
|
+
bundleToKeep.internalizedAssets.intersect(bundleToRemove.internalizedAssets);
|
|
1079
|
+
} else {
|
|
1080
|
+
bundleToKeep.internalizedAssets.clear();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
} else {
|
|
1084
|
+
(0, _assert().default)(bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets, 'All shared bundles should have internalized assets');
|
|
1085
|
+
bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// Merge and clean up source bundles
|
|
1089
|
+
for (let sourceBundleId of bundleToRemove.sourceBundles) {
|
|
1090
|
+
if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
if (sourceBundleId !== bundleToKeepId) {
|
|
1094
|
+
bundleToKeep.sourceBundles.add(sourceBundleId);
|
|
1095
|
+
bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
|
|
1099
|
+
bundleToKeep.sourceBundles.delete(bundleToRemoveId);
|
|
1100
|
+
for (let bundle of bundleGraph.getNodeIdsConnectedFrom(bundleToRemoveId)) {
|
|
1101
|
+
let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundle));
|
|
1102
|
+
if (bundleNode === 'root') {
|
|
1103
|
+
continue;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// If the bundle is a source bundle, add it to the bundle to keep
|
|
1107
|
+
if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
|
|
1108
|
+
bundleNode.sourceBundles.delete(bundleToRemoveId);
|
|
1109
|
+
if (bundle !== bundleToKeepId) {
|
|
1110
|
+
bundleNode.sourceBundles.add(bundleToKeepId);
|
|
1111
|
+
bundleGraph.addEdge(bundleToKeepId, bundle);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// Merge bundle roots
|
|
1117
|
+
for (let bundleRoot of bundleToRemove.bundleRoots) {
|
|
1118
|
+
bundleToKeep.bundleRoots.add(bundleRoot);
|
|
1119
|
+
}
|
|
1120
|
+
if (bundleToRemove.mainEntryAsset != null) {
|
|
1121
|
+
(0, _assert().default)(bundleToKeep.mainEntryAsset != null);
|
|
1122
|
+
|
|
1123
|
+
// Merge the bundles in bundle group
|
|
1124
|
+
let bundlesInRemoveBundleGroup = getBundlesForBundleGroup(bundleToRemoveId);
|
|
1125
|
+
let removedBundleSharedBundles = new Set();
|
|
1126
|
+
for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
|
|
1127
|
+
if (bundleIdInGroup === bundleToRemoveId) {
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
1130
|
+
bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
|
|
1131
|
+
removedBundleSharedBundles.add(bundleIdInGroup);
|
|
1132
|
+
}
|
|
1133
|
+
if ((0, _featureFlags().getFeatureFlag)('removeRedundantSharedBundles')) {
|
|
1134
|
+
// Merge any shared bundles that now have the same source bundles due to
|
|
1135
|
+
// the current bundle merge
|
|
1136
|
+
let sharedBundles = new (_utils().DefaultMap)(() => []);
|
|
1137
|
+
for (let bundleId of removedBundleSharedBundles) {
|
|
1138
|
+
let bundleNode = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
|
|
1139
|
+
(0, _assert().default)(bundleNode !== 'root');
|
|
1140
|
+
if (bundleNode.mainEntryAsset != null || bundleNode.manualSharedBundle != null) {
|
|
1141
|
+
continue;
|
|
1142
|
+
}
|
|
1143
|
+
let key = Array.from(bundleNode.sourceBundles).filter(sourceBundle => sourceBundle !== bundleToRemoveId).sort().join(',') + '.' + bundleNode.type;
|
|
1144
|
+
sharedBundles.get(key).push(bundleId);
|
|
1145
|
+
}
|
|
1146
|
+
for (let sharedBundlesToMerge of sharedBundles.values()) {
|
|
1147
|
+
if (sharedBundlesToMerge.length > 1) {
|
|
1148
|
+
let [firstBundleId, ...rest] = sharedBundlesToMerge;
|
|
1149
|
+
for (let bundleId of rest) {
|
|
1150
|
+
mergeBundles(firstBundleId, bundleId, 'redundant-shared');
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// Remove old bundle group
|
|
1157
|
+
bundleGroupBundleIds.delete(bundleToRemoveId);
|
|
1158
|
+
|
|
1159
|
+
// Clean up bundle roots
|
|
1160
|
+
let bundleRootToRemoveNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToRemove.mainEntryAsset)));
|
|
1161
|
+
let bundleRootToKeepNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get((0, _nullthrows().default)(bundleToKeep.mainEntryAsset)));
|
|
1162
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootToRemoveNodeId)) {
|
|
1163
|
+
bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
|
|
1164
|
+
bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
|
|
1165
|
+
}
|
|
1166
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(bundleRootToRemoveNodeId)) {
|
|
1167
|
+
bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
|
|
1168
|
+
bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
|
|
1169
|
+
}
|
|
1170
|
+
bundleRoots.set((0, _nullthrows().default)(bundleToRemove.mainEntryAsset), [bundleToKeepId, bundleToKeepId]);
|
|
1171
|
+
|
|
1172
|
+
// Merge dependency bundle graph
|
|
1173
|
+
for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), _graph().ALL_EDGE_TYPES)) {
|
|
1174
|
+
let dependencyNode = (0, _nullthrows().default)(dependencyBundleGraph.getNode(dependencyNodeId));
|
|
1175
|
+
(0, _assert().default)(dependencyNode.type === 'dependency');
|
|
1176
|
+
|
|
1177
|
+
// Add dependency to the bundle to keep
|
|
1178
|
+
dependencyBundleGraph.addEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)), dependencyPriorityEdges[dependencyNode.value.priority]);
|
|
1179
|
+
// Remove dependency from the bundle to remove
|
|
1180
|
+
dependencyBundleGraph.removeEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), dependencyPriorityEdges[dependencyNode.value.priority]);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
bundleGraph.removeNode(bundleToRemoveId);
|
|
1185
|
+
}
|
|
1186
|
+
function mergeSharedBundles(mergeConfig) {
|
|
1187
|
+
// Find all shared bundles
|
|
1188
|
+
let sharedBundles = new Set();
|
|
1189
|
+
bundleGraph.traverse(nodeId => {
|
|
1190
|
+
let bundle = bundleGraph.getNode(nodeId);
|
|
1191
|
+
if (!bundle) {
|
|
1192
|
+
throw new Error(`Unable to find bundle ${nodeId} in bundle graph`);
|
|
1193
|
+
}
|
|
1194
|
+
if (bundle === 'root') {
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
// Only consider JS shared bundles and non-reused bundles.
|
|
1199
|
+
// These count potentially be considered for merging in future but they're
|
|
1200
|
+
// more complicated to merge
|
|
1201
|
+
if (bundle.sourceBundles.size > 0 && bundle.manualSharedBundle == null && !bundle.mainEntryAsset && bundle.type === 'js') {
|
|
1202
|
+
sharedBundles.add(nodeId);
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
1205
|
+
let clusters = (0, _bundleMerge.findMergeCandidates)(bundleGraph, Array.from(sharedBundles), mergeConfig.map(config => {
|
|
1206
|
+
var _config$sourceBundles;
|
|
1207
|
+
return {
|
|
1208
|
+
...config,
|
|
1209
|
+
sourceBundles: (_config$sourceBundles = config.sourceBundles) === null || _config$sourceBundles === void 0 ? void 0 : _config$sourceBundles.map(assetMatch => {
|
|
1210
|
+
let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
|
|
1211
|
+
if (sourceBundleNodeId == null) {
|
|
1212
|
+
throw new Error(`Source bundle ${assetMatch} not found in merge source bundle lookup`);
|
|
1213
|
+
}
|
|
1214
|
+
return sourceBundleNodeId;
|
|
1215
|
+
})
|
|
1216
|
+
};
|
|
1217
|
+
}));
|
|
1218
|
+
let mergedBundles = new Set();
|
|
1219
|
+
for (let cluster of clusters) {
|
|
1220
|
+
let [mergeTarget, ...rest] = cluster;
|
|
1221
|
+
for (let bundleIdToMerge of rest) {
|
|
1222
|
+
mergeBundles(mergeTarget, bundleIdToMerge, 'shared-merge');
|
|
1223
|
+
}
|
|
1224
|
+
mergedBundles.add(mergeTarget);
|
|
1225
|
+
}
|
|
1226
|
+
if ((0, _featureFlags().getFeatureFlag)('supportWebpackChunkName')) {
|
|
1227
|
+
return mergedBundles;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
function mergeAsyncBundles({
|
|
1231
|
+
bundleSize,
|
|
1232
|
+
maxOverfetchSize,
|
|
1233
|
+
ignore
|
|
1234
|
+
}) {
|
|
1235
|
+
let mergeCandidates = [];
|
|
1236
|
+
let ignoreRegexes = (ignore === null || ignore === void 0 ? void 0 : ignore.map(glob => (0, _utils().globToRegex)(glob))) ?? [];
|
|
1237
|
+
let isIgnored = bundle => {
|
|
1238
|
+
if (!bundle.mainEntryAsset) {
|
|
1239
|
+
return false;
|
|
1240
|
+
}
|
|
1241
|
+
let mainEntryFilePath = _path().default.relative(config.projectRoot, (0, _nullthrows().default)(bundle.mainEntryAsset).filePath);
|
|
1242
|
+
return ignoreRegexes.some(regex => regex.test(mainEntryFilePath));
|
|
1243
|
+
};
|
|
1244
|
+
for (let [_bundleRootAsset, [bundleRootBundleId]] of bundleRoots) {
|
|
1245
|
+
let bundleRootBundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleRootBundleId));
|
|
1246
|
+
(0, _assert().default)(bundleRootBundle !== 'root');
|
|
1247
|
+
if (bundleRootBundle.type === 'js' && bundleRootBundle.bundleBehavior !== 'inline' && bundleRootBundle.bundleBehavior !== 'inlineIsolated' && bundleRootBundle.size <= bundleSize && !isIgnored(bundleRootBundle)) {
|
|
1248
|
+
mergeCandidates.push(bundleRootBundleId);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
let candidates = [];
|
|
1252
|
+
for (let i = 0; i < mergeCandidates.length; i++) {
|
|
1253
|
+
for (let j = i + 1; j < mergeCandidates.length; j++) {
|
|
1254
|
+
let a = mergeCandidates[i];
|
|
1255
|
+
let b = mergeCandidates[j];
|
|
1256
|
+
if (a === b) continue; // Skip self-comparison
|
|
1257
|
+
|
|
1258
|
+
candidates.push(scoreAsyncMerge(a, b, maxOverfetchSize));
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
let sortByScore = (a, b) => {
|
|
1262
|
+
let diff = a.score - b.score;
|
|
1263
|
+
if (diff > 0) {
|
|
1264
|
+
return 1;
|
|
1265
|
+
} else if (diff < 0) {
|
|
1266
|
+
return -1;
|
|
1267
|
+
}
|
|
1268
|
+
return 0;
|
|
1269
|
+
};
|
|
1270
|
+
candidates = candidates.filter(({
|
|
1271
|
+
overfetchSize,
|
|
1272
|
+
score
|
|
1273
|
+
}) => overfetchSize <= maxOverfetchSize && score > 0).sort(sortByScore);
|
|
1274
|
+
|
|
1275
|
+
// Tracks the bundles that have been merged
|
|
1276
|
+
let merged = new Set();
|
|
1277
|
+
// Tracks the deleted bundles to the bundle they were merged into.
|
|
1278
|
+
let mergeRemap = new Map();
|
|
1279
|
+
// Tracks the bundles that have been rescored and added back into the
|
|
1280
|
+
// candidates.
|
|
1281
|
+
let rescored = new (_utils().DefaultMap)(() => new Set());
|
|
1282
|
+
do {
|
|
1283
|
+
let [a, b] = (0, _nullthrows().default)(candidates.pop()).bundleIds;
|
|
1284
|
+
if (bundleGraph.hasNode(a) && bundleGraph.hasNode(b) && (!merged.has(a) && !merged.has(b) || rescored.get(a).has(b))) {
|
|
1285
|
+
mergeRemap.set(b, a);
|
|
1286
|
+
merged.add(a);
|
|
1287
|
+
rescored.get(a).clear();
|
|
1288
|
+
mergeBundles(a, b, 'async-merge');
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
// One or both of the bundles have been previously merged, so we'll
|
|
1293
|
+
// rescore and add the result back into the list of candidates.
|
|
1294
|
+
let getMergedBundleId = bundleId => {
|
|
1295
|
+
let seen = new Set();
|
|
1296
|
+
while (!bundleGraph.hasNode(bundleId) && !seen.has(bundleId)) {
|
|
1297
|
+
seen.add(bundleId);
|
|
1298
|
+
bundleId = (0, _nullthrows().default)(mergeRemap.get(bundleId));
|
|
1299
|
+
}
|
|
1300
|
+
if (!bundleGraph.hasNode(bundleId)) {
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
return bundleId;
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
// Map a and b to their merged bundle ids if they've already been merged
|
|
1307
|
+
let currentA = getMergedBundleId(a);
|
|
1308
|
+
let currentB = getMergedBundleId(b);
|
|
1309
|
+
if (!currentA || !currentB ||
|
|
1310
|
+
// Bundles are already merged
|
|
1311
|
+
currentA === currentB) {
|
|
1312
|
+
// This combiniation is not valid, so we skip it.
|
|
1313
|
+
continue;
|
|
1314
|
+
}
|
|
1315
|
+
let candidate = scoreAsyncMerge(currentA, currentB, maxOverfetchSize);
|
|
1316
|
+
if (candidate.overfetchSize <= maxOverfetchSize && candidate.score > 0) {
|
|
1317
|
+
_sortedArrayFunctions().default.add(candidates, candidate, sortByScore);
|
|
1318
|
+
rescored.get(currentA).add(currentB);
|
|
1319
|
+
}
|
|
1320
|
+
} while (candidates.length > 0);
|
|
1321
|
+
}
|
|
1322
|
+
function getBundle(bundleId) {
|
|
1323
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1324
|
+
if (bundle === 'root') {
|
|
1325
|
+
throw new Error(`Cannot access root bundle`);
|
|
1326
|
+
}
|
|
1327
|
+
if (bundle == null) {
|
|
1328
|
+
throw new Error(`Bundle ${bundleId} not found in bundle graph`);
|
|
1329
|
+
}
|
|
1330
|
+
return bundle;
|
|
1331
|
+
}
|
|
947
1332
|
function getBigIntFromContentKey(contentKey) {
|
|
948
1333
|
let b = Buffer.alloc(64);
|
|
949
1334
|
b.write(contentKey);
|
|
950
|
-
// $FlowFixMe Flow doesn't have BigInt types in this version
|
|
951
1335
|
return b.readBigInt64BE();
|
|
952
1336
|
}
|
|
953
1337
|
// Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
|
|
@@ -977,6 +1361,53 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
977
1361
|
}, bundleGroupId);
|
|
978
1362
|
return bundlesInABundleGroup;
|
|
979
1363
|
}
|
|
1364
|
+
function scoreAsyncMerge(bundleAId, bundleBId, maxOverfetchSize) {
|
|
1365
|
+
let bundleGroupA = new Set(getBundlesForBundleGroup(bundleAId));
|
|
1366
|
+
let bundleGroupB = new Set(getBundlesForBundleGroup(bundleBId));
|
|
1367
|
+
let overlapSize = 0;
|
|
1368
|
+
let overfetchSize = 0;
|
|
1369
|
+
for (let bundleId of new Set([...bundleGroupA, ...bundleGroupB])) {
|
|
1370
|
+
let bundle = getBundle(bundleId);
|
|
1371
|
+
if (bundleGroupA.has(bundleId) && bundleGroupB.has(bundleId)) {
|
|
1372
|
+
overlapSize += bundle.size;
|
|
1373
|
+
} else {
|
|
1374
|
+
overfetchSize += bundle.size;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
let overlapPercent = overlapSize / (overfetchSize + overlapSize);
|
|
1378
|
+
let bundleAParents = getBundleParents(bundleAId);
|
|
1379
|
+
let bundleBParents = getBundleParents(bundleBId);
|
|
1380
|
+
let sharedParentOverlap = 0;
|
|
1381
|
+
let sharedParentMismatch = 0;
|
|
1382
|
+
for (let bundleId of new Set([...bundleAParents, ...bundleBParents])) {
|
|
1383
|
+
if (bundleAParents.has(bundleId) && bundleBParents.has(bundleId)) {
|
|
1384
|
+
sharedParentOverlap++;
|
|
1385
|
+
} else {
|
|
1386
|
+
sharedParentMismatch++;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
let overfetchScore = overfetchSize / maxOverfetchSize;
|
|
1390
|
+
let sharedParentPercent = sharedParentOverlap / (sharedParentOverlap + sharedParentMismatch);
|
|
1391
|
+
let score = sharedParentPercent + overlapPercent + overfetchScore;
|
|
1392
|
+
return {
|
|
1393
|
+
overfetchSize,
|
|
1394
|
+
score,
|
|
1395
|
+
bundleIds: [bundleAId, bundleBId]
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
function getBundleParents(bundleId) {
|
|
1399
|
+
let parents = new Set();
|
|
1400
|
+
let {
|
|
1401
|
+
bundleRoots
|
|
1402
|
+
} = getBundle(bundleId);
|
|
1403
|
+
for (let bundleRoot of bundleRoots) {
|
|
1404
|
+
let bundleRootNodeId = (0, _nullthrows().default)(assetToBundleRootNodeId.get(bundleRoot));
|
|
1405
|
+
for (let parentId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootNodeId, _graph().ALL_EDGE_TYPES)) {
|
|
1406
|
+
parents.add(parentId);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
return parents;
|
|
1410
|
+
}
|
|
980
1411
|
function getBundleFromBundleRoot(bundleRoot) {
|
|
981
1412
|
let bundle = bundleGraph.getNode((0, _nullthrows().default)(bundleRoots.get(bundleRoot))[0]);
|
|
982
1413
|
(0, _assert().default)(bundle !== 'root' && bundle != null);
|
|
@@ -1026,6 +1457,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
1026
1457
|
}
|
|
1027
1458
|
bundleGraph.removeNode(bundleId);
|
|
1028
1459
|
}
|
|
1460
|
+
stats.report(bundleId => {
|
|
1461
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1462
|
+
(0, _assert().default)(bundle !== 'root');
|
|
1463
|
+
return bundle;
|
|
1464
|
+
});
|
|
1029
1465
|
return {
|
|
1030
1466
|
assets,
|
|
1031
1467
|
bundleGraph,
|
|
@@ -1042,6 +1478,7 @@ function createBundle(opts) {
|
|
|
1042
1478
|
bundleBehavior: opts.bundleBehavior,
|
|
1043
1479
|
env: (0, _nullthrows().default)(opts.env),
|
|
1044
1480
|
mainEntryAsset: null,
|
|
1481
|
+
bundleRoots: new Set(),
|
|
1045
1482
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1046
1483
|
needsStableName: Boolean(opts.needsStableName),
|
|
1047
1484
|
size: 0,
|
|
@@ -1057,6 +1494,7 @@ function createBundle(opts) {
|
|
|
1057
1494
|
bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
|
|
1058
1495
|
env: opts.env ?? asset.env,
|
|
1059
1496
|
mainEntryAsset: asset,
|
|
1497
|
+
bundleRoots: new Set([asset]),
|
|
1060
1498
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1061
1499
|
needsStableName: Boolean(opts.needsStableName),
|
|
1062
1500
|
size: asset.stats.size,
|