UNPKG

5.16 kBJavaScriptView Raw
1// @flow strict-local
2
3import type {Dependency} from '@parcel/types';
4import type {
5 AssetRequestDesc,
6 Bundle as InternalBundle,
7 NodeId,
8 ParcelOptions,
9} from './types';
10import type InternalBundleGraph from './BundleGraph';
11import type AssetGraphBuilder from './AssetGraphBuilder';
12import type ParcelConfig from './ParcelConfig';
13import type PluginOptions from './public/PluginOptions';
14
15import invariant from 'assert';
16import nullthrows from 'nullthrows';
17import AssetGraph, {nodeFromAssetGroup} from './AssetGraph';
18import BundleGraph from './public/BundleGraph';
19import {removeAssetGroups} from './BundleGraph';
20import {NamedBundle} from './public/Bundle';
21import {setDifference} from '@parcel/utils';
22import {PluginLogger} from '@parcel/logger';
23import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
24
25type RuntimeConnection = {|
26 bundle: InternalBundle,
27 assetRequest: AssetRequestDesc,
28 dependency: ?Dependency,
29 isEntry: ?boolean,
30|};
31
32export default async function applyRuntimes({
33 bundleGraph,
34 config,
35 options,
36 pluginOptions,
37 runtimesBuilder,
38}: {|
39 bundleGraph: InternalBundleGraph,
40 config: ParcelConfig,
41 options: ParcelOptions,
42 pluginOptions: PluginOptions,
43 runtimesBuilder: AssetGraphBuilder,
44|}): Promise<void> {
45 let connections: Array<RuntimeConnection> = [];
46
47 for (let bundle of bundleGraph.getBundles()) {
48 let runtimes = await config.getRuntimes(bundle.env.context);
49 for (let runtime of runtimes) {
50 try {
51 let applied = await runtime.plugin.apply({
52 bundle: new NamedBundle(bundle, bundleGraph, options),
53 bundleGraph: new BundleGraph(bundleGraph, options),
54 options: pluginOptions,
55 logger: new PluginLogger({origin: runtime.name}),
56 });
57
58 if (applied) {
59 let runtimeAssets = Array.isArray(applied) ? applied : [applied];
60 for (let {code, dependency, filePath, isEntry} of runtimeAssets) {
61 let assetRequest = {
62 code,
63 filePath,
64 env: bundle.env,
65 };
66 connections.push({
67 bundle,
68 assetRequest,
69 dependency: dependency,
70 isEntry,
71 });
72 }
73 }
74 } catch (e) {
75 throw new ThrowableDiagnostic({
76 diagnostic: errorToDiagnostic(e, runtime.name),
77 });
78 }
79 }
80 }
81
82 let runtimesAssetGraph = await reconcileNewRuntimes(
83 runtimesBuilder,
84 connections,
85 );
86
87 let runtimesGraph = removeAssetGroups(runtimesAssetGraph);
88
89 // merge the transformed asset into the bundle's graph, and connect
90 // the node to it.
91 bundleGraph._graph.merge(runtimesGraph);
92
93 for (let {bundle, assetRequest, dependency, isEntry} of connections) {
94 let assetGroupNode = nodeFromAssetGroup(assetRequest);
95 let assetGroupAssets = runtimesAssetGraph.getNodesConnectedFrom(
96 assetGroupNode,
97 );
98 invariant(assetGroupAssets.length === 1);
99 let runtimeNode = assetGroupAssets[0];
100 invariant(runtimeNode.type === 'asset');
101
102 let duplicatedAssetIds: Set<NodeId> = new Set();
103 runtimesGraph.traverse((node, _, actions) => {
104 if (node.type !== 'dependency') {
105 return;
106 }
107
108 let assets = runtimesGraph.getNodesConnectedFrom(node).map(assetNode => {
109 invariant(assetNode.type === 'asset');
110 return assetNode.value;
111 });
112
113 for (let asset of assets) {
114 if (bundleGraph.isAssetInAncestorBundles(bundle, asset)) {
115 duplicatedAssetIds.add(asset.id);
116 actions.skipChildren();
117 }
118 }
119 }, runtimeNode);
120
121 runtimesGraph.traverse((node, _, actions) => {
122 if (node.type === 'asset' || node.type === 'dependency') {
123 if (duplicatedAssetIds.has(node.id)) {
124 actions.skipChildren();
125 return;
126 }
127
128 bundleGraph._graph.addEdge(bundle.id, node.id, 'contains');
129 }
130 }, runtimeNode);
131
132 bundleGraph._graph.addEdge(
133 dependency
134 ? dependency.id
135 : nullthrows(bundleGraph._graph.getNode(bundle.id)).id,
136 runtimeNode.id,
137 );
138
139 if (isEntry) {
140 bundle.entryAssetIds.unshift(runtimeNode.id);
141 }
142 }
143}
144
145async function reconcileNewRuntimes(
146 runtimesBuilder: AssetGraphBuilder,
147 connections: Array<RuntimeConnection>,
148): Promise<AssetGraph> {
149 let {assetGraph} = runtimesBuilder;
150
151 let assetRequestNodesById = new Map(
152 connections
153 .map(t => t.assetRequest)
154 .map(request => {
155 let node = nodeFromAssetGroup(request);
156 return [node.id, node];
157 }),
158 );
159 let newRequestIds = new Set(assetRequestNodesById.keys());
160 let oldRequestIds = new Set(
161 assetGraph.getEntryAssetGroupNodes().map(node => node.id),
162 );
163
164 let toAdd = setDifference(newRequestIds, oldRequestIds);
165 let toRemove = setDifference(oldRequestIds, newRequestIds);
166
167 assetGraph.replaceNodesConnectedTo(
168 nullthrows(assetGraph.getRootNode()),
169 [...toAdd].map(requestId =>
170 nullthrows(assetRequestNodesById.get(requestId)),
171 ),
172 node => toRemove.has(node.id),
173 );
174
175 // rebuild the graph
176 return (await runtimesBuilder.build()).assetGraph;
177}