@atlaspack/bundler-default 2.14.5-canary.36 → 2.14.5-canary.361
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 +613 -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 +106 -37
- package/lib/bundlerConfig.js +52 -6
- package/lib/decorateLegacyGraph.js +24 -3
- package/lib/idealGraph.js +410 -55
- 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} +106 -45
- package/src/{decorateLegacyGraph.js → decorateLegacyGraph.ts} +26 -7
- package/src/{idealGraph.js → idealGraph.ts} +729 -137
- package/src/memoize.ts +32 -0
- package/src/stats.ts +97 -0
- package/tsconfig.json +30 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/src/bundleMerge.js +0 -103
|
@@ -0,0 +1,1471 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.idealBundleGraphEdges = void 0;
|
|
7
|
+
exports.createIdealGraph = createIdealGraph;
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const sorted_array_functions_1 = __importDefault(require("sorted-array-functions"));
|
|
10
|
+
const feature_flags_1 = require("@atlaspack/feature-flags");
|
|
11
|
+
const graph_1 = require("@atlaspack/graph");
|
|
12
|
+
const utils_1 = require("@atlaspack/utils");
|
|
13
|
+
const assert_1 = __importDefault(require("assert"));
|
|
14
|
+
const nullthrows_1 = __importDefault(require("nullthrows"));
|
|
15
|
+
const bundleMerge_1 = require("./bundleMerge");
|
|
16
|
+
const stats_1 = require("./stats");
|
|
17
|
+
const dependencyPriorityEdges = {
|
|
18
|
+
sync: 1,
|
|
19
|
+
parallel: 2,
|
|
20
|
+
lazy: 3,
|
|
21
|
+
conditional: 4,
|
|
22
|
+
};
|
|
23
|
+
exports.idealBundleGraphEdges = Object.freeze({
|
|
24
|
+
default: 1,
|
|
25
|
+
conditional: 2,
|
|
26
|
+
});
|
|
27
|
+
function isNonRootBundle(bundle, message) {
|
|
28
|
+
let existingBundle = (0, nullthrows_1.default)(bundle, message);
|
|
29
|
+
(0, assert_1.default)(existingBundle !== 'root', "Bundle cannot be 'root'");
|
|
30
|
+
return existingBundle;
|
|
31
|
+
}
|
|
32
|
+
function createIdealGraph(assetGraph, config, entries, logger) {
|
|
33
|
+
// Asset to the bundle and group it's an entry of
|
|
34
|
+
let bundleRoots = new Map();
|
|
35
|
+
let bundles = new Map();
|
|
36
|
+
let dependencyBundleGraph = new graph_1.ContentGraph();
|
|
37
|
+
let assetReference = new utils_1.DefaultMap(() => []);
|
|
38
|
+
let stats = new stats_1.Stats(config.projectRoot);
|
|
39
|
+
// A Graph of Bundles and a root node (dummy string), which models only Bundles, and connections to their
|
|
40
|
+
// referencing Bundle. There are no actual BundleGroup nodes, just bundles that take on that role.
|
|
41
|
+
let bundleGraph = new graph_1.Graph();
|
|
42
|
+
let stack = [];
|
|
43
|
+
let bundleRootEdgeTypes = {
|
|
44
|
+
parallel: 1,
|
|
45
|
+
lazy: 2,
|
|
46
|
+
};
|
|
47
|
+
// Graph that models bundleRoots, with parallel & async deps only to inform reachability
|
|
48
|
+
let bundleRootGraph = new graph_1.Graph();
|
|
49
|
+
let assetToBundleRootNodeId = new Map();
|
|
50
|
+
let bundleGroupBundleIds = new Set();
|
|
51
|
+
let bundleGraphRootNodeId = (0, nullthrows_1.default)(bundleGraph.addNode('root'));
|
|
52
|
+
bundleGraph.setRootNodeId(bundleGraphRootNodeId);
|
|
53
|
+
// Step Create Entry Bundles
|
|
54
|
+
for (let [asset, dependency] of entries) {
|
|
55
|
+
let bundle = createBundle({
|
|
56
|
+
asset,
|
|
57
|
+
target: (0, nullthrows_1.default)(dependency.target),
|
|
58
|
+
needsStableName: dependency.isEntry,
|
|
59
|
+
});
|
|
60
|
+
let nodeId = bundleGraph.addNode(bundle);
|
|
61
|
+
bundles.set(asset.id, nodeId);
|
|
62
|
+
bundleRoots.set(asset, [nodeId, nodeId]);
|
|
63
|
+
bundleGraph.addEdge(bundleGraphRootNodeId, nodeId);
|
|
64
|
+
dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, {
|
|
65
|
+
value: dependency,
|
|
66
|
+
type: 'dependency',
|
|
67
|
+
}), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(nodeId), {
|
|
68
|
+
value: bundle,
|
|
69
|
+
type: 'bundle',
|
|
70
|
+
}), dependencyPriorityEdges[dependency.priority]);
|
|
71
|
+
bundleGroupBundleIds.add(nodeId);
|
|
72
|
+
}
|
|
73
|
+
let assets = [];
|
|
74
|
+
let assetToIndex = new Map();
|
|
75
|
+
function makeManualAssetToConfigLookup() {
|
|
76
|
+
let manualAssetToConfig = new Map();
|
|
77
|
+
let constantModuleToMSB = new utils_1.DefaultMap(() => []);
|
|
78
|
+
if (config.manualSharedBundles.length === 0) {
|
|
79
|
+
return { manualAssetToConfig, constantModuleToMSB };
|
|
80
|
+
}
|
|
81
|
+
let parentsToConfig = new utils_1.DefaultMap(() => []);
|
|
82
|
+
for (let c of config.manualSharedBundles) {
|
|
83
|
+
if (c.root != null) {
|
|
84
|
+
parentsToConfig.get(path_1.default.join(config.projectRoot, c.root)).push(c);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let numParentsToFind = parentsToConfig.size;
|
|
88
|
+
let configToParentAsset = new Map();
|
|
89
|
+
assetGraph.traverse((node, _, actions) => {
|
|
90
|
+
if (node.type === 'asset' && parentsToConfig.has(node.value.filePath)) {
|
|
91
|
+
for (let c of parentsToConfig.get(node.value.filePath)) {
|
|
92
|
+
configToParentAsset.set(c, node.value);
|
|
93
|
+
}
|
|
94
|
+
numParentsToFind--;
|
|
95
|
+
if (numParentsToFind === 0) {
|
|
96
|
+
// If we've found all parents we can stop traversal
|
|
97
|
+
actions.stop();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
// Process in reverse order so earlier configs take precedence
|
|
102
|
+
for (let c of config.manualSharedBundles.reverse()) {
|
|
103
|
+
if (c.root != null && !configToParentAsset.has(c)) {
|
|
104
|
+
logger.warn({
|
|
105
|
+
origin: '@atlaspack/bundler-default',
|
|
106
|
+
message: `Manual shared bundle "${c.name}" skipped, no root asset found`,
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
let parentAsset = configToParentAsset.get(c);
|
|
111
|
+
let assetRegexes = c.assets.map((glob) => (0, utils_1.globToRegex)(glob));
|
|
112
|
+
assetGraph.traverse((node, _, actions) => {
|
|
113
|
+
if (node.type === 'asset' &&
|
|
114
|
+
(!Array.isArray(c.types) || c.types.includes(node.value.type))) {
|
|
115
|
+
let projectRelativePath = path_1.default.relative(config.projectRoot, node.value.filePath);
|
|
116
|
+
// We track all matching MSB's for constant modules as they are never duplicated
|
|
117
|
+
// and need to be assigned to all matching bundles
|
|
118
|
+
if (node.value.meta.isConstantModule === true) {
|
|
119
|
+
constantModuleToMSB.get(node.value).push(c);
|
|
120
|
+
}
|
|
121
|
+
if (assetRegexes.some((regex) => regex.test(projectRelativePath))) {
|
|
122
|
+
manualAssetToConfig.set(node.value, c);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (node.type === 'dependency' &&
|
|
126
|
+
(node.value.priority === 'lazy' ||
|
|
127
|
+
((0, feature_flags_1.getFeatureFlag)('conditionalBundlingApi') &&
|
|
128
|
+
node.value.priority === 'conditional')) &&
|
|
129
|
+
parentAsset) {
|
|
130
|
+
// Don't walk past the bundle group assets
|
|
131
|
+
actions.skipChildren();
|
|
132
|
+
}
|
|
133
|
+
}, parentAsset);
|
|
134
|
+
}
|
|
135
|
+
return { manualAssetToConfig, constantModuleToMSB };
|
|
136
|
+
}
|
|
137
|
+
//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
|
|
138
|
+
let manualSharedMap = new Map();
|
|
139
|
+
// May need a map to be able to look up NON- bundle root assets which need special case instructions
|
|
140
|
+
// Use this when placing assets into bundles, to avoid duplication
|
|
141
|
+
let manualAssetToBundle = new Map();
|
|
142
|
+
let { manualAssetToConfig, constantModuleToMSB } = makeManualAssetToConfigLookup();
|
|
143
|
+
let manualBundleToInternalizedAsset = new utils_1.DefaultMap(() => []);
|
|
144
|
+
let mergeSourceBundleLookup = new Map();
|
|
145
|
+
let mergeSourceBundleAssets = new Set(config.sharedBundleMerge?.flatMap((c) => c.sourceBundles?.map((assetMatch) => path_1.default.join(config.projectRoot, assetMatch)) ?? []));
|
|
146
|
+
/**
|
|
147
|
+
* Step Create Bundles: Traverse the assetGraph (aka MutableBundleGraph) and create bundles
|
|
148
|
+
* for asset type changes, parallel, inline, and async or lazy dependencies,
|
|
149
|
+
* adding only that asset to each bundle, not its entire subgraph.
|
|
150
|
+
*/
|
|
151
|
+
assetGraph.traverse({
|
|
152
|
+
enter(node, context, actions) {
|
|
153
|
+
if (node.type === 'asset') {
|
|
154
|
+
if (context?.type === 'dependency' &&
|
|
155
|
+
context?.value.isEntry &&
|
|
156
|
+
!entries.has(node.value)) {
|
|
157
|
+
// Skip whole subtrees of other targets by skipping those entries
|
|
158
|
+
actions.skipChildren();
|
|
159
|
+
return node;
|
|
160
|
+
}
|
|
161
|
+
assetToIndex.set(node.value, assets.length);
|
|
162
|
+
assets.push(node.value);
|
|
163
|
+
let bundleIdTuple = bundleRoots.get(node.value);
|
|
164
|
+
if (bundleIdTuple && bundleIdTuple[0] === bundleIdTuple[1]) {
|
|
165
|
+
// Push to the stack (only) when a new bundle is created
|
|
166
|
+
stack.push([node.value, bundleIdTuple[0]]);
|
|
167
|
+
}
|
|
168
|
+
else if (bundleIdTuple) {
|
|
169
|
+
// Otherwise, push on the last bundle that marks the start of a BundleGroup
|
|
170
|
+
stack.push([node.value, stack[stack.length - 1][1]]);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (node.type === 'dependency') {
|
|
174
|
+
if (context == null) {
|
|
175
|
+
return node;
|
|
176
|
+
}
|
|
177
|
+
let dependency = node.value;
|
|
178
|
+
(0, assert_1.default)(context?.type === 'asset');
|
|
179
|
+
let assets = assetGraph.getDependencyAssets(dependency);
|
|
180
|
+
if (assets.length === 0) {
|
|
181
|
+
return node;
|
|
182
|
+
}
|
|
183
|
+
for (let childAsset of assets) {
|
|
184
|
+
let bundleId = bundles.get(childAsset.id);
|
|
185
|
+
let bundle;
|
|
186
|
+
// MSB Step 1: Match glob on filepath and type for any asset
|
|
187
|
+
let manualSharedBundleKey;
|
|
188
|
+
let manualSharedObject = manualAssetToConfig.get(childAsset);
|
|
189
|
+
if (manualSharedObject) {
|
|
190
|
+
// MSB Step 2: Generate a key for which to look up this manual bundle with
|
|
191
|
+
manualSharedBundleKey =
|
|
192
|
+
manualSharedObject.name + ',' + childAsset.type;
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
// MSB Step 3: If a bundle for these globs already exists, use it
|
|
196
|
+
manualSharedBundleKey != null &&
|
|
197
|
+
manualSharedMap.has(manualSharedBundleKey)) {
|
|
198
|
+
bundleId = (0, nullthrows_1.default)(manualSharedMap.get(manualSharedBundleKey));
|
|
199
|
+
}
|
|
200
|
+
if (dependency.priority === 'lazy' ||
|
|
201
|
+
((0, feature_flags_1.getFeatureFlag)('conditionalBundlingApi') &&
|
|
202
|
+
node.value.priority === 'conditional') ||
|
|
203
|
+
childAsset.bundleBehavior === 'isolated' || // An isolated Dependency, or Bundle must contain all assets it needs to load.
|
|
204
|
+
childAsset.bundleBehavior === 'inlineIsolated') {
|
|
205
|
+
if (bundleId == null) {
|
|
206
|
+
let firstBundleGroup = (0, nullthrows_1.default)(bundleGraph.getNode(stack[0][1]));
|
|
207
|
+
(0, assert_1.default)(firstBundleGroup !== 'root');
|
|
208
|
+
bundle = createBundle({
|
|
209
|
+
asset: childAsset,
|
|
210
|
+
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
211
|
+
needsStableName: dependency.bundleBehavior === 'inline' ||
|
|
212
|
+
childAsset.bundleBehavior === 'inline' ||
|
|
213
|
+
dependency.bundleBehavior === 'inlineIsolated' ||
|
|
214
|
+
childAsset.bundleBehavior === 'inlineIsolated'
|
|
215
|
+
? false
|
|
216
|
+
: dependency.isEntry || dependency.needsStableName,
|
|
217
|
+
target: firstBundleGroup.target,
|
|
218
|
+
});
|
|
219
|
+
bundleId = bundleGraph.addNode(bundle);
|
|
220
|
+
bundles.set(childAsset.id, bundleId);
|
|
221
|
+
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
222
|
+
bundleGroupBundleIds.add(bundleId);
|
|
223
|
+
bundleGraph.addEdge(bundleGraphRootNodeId, bundleId);
|
|
224
|
+
// If this asset is relevant for merging then track it's source
|
|
225
|
+
// bundle id for later
|
|
226
|
+
if (mergeSourceBundleAssets.has(childAsset.filePath)) {
|
|
227
|
+
mergeSourceBundleLookup.set(path_1.default.relative(config.projectRoot, childAsset.filePath), bundleId);
|
|
228
|
+
}
|
|
229
|
+
if (manualSharedObject) {
|
|
230
|
+
// MSB Step 4: If this was the first instance of a match, mark mainAsset for internalization
|
|
231
|
+
// since MSBs should not have main entry assets
|
|
232
|
+
manualBundleToInternalizedAsset
|
|
233
|
+
.get(bundleId)
|
|
234
|
+
.push(childAsset);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
bundle = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
239
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
240
|
+
if (
|
|
241
|
+
// If this dependency requests isolated, but the bundle is not,
|
|
242
|
+
// make the bundle isolated for all uses.
|
|
243
|
+
(dependency.bundleBehavior === 'isolated' ||
|
|
244
|
+
dependency.bundleBehavior === 'inlineIsolated') &&
|
|
245
|
+
bundle.bundleBehavior == null) {
|
|
246
|
+
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, {
|
|
250
|
+
value: dependency,
|
|
251
|
+
type: 'dependency',
|
|
252
|
+
}), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
253
|
+
value: bundle,
|
|
254
|
+
type: 'bundle',
|
|
255
|
+
}), dependencyPriorityEdges[dependency.priority]);
|
|
256
|
+
if ((0, feature_flags_1.getFeatureFlag)('conditionalBundlingApi') &&
|
|
257
|
+
dependency.priority === 'conditional') {
|
|
258
|
+
let [referencingBundleRoot, bundleGroupNodeId] = (0, nullthrows_1.default)(stack[stack.length - 1]);
|
|
259
|
+
let referencingBundleId = (0, nullthrows_1.default)(bundleRoots.get(referencingBundleRoot))[0];
|
|
260
|
+
if (config.loadConditionalBundlesInParallel) {
|
|
261
|
+
// When configured, serve conditional bundles in parallel so we don't get module not found errors
|
|
262
|
+
bundleRoots.set(childAsset, [bundleId, bundleGroupNodeId]);
|
|
263
|
+
bundleGraph.addEdge(referencingBundleId, bundleId);
|
|
264
|
+
}
|
|
265
|
+
// Add conditional edge to track which bundles request each other
|
|
266
|
+
bundleGraph.addEdge(referencingBundleId, bundleId, exports.idealBundleGraphEdges.conditional);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
else if (dependency.priority === 'parallel' ||
|
|
270
|
+
childAsset.bundleBehavior === 'inline') {
|
|
271
|
+
// The referencing bundleRoot is the root of a Bundle that first brings in another bundle (essentially the FIRST parent of a bundle, this may or may not be a bundleGroup)
|
|
272
|
+
let [referencingBundleRoot, bundleGroupNodeId] = (0, nullthrows_1.default)(stack[stack.length - 1]);
|
|
273
|
+
let bundleGroup = (0, nullthrows_1.default)(bundleGraph.getNode(bundleGroupNodeId));
|
|
274
|
+
(0, assert_1.default)(bundleGroup !== 'root');
|
|
275
|
+
let referencingBundleId = (0, nullthrows_1.default)(bundleRoots.get(referencingBundleRoot))[0];
|
|
276
|
+
let referencingBundle = (0, nullthrows_1.default)(bundleGraph.getNode(referencingBundleId));
|
|
277
|
+
(0, assert_1.default)(referencingBundle !== 'root');
|
|
278
|
+
if (bundleId == null) {
|
|
279
|
+
bundle = createBundle({
|
|
280
|
+
// Bundles created from type changes shouldn't have an entry asset.
|
|
281
|
+
asset: childAsset,
|
|
282
|
+
type: childAsset.type,
|
|
283
|
+
env: childAsset.env,
|
|
284
|
+
bundleBehavior: dependency.bundleBehavior ?? childAsset.bundleBehavior,
|
|
285
|
+
target: referencingBundle.target,
|
|
286
|
+
needsStableName: childAsset.bundleBehavior === 'inline' ||
|
|
287
|
+
dependency.bundleBehavior === 'inline' ||
|
|
288
|
+
dependency.bundleBehavior === 'inlineIsolated' ||
|
|
289
|
+
(dependency.priority === 'parallel' &&
|
|
290
|
+
!dependency.needsStableName)
|
|
291
|
+
? false
|
|
292
|
+
: referencingBundle.needsStableName,
|
|
293
|
+
});
|
|
294
|
+
bundleId = bundleGraph.addNode(bundle);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
let bundleNode = bundleGraph.getNode(bundleId);
|
|
298
|
+
(0, assert_1.default)(bundleNode != null && bundleNode !== 'root');
|
|
299
|
+
bundle = bundleNode;
|
|
300
|
+
if (
|
|
301
|
+
// If this dependency requests isolated, but the bundle is not,
|
|
302
|
+
// make the bundle isolated for all uses.
|
|
303
|
+
(dependency.bundleBehavior === 'isolated' ||
|
|
304
|
+
dependency.bundleBehavior === 'inlineIsolated') &&
|
|
305
|
+
bundle.bundleBehavior == null) {
|
|
306
|
+
bundle.bundleBehavior = dependency.bundleBehavior;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
bundles.set(childAsset.id, bundleId);
|
|
310
|
+
// A bundle can belong to multiple bundlegroups, all the bundle groups of it's
|
|
311
|
+
// ancestors, and all async and entry bundles before it are "bundle groups"
|
|
312
|
+
// TODO: We may need to track bundles to all bundleGroups it belongs to in the future.
|
|
313
|
+
bundleRoots.set(childAsset, [bundleId, bundleGroupNodeId]);
|
|
314
|
+
bundleGraph.addEdge(referencingBundleId, bundleId);
|
|
315
|
+
if (bundleId != bundleGroupNodeId) {
|
|
316
|
+
dependencyBundleGraph.addEdge(dependencyBundleGraph.addNodeByContentKeyIfNeeded(dependency.id, {
|
|
317
|
+
value: dependency,
|
|
318
|
+
type: 'dependency',
|
|
319
|
+
}), dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
320
|
+
value: bundle,
|
|
321
|
+
type: 'bundle',
|
|
322
|
+
}), dependencyPriorityEdges.parallel);
|
|
323
|
+
}
|
|
324
|
+
assetReference.get(childAsset).push([dependency, bundle]);
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
bundleId = null;
|
|
328
|
+
}
|
|
329
|
+
if (manualSharedObject && bundleId != null) {
|
|
330
|
+
// MSB Step 5: At this point we've either created or found an existing MSB bundle
|
|
331
|
+
// add the asset if it doesn't already have it and set key
|
|
332
|
+
(0, assert_1.default)(bundle !== 'root' && bundle != null && bundleId != null);
|
|
333
|
+
manualAssetToBundle.set(childAsset, bundleId);
|
|
334
|
+
if (!bundle.assets.has(childAsset)) {
|
|
335
|
+
// Add asset to bundle
|
|
336
|
+
bundle.assets.add(childAsset);
|
|
337
|
+
bundle.size += childAsset.stats.size;
|
|
338
|
+
}
|
|
339
|
+
bundles.set(childAsset.id, bundleId);
|
|
340
|
+
bundleRoots.set(childAsset, [bundleId, bundleId]);
|
|
341
|
+
(0, assert_1.default)(manualSharedBundleKey != null);
|
|
342
|
+
// Ensure we set key to BundleId so the next glob match uses the appropriate bundle
|
|
343
|
+
if (!manualSharedMap.has(manualSharedBundleKey)) {
|
|
344
|
+
manualSharedMap.set(manualSharedBundleKey, bundleId);
|
|
345
|
+
}
|
|
346
|
+
bundle.manualSharedBundle = manualSharedObject.name;
|
|
347
|
+
bundle.uniqueKey = manualSharedObject.name + childAsset.type;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return node;
|
|
352
|
+
},
|
|
353
|
+
exit(node) {
|
|
354
|
+
if (stack[stack.length - 1]?.[0] === node.value) {
|
|
355
|
+
stack.pop();
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
}, null, { skipUnusedDependencies: true });
|
|
359
|
+
// Strip MSBs of entries
|
|
360
|
+
for (let [nodeId, internalizedAssets,] of manualBundleToInternalizedAsset.entries()) {
|
|
361
|
+
let bundle = bundleGraph.getNode(nodeId);
|
|
362
|
+
(0, assert_1.default)(bundle != null && bundle !== 'root');
|
|
363
|
+
if (!bundle.internalizedAssets) {
|
|
364
|
+
bundle.internalizedAssets = new graph_1.BitSet(assets.length);
|
|
365
|
+
}
|
|
366
|
+
for (let asset of internalizedAssets) {
|
|
367
|
+
bundle.internalizedAssets.add((0, nullthrows_1.default)(assetToIndex.get(asset)));
|
|
368
|
+
}
|
|
369
|
+
bundle.mainEntryAsset = null;
|
|
370
|
+
bundleGroupBundleIds.delete(nodeId); // manual bundles can now act as shared, non-bundle group, should they be non-bundleRoots as well?
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Step Determine Reachability: Determine reachability for every asset from each bundleRoot.
|
|
374
|
+
* This is later used to determine which bundles to place each asset in. We build up two
|
|
375
|
+
* structures, one traversal each. ReachableRoots to store sync relationships,
|
|
376
|
+
* and bundleRootGraph to store the minimal availability through `parallel` and `async` relationships.
|
|
377
|
+
* The two graphs, are used to build up ancestorAssets, a structure which holds all availability by
|
|
378
|
+
* all means for each asset.
|
|
379
|
+
*/
|
|
380
|
+
let rootNodeId = bundleRootGraph.addNode(-1);
|
|
381
|
+
bundleRootGraph.setRootNodeId(rootNodeId);
|
|
382
|
+
for (let [root] of bundleRoots) {
|
|
383
|
+
let nodeId = bundleRootGraph.addNode((0, nullthrows_1.default)(assetToIndex.get(root)));
|
|
384
|
+
assetToBundleRootNodeId.set(root, nodeId);
|
|
385
|
+
if (entries.has(root)) {
|
|
386
|
+
bundleRootGraph.addEdge(rootNodeId, nodeId);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
// reachableRoots is an array of bit sets for each asset. Each bit set
|
|
390
|
+
// indicates which bundle roots are reachable from that asset synchronously.
|
|
391
|
+
let reachableRoots = [];
|
|
392
|
+
for (let i = 0; i < assets.length; i++) {
|
|
393
|
+
reachableRoots.push(new graph_1.BitSet(bundleRootGraph.nodes.length));
|
|
394
|
+
}
|
|
395
|
+
// reachableAssets is the inverse mapping of reachableRoots. For each bundle root,
|
|
396
|
+
// it contains a bit set that indicates which assets are reachable from it.
|
|
397
|
+
let reachableAssets = [];
|
|
398
|
+
// ancestorAssets maps bundle roots to the set of all assets available to it at runtime,
|
|
399
|
+
// including in earlier parallel bundles. These are intersected through all paths to
|
|
400
|
+
// the bundle to ensure that the available assets are always present no matter in which
|
|
401
|
+
// order the bundles are loaded.
|
|
402
|
+
let ancestorAssets = [];
|
|
403
|
+
let inlineConstantDeps = new utils_1.DefaultMap(() => new Set());
|
|
404
|
+
for (let [bundleRootId, assetId] of bundleRootGraph.nodes.entries()) {
|
|
405
|
+
let reachable = new graph_1.BitSet(assets.length);
|
|
406
|
+
reachableAssets.push(reachable);
|
|
407
|
+
ancestorAssets.push(null);
|
|
408
|
+
if (bundleRootId == rootNodeId || assetId == null)
|
|
409
|
+
continue;
|
|
410
|
+
// Add sync relationships to ReachableRoots
|
|
411
|
+
let root = assets[assetId];
|
|
412
|
+
assetGraph.traverse((node, _, actions) => {
|
|
413
|
+
if (node.value === root) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (node.type === 'dependency') {
|
|
417
|
+
let dependency = node.value;
|
|
418
|
+
if (dependency.priority !== 'sync' &&
|
|
419
|
+
dependencyBundleGraph.hasContentKey(dependency.id)) {
|
|
420
|
+
let assets = assetGraph.getDependencyAssets(dependency);
|
|
421
|
+
if (assets.length === 0) {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
(0, assert_1.default)(assets.length === 1);
|
|
425
|
+
let bundleRoot = assets[0];
|
|
426
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode((0, nullthrows_1.default)(bundles.get(bundleRoot.id))));
|
|
427
|
+
if (bundle !== 'root' &&
|
|
428
|
+
bundle.bundleBehavior == null &&
|
|
429
|
+
!bundle.env.isIsolated() &&
|
|
430
|
+
bundle.env.context === root.env.context) {
|
|
431
|
+
bundleRootGraph.addEdge(bundleRootId, (0, nullthrows_1.default)(assetToBundleRootNodeId.get(bundleRoot)), dependency.priority === 'parallel' ||
|
|
432
|
+
(config.loadConditionalBundlesInParallel &&
|
|
433
|
+
dependency.priority === 'conditional')
|
|
434
|
+
? bundleRootEdgeTypes.parallel
|
|
435
|
+
: bundleRootEdgeTypes.lazy);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (dependency.priority !== 'sync') {
|
|
439
|
+
actions.skipChildren();
|
|
440
|
+
}
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
//asset node type
|
|
444
|
+
let asset = node.value;
|
|
445
|
+
if (asset.bundleBehavior != null) {
|
|
446
|
+
actions.skipChildren();
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
let assetIndex = (0, nullthrows_1.default)(assetToIndex.get(node.value));
|
|
450
|
+
reachable.add(assetIndex);
|
|
451
|
+
reachableRoots[assetIndex].add(bundleRootId);
|
|
452
|
+
if (asset.meta.isConstantModule === true) {
|
|
453
|
+
let parents = assetGraph
|
|
454
|
+
.getIncomingDependencies(asset)
|
|
455
|
+
.map((dep) => (0, nullthrows_1.default)(assetGraph.getAssetWithDependency(dep)));
|
|
456
|
+
for (let parent of parents) {
|
|
457
|
+
inlineConstantDeps.get(parent).add(asset);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return;
|
|
461
|
+
}, root, { skipUnusedDependencies: true });
|
|
462
|
+
}
|
|
463
|
+
for (let entry of entries.keys()) {
|
|
464
|
+
// Initialize an empty set of ancestors available to entries
|
|
465
|
+
let entryId = (0, nullthrows_1.default)(assetToBundleRootNodeId.get(entry));
|
|
466
|
+
ancestorAssets[entryId] = new graph_1.BitSet(assets.length);
|
|
467
|
+
}
|
|
468
|
+
// Step Determine Availability
|
|
469
|
+
// Visit nodes in a topological order, visiting parent nodes before child nodes.
|
|
470
|
+
// This allows us to construct an understanding of which assets will already be
|
|
471
|
+
// loaded and available when a bundle runs, by pushing available assets downwards and
|
|
472
|
+
// computing the intersection of assets available through all possible paths to a bundle.
|
|
473
|
+
// We call this structure ancestorAssets, a Map that tracks a bundleRoot,
|
|
474
|
+
// to all assets available to it (meaning they will exist guaranteed when the bundleRoot is loaded)
|
|
475
|
+
// The topological sort ensures all parents are visited before the node we want to process.
|
|
476
|
+
for (let nodeId of bundleRootGraph.topoSort(graph_1.ALL_EDGE_TYPES)) {
|
|
477
|
+
if (nodeId === rootNodeId)
|
|
478
|
+
continue;
|
|
479
|
+
const bundleRoot = assets[(0, nullthrows_1.default)(bundleRootGraph.getNode(nodeId))];
|
|
480
|
+
let bundleGroupId = (0, nullthrows_1.default)(bundleRoots.get(bundleRoot))[1];
|
|
481
|
+
// At a BundleRoot, we access it's available assets (via ancestorAssets),
|
|
482
|
+
// and add to that all assets within the bundles in that BundleGroup.
|
|
483
|
+
// This set is available to all bundles in a particular bundleGroup because
|
|
484
|
+
// bundleGroups are just bundles loaded at the same time. However it is
|
|
485
|
+
// not true that a bundle's available assets = all assets of all the bundleGroups
|
|
486
|
+
// it belongs to. It's the intersection of those sets.
|
|
487
|
+
let available;
|
|
488
|
+
if (bundleRoot.bundleBehavior === 'isolated' ||
|
|
489
|
+
bundleRoot.bundleBehavior === 'inlineIsolated') {
|
|
490
|
+
available = new graph_1.BitSet(assets.length);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
available = (0, nullthrows_1.default)(ancestorAssets[nodeId]).clone();
|
|
494
|
+
for (let bundleIdInGroup of [
|
|
495
|
+
bundleGroupId,
|
|
496
|
+
...bundleGraph.getNodeIdsConnectedFrom(bundleGroupId),
|
|
497
|
+
]) {
|
|
498
|
+
let bundleInGroup = (0, nullthrows_1.default)(bundleGraph.getNode(bundleIdInGroup));
|
|
499
|
+
(0, assert_1.default)(bundleInGroup !== 'root');
|
|
500
|
+
if (bundleInGroup.bundleBehavior != null) {
|
|
501
|
+
continue;
|
|
502
|
+
}
|
|
503
|
+
for (let bundleRoot of bundleInGroup.assets) {
|
|
504
|
+
// Assets directly connected to current bundleRoot
|
|
505
|
+
available.add((0, nullthrows_1.default)(assetToIndex.get(bundleRoot)));
|
|
506
|
+
available.union(reachableAssets[(0, nullthrows_1.default)(assetToBundleRootNodeId.get(bundleRoot))]);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
// Now that we have bundleGroup availability, we will propagate that down to all the children
|
|
511
|
+
// of this bundleGroup. For a child, we also must maintain parallel availability. If it has
|
|
512
|
+
// parallel siblings that come before it, those, too, are available to it. Add those parallel
|
|
513
|
+
// available assets to the set of available assets for this child as well.
|
|
514
|
+
let children = bundleRootGraph.getNodeIdsConnectedFrom(nodeId, graph_1.ALL_EDGE_TYPES);
|
|
515
|
+
let parallelAvailability = new graph_1.BitSet(assets.length);
|
|
516
|
+
for (let childId of children) {
|
|
517
|
+
let assetId = (0, nullthrows_1.default)(bundleRootGraph.getNode(childId));
|
|
518
|
+
let child = assets[assetId];
|
|
519
|
+
let bundleBehavior = getBundleFromBundleRoot(child).bundleBehavior;
|
|
520
|
+
if (bundleBehavior != null) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
let isParallel = bundleRootGraph.hasEdge(nodeId, childId, bundleRootEdgeTypes.parallel);
|
|
524
|
+
// Most of the time, a child will have many parent bundleGroups,
|
|
525
|
+
// so the next time we peek at a child from another parent, we will
|
|
526
|
+
// intersect the availability built there with the previously computed
|
|
527
|
+
// availability. this ensures no matter which bundleGroup loads a particular bundle,
|
|
528
|
+
// it will only assume availability of assets it has under any circumstance
|
|
529
|
+
const childAvailableAssets = ancestorAssets[childId];
|
|
530
|
+
let currentChildAvailable = isParallel
|
|
531
|
+
? graph_1.BitSet.union(parallelAvailability, available)
|
|
532
|
+
: available;
|
|
533
|
+
if (childAvailableAssets != null) {
|
|
534
|
+
childAvailableAssets.intersect(currentChildAvailable);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
ancestorAssets[childId] = currentChildAvailable.clone();
|
|
538
|
+
}
|
|
539
|
+
if (isParallel) {
|
|
540
|
+
parallelAvailability.union(reachableAssets[childId]);
|
|
541
|
+
parallelAvailability.add(assetId); //The next sibling should have older sibling available via parallel
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// Step Internalize async bundles - internalize Async bundles if and only if,
|
|
546
|
+
// the bundle is synchronously available elsewhere.
|
|
547
|
+
// We can query sync assets available via reachableRoots. If the parent has
|
|
548
|
+
// the bundleRoot by reachableRoots AND ancestorAssets, internalize it.
|
|
549
|
+
for (let [id, bundleRootId] of bundleRootGraph.nodes.entries()) {
|
|
550
|
+
if (bundleRootId == null || id === rootNodeId)
|
|
551
|
+
continue;
|
|
552
|
+
let bundleRoot = assets[bundleRootId];
|
|
553
|
+
if (manualAssetToConfig.has(bundleRoot)) {
|
|
554
|
+
// We internalize for MSBs later, we should never delete MSBs
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
let parentRoots = bundleRootGraph.getNodeIdsConnectedTo(id, graph_1.ALL_EDGE_TYPES);
|
|
558
|
+
let canDelete = getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'isolated' &&
|
|
559
|
+
getBundleFromBundleRoot(bundleRoot).bundleBehavior !== 'inlineIsolated';
|
|
560
|
+
if (parentRoots.length === 0)
|
|
561
|
+
continue;
|
|
562
|
+
for (let parentId of parentRoots) {
|
|
563
|
+
if (parentId === rootNodeId) {
|
|
564
|
+
// connected to root.
|
|
565
|
+
canDelete = false;
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (reachableAssets[parentId].has(bundleRootId) ||
|
|
569
|
+
ancestorAssets[parentId]?.has(bundleRootId)) {
|
|
570
|
+
let parentAssetId = (0, nullthrows_1.default)(bundleRootGraph.getNode(parentId));
|
|
571
|
+
let parent = assets[parentAssetId];
|
|
572
|
+
let parentBundle = bundleGraph.getNode((0, nullthrows_1.default)(bundles.get(parent.id)));
|
|
573
|
+
(0, assert_1.default)(parentBundle != null && parentBundle !== 'root');
|
|
574
|
+
if (!parentBundle.internalizedAssets) {
|
|
575
|
+
parentBundle.internalizedAssets = new graph_1.BitSet(assets.length);
|
|
576
|
+
}
|
|
577
|
+
parentBundle.internalizedAssets.add(bundleRootId);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
canDelete = false;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (canDelete) {
|
|
584
|
+
deleteBundle(bundleRoot);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function assignInlineConstants(parentAsset, bundle) {
|
|
588
|
+
for (let inlineConstant of inlineConstantDeps.get(parentAsset)) {
|
|
589
|
+
if (!bundle.assets.has(inlineConstant)) {
|
|
590
|
+
bundle.assets.add(inlineConstant);
|
|
591
|
+
bundle.size += inlineConstant.stats.size;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
// Step Insert Or Share: Place all assets into bundles or create shared bundles. Each asset
|
|
596
|
+
// is placed into a single bundle based on the bundle entries it is reachable from.
|
|
597
|
+
// This creates a maximally code split bundle graph with no duplication.
|
|
598
|
+
let reachable = new graph_1.BitSet(assets.length);
|
|
599
|
+
let reachableNonEntries = new graph_1.BitSet(assets.length);
|
|
600
|
+
let reachableIntersection = new graph_1.BitSet(assets.length);
|
|
601
|
+
for (let i = 0; i < assets.length; i++) {
|
|
602
|
+
let asset = assets[i];
|
|
603
|
+
let manualSharedObject = manualAssetToConfig.get(asset);
|
|
604
|
+
if (bundleRoots.has(asset) && inlineConstantDeps.get(asset).size > 0) {
|
|
605
|
+
let entryBundleId = (0, nullthrows_1.default)(bundleRoots.get(asset))[0];
|
|
606
|
+
let entryBundle = (0, nullthrows_1.default)(bundleGraph.getNode(entryBundleId));
|
|
607
|
+
(0, assert_1.default)(entryBundle !== 'root');
|
|
608
|
+
assignInlineConstants(asset, entryBundle);
|
|
609
|
+
}
|
|
610
|
+
if (asset.meta.isConstantModule === true) {
|
|
611
|
+
// Ignore constant modules as they are placed with their direct parents
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
// Unreliable bundleRoot assets which need to pulled in by shared bundles or other means.
|
|
615
|
+
// Filter out entries, since they can't have shared bundles.
|
|
616
|
+
// Neither can non-splittable, isolated, or needing of stable name bundles.
|
|
617
|
+
// Reserve those filtered out bundles since we add the asset back into them.
|
|
618
|
+
reachableNonEntries.clear();
|
|
619
|
+
reachableRoots[i].forEach((nodeId) => {
|
|
620
|
+
let assetId = bundleRootGraph.getNode(nodeId);
|
|
621
|
+
if (assetId == null)
|
|
622
|
+
return; // deleted
|
|
623
|
+
let a = assets[assetId];
|
|
624
|
+
if (entries.has(a) ||
|
|
625
|
+
!a.isBundleSplittable ||
|
|
626
|
+
(bundleRoots.get(a) &&
|
|
627
|
+
(getBundleFromBundleRoot(a).needsStableName ||
|
|
628
|
+
getBundleFromBundleRoot(a).bundleBehavior === 'isolated' ||
|
|
629
|
+
getBundleFromBundleRoot(a).bundleBehavior === 'inlineIsolated'))) {
|
|
630
|
+
// Add asset to non-splittable bundles.
|
|
631
|
+
addAssetToBundleRoot(asset, a);
|
|
632
|
+
}
|
|
633
|
+
else if (!ancestorAssets[nodeId]?.has(i)) {
|
|
634
|
+
// Filter out bundles from this asset's reachable array if
|
|
635
|
+
// bundle does not contain the asset in its ancestry
|
|
636
|
+
reachableNonEntries.add(assetId);
|
|
637
|
+
}
|
|
638
|
+
});
|
|
639
|
+
reachable.bits.set(reachableNonEntries.bits);
|
|
640
|
+
// If we encounter a "manual" asset, draw an edge from reachable to its MSB
|
|
641
|
+
if (manualSharedObject && !reachable.empty()) {
|
|
642
|
+
let bundle;
|
|
643
|
+
let bundleId;
|
|
644
|
+
let manualSharedBundleKey = manualSharedObject.name + ',' + asset.type;
|
|
645
|
+
let sourceBundles = [];
|
|
646
|
+
reachable.forEach((id) => {
|
|
647
|
+
sourceBundles.push((0, nullthrows_1.default)(bundleRoots.get(assets[id]))[0]);
|
|
648
|
+
});
|
|
649
|
+
if (!manualSharedMap.has(manualSharedBundleKey)) {
|
|
650
|
+
let firstSourceBundle = (0, nullthrows_1.default)(bundleGraph.getNode(sourceBundles[0]));
|
|
651
|
+
(0, assert_1.default)(firstSourceBundle !== 'root');
|
|
652
|
+
bundle = createBundle({
|
|
653
|
+
env: firstSourceBundle.env,
|
|
654
|
+
manualSharedBundle: manualSharedObject?.name,
|
|
655
|
+
sourceBundles: new Set(sourceBundles),
|
|
656
|
+
target: firstSourceBundle.target,
|
|
657
|
+
type: asset.type,
|
|
658
|
+
uniqueKey: manualSharedBundleKey,
|
|
659
|
+
});
|
|
660
|
+
bundle.assets.add(asset);
|
|
661
|
+
bundleId = bundleGraph.addNode(bundle);
|
|
662
|
+
manualSharedMap.set(manualSharedBundleKey, bundleId);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
bundleId = (0, nullthrows_1.default)(manualSharedMap.get(manualSharedBundleKey));
|
|
666
|
+
let bundleNode = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
667
|
+
(0, assert_1.default)(bundleNode != null && bundleNode !== 'root', 'We tried to use the root incorrectly');
|
|
668
|
+
bundle = bundleNode;
|
|
669
|
+
if (!bundle.assets.has(asset)) {
|
|
670
|
+
bundle.assets.add(asset);
|
|
671
|
+
bundle.size += asset.stats.size;
|
|
672
|
+
}
|
|
673
|
+
for (let s of sourceBundles) {
|
|
674
|
+
if (s != bundleId) {
|
|
675
|
+
bundle.sourceBundles.add(s);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
for (let sourceBundleId of sourceBundles) {
|
|
680
|
+
if (bundleId !== sourceBundleId) {
|
|
681
|
+
bundleGraph.addEdge(sourceBundleId, bundleId);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
685
|
+
value: bundle,
|
|
686
|
+
type: 'bundle',
|
|
687
|
+
});
|
|
688
|
+
continue;
|
|
689
|
+
}
|
|
690
|
+
// Finally, filter out bundleRoots (bundles) from this assets
|
|
691
|
+
// reachable if they are subgraphs, and reuse that subgraph bundle
|
|
692
|
+
// by drawing an edge. Essentially, if two bundles within an asset's
|
|
693
|
+
// reachable array, have an ancestor-subgraph relationship, draw that edge.
|
|
694
|
+
// This allows for us to reuse a bundle instead of making a shared bundle if
|
|
695
|
+
// a bundle represents the exact set of assets a set of bundles would share
|
|
696
|
+
// if a bundle b is a subgraph of another bundle f, reuse it, drawing an edge between the two
|
|
697
|
+
if (config.disableSharedBundles === false) {
|
|
698
|
+
reachableNonEntries.forEach((candidateId) => {
|
|
699
|
+
let candidateSourceBundleRoot = assets[candidateId];
|
|
700
|
+
let candidateSourceBundleId = (0, nullthrows_1.default)(bundleRoots.get(candidateSourceBundleRoot))[0];
|
|
701
|
+
if (candidateSourceBundleRoot.env.isIsolated()) {
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
let reuseableBundleId = bundles.get(asset.id);
|
|
705
|
+
if (reuseableBundleId != null) {
|
|
706
|
+
reachable.delete(candidateId);
|
|
707
|
+
bundleGraph.addEdge(candidateSourceBundleId, reuseableBundleId);
|
|
708
|
+
let reusableBundle = bundleGraph.getNode(reuseableBundleId);
|
|
709
|
+
(0, assert_1.default)(reusableBundle !== 'root' && reusableBundle != null);
|
|
710
|
+
reusableBundle.sourceBundles.add(candidateSourceBundleId);
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
// Asset is not a bundleRoot, but if its ancestor bundle (in the asset's reachable) can be
|
|
714
|
+
// reused as a subgraph of another bundleRoot in its reachable, reuse it
|
|
715
|
+
reachableIntersection.bits.set(reachableNonEntries.bits);
|
|
716
|
+
reachableIntersection.intersect(reachableAssets[(0, nullthrows_1.default)(assetToBundleRootNodeId.get(candidateSourceBundleRoot))]);
|
|
717
|
+
reachableIntersection.forEach((otherCandidateId) => {
|
|
718
|
+
// In the case of a circular dependency, you may end up with two
|
|
719
|
+
// reusable bundles that each delete the other, leaving no reusable
|
|
720
|
+
// bundles actually reachable. This check is to avoid assigning the
|
|
721
|
+
// asset to a reusable bundle that has already been marked unreachable.
|
|
722
|
+
if (!reachable.has(otherCandidateId))
|
|
723
|
+
return;
|
|
724
|
+
let otherReuseCandidate = assets[otherCandidateId];
|
|
725
|
+
if (candidateSourceBundleRoot === otherReuseCandidate)
|
|
726
|
+
return;
|
|
727
|
+
let reusableBundleId = (0, nullthrows_1.default)(bundles.get(otherReuseCandidate.id));
|
|
728
|
+
reachable.delete(candidateId);
|
|
729
|
+
bundleGraph.addEdge((0, nullthrows_1.default)(bundles.get(candidateSourceBundleRoot.id)), reusableBundleId);
|
|
730
|
+
let reusableBundle = bundleGraph.getNode(reusableBundleId);
|
|
731
|
+
(0, assert_1.default)(reusableBundle !== 'root' && reusableBundle != null);
|
|
732
|
+
reusableBundle.sourceBundles.add(candidateSourceBundleId);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
let reachableArray = [];
|
|
738
|
+
reachable.forEach((id) => {
|
|
739
|
+
reachableArray.push(assets[id]);
|
|
740
|
+
});
|
|
741
|
+
// Create shared bundles for splittable bundles.
|
|
742
|
+
if (config.disableSharedBundles === false &&
|
|
743
|
+
reachableArray.length > config.minBundles) {
|
|
744
|
+
let sourceBundles = reachableArray.map((a) => (0, nullthrows_1.default)(bundleRoots.get(a))[0]);
|
|
745
|
+
let key = reachableArray.map((a) => a.id).join(',') + '.' + asset.type;
|
|
746
|
+
let bundleId = bundles.get(key);
|
|
747
|
+
let bundle;
|
|
748
|
+
if (bundleId == null) {
|
|
749
|
+
let firstSourceBundle = (0, nullthrows_1.default)(bundleGraph.getNode(sourceBundles[0]));
|
|
750
|
+
(0, assert_1.default)(firstSourceBundle !== 'root');
|
|
751
|
+
bundle = createBundle({
|
|
752
|
+
env: firstSourceBundle.env,
|
|
753
|
+
sourceBundles: new Set(sourceBundles),
|
|
754
|
+
target: firstSourceBundle.target,
|
|
755
|
+
type: asset.type,
|
|
756
|
+
});
|
|
757
|
+
let sharedInternalizedAssets = firstSourceBundle.internalizedAssets
|
|
758
|
+
? firstSourceBundle.internalizedAssets.clone()
|
|
759
|
+
: new graph_1.BitSet(assets.length);
|
|
760
|
+
for (let p of sourceBundles) {
|
|
761
|
+
let parentBundle = (0, nullthrows_1.default)(bundleGraph.getNode(p));
|
|
762
|
+
(0, assert_1.default)(parentBundle !== 'root');
|
|
763
|
+
if (parentBundle === firstSourceBundle)
|
|
764
|
+
continue;
|
|
765
|
+
if (parentBundle.internalizedAssets) {
|
|
766
|
+
sharedInternalizedAssets.intersect(parentBundle.internalizedAssets);
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
sharedInternalizedAssets.clear();
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
bundle.internalizedAssets = sharedInternalizedAssets;
|
|
773
|
+
bundleId = bundleGraph.addNode(bundle);
|
|
774
|
+
bundles.set(key, bundleId);
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
let bundleNode = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
778
|
+
(0, assert_1.default)(bundleNode !== 'root');
|
|
779
|
+
bundle = bundleNode;
|
|
780
|
+
}
|
|
781
|
+
bundle.assets.add(asset);
|
|
782
|
+
bundle.size += asset.stats.size;
|
|
783
|
+
assignInlineConstants(asset, bundle);
|
|
784
|
+
for (let sourceBundleId of sourceBundles) {
|
|
785
|
+
if (bundleId !== sourceBundleId) {
|
|
786
|
+
bundleGraph.addEdge(sourceBundleId, bundleId);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
dependencyBundleGraph.addNodeByContentKeyIfNeeded(String(bundleId), {
|
|
790
|
+
value: bundle,
|
|
791
|
+
type: 'bundle',
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
else if (config.disableSharedBundles === true ||
|
|
795
|
+
reachableArray.length <= config.minBundles) {
|
|
796
|
+
for (let root of reachableArray) {
|
|
797
|
+
addAssetToBundleRoot(asset, root);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
let manualSharedBundleIds = new Set([...manualSharedMap.values()]);
|
|
802
|
+
let modifiedSourceBundles = new Set();
|
|
803
|
+
// Step split manual shared bundles for those that have the "split" property set
|
|
804
|
+
let remainderMap = new utils_1.DefaultMap(() => []);
|
|
805
|
+
for (let id of manualSharedMap.values()) {
|
|
806
|
+
let manualBundle = bundleGraph.getNode(id);
|
|
807
|
+
(0, assert_1.default)(manualBundle !== 'root' && manualBundle != null);
|
|
808
|
+
if (manualBundle.sourceBundles.size > 0) {
|
|
809
|
+
let firstSourceBundleNode = (0, nullthrows_1.default)(bundleGraph.getNode([...manualBundle.sourceBundles][0]));
|
|
810
|
+
(0, assert_1.default)(firstSourceBundleNode !== 'root');
|
|
811
|
+
let firstSourceBundle = firstSourceBundleNode;
|
|
812
|
+
let firstAsset = [...manualBundle.assets][0];
|
|
813
|
+
let manualSharedObject = manualAssetToConfig.get(firstAsset);
|
|
814
|
+
(0, assert_1.default)(manualSharedObject != null);
|
|
815
|
+
let modNum = manualAssetToConfig.get(firstAsset)?.split;
|
|
816
|
+
if (modNum != null) {
|
|
817
|
+
for (let a of [...manualBundle.assets]) {
|
|
818
|
+
let numRep = getBigIntFromContentKey(a.id);
|
|
819
|
+
let r = Number(numRep % BigInt(modNum));
|
|
820
|
+
remainderMap.get(r).push(a);
|
|
821
|
+
}
|
|
822
|
+
for (let i = 1; i < [...remainderMap.keys()].length; i++) {
|
|
823
|
+
let bundle = createBundle({
|
|
824
|
+
env: firstSourceBundle.env,
|
|
825
|
+
manualSharedBundle: manualSharedObject.name,
|
|
826
|
+
sourceBundles: manualBundle.sourceBundles,
|
|
827
|
+
target: firstSourceBundle.target,
|
|
828
|
+
type: firstSourceBundle.type,
|
|
829
|
+
uniqueKey: manualSharedObject.name + firstSourceBundle.type + i,
|
|
830
|
+
});
|
|
831
|
+
bundle.internalizedAssets = manualBundle.internalizedAssets;
|
|
832
|
+
let bundleId = bundleGraph.addNode(bundle);
|
|
833
|
+
manualSharedBundleIds.add(bundleId);
|
|
834
|
+
for (let sourceBundleId of manualBundle.sourceBundles) {
|
|
835
|
+
if (bundleId !== sourceBundleId) {
|
|
836
|
+
bundleGraph.addEdge(sourceBundleId, bundleId);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
for (let sp of remainderMap.get(i)) {
|
|
840
|
+
bundle.assets.add(sp);
|
|
841
|
+
bundle.size += sp.stats.size;
|
|
842
|
+
manualBundle.assets.delete(sp);
|
|
843
|
+
manualBundle.size -= sp.stats.size;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// Step insert constant modules into manual shared bundles.
|
|
850
|
+
// We have to do this separately as they're the only case where a single asset can
|
|
851
|
+
// match multiple MSB's
|
|
852
|
+
for (let [asset, msbs] of constantModuleToMSB.entries()) {
|
|
853
|
+
for (let manualSharedObject of msbs) {
|
|
854
|
+
let bundleId = manualSharedMap.get(manualSharedObject.name + ',js');
|
|
855
|
+
if (bundleId == null)
|
|
856
|
+
continue;
|
|
857
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
858
|
+
(0, assert_1.default)(bundle != null && bundle !== 'root', 'We tried to use the root incorrectly');
|
|
859
|
+
if (!bundle.assets.has(asset)) {
|
|
860
|
+
bundle.assets.add(asset);
|
|
861
|
+
bundle.size += asset.stats.size;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if ((0, feature_flags_1.getFeatureFlag)('supportWebpackChunkName')) {
|
|
866
|
+
// Merge webpack chunk name bundles
|
|
867
|
+
let chunkNameBundles = new utils_1.DefaultMap(() => new Set());
|
|
868
|
+
for (let [nodeId, node] of dependencyBundleGraph.nodes.entries()) {
|
|
869
|
+
// meta.chunkName is set by the Rust transformer, so we just need to find
|
|
870
|
+
// bundles that have a chunkName set.
|
|
871
|
+
if (!node ||
|
|
872
|
+
node.type !== 'dependency' ||
|
|
873
|
+
typeof node.value.meta.chunkName !== 'string') {
|
|
874
|
+
continue;
|
|
875
|
+
}
|
|
876
|
+
let connectedBundles = dependencyBundleGraph.getNodeIdsConnectedFrom(nodeId, dependencyPriorityEdges[node.value.priority]);
|
|
877
|
+
if (connectedBundles.length === 0) {
|
|
878
|
+
continue;
|
|
879
|
+
}
|
|
880
|
+
(0, assert_1.default)(connectedBundles.length === 1, 'Expected webpackChunkName dependency to be connected to no more than one bundle');
|
|
881
|
+
let bundleId = connectedBundles[0];
|
|
882
|
+
let bundleNode = dependencyBundleGraph.getNode(bundleId);
|
|
883
|
+
(0, assert_1.default)(bundleNode != null && bundleNode.type === 'bundle');
|
|
884
|
+
// If a bundle does not have a main entry asset, it's somehow just a
|
|
885
|
+
// shared bundle, and will be merged/deleted by other means.
|
|
886
|
+
if (bundleNode.value.mainEntryAsset == null) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
let bundleNodeId = null;
|
|
890
|
+
let mainEntryAssetId = bundleNode.value.mainEntryAsset?.id;
|
|
891
|
+
if (mainEntryAssetId != null) {
|
|
892
|
+
bundleNodeId = bundles.get(mainEntryAssetId);
|
|
893
|
+
}
|
|
894
|
+
if (bundleNodeId == null) {
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
chunkNameBundles
|
|
898
|
+
.get(node.value.meta.chunkName)
|
|
899
|
+
// DependencyBundleGraph uses content keys as node ids, so we can use that
|
|
900
|
+
// to get the bundle id.
|
|
901
|
+
.add(bundleNodeId);
|
|
902
|
+
}
|
|
903
|
+
for (let [chunkName, bundleIds] of chunkNameBundles.entries()) {
|
|
904
|
+
// The `[request]` placeholder is not yet supported
|
|
905
|
+
if (bundleIds.size <= 1 ||
|
|
906
|
+
(typeof chunkName === 'string' && chunkName.includes('[request]'))) {
|
|
907
|
+
continue; // Nothing to merge
|
|
908
|
+
}
|
|
909
|
+
// Merge all bundles with the same chunk name into the first one.
|
|
910
|
+
let [firstBundleId, ...rest] = Array.from(bundleIds);
|
|
911
|
+
for (let bundleId of rest) {
|
|
912
|
+
mergeBundles(firstBundleId, bundleId, 'webpack-chunk-name');
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
// Step merge async bundles that meet the configured params
|
|
917
|
+
if (config.asyncBundleMerge) {
|
|
918
|
+
mergeAsyncBundles(config.asyncBundleMerge);
|
|
919
|
+
}
|
|
920
|
+
// Step merge shared bundles that meet the configured params
|
|
921
|
+
if (config.sharedBundleMerge && config.sharedBundleMerge.length > 0) {
|
|
922
|
+
mergeSharedBundles(config.sharedBundleMerge);
|
|
923
|
+
}
|
|
924
|
+
// Step Merge Share Bundles: Merge any shared bundles under the minimum bundle size back into
|
|
925
|
+
// their source bundles, and remove the bundle.
|
|
926
|
+
// We should include "bundle reuse" as shared bundles that may be removed but the bundle itself would have to be retained
|
|
927
|
+
for (let [bundleNodeId, bundle] of bundleGraph.nodes.entries()) {
|
|
928
|
+
if (!bundle || bundle === 'root')
|
|
929
|
+
continue;
|
|
930
|
+
if (bundle.sourceBundles.size > 0 &&
|
|
931
|
+
bundle.mainEntryAsset == null &&
|
|
932
|
+
bundle.size < config.minBundleSize &&
|
|
933
|
+
!manualSharedBundleIds.has(bundleNodeId)) {
|
|
934
|
+
removeBundle(bundleGraph, bundleNodeId, assetReference);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
// Step Remove Shared Bundles: Remove shared bundles from bundle groups that hit the parallel request limit.
|
|
938
|
+
if (config.disableSharedBundles === false) {
|
|
939
|
+
for (let bundleGroupId of bundleGraph.getNodeIdsConnectedFrom(rootNodeId)) {
|
|
940
|
+
// Find shared bundles in this bundle group.
|
|
941
|
+
let bundleId = bundleGroupId;
|
|
942
|
+
// We should include "bundle reuse" as shared bundles that may be removed but the bundle itself would have to be retained
|
|
943
|
+
let bundleIdsInGroup = getBundlesForBundleGroup(bundleId); //get all bundlegrups this bundle is an ancestor of
|
|
944
|
+
// Filter out inline assests as they should not contribute to PRL
|
|
945
|
+
let numBundlesContributingToPRL = bundleIdsInGroup.reduce((count, b) => {
|
|
946
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode(b));
|
|
947
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
948
|
+
return (count +
|
|
949
|
+
Number(bundle.bundleBehavior !== 'inline' &&
|
|
950
|
+
bundle.bundleBehavior !== 'inlineIsolated'));
|
|
951
|
+
}, 0);
|
|
952
|
+
if (numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
953
|
+
let sharedBundleIdsInBundleGroup = bundleIdsInGroup.filter((b) => {
|
|
954
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode(b));
|
|
955
|
+
// shared bundles must have source bundles, we could have a bundle
|
|
956
|
+
// connected to another bundle that isnt a shared bundle, so check
|
|
957
|
+
return (bundle !== 'root' &&
|
|
958
|
+
bundle.sourceBundles.size > 0 &&
|
|
959
|
+
bundleId != b &&
|
|
960
|
+
!manualSharedBundleIds.has(b));
|
|
961
|
+
});
|
|
962
|
+
// Sort the bundles so the smallest ones are removed first.
|
|
963
|
+
let sharedBundlesInGroup = sharedBundleIdsInBundleGroup
|
|
964
|
+
.map((id) => ({
|
|
965
|
+
id,
|
|
966
|
+
bundle: (0, nullthrows_1.default)(bundleGraph.getNode(id)),
|
|
967
|
+
}))
|
|
968
|
+
.map(({ id, bundle }) => {
|
|
969
|
+
// For Flow
|
|
970
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
971
|
+
return { id, bundle };
|
|
972
|
+
})
|
|
973
|
+
.sort((a, b) => b.bundle.size - a.bundle.size);
|
|
974
|
+
// Remove bundles until the bundle group is within the parallel request limit.
|
|
975
|
+
while (sharedBundlesInGroup.length > 0 &&
|
|
976
|
+
numBundlesContributingToPRL > config.maxParallelRequests) {
|
|
977
|
+
let bundleTuple = sharedBundlesInGroup.pop();
|
|
978
|
+
if (!bundleTuple)
|
|
979
|
+
break;
|
|
980
|
+
let bundleToRemove = bundleTuple.bundle;
|
|
981
|
+
let bundleIdToRemove = bundleTuple.id;
|
|
982
|
+
//TODO add integration test where bundles in bunlde group > max parallel request limit & only remove a couple shared bundles
|
|
983
|
+
// but total # bundles still exceeds limit due to non shared bundles
|
|
984
|
+
// Add all assets in the shared bundle into the source bundles that are within this bundle group.
|
|
985
|
+
let sourceBundles = [...bundleToRemove.sourceBundles].filter((b) => bundleIdsInGroup.includes(b));
|
|
986
|
+
for (let sourceBundleId of sourceBundles) {
|
|
987
|
+
let sourceBundle = (0, nullthrows_1.default)(bundleGraph.getNode(sourceBundleId));
|
|
988
|
+
(0, assert_1.default)(sourceBundle !== 'root');
|
|
989
|
+
modifiedSourceBundles.add(sourceBundle);
|
|
990
|
+
bundleToRemove.sourceBundles.delete(sourceBundleId);
|
|
991
|
+
for (let asset of bundleToRemove.assets) {
|
|
992
|
+
addAssetToBundleRoot(asset, (0, nullthrows_1.default)(sourceBundle.mainEntryAsset));
|
|
993
|
+
}
|
|
994
|
+
//This case is specific to reused bundles, which can have shared bundles attached to it
|
|
995
|
+
for (let childId of bundleGraph.getNodeIdsConnectedFrom(bundleIdToRemove)) {
|
|
996
|
+
let child = bundleGraph.getNode(childId);
|
|
997
|
+
(0, assert_1.default)(child !== 'root' && child != null);
|
|
998
|
+
child.sourceBundles.add(sourceBundleId);
|
|
999
|
+
bundleGraph.addEdge(sourceBundleId, childId);
|
|
1000
|
+
}
|
|
1001
|
+
// needs to add test case where shared bundle is removed from ONE bundlegroup but not from the whole graph!
|
|
1002
|
+
// Remove the edge from this bundle group to the shared bundle.
|
|
1003
|
+
// If there is now only a single bundle group that contains this bundle,
|
|
1004
|
+
// merge it into the remaining source bundles. If it is orphaned entirely, remove it.
|
|
1005
|
+
let incomingNodeCount = bundleGraph.getNodeIdsConnectedTo(bundleIdToRemove).length;
|
|
1006
|
+
if (incomingNodeCount <= 2 &&
|
|
1007
|
+
//Never fully remove reused bundles
|
|
1008
|
+
bundleToRemove.mainEntryAsset == null) {
|
|
1009
|
+
// If one bundle group removes a shared bundle, but the other *can* keep it, still remove because that shared bundle is pointless (only one source bundle)
|
|
1010
|
+
removeBundle(bundleGraph, bundleIdToRemove, assetReference);
|
|
1011
|
+
// Stop iterating through bundleToRemove's sourceBundles as the bundle has been removed.
|
|
1012
|
+
break;
|
|
1013
|
+
}
|
|
1014
|
+
else {
|
|
1015
|
+
bundleGraph.removeEdge(sourceBundleId, bundleIdToRemove);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
numBundlesContributingToPRL--;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
function mergeBundles(bundleToKeepId, bundleToRemoveId, reason) {
|
|
1024
|
+
stats.trackMerge(bundleToKeepId, bundleToRemoveId, reason);
|
|
1025
|
+
let bundleToKeep = isNonRootBundle(bundleGraph.getNode(bundleToKeepId), `Bundle ${bundleToKeepId} not found`);
|
|
1026
|
+
let bundleToRemove = isNonRootBundle(bundleGraph.getNode(bundleToRemoveId), `Bundle ${bundleToRemoveId} not found`);
|
|
1027
|
+
modifiedSourceBundles.add(bundleToKeep);
|
|
1028
|
+
for (let asset of bundleToRemove.assets) {
|
|
1029
|
+
bundleToKeep.assets.add(asset);
|
|
1030
|
+
bundleToKeep.size += asset.stats.size;
|
|
1031
|
+
let newAssetReference = assetReference
|
|
1032
|
+
.get(asset)
|
|
1033
|
+
.map(([dep, bundle]) => bundle === bundleToRemove ? [dep, bundleToKeep] : [dep, bundle]);
|
|
1034
|
+
assetReference.set(asset, newAssetReference);
|
|
1035
|
+
}
|
|
1036
|
+
// Merge any internalized assets
|
|
1037
|
+
if ((0, feature_flags_1.getFeatureFlag)('supportWebpackChunkName')) {
|
|
1038
|
+
if (bundleToKeep.internalizedAssets != null) {
|
|
1039
|
+
if (bundleToRemove.internalizedAssets != null) {
|
|
1040
|
+
bundleToKeep.internalizedAssets.intersect(bundleToRemove.internalizedAssets);
|
|
1041
|
+
}
|
|
1042
|
+
else {
|
|
1043
|
+
bundleToKeep.internalizedAssets.clear();
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
else {
|
|
1048
|
+
(0, assert_1.default)(bundleToKeep.internalizedAssets && bundleToRemove.internalizedAssets, 'All shared bundles should have internalized assets');
|
|
1049
|
+
bundleToKeep.internalizedAssets.union(bundleToRemove.internalizedAssets);
|
|
1050
|
+
}
|
|
1051
|
+
// Merge and clean up source bundles
|
|
1052
|
+
for (let sourceBundleId of bundleToRemove.sourceBundles) {
|
|
1053
|
+
if (bundleToKeep.sourceBundles.has(sourceBundleId)) {
|
|
1054
|
+
continue;
|
|
1055
|
+
}
|
|
1056
|
+
if (sourceBundleId !== bundleToKeepId) {
|
|
1057
|
+
bundleToKeep.sourceBundles.add(sourceBundleId);
|
|
1058
|
+
bundleGraph.addEdge(sourceBundleId, bundleToKeepId);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if ((0, feature_flags_1.getFeatureFlag)('supportWebpackChunkName')) {
|
|
1062
|
+
bundleToKeep.sourceBundles.delete(bundleToRemoveId);
|
|
1063
|
+
for (let bundle of bundleGraph.getNodeIdsConnectedFrom(bundleToRemoveId)) {
|
|
1064
|
+
let bundleNode = (0, nullthrows_1.default)(bundleGraph.getNode(bundle));
|
|
1065
|
+
if (bundleNode === 'root') {
|
|
1066
|
+
continue;
|
|
1067
|
+
}
|
|
1068
|
+
// If the bundle is a source bundle, add it to the bundle to keep
|
|
1069
|
+
if (bundleNode.sourceBundles.has(bundleToRemoveId)) {
|
|
1070
|
+
bundleNode.sourceBundles.delete(bundleToRemoveId);
|
|
1071
|
+
if (bundle !== bundleToKeepId) {
|
|
1072
|
+
bundleNode.sourceBundles.add(bundleToKeepId);
|
|
1073
|
+
bundleGraph.addEdge(bundleToKeepId, bundle);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
// Merge bundle roots
|
|
1078
|
+
for (let bundleRoot of bundleToRemove.bundleRoots) {
|
|
1079
|
+
bundleToKeep.bundleRoots.add(bundleRoot);
|
|
1080
|
+
}
|
|
1081
|
+
if (bundleToRemove.mainEntryAsset != null) {
|
|
1082
|
+
(0, assert_1.default)(bundleToKeep.mainEntryAsset != null);
|
|
1083
|
+
// Merge the bundles in bundle group
|
|
1084
|
+
let bundlesInRemoveBundleGroup = getBundlesForBundleGroup(bundleToRemoveId);
|
|
1085
|
+
let removedBundleSharedBundles = new Set();
|
|
1086
|
+
for (let bundleIdInGroup of bundlesInRemoveBundleGroup) {
|
|
1087
|
+
if (bundleIdInGroup === bundleToRemoveId) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
bundleGraph.addEdge(bundleToKeepId, bundleIdInGroup);
|
|
1091
|
+
removedBundleSharedBundles.add(bundleIdInGroup);
|
|
1092
|
+
}
|
|
1093
|
+
if ((0, feature_flags_1.getFeatureFlag)('removeRedundantSharedBundles')) {
|
|
1094
|
+
// Merge any shared bundles that now have the same source bundles due to
|
|
1095
|
+
// the current bundle merge
|
|
1096
|
+
let sharedBundles = new utils_1.DefaultMap(() => []);
|
|
1097
|
+
for (let bundleId of removedBundleSharedBundles) {
|
|
1098
|
+
let bundleNode = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
1099
|
+
(0, assert_1.default)(bundleNode !== 'root');
|
|
1100
|
+
if (bundleNode.mainEntryAsset != null ||
|
|
1101
|
+
bundleNode.manualSharedBundle != null) {
|
|
1102
|
+
continue;
|
|
1103
|
+
}
|
|
1104
|
+
let key = Array.from(bundleNode.sourceBundles)
|
|
1105
|
+
.filter((sourceBundle) => sourceBundle !== bundleToRemoveId)
|
|
1106
|
+
.sort()
|
|
1107
|
+
.join(',') +
|
|
1108
|
+
'.' +
|
|
1109
|
+
bundleNode.type;
|
|
1110
|
+
sharedBundles.get(key).push(bundleId);
|
|
1111
|
+
}
|
|
1112
|
+
for (let sharedBundlesToMerge of sharedBundles.values()) {
|
|
1113
|
+
if (sharedBundlesToMerge.length > 1) {
|
|
1114
|
+
let [firstBundleId, ...rest] = sharedBundlesToMerge;
|
|
1115
|
+
for (let bundleId of rest) {
|
|
1116
|
+
mergeBundles(firstBundleId, bundleId, 'redundant-shared');
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
// Remove old bundle group
|
|
1122
|
+
bundleGroupBundleIds.delete(bundleToRemoveId);
|
|
1123
|
+
// Clean up bundle roots
|
|
1124
|
+
let bundleRootToRemoveNodeId = (0, nullthrows_1.default)(assetToBundleRootNodeId.get((0, nullthrows_1.default)(bundleToRemove.mainEntryAsset)));
|
|
1125
|
+
let bundleRootToKeepNodeId = (0, nullthrows_1.default)(assetToBundleRootNodeId.get((0, nullthrows_1.default)(bundleToKeep.mainEntryAsset)));
|
|
1126
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootToRemoveNodeId)) {
|
|
1127
|
+
bundleRootGraph.addEdge(nodeId, bundleRootToKeepNodeId);
|
|
1128
|
+
bundleRootGraph.removeEdge(nodeId, bundleRootToRemoveNodeId);
|
|
1129
|
+
}
|
|
1130
|
+
for (let nodeId of bundleRootGraph.getNodeIdsConnectedFrom(bundleRootToRemoveNodeId)) {
|
|
1131
|
+
bundleRootGraph.addEdge(bundleRootToKeepNodeId, nodeId);
|
|
1132
|
+
bundleRootGraph.removeEdge(bundleRootToRemoveNodeId, nodeId);
|
|
1133
|
+
}
|
|
1134
|
+
bundleRoots.set((0, nullthrows_1.default)(bundleToRemove.mainEntryAsset), [
|
|
1135
|
+
bundleToKeepId,
|
|
1136
|
+
bundleToKeepId,
|
|
1137
|
+
]);
|
|
1138
|
+
// Merge dependency bundle graph
|
|
1139
|
+
for (let dependencyNodeId of dependencyBundleGraph.getNodeIdsConnectedTo(dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), graph_1.ALL_EDGE_TYPES)) {
|
|
1140
|
+
let dependencyNode = (0, nullthrows_1.default)(dependencyBundleGraph.getNode(dependencyNodeId));
|
|
1141
|
+
(0, assert_1.default)(dependencyNode.type === 'dependency');
|
|
1142
|
+
// Add dependency to the bundle to keep
|
|
1143
|
+
dependencyBundleGraph.addEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToKeepId)), dependencyPriorityEdges[dependencyNode.value.priority]);
|
|
1144
|
+
// Remove dependency from the bundle to remove
|
|
1145
|
+
dependencyBundleGraph.removeEdge(dependencyNodeId, dependencyBundleGraph.getNodeIdByContentKey(String(bundleToRemoveId)), dependencyPriorityEdges[dependencyNode.value.priority]);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
bundleGraph.removeNode(bundleToRemoveId);
|
|
1150
|
+
}
|
|
1151
|
+
function mergeSharedBundles(mergeConfig) {
|
|
1152
|
+
// Find all shared bundles
|
|
1153
|
+
let sharedBundles = new Set();
|
|
1154
|
+
bundleGraph.traverse((nodeId) => {
|
|
1155
|
+
let bundle = bundleGraph.getNode(nodeId);
|
|
1156
|
+
if (!bundle) {
|
|
1157
|
+
throw new Error(`Unable to find bundle ${nodeId} in bundle graph`);
|
|
1158
|
+
}
|
|
1159
|
+
if (bundle === 'root') {
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
// Only consider JS shared bundles and non-reused bundles.
|
|
1163
|
+
// These count potentially be considered for merging in future but they're
|
|
1164
|
+
// more complicated to merge
|
|
1165
|
+
if (bundle.sourceBundles.size > 0 &&
|
|
1166
|
+
bundle.manualSharedBundle == null &&
|
|
1167
|
+
!bundle.mainEntryAsset &&
|
|
1168
|
+
bundle.type === 'js') {
|
|
1169
|
+
sharedBundles.add(nodeId);
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
let clusters = (0, bundleMerge_1.findMergeCandidates)(bundleGraph, Array.from(sharedBundles), mergeConfig.map((config) => ({
|
|
1173
|
+
...config,
|
|
1174
|
+
sourceBundles: config.sourceBundles?.map((assetMatch) => {
|
|
1175
|
+
let sourceBundleNodeId = mergeSourceBundleLookup.get(assetMatch);
|
|
1176
|
+
if (sourceBundleNodeId == null) {
|
|
1177
|
+
throw new Error(`Source bundle ${assetMatch} not found in merge source bundle lookup`);
|
|
1178
|
+
}
|
|
1179
|
+
return sourceBundleNodeId;
|
|
1180
|
+
}),
|
|
1181
|
+
})));
|
|
1182
|
+
let mergedBundles = new Set();
|
|
1183
|
+
for (let cluster of clusters) {
|
|
1184
|
+
let [mergeTarget, ...rest] = cluster;
|
|
1185
|
+
for (let bundleIdToMerge of rest) {
|
|
1186
|
+
mergeBundles(mergeTarget, bundleIdToMerge, 'shared-merge');
|
|
1187
|
+
}
|
|
1188
|
+
mergedBundles.add(mergeTarget);
|
|
1189
|
+
}
|
|
1190
|
+
if ((0, feature_flags_1.getFeatureFlag)('supportWebpackChunkName')) {
|
|
1191
|
+
return mergedBundles;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
function mergeAsyncBundles({ bundleSize, maxOverfetchSize, ignore, }) {
|
|
1195
|
+
let mergeCandidates = [];
|
|
1196
|
+
let ignoreRegexes = ignore?.map((glob) => (0, utils_1.globToRegex)(glob)) ?? [];
|
|
1197
|
+
let isIgnored = (bundle) => {
|
|
1198
|
+
if (!bundle.mainEntryAsset) {
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
let mainEntryFilePath = path_1.default.relative(config.projectRoot, (0, nullthrows_1.default)(bundle.mainEntryAsset).filePath);
|
|
1202
|
+
return ignoreRegexes.some((regex) => regex.test(mainEntryFilePath));
|
|
1203
|
+
};
|
|
1204
|
+
for (let [_bundleRootAsset, [bundleRootBundleId]] of bundleRoots) {
|
|
1205
|
+
let bundleRootBundle = (0, nullthrows_1.default)(bundleGraph.getNode(bundleRootBundleId));
|
|
1206
|
+
(0, assert_1.default)(bundleRootBundle !== 'root');
|
|
1207
|
+
if (bundleRootBundle.type === 'js' &&
|
|
1208
|
+
bundleRootBundle.bundleBehavior !== 'inline' &&
|
|
1209
|
+
bundleRootBundle.bundleBehavior !== 'inlineIsolated' &&
|
|
1210
|
+
bundleRootBundle.size <= bundleSize &&
|
|
1211
|
+
!isIgnored(bundleRootBundle)) {
|
|
1212
|
+
mergeCandidates.push(bundleRootBundleId);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
let candidates = [];
|
|
1216
|
+
for (let i = 0; i < mergeCandidates.length; i++) {
|
|
1217
|
+
for (let j = i + 1; j < mergeCandidates.length; j++) {
|
|
1218
|
+
let a = mergeCandidates[i];
|
|
1219
|
+
let b = mergeCandidates[j];
|
|
1220
|
+
if (a === b)
|
|
1221
|
+
continue; // Skip self-comparison
|
|
1222
|
+
candidates.push(scoreAsyncMerge(a, b, maxOverfetchSize));
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
let sortByScore = (a, b) => {
|
|
1226
|
+
let diff = a.score - b.score;
|
|
1227
|
+
if (diff > 0) {
|
|
1228
|
+
return 1;
|
|
1229
|
+
}
|
|
1230
|
+
else if (diff < 0) {
|
|
1231
|
+
return -1;
|
|
1232
|
+
}
|
|
1233
|
+
return 0;
|
|
1234
|
+
};
|
|
1235
|
+
candidates = candidates
|
|
1236
|
+
.filter(({ overfetchSize, score }) => overfetchSize <= maxOverfetchSize && score > 0)
|
|
1237
|
+
.sort(sortByScore);
|
|
1238
|
+
// Tracks the bundles that have been merged
|
|
1239
|
+
let merged = new Set();
|
|
1240
|
+
// Tracks the deleted bundles to the bundle they were merged into.
|
|
1241
|
+
let mergeRemap = new Map();
|
|
1242
|
+
// Tracks the bundles that have been rescored and added back into the
|
|
1243
|
+
// candidates.
|
|
1244
|
+
let rescored = new utils_1.DefaultMap(() => new Set());
|
|
1245
|
+
do {
|
|
1246
|
+
let [a, b] = (0, nullthrows_1.default)(candidates.pop()).bundleIds;
|
|
1247
|
+
if (bundleGraph.hasNode(a) &&
|
|
1248
|
+
bundleGraph.hasNode(b) &&
|
|
1249
|
+
((!merged.has(a) && !merged.has(b)) || rescored.get(a).has(b))) {
|
|
1250
|
+
mergeRemap.set(b, a);
|
|
1251
|
+
merged.add(a);
|
|
1252
|
+
rescored.get(a).clear();
|
|
1253
|
+
mergeBundles(a, b, 'async-merge');
|
|
1254
|
+
continue;
|
|
1255
|
+
}
|
|
1256
|
+
// One or both of the bundles have been previously merged, so we'll
|
|
1257
|
+
// rescore and add the result back into the list of candidates.
|
|
1258
|
+
let getMergedBundleId = (bundleId) => {
|
|
1259
|
+
let seen = new Set();
|
|
1260
|
+
while (!bundleGraph.hasNode(bundleId) && !seen.has(bundleId)) {
|
|
1261
|
+
seen.add(bundleId);
|
|
1262
|
+
bundleId = (0, nullthrows_1.default)(mergeRemap.get(bundleId));
|
|
1263
|
+
}
|
|
1264
|
+
if (!bundleGraph.hasNode(bundleId)) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
return bundleId;
|
|
1268
|
+
};
|
|
1269
|
+
// Map a and b to their merged bundle ids if they've already been merged
|
|
1270
|
+
let currentA = getMergedBundleId(a);
|
|
1271
|
+
let currentB = getMergedBundleId(b);
|
|
1272
|
+
if (!currentA ||
|
|
1273
|
+
!currentB ||
|
|
1274
|
+
// Bundles are already merged
|
|
1275
|
+
currentA === currentB) {
|
|
1276
|
+
// This combiniation is not valid, so we skip it.
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
let candidate = scoreAsyncMerge(currentA, currentB, maxOverfetchSize);
|
|
1280
|
+
if (candidate.overfetchSize <= maxOverfetchSize && candidate.score > 0) {
|
|
1281
|
+
sorted_array_functions_1.default.add(candidates, candidate, sortByScore);
|
|
1282
|
+
rescored.get(currentA).add(currentB);
|
|
1283
|
+
}
|
|
1284
|
+
} while (candidates.length > 0);
|
|
1285
|
+
}
|
|
1286
|
+
function getBundle(bundleId) {
|
|
1287
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1288
|
+
if (bundle === 'root') {
|
|
1289
|
+
throw new Error(`Cannot access root bundle`);
|
|
1290
|
+
}
|
|
1291
|
+
if (bundle == null) {
|
|
1292
|
+
throw new Error(`Bundle ${bundleId} not found in bundle graph`);
|
|
1293
|
+
}
|
|
1294
|
+
return bundle;
|
|
1295
|
+
}
|
|
1296
|
+
function getBigIntFromContentKey(contentKey) {
|
|
1297
|
+
let b = Buffer.alloc(64);
|
|
1298
|
+
b.write(contentKey);
|
|
1299
|
+
return b.readBigInt64BE();
|
|
1300
|
+
}
|
|
1301
|
+
// Fix asset order in source bundles as they are likely now incorrect after shared bundle deletion
|
|
1302
|
+
if (modifiedSourceBundles.size > 0) {
|
|
1303
|
+
let assetOrderMap = new Map(assets.map((a, index) => [a, index]));
|
|
1304
|
+
for (let bundle of modifiedSourceBundles) {
|
|
1305
|
+
bundle.assets = new Set([...bundle.assets].sort((a, b) => {
|
|
1306
|
+
let aIndex = (0, nullthrows_1.default)(assetOrderMap.get(a));
|
|
1307
|
+
let bIndex = (0, nullthrows_1.default)(assetOrderMap.get(b));
|
|
1308
|
+
return aIndex - bIndex;
|
|
1309
|
+
}));
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function deleteBundle(bundleRoot) {
|
|
1313
|
+
bundleGraph.removeNode((0, nullthrows_1.default)(bundles.get(bundleRoot.id)));
|
|
1314
|
+
bundleRoots.delete(bundleRoot);
|
|
1315
|
+
bundles.delete(bundleRoot.id);
|
|
1316
|
+
let bundleRootId = assetToBundleRootNodeId.get(bundleRoot);
|
|
1317
|
+
if (bundleRootId != null && bundleRootGraph.hasNode(bundleRootId)) {
|
|
1318
|
+
bundleRootGraph.removeNode(bundleRootId);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
function getBundlesForBundleGroup(bundleGroupId) {
|
|
1322
|
+
let bundlesInABundleGroup = [];
|
|
1323
|
+
bundleGraph.traverse((nodeId) => {
|
|
1324
|
+
bundlesInABundleGroup.push(nodeId);
|
|
1325
|
+
}, bundleGroupId);
|
|
1326
|
+
return bundlesInABundleGroup;
|
|
1327
|
+
}
|
|
1328
|
+
function scoreAsyncMerge(bundleAId, bundleBId, maxOverfetchSize) {
|
|
1329
|
+
let bundleGroupA = new Set(getBundlesForBundleGroup(bundleAId));
|
|
1330
|
+
let bundleGroupB = new Set(getBundlesForBundleGroup(bundleBId));
|
|
1331
|
+
let overlapSize = 0;
|
|
1332
|
+
let overfetchSize = 0;
|
|
1333
|
+
for (let bundleId of new Set([...bundleGroupA, ...bundleGroupB])) {
|
|
1334
|
+
let bundle = getBundle(bundleId);
|
|
1335
|
+
if (bundleGroupA.has(bundleId) && bundleGroupB.has(bundleId)) {
|
|
1336
|
+
overlapSize += bundle.size;
|
|
1337
|
+
}
|
|
1338
|
+
else {
|
|
1339
|
+
overfetchSize += bundle.size;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
let overlapPercent = overlapSize / (overfetchSize + overlapSize);
|
|
1343
|
+
let bundleAParents = getBundleParents(bundleAId);
|
|
1344
|
+
let bundleBParents = getBundleParents(bundleBId);
|
|
1345
|
+
let sharedParentOverlap = 0;
|
|
1346
|
+
let sharedParentMismatch = 0;
|
|
1347
|
+
for (let bundleId of new Set([...bundleAParents, ...bundleBParents])) {
|
|
1348
|
+
if (bundleAParents.has(bundleId) && bundleBParents.has(bundleId)) {
|
|
1349
|
+
sharedParentOverlap++;
|
|
1350
|
+
}
|
|
1351
|
+
else {
|
|
1352
|
+
sharedParentMismatch++;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
let overfetchScore = overfetchSize / maxOverfetchSize;
|
|
1356
|
+
let sharedParentPercent = sharedParentOverlap / (sharedParentOverlap + sharedParentMismatch);
|
|
1357
|
+
let score = sharedParentPercent + overlapPercent + overfetchScore;
|
|
1358
|
+
return {
|
|
1359
|
+
overfetchSize,
|
|
1360
|
+
score,
|
|
1361
|
+
bundleIds: [bundleAId, bundleBId],
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
function getBundleParents(bundleId) {
|
|
1365
|
+
let parents = new Set();
|
|
1366
|
+
let { bundleRoots } = getBundle(bundleId);
|
|
1367
|
+
for (let bundleRoot of bundleRoots) {
|
|
1368
|
+
let bundleRootNodeId = (0, nullthrows_1.default)(assetToBundleRootNodeId.get(bundleRoot));
|
|
1369
|
+
for (let parentId of bundleRootGraph.getNodeIdsConnectedTo(bundleRootNodeId, graph_1.ALL_EDGE_TYPES)) {
|
|
1370
|
+
parents.add(parentId);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return parents;
|
|
1374
|
+
}
|
|
1375
|
+
function getBundleFromBundleRoot(bundleRoot) {
|
|
1376
|
+
let bundle = bundleGraph.getNode((0, nullthrows_1.default)(bundleRoots.get(bundleRoot))[0]);
|
|
1377
|
+
(0, assert_1.default)(bundle !== 'root' && bundle != null);
|
|
1378
|
+
return bundle;
|
|
1379
|
+
}
|
|
1380
|
+
function addAssetToBundleRoot(asset, bundleRoot) {
|
|
1381
|
+
let [bundleId, bundleGroupId] = (0, nullthrows_1.default)(bundleRoots.get(bundleRoot));
|
|
1382
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
1383
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
1384
|
+
if (asset.type !== bundle.type) {
|
|
1385
|
+
let bundleGroup = (0, nullthrows_1.default)(bundleGraph.getNode(bundleGroupId));
|
|
1386
|
+
(0, assert_1.default)(bundleGroup !== 'root');
|
|
1387
|
+
let key = (0, nullthrows_1.default)(bundleGroup.mainEntryAsset).id + '.' + asset.type;
|
|
1388
|
+
let typeChangeBundleId = bundles.get(key);
|
|
1389
|
+
if (typeChangeBundleId == null) {
|
|
1390
|
+
let typeChangeBundle = createBundle({
|
|
1391
|
+
uniqueKey: key,
|
|
1392
|
+
needsStableName: bundle.needsStableName,
|
|
1393
|
+
bundleBehavior: bundle.bundleBehavior,
|
|
1394
|
+
type: asset.type,
|
|
1395
|
+
target: bundle.target,
|
|
1396
|
+
env: bundle.env,
|
|
1397
|
+
});
|
|
1398
|
+
typeChangeBundleId = bundleGraph.addNode(typeChangeBundle);
|
|
1399
|
+
bundleGraph.addEdge(bundleId, typeChangeBundleId);
|
|
1400
|
+
bundles.set(key, typeChangeBundleId);
|
|
1401
|
+
bundle = typeChangeBundle;
|
|
1402
|
+
}
|
|
1403
|
+
else {
|
|
1404
|
+
bundle = (0, nullthrows_1.default)(bundleGraph.getNode(typeChangeBundleId));
|
|
1405
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
bundle.assets.add(asset);
|
|
1409
|
+
bundle.size += asset.stats.size;
|
|
1410
|
+
assignInlineConstants(asset, bundle);
|
|
1411
|
+
}
|
|
1412
|
+
function removeBundle(bundleGraph, bundleId, assetReference) {
|
|
1413
|
+
let bundle = (0, nullthrows_1.default)(bundleGraph.getNode(bundleId));
|
|
1414
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
1415
|
+
for (let asset of bundle.assets) {
|
|
1416
|
+
assetReference.set(asset, assetReference.get(asset).filter((t) => !t.includes(bundle)));
|
|
1417
|
+
for (let sourceBundleId of bundle.sourceBundles) {
|
|
1418
|
+
let sourceBundle = (0, nullthrows_1.default)(bundleGraph.getNode(sourceBundleId));
|
|
1419
|
+
(0, assert_1.default)(sourceBundle !== 'root');
|
|
1420
|
+
addAssetToBundleRoot(asset, (0, nullthrows_1.default)(sourceBundle.mainEntryAsset));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
bundleGraph.removeNode(bundleId);
|
|
1424
|
+
}
|
|
1425
|
+
stats.report((bundleId) => {
|
|
1426
|
+
let bundle = bundleGraph.getNode(bundleId);
|
|
1427
|
+
(0, assert_1.default)(bundle !== 'root');
|
|
1428
|
+
return bundle;
|
|
1429
|
+
});
|
|
1430
|
+
return {
|
|
1431
|
+
assets,
|
|
1432
|
+
bundleGraph,
|
|
1433
|
+
dependencyBundleGraph,
|
|
1434
|
+
bundleGroupBundleIds,
|
|
1435
|
+
assetReference,
|
|
1436
|
+
manualAssetToBundle,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function createBundle(opts) {
|
|
1440
|
+
if (opts.asset == null) {
|
|
1441
|
+
return {
|
|
1442
|
+
assets: new Set(),
|
|
1443
|
+
bundleBehavior: opts.bundleBehavior,
|
|
1444
|
+
env: (0, nullthrows_1.default)(opts.env),
|
|
1445
|
+
mainEntryAsset: null,
|
|
1446
|
+
bundleRoots: new Set(),
|
|
1447
|
+
manualSharedBundle: opts.manualSharedBundle,
|
|
1448
|
+
needsStableName: Boolean(opts.needsStableName),
|
|
1449
|
+
size: 0,
|
|
1450
|
+
sourceBundles: opts.sourceBundles ?? new Set(),
|
|
1451
|
+
target: opts.target,
|
|
1452
|
+
type: (0, nullthrows_1.default)(opts.type),
|
|
1453
|
+
uniqueKey: opts.uniqueKey,
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
let asset = (0, nullthrows_1.default)(opts.asset);
|
|
1457
|
+
return {
|
|
1458
|
+
assets: new Set([asset]),
|
|
1459
|
+
bundleBehavior: opts.bundleBehavior ?? asset.bundleBehavior,
|
|
1460
|
+
env: opts.env ?? asset.env,
|
|
1461
|
+
mainEntryAsset: asset,
|
|
1462
|
+
bundleRoots: new Set([asset]),
|
|
1463
|
+
manualSharedBundle: opts.manualSharedBundle,
|
|
1464
|
+
needsStableName: Boolean(opts.needsStableName),
|
|
1465
|
+
size: asset.stats.size,
|
|
1466
|
+
sourceBundles: opts.sourceBundles ?? new Set(),
|
|
1467
|
+
target: opts.target,
|
|
1468
|
+
type: opts.type ?? asset.type,
|
|
1469
|
+
uniqueKey: opts.uniqueKey,
|
|
1470
|
+
};
|
|
1471
|
+
}
|