UNPKG

17.3 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.removeAssetGroups = removeAssetGroups;
7exports.default = void 0;
8
9var _assert = _interopRequireDefault(require("assert"));
10
11var _crypto = _interopRequireDefault(require("crypto"));
12
13var _nullthrows = _interopRequireDefault(require("nullthrows"));
14
15var _utils = require("@parcel/utils");
16
17var _utils2 = require("./utils");
18
19var _Graph = _interopRequireWildcard(require("./Graph"));
20
21function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
22
23function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
24
25function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
26
27function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
28
29class BundleGraph {
30 // TODO: These hashes are being invalidated in mutative methods, but this._graph is not a private
31 // property so it is possible to reach in and mutate the graph without invalidating these hashes.
32 // It needs to be exposed in BundlerRunner for now based on how applying runtimes works and the
33 // BundlerRunner takes care of invalidating hashes when runtimes are applied, but this is not ideal.
34 constructor({
35 graph,
36 bundleContentHashes
37 }) {
38 _defineProperty(this, "_bundleContentHashes", void 0);
39
40 _defineProperty(this, "_graph", void 0);
41
42 this._graph = graph;
43 this._bundleContentHashes = bundleContentHashes || new Map();
44 }
45
46 static deserialize(opts) {
47 return new BundleGraph({
48 graph: opts._graph,
49 bundleContentHashes: opts._bundleContentHashes
50 });
51 }
52
53 addAssetGraphToBundle(asset, bundle) {
54 // The root asset should be reached directly from the bundle in traversal.
55 // Its children will be traversed from there.
56 this._graph.addEdge(bundle.id, asset.id);
57
58 this._graph.traverse((node, _, actions) => {
59 if (node.type === 'bundle_group') {
60 actions.skipChildren();
61 return;
62 }
63
64 if (node.type === 'asset' && !this.bundleHasAsset(bundle, node.value)) {
65 bundle.stats.size += node.value.stats.size;
66 }
67
68 if (node.type === 'asset' || node.type === 'dependency') {
69 this._graph.addEdge(bundle.id, node.id, 'contains');
70 }
71
72 if (node.type === 'dependency') {
73 for (let bundleGroupNode of this._graph.getNodesConnectedFrom(node).filter(node => node.type === 'bundle_group')) {
74 this._graph.addEdge(bundle.id, bundleGroupNode.id, 'bundle');
75 }
76 }
77 }, (0, _nullthrows.default)(this._graph.getNode(asset.id)));
78
79 this._bundleContentHashes.delete(bundle.id);
80 }
81
82 removeAssetGraphFromBundle(asset, bundle) {
83 // Remove all contains edges from the bundle to the nodes in the asset's
84 // subgraph.
85 this._graph.traverse((node, context, actions) => {
86 if (node.type === 'bundle_group') {
87 actions.skipChildren();
88 return;
89 }
90
91 if (node.type === 'asset' || node.type === 'dependency') {
92 if (this._graph.hasEdge(bundle.id, node.id, 'contains')) {
93 this._graph.removeEdge(bundle.id, node.id, 'contains', // Removing this contains edge should not orphan the connected node. This
94 // is disabled for performance reasons as these edges are removed as part
95 // of a traversal, and checking for orphans becomes quite expensive in
96 // aggregate.
97 false
98 /* removeOrphans */
99 );
100
101 if (node.type === 'asset') {
102 bundle.stats.size -= asset.stats.size;
103 }
104 } else {
105 actions.skipChildren();
106 }
107 }
108
109 if (node.type === 'dependency') {
110 for (let bundleGroupNode of this._graph.getNodesConnectedFrom(node).filter(node => node.type === 'bundle_group')) {
111 let inboundDependencies = this._graph.getNodesConnectedTo(bundleGroupNode).filter(node => node.type === 'dependency'); // If every inbound dependency to this bundle group does not belong to this bundle,
112 // then the connection between this bundle and the group is safe to remove.
113
114
115 if (inboundDependencies.every(depNode => !this._graph.hasEdge(bundle.id, depNode.id, 'contains'))) {
116 this._graph.removeEdge(bundle.id, bundleGroupNode.id, 'bundle');
117 }
118 }
119 }
120 }, (0, _nullthrows.default)(this._graph.getNode(asset.id))); // Remove the untyped edge from the bundle to the entry.
121
122
123 if (this._graph.hasEdge(bundle.id, asset.id)) {
124 this._graph.removeEdge(bundle.id, asset.id);
125 }
126
127 this._bundleContentHashes.delete(bundle.id);
128 }
129
130 createAssetReference(dependency, asset) {
131 this._graph.addEdge(dependency.id, asset.id, 'references');
132
133 if (this._graph.hasEdge(dependency.id, asset.id)) {
134 this._graph.removeEdge(dependency.id, asset.id);
135 }
136 }
137
138 findBundlesWithAsset(asset) {
139 return this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(asset.id)), 'contains').filter(node => node.type === 'bundle').map(node => {
140 (0, _assert.default)(node.type === 'bundle');
141 return node.value;
142 });
143 }
144
145 getDependencyAssets(dependency) {
146 let dependencyNode = (0, _nullthrows.default)(this._graph.getNode(dependency.id));
147 return this._graph.getNodesConnectedFrom(dependencyNode).filter(node => node.type === 'asset').map(node => {
148 (0, _assert.default)(node.type === 'asset');
149 return node.value;
150 });
151 }
152
153 getDependencyResolution(dep) {
154 let depNode = this._graph.getNode(dep.id);
155
156 if (!depNode) {
157 return null;
158 }
159
160 let res = null;
161
162 function findFirstAsset(node, _, traversal) {
163 if (node.type === 'asset') {
164 res = node.value;
165 traversal.stop();
166 } else if (node.id !== dep.id) {
167 traversal.skipChildren();
168 }
169 } // TODO: Combine with multiple edge type traversal?
170
171
172 this._graph.traverse(findFirstAsset, depNode);
173
174 if (!res) {
175 // Prefer real assets when resolving dependencies, but use the first
176 // asset reference in absence of a real one.
177 this._graph.traverse(findFirstAsset, depNode, 'references');
178 }
179
180 return res;
181 }
182
183 getDependencies(asset) {
184 let node = this._graph.getNode(asset.id);
185
186 if (!node) {
187 throw new Error('Asset not found');
188 }
189
190 return this._graph.getNodesConnectedFrom(node).map(node => {
191 (0, _assert.default)(node.type === 'dependency');
192 return node.value;
193 });
194 }
195
196 traverseAssets(bundle, visit) {
197 return this.traverseBundle(bundle, (0, _Graph.mapVisitor)(node => node.type === 'asset' ? node.value : null, visit));
198 }
199
200 isAssetReferenced(asset) {
201 return this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(asset.id)), 'references').length > 0;
202 }
203
204 isAssetReferencedByAssetType(asset, type) {
205 let referringBundles = new Set(this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(asset.id)), 'contains')); // is `asset` referenced by a dependency from an asset of `type`
206
207 return (0, _utils.flatMap)(this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(asset.id))).filter(node => {
208 // Does this dependency belong to a bundle that does not include the
209 // asset it resolves to? If so, this asset is needed by a bundle but
210 // does not belong to it.
211 return this._graph.getNodesConnectedTo(node, 'contains').filter(node => node.type === 'bundle').some(b => !referringBundles.has(b));
212 }), node => {
213 (0, _assert.default)(node.type === 'dependency');
214 return this._graph.getNodesConnectedTo(node, null);
215 }).filter(node => node.type === 'asset').some(node => {
216 (0, _assert.default)(node.type === 'asset');
217 return node.value.type === type;
218 });
219 }
220
221 hasParentBundleOfType(bundle, type) {
222 return (0, _utils.flatMap)(this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(bundle.id)), 'bundle'), node => this._graph.getNodesConnectedTo(node, 'bundle')).every(node => node.type === 'bundle' && node.value.type === type);
223 }
224
225 isAssetInAncestorBundles(bundle, asset) {
226 let parentBundleNodes = (0, _utils.flatMap)(this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(bundle.id)), 'bundle'), bundleGroupNode => {
227 (0, _assert.default)(bundleGroupNode.type === 'bundle_group');
228 return this._graph.getNodesConnectedTo(bundleGroupNode, 'bundle');
229 });
230 return parentBundleNodes.every(parentNode => {
231 let inBundle;
232
233 this._graph.traverseAncestors(parentNode, (node, ctx, actions) => {
234 if (node.type !== 'bundle' || node.id === bundle.id) {
235 return;
236 } // Don't deduplicate when context changes
237
238
239 if (node.value.env.context !== bundle.env.context) {
240 actions.skipChildren();
241 return;
242 }
243
244 if (this._graph.hasEdge(node.value.id, asset.id, 'contains')) {
245 inBundle = true;
246 actions.stop();
247 }
248 }, 'bundle');
249
250 return inBundle;
251 });
252 }
253
254 traverseBundle(bundle, visit) {
255 return this._graph.filteredTraverse((node, actions) => {
256 if (node.id === bundle.id) {
257 return;
258 }
259
260 if (node.type === 'dependency' || node.type === 'asset') {
261 if (this._graph.hasEdge(bundle.id, node.id, 'contains')) {
262 return node;
263 }
264 }
265
266 actions.skipChildren();
267 }, visit, (0, _nullthrows.default)(this._graph.getNode(bundle.id)));
268 }
269
270 traverseContents(visit) {
271 return this._graph.filteredTraverse(node => node.type === 'asset' || node.type === 'dependency' ? node : null, visit);
272 }
273
274 getChildBundles(bundle) {
275 let bundles = [];
276 this.traverseBundles((b, _, actions) => {
277 if (bundle.id === b.id) {
278 return;
279 }
280
281 bundles.push(b);
282 actions.skipChildren();
283 }, bundle);
284 return bundles;
285 }
286
287 traverseBundles(visit, startBundle) {
288 return this._graph.filteredTraverse(node => node.type === 'bundle' ? node.value : null, visit, startBundle ? (0, _nullthrows.default)(this._graph.getNode(startBundle.id)) : null, 'bundle');
289 }
290
291 getBundles() {
292 let bundles = [];
293 this.traverseBundles(bundle => {
294 bundles.push(bundle);
295 });
296 return bundles;
297 }
298
299 getTotalSize(asset) {
300 let size = 0;
301
302 this._graph.traverse((node, _, actions) => {
303 if (node.type === 'bundle_group') {
304 actions.skipChildren();
305 return;
306 }
307
308 if (node.type === 'asset') {
309 size += node.value.stats.size;
310 }
311 }, (0, _nullthrows.default)(this._graph.getNode(asset.id)));
312
313 return size;
314 }
315
316 getBundleGroupsContainingBundle(bundle) {
317 return this._graph.getNodesConnectedTo((0, _nullthrows.default)(this._graph.getNode(bundle.id)), 'bundle').filter(node => node.type === 'bundle_group').map(node => {
318 (0, _assert.default)(node.type === 'bundle_group');
319 return node.value;
320 });
321 }
322
323 getBundlesInBundleGroup(bundleGroup) {
324 return this._graph.getNodesConnectedFrom((0, _nullthrows.default)(this._graph.getNode((0, _utils2.getBundleGroupId)(bundleGroup))), 'bundle').filter(node => node.type === 'bundle').map(node => {
325 (0, _assert.default)(node.type === 'bundle');
326 return node.value;
327 });
328 }
329
330 getSiblingBundles(bundle) {
331 let siblings = [];
332 let bundleGroups = this.getBundleGroupsContainingBundle(bundle);
333
334 for (let bundleGroup of bundleGroups) {
335 let bundles = this.getBundlesInBundleGroup(bundleGroup);
336
337 for (let b of bundles) {
338 if (b.id !== bundle.id) {
339 siblings.push(b);
340 }
341 }
342 }
343
344 return siblings;
345 }
346
347 getIncomingDependencies(asset) {
348 let node = this._graph.getNode(asset.id);
349
350 if (!node) {
351 return [];
352 }
353
354 return this._graph.findAncestors(node, node => node.type === 'dependency').map(node => {
355 (0, _assert.default)(node.type === 'dependency');
356 return node.value;
357 });
358 }
359
360 bundleHasAsset(bundle, asset) {
361 return this._graph.hasEdge(bundle.id, asset.id, 'contains');
362 }
363
364 filteredTraverse(bundle, filter, visit) {
365 return this._graph.filteredTraverse(filter, visit, (0, _nullthrows.default)(this._graph.getNode(bundle.id)));
366 }
367
368 resolveSymbol(asset, symbol) {
369 let identifier = asset.symbols.get(symbol);
370
371 if (symbol === '*') {
372 return {
373 asset,
374 exportSymbol: '*',
375 symbol: identifier
376 };
377 }
378
379 let deps = this.getDependencies(asset).reverse();
380
381 for (let dep of deps) {
382 // If this is a re-export, find the original module.
383 let symbolLookup = new Map([...dep.symbols].map(([key, val]) => [val, key]));
384 let depSymbol = symbolLookup.get(identifier);
385
386 if (depSymbol != null) {
387 let resolved = this.getDependencyResolution(dep);
388
389 if (!resolved) {
390 // External module.
391 break;
392 }
393
394 let {
395 asset: resolvedAsset,
396 symbol: resolvedSymbol,
397 exportSymbol
398 } = this.resolveSymbol(resolved, depSymbol); // If it didn't resolve to anything (likely CommonJS), pass through where we got to
399
400 if (resolvedSymbol == null) {
401 return {
402 asset: resolvedAsset,
403 symbol: resolvedSymbol,
404 exportSymbol
405 };
406 } // Otherwise, keep the original symbol name along with the resolved symbol
407
408
409 return {
410 asset: resolvedAsset,
411 symbol: resolvedSymbol,
412 exportSymbol: symbol
413 };
414 } // If this module exports wildcards, resolve the original module.
415 // Default exports are excluded from wildcard exports.
416
417
418 if (dep.symbols.get('*') === '*' && symbol !== 'default') {
419 let resolved = (0, _nullthrows.default)(this.getDependencyResolution(dep));
420 let result = this.resolveSymbol(resolved, symbol);
421
422 if (result.symbol != null) {
423 return {
424 asset: result.asset,
425 symbol: result.symbol,
426 exportSymbol: symbol
427 };
428 }
429 }
430 }
431
432 return {
433 asset,
434 exportSymbol: symbol,
435 symbol: identifier
436 };
437 }
438
439 getExportedSymbols(asset) {
440 let symbols = [];
441
442 for (let symbol of asset.symbols.keys()) {
443 symbols.push(this.resolveSymbol(asset, symbol));
444 }
445
446 let deps = this.getDependencies(asset);
447
448 for (let dep of deps) {
449 if (dep.symbols.get('*') === '*') {
450 let resolved = (0, _nullthrows.default)(this.getDependencyResolution(dep));
451 let exported = this.getExportedSymbols(resolved).filter(s => s.exportSymbol !== 'default');
452 symbols.push(...exported);
453 }
454 }
455
456 return symbols;
457 }
458
459 getContentHash(bundle) {
460 let existingHash = this._bundleContentHashes.get(bundle.id);
461
462 if (existingHash != null) {
463 return existingHash;
464 }
465
466 let hash = _crypto.default.createHash('md5'); // TODO: sort??
467
468
469 this.traverseAssets(bundle, asset => {
470 hash.update([asset.outputHash, asset.filePath].join(':'));
471 });
472 let hashHex = hash.digest('hex');
473
474 this._bundleContentHashes.set(bundle.id, hashHex);
475
476 return hashHex;
477 }
478
479 getHash(bundle) {
480 let hash = _crypto.default.createHash('md5');
481
482 this.traverseBundles(childBundle => {
483 hash.update(this.getContentHash(childBundle));
484 }, bundle);
485 hash.update(JSON.stringify((0, _utils.objectSortedEntriesDeep)(bundle.env)));
486 return hash.digest('hex');
487 }
488
489}
490
491exports.default = BundleGraph;
492
493function removeAssetGroups(assetGraph) {
494 let graph = new _Graph.default();
495 let rootNode = assetGraph.getRootNode();
496 (0, _assert.default)(rootNode != null && rootNode.type === 'root');
497 graph.setRootNode(rootNode);
498 let assetGroupIds = new Set();
499
500 for (let [, node] of assetGraph.nodes) {
501 if (node.type === 'asset_group') {
502 assetGroupIds.add(node.id);
503 } else {
504 graph.addNode(node);
505 }
506 }
507
508 for (let edge of assetGraph.getAllEdges()) {
509 let fromIds;
510
511 if (assetGroupIds.has(edge.from)) {
512 fromIds = [...assetGraph.inboundEdges.get(edge.from).get(null)];
513 } else {
514 fromIds = [edge.from];
515 }
516
517 for (let from of fromIds) {
518 if (assetGroupIds.has(edge.to)) {
519 for (let to of assetGraph.outboundEdges.get(edge.to).get(null)) {
520 graph.addEdge(from, to);
521 }
522 } else {
523 graph.addEdge(from, edge.to);
524 }
525 }
526 }
527
528 return graph;
529}
\No newline at end of file