@atlaspack/bundler-default 2.14.5-canary.19 → 2.14.5-canary.190
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 +384 -0
- package/lib/DefaultBundler.js +6 -1
- package/lib/MonolithicBundler.js +11 -3
- package/lib/bundleMerge.js +106 -37
- package/lib/bundlerConfig.js +51 -5
- package/lib/decorateLegacyGraph.js +24 -3
- package/lib/idealGraph.js +426 -50
- 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} +105 -44
- package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +26 -7
- package/src/{idealGraph.js → idealGraph.ts} +669 -102
- package/src/memoize.ts +32 -0
- package/src/stats.ts +97 -0
- package/tsconfig.json +4 -0
- package/src/bundleMerge.js +0 -103
package/lib/idealGraph.js
CHANGED
|
@@ -12,6 +12,13 @@ function _path() {
|
|
|
12
12
|
};
|
|
13
13
|
return data;
|
|
14
14
|
}
|
|
15
|
+
function _sortedArrayFunctions() {
|
|
16
|
+
const data = _interopRequireDefault(require("sorted-array-functions"));
|
|
17
|
+
_sortedArrayFunctions = function () {
|
|
18
|
+
return data;
|
|
19
|
+
};
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
15
22
|
function _featureFlags() {
|
|
16
23
|
const data = require("@atlaspack/feature-flags");
|
|
17
24
|
_featureFlags = function () {
|
|
@@ -48,8 +55,10 @@ function _nullthrows() {
|
|
|
48
55
|
return data;
|
|
49
56
|
}
|
|
50
57
|
var _bundleMerge = require("./bundleMerge");
|
|
58
|
+
var _stats = require("./stats");
|
|
51
59
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
52
60
|
/* BundleRoot - An asset that is the main entry of a Bundle. */
|
|
61
|
+
|
|
53
62
|
const dependencyPriorityEdges = {
|
|
54
63
|
sync: 1,
|
|
55
64
|
parallel: 2,
|
|
@@ -65,12 +74,19 @@ const idealBundleGraphEdges = exports.idealBundleGraphEdges = Object.freeze({
|
|
|
65
74
|
// which mutates the assetGraph into the bundleGraph we would
|
|
66
75
|
// expect from default bundler
|
|
67
76
|
|
|
77
|
+
function isNonRootBundle(bundle, message) {
|
|
78
|
+
let existingBundle = (0, _nullthrows().default)(bundle, message);
|
|
79
|
+
(0, _assert().default)(existingBundle !== 'root', "Bundle cannot be 'root'");
|
|
80
|
+
return existingBundle;
|
|
81
|
+
}
|
|
68
82
|
function createIdealGraph(assetGraph, config, entries, logger) {
|
|
83
|
+
var _config$sharedBundleM;
|
|
69
84
|
// Asset to the bundle and group it's an entry of
|
|
70
85
|
let bundleRoots = new Map();
|
|
71
86
|
let bundles = new Map();
|
|
72
87
|
let dependencyBundleGraph = new (_graph().ContentGraph)();
|
|
73
88
|
let assetReference = new (_utils().DefaultMap)(() => []);
|
|
89
|
+
let stats = new _stats.Stats(config.projectRoot);
|
|
74
90
|
|
|
75
91
|
// A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
|
|
76
92
|
// referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role.
|
|
@@ -108,15 +124,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
108
124
|
}
|
|
109
125
|
let assets = [];
|
|
110
126
|
let assetToIndex = new Map();
|
|
111
|
-
|
|
112
|
-
let manualSharedMap = new Map();
|
|
113
|
-
// May need a map to be able to look up NON- bundle root assets which need special case instructions
|
|
114
|
-
// Use this when placing assets into bundles, to avoid duplication
|
|
115
|
-
let manualAssetToBundle = new Map();
|
|
116
|
-
let {
|
|
117
|
-
manualAssetToConfig,
|
|
118
|
-
constantModuleToMSB
|
|
119
|
-
} = function makeManualAssetToConfigLookup() {
|
|
127
|
+
function makeManualAssetToConfigLookup() {
|
|
120
128
|
let manualAssetToConfig = new Map();
|
|
121
129
|
let constantModuleToMSB = new (_utils().DefaultMap)(() => []);
|
|
122
130
|
if (config.manualSharedBundles.length === 0) {
|
|
@@ -128,6 +136,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
128
136
|
let parentsToConfig = new (_utils().DefaultMap)(() => []);
|
|
129
137
|
for (let c of config.manualSharedBundles) {
|
|
130
138
|
if (c.root != null) {
|
|
139
|
+
// @ts-expect-error TS2345
|
|
131
140
|
parentsToConfig.get(_path().default.join(config.projectRoot, c.root)).push(c);
|
|
132
141
|
}
|
|
133
142
|
}
|
|
@@ -160,17 +169,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
160
169
|
assetGraph.traverse((node, _, actions) => {
|
|
161
170
|
if (node.type === 'asset' && (!Array.isArray(c.types) || c.types.includes(node.value.type))) {
|
|
162
171
|
let projectRelativePath = _path().default.relative(config.projectRoot, node.value.filePath);
|
|
163
|
-
if (!assetRegexes.some(regex => regex.test(projectRelativePath))) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
172
|
|
|
167
173
|
// We track all matching MSB's for constant modules as they are never duplicated
|
|
168
174
|
// and need to be assigned to all matching bundles
|
|
169
175
|
if (node.value.meta.isConstantModule === true) {
|
|
176
|
+
// @ts-expect-error TS2345
|
|
170
177
|
constantModuleToMSB.get(node.value).push(c);
|
|
171
178
|
}
|
|
172
|
-
|
|
173
|
-
|
|
179
|
+
if (assetRegexes.some(regex => regex.test(projectRelativePath))) {
|
|
180
|
+
manualAssetToConfig.set(node.value, c);
|
|
181
|
+
}
|
|
174
182
|
}
|
|
175
183
|
if (node.type === 'dependency' && (node.value.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional') && parentAsset) {
|
|
176
184
|
// Don't walk past the bundle group assets
|
|
@@ -182,8 +190,23 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
182
190
|
manualAssetToConfig,
|
|
183
191
|
constantModuleToMSB
|
|
184
192
|
};
|
|
185
|
-
}
|
|
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();
|
|
186
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
|
+
}));
|
|
187
210
|
|
|
188
211
|
/**
|
|
189
212
|
* Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
|
|
@@ -191,7 +214,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
191
214
|
* adding only that asset to each bundle, not its entire subgraph.
|
|
192
215
|
*/
|
|
193
216
|
assetGraph.traverse({
|
|
194
|
-
enter(node, context,
|
|
217
|
+
enter(node, context,
|
|
218
|
+
// @ts-expect-error TS2304
|
|
219
|
+
actions) {
|
|
195
220
|
if (node.type === 'asset') {
|
|
196
221
|
if ((context === null || context === void 0 ? void 0 : context.type) === 'dependency' && context !== null && context !== void 0 && context.value.isEntry && !entries.has(node.value)) {
|
|
197
222
|
// Skip whole subtrees of other targets by skipping those entries
|
|
@@ -234,15 +259,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
234
259
|
manualSharedBundleKey != null && manualSharedMap.has(manualSharedBundleKey)) {
|
|
235
260
|
bundleId = (0, _nullthrows().default)(manualSharedMap.get(manualSharedBundleKey));
|
|
236
261
|
}
|
|
237
|
-
if (dependency.priority === 'lazy' || (0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && node.value.priority === 'conditional' || childAsset.bundleBehavior === 'isolated'
|
|
238
|
-
|
|
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') {
|
|
239
265
|
if (bundleId == null) {
|
|
240
266
|
let firstBundleGroup = (0, _nullthrows().default)(bundleGraph.getNode(stack[0][1]));
|
|
241
267
|
(0, _assert().default)(firstBundleGroup !== 'root');
|
|
242
268
|
bundle = createBundle({
|
|
243
269
|
asset: childAsset,
|
|
244
270
|
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
245
|
-
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,
|
|
246
272
|
target: firstBundleGroup.target
|
|
247
273
|
});
|
|
248
274
|
bundleId = bundleGraph.addNode(bundle);
|
|
@@ -250,6 +276,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
250
276
|
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
251
277
|
bundleGroupBundleIds.add(bundleId);
|
|
252
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
|
+
}
|
|
253
284
|
if (manualSharedObject) {
|
|
254
285
|
// MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
|
|
255
286
|
// since MSBs should not have main entry assets
|
|
@@ -261,7 +292,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
261
292
|
if (
|
|
262
293
|
// If this dependency requests isolated, but the bundle is not,
|
|
263
294
|
// make the bundle isolated for all uses.
|
|
264
|
-
dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
|
|
295
|
+
(dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
|
|
265
296
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
266
297
|
}
|
|
267
298
|
}
|
|
@@ -271,7 +302,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
271
302
|
}), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
272
303
|
value: bundle,
|
|
273
304
|
type: 'bundle'
|
|
274
|
-
}),
|
|
305
|
+
}),
|
|
306
|
+
// @ts-expect-error TS7053
|
|
307
|
+
dependencyPriorityEdges[dependency.priority]);
|
|
275
308
|
if ((0, _featureFlags().getFeatureFlag)('conditionalBundlingApi') && dependency.priority === 'conditional') {
|
|
276
309
|
let [referencingBundleRoot, bundleGroupNodeId] = (0, _nullthrows().default)(stack[stack.length - 1]);
|
|
277
310
|
let referencingBundleId = (0, _nullthrows().default)(bundleRoots.get(referencingBundleRoot))[0];
|
|
@@ -300,7 +333,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
300
333
|
env: childAsset.env,
|
|
301
334
|
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
302
335
|
target: referencingBundle.target,
|
|
303
|
-
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
|
|
304
337
|
});
|
|
305
338
|
bundleId = bundleGraph.addNode(bundle);
|
|
306
339
|
} else {
|
|
@@ -309,7 +342,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
309
342
|
if (
|
|
310
343
|
// If this dependency requests isolated, but the bundle is not,
|
|
311
344
|
// make the bundle isolated for all uses.
|
|
312
|
-
dependency.bundleBehavior === 'isolated' && bundle.bundleBehavior == null) {
|
|
345
|
+
(dependency.bundleBehavior === 'isolated' || dependency.bundleBehavior === 'inlineIsolated') && bundle.bundleBehavior == null) {
|
|
313
346
|
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
314
347
|
}
|
|
315
348
|
}
|
|
@@ -331,13 +364,16 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
331
364
|
}
|
|
332
365
|
assetReference.get(childAsset).push([dependency, bundle]);
|
|
333
366
|
} else {
|
|
367
|
+
// @ts-expect-error TS2322
|
|
334
368
|
bundleId = null;
|
|
335
369
|
}
|
|
336
370
|
if (manualSharedObject && bundleId != null) {
|
|
337
371
|
// MSB Step 5: At this point we've either created or found an existing MSB bundle
|
|
338
372
|
// add the asset if it doesn't already have it and set key
|
|
339
373
|
|
|
340
|
-
(0, _assert().default)(
|
|
374
|
+
(0, _assert().default)(
|
|
375
|
+
// @ts-expect-error TS2367
|
|
376
|
+
bundle !== 'root' && bundle != null && bundleId != null);
|
|
341
377
|
manualAssetToBundle.set(childAsset, bundleId);
|
|
342
378
|
if (!bundle.assets.has(childAsset)) {
|
|
343
379
|
// Add asset to bundle
|
|
@@ -358,6 +394,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
358
394
|
}
|
|
359
395
|
return node;
|
|
360
396
|
},
|
|
397
|
+
// @ts-expect-error TS2322
|
|
361
398
|
exit(node) {
|
|
362
399
|
var _stack;
|
|
363
400
|
if (((_stack = stack[stack.length - 1]) === null || _stack === void 0 ? void 0 : _stack[0]) === node.value) {
|
|
@@ -495,7 +532,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
495
532
|
// not true that a bundle's available assets = all assets of all the bundleGroups
|
|
496
533
|
// it belongs to. It's the intersection of those sets.
|
|
497
534
|
let available;
|
|
498
|
-
if (bundleRoot.bundleBehavior === 'isolated') {
|
|
535
|
+
if (bundleRoot.bundleBehavior === 'isolated' || bundleRoot.bundleBehavior === 'inlineIsolated') {
|
|
499
536
|
available = new (_graph().BitSet)(assets.length);
|
|
500
537
|
} else {
|
|
501
538
|
available = (0, _nullthrows().default)(ancestorAssets[nodeId]).clone();
|
|
@@ -558,7 +595,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
558
595
|
continue;
|
|
559
596
|
}
|
|
560
597
|
let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, _graph().ALL_EDGE_TYPES);
|
|
561
|
-
let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated';
|
|
598
|
+
let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated' && getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'inlineIsolated';
|
|
562
599
|
if (parentRoots.length === 0) continue;
|
|
563
600
|
for (let parentId of parentRoots) {
|
|
564
601
|
var _ancestorAssets$paren;
|
|
@@ -586,8 +623,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
586
623
|
}
|
|
587
624
|
function assignInlineConstants(parentAsset, bundle) {
|
|
588
625
|
for (let inlineConstant of inlineConstantDeps.get(parentAsset)) {
|
|
626
|
+
// @ts-expect-error TS2345
|
|
589
627
|
if (!bundle.assets.has(inlineConstant)) {
|
|
628
|
+
// @ts-expect-error TS2345
|
|
590
629
|
bundle.assets.add(inlineConstant);
|
|
630
|
+
// @ts-expect-error TS18046
|
|
591
631
|
bundle.size += inlineConstant.stats.size;
|
|
592
632
|
}
|
|
593
633
|
}
|
|
@@ -623,7 +663,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
623
663
|
let assetId = bundleRootGraph.getNode(nodeId);
|
|
624
664
|
if (assetId == null) return; // deleted
|
|
625
665
|
let a = assets[assetId];
|
|
626
|
-
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')) {
|
|
627
667
|
// Add asset to non-splittable bundles.
|
|
628
668
|
addAssetToBundleRoot(asset, a);
|
|
629
669
|
} else if (!((_ancestorAssets$nodeI = ancestorAssets[nodeId]) !== null && _ancestorAssets$nodeI !== void 0 && _ancestorAssets$nodeI.has(i))) {
|
|
@@ -785,6 +825,8 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
785
825
|
}
|
|
786
826
|
}
|
|
787
827
|
let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
|
|
828
|
+
let modifiedSourceBundles = new Set();
|
|
829
|
+
|
|
788
830
|
// Step split manual shared bundles for those that have the "split" property set
|
|
789
831
|
let remainderMap = new (_utils().DefaultMap)(() => []);
|
|
790
832
|
for (let id of manualSharedMap.values()) {
|
|
@@ -801,8 +843,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
801
843
|
if (modNum != null) {
|
|
802
844
|
for (let a of [...manualBundle.assets]) {
|
|
803
845
|
let numRep = getBigIntFromContentKey(a.id);
|
|
804
|
-
// $FlowFixMe Flow doesn't know about BigInt
|
|
805
846
|
let r = Number(numRep % BigInt(modNum));
|
|
847
|
+
|
|
848
|
+
// @ts-expect-error TS2345
|
|
806
849
|
remainderMap.get(r).push(a);
|
|
807
850
|
}
|
|
808
851
|
for (let i = 1; i < [...remainderMap.keys()].length; i++) {
|
|
@@ -824,8 +867,10 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
824
867
|
}
|
|
825
868
|
for (let sp of remainderMap.get(i)) {
|
|
826
869
|
bundle.assets.add(sp);
|
|
870
|
+
// @ts-expect-error TS2339
|
|
827
871
|
bundle.size += sp.stats.size;
|
|
828
872
|
manualBundle.assets.delete(sp);
|
|
873
|
+
// @ts-expect-error TS2339
|
|
829
874
|
manualBundle.size -= sp.stats.size;
|
|
830
875
|
}
|
|
831
876
|
}
|
|
@@ -838,21 +883,80 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
838
883
|
// match multiple MSB's
|
|
839
884
|
for (let [asset, msbs] of constantModuleToMSB.entries()) {
|
|
840
885
|
for (let manualSharedObject of msbs) {
|
|
886
|
+
// @ts-expect-error TS2339
|
|
841
887
|
let bundleId = manualSharedMap.get(manualSharedObject.name + ',js');
|
|
842
888
|
if (bundleId == null) continue;
|
|
843
889
|
let bundle = (0, _nullthrows().default)(bundleGraph.getNode(bundleId));
|
|
844
890
|
(0, _assert().default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly');
|
|
891
|
+
|
|
892
|
+
// @ts-expect-error TS2345
|
|
845
893
|
if (!bundle.assets.has(asset)) {
|
|
894
|
+
// @ts-expect-error TS2345
|
|
846
895
|
bundle.assets.add(asset);
|
|
896
|
+
// @ts-expect-error TS18046
|
|
847
897
|
bundle.size += asset.stats.size;
|
|
848
898
|
}
|
|
849
899
|
}
|
|
850
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
|
+
}
|
|
851
956
|
|
|
852
|
-
// Step merge shared bundles that meet the
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
mergeOverlapBundles();
|
|
957
|
+
// Step merge shared bundles that meet the configured params
|
|
958
|
+
if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
|
|
959
|
+
mergeSharedBundles(config.sharedBundleMerge);
|
|
856
960
|
}
|
|
857
961
|
|
|
858
962
|
// Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
|
|
@@ -866,7 +970,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
866
970
|
}
|
|
867
971
|
|
|
868
972
|
// Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
|
|
869
|
-
|
|
973
|
+
|
|
870
974
|
if (config.disableSharedBundles === false) {
|
|
871
975
|
for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
|
|
872
976
|
// Find shared bundles in this bundle group.
|
|
@@ -879,7 +983,7 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
879
983
|
let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
|
|
880
984
|
let bundle = (0, _nullthrows().default)(bundleGraph.getNode(b));
|
|
881
985
|
(0, _assert().default)(bundle !== 'root');
|
|
882
|
-
return count + (bundle.bundleBehavior !== 'inline');
|
|
986
|
+
return count + Number(bundle.bundleBehavior !== 'inline' && bundle.bundleBehavior !== 'inlineIsolated');
|
|
883
987
|
}, 0);
|
|
884
988
|
if (numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
885
989
|
let sharedBundleIdsInBundleGroup = bundleIdsInGroup.filter(b => {
|
|
@@ -908,7 +1012,9 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
908
1012
|
// Remove bundles until the bundle group is within the parallel request limit.
|
|
909
1013
|
while (sharedBundlesInGroup.length > 0 && numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
910
1014
|
let bundleTuple = sharedBundlesInGroup.pop();
|
|
1015
|
+
// @ts-expect-error TS18048
|
|
911
1016
|
let bundleToRemove = bundleTuple.bundle;
|
|
1017
|
+
// @ts-expect-error TS18048
|
|
912
1018
|
let bundleIdToRemove = bundleTuple.id;
|
|
913
1019
|
//TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
|
|
914
1020
|
// but total # bundles still exceeds limit due to non shared bundles
|
|
@@ -951,35 +1057,133 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
951
1057
|
}
|
|
952
1058
|
}
|
|
953
1059
|
}
|
|
954
|
-
function mergeBundles(
|
|
955
|
-
|
|
956
|
-
let
|
|
957
|
-
|
|
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);
|
|
958
1065
|
for (let asset of bundleToRemove.assets) {
|
|
959
1066
|
bundleToKeep.assets.add(asset);
|
|
960
1067
|
bundleToKeep.size += asset.stats.size;
|
|
961
1068
|
let newAssetReference = assetReference.get(asset).map(([dep, bundle]) => bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle]);
|
|
1069
|
+
|
|
1070
|
+
// @ts-expect-error TS2345
|
|
962
1071
|
assetReference.set(asset, newAssetReference);
|
|
963
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
|
|
964
1089
|
for (let sourceBundleId of bundleToRemove.sourceBundles) {
|
|
965
1090
|
if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
|
|
966
1091
|
continue;
|
|
967
1092
|
}
|
|
968
|
-
|
|
969
|
-
|
|
1093
|
+
if (sourceBundleId !== bundleToKeepId) {
|
|
1094
|
+
bundleToKeep.sourceBundles.add(sourceBundleId);
|
|
1095
|
+
bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
|
|
1096
|
+
}
|
|
970
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
|
+
}
|
|
971
1105
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
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
|
+
}
|
|
978
1182
|
}
|
|
979
1183
|
}
|
|
980
1184
|
bundleGraph.removeNode(bundleToRemoveId);
|
|
981
1185
|
}
|
|
982
|
-
function
|
|
1186
|
+
function mergeSharedBundles(mergeConfig) {
|
|
983
1187
|
// Find all shared bundles
|
|
984
1188
|
let sharedBundles = new Set();
|
|
985
1189
|
bundleGraph.traverse(nodeId => {
|
|
@@ -998,18 +1202,136 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
998
1202
|
sharedBundles.add(nodeId);
|
|
999
1203
|
}
|
|
1000
1204
|
});
|
|
1001
|
-
let clusters = (0, _bundleMerge.findMergeCandidates)(bundleGraph, Array.from(sharedBundles), config
|
|
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();
|
|
1002
1219
|
for (let cluster of clusters) {
|
|
1003
1220
|
let [mergeTarget, ...rest] = cluster;
|
|
1004
1221
|
for (let bundleIdToMerge of rest) {
|
|
1005
|
-
mergeBundles(
|
|
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));
|
|
1006
1259
|
}
|
|
1007
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;
|
|
1008
1331
|
}
|
|
1009
1332
|
function getBigIntFromContentKey(contentKey) {
|
|
1010
1333
|
let b = Buffer.alloc(64);
|
|
1011
1334
|
b.write(contentKey);
|
|
1012
|
-
// $FlowFixMe Flow doesn't have BigInt types in this version
|
|
1013
1335
|
return b.readBigInt64BE();
|
|
1014
1336
|
}
|
|
1015
1337
|
// Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
|
|
@@ -1039,6 +1361,53 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
1039
1361
|
}, bundleGroupId);
|
|
1040
1362
|
return bundlesInABundleGroup;
|
|
1041
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
|
+
}
|
|
1042
1411
|
function getBundleFromBundleRoot(bundleRoot) {
|
|
1043
1412
|
let bundle = bundleGraph.getNode((0, _nullthrows().default)(bundleRoots.get(bundleRoot))[0]);
|
|
1044
1413
|
(0, _assert().default)(bundle !== 'root' && bundle != null);
|
|
@@ -1088,6 +1457,11 @@ function createIdealGraph(assetGraph, config, entries, logger) {
|
|
|
1088
1457
|
}
|
|
1089
1458
|
bundleGraph.removeNode(bundleId);
|
|
1090
1459
|
}
|
|
1460
|
+
stats.report(bundleId => {
|
|
1461
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1462
|
+
(0, _assert().default)(bundle !== 'root');
|
|
1463
|
+
return bundle;
|
|
1464
|
+
});
|
|
1091
1465
|
return {
|
|
1092
1466
|
assets,
|
|
1093
1467
|
bundleGraph,
|
|
@@ -1104,6 +1478,7 @@ function createBundle(opts) {
|
|
|
1104
1478
|
bundleBehavior: opts.bundleBehavior,
|
|
1105
1479
|
env: (0, _nullthrows().default)(opts.env),
|
|
1106
1480
|
mainEntryAsset: null,
|
|
1481
|
+
bundleRoots: new Set(),
|
|
1107
1482
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1108
1483
|
needsStableName: Boolean(opts.needsStableName),
|
|
1109
1484
|
size: 0,
|
|
@@ -1119,6 +1494,7 @@ function createBundle(opts) {
|
|
|
1119
1494
|
bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
|
|
1120
1495
|
env: opts.env ?? asset.env,
|
|
1121
1496
|
mainEntryAsset: asset,
|
|
1497
|
+
bundleRoots: new Set([asset]),
|
|
1122
1498
|
manualSharedBundle: opts.manualSharedBundle,
|
|
1123
1499
|
needsStableName: Boolean(opts.needsStableName),
|
|
1124
1500
|
size: asset.stats.size,
|