@atlaspack/core 2.12.1-canary.3416 → 2.12.1-canary.3418

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.
@@ -94,30 +94,9 @@ function nameRuntimeBundle(bundle, siblingBundle) {
94
94
  * (assets created during the build that don't actually exist on disk) and then
95
95
  * linking them into the bundle graph.
96
96
  *
97
- * Usually, the assets returned from a runtime will go in the same bundle. It is
98
- * possible though, for a runtime to return an asset with a `parallel` priority,
99
- * which allows it to be moved to a separate bundle. In practice, this is
100
- * usually used to generate application manifest files.
101
- *
102
- * When adding a manifest bundle (a whole new separate bundle) during a runtime,
103
- * it needs to be added to a bundle group which will be potentially referenced
104
- * by another bundle group. To avoid trying to reference a manifest entry which
105
- * hasn't been created yet, we process the bundles from the bottom up, so that
106
- * children will always be available when parents try to reference them.
107
- *
108
- * However, when merging those connections in to the bundle graph, the reversed
109
- * order can create a situation where the child bundles thought they were coming
110
- * first and so take responsibility for loading in shared bundles. When the
111
- * parents actually load first, they're expecting bundles to be loaded which
112
- * aren't yet, creating module not found errors.
113
- *
114
- * To fix that, we restore the forward topological order once all the
115
- * connections are created.
116
- *
117
97
  * Introduction of manifest bundles: https://github.com/parcel-bundler/parcel/pull/8837
118
98
  * Introduction of reverse topology: https://github.com/parcel-bundler/parcel/pull/8981
119
99
  */
120
-
121
100
  async function applyRuntimes({
122
101
  bundleGraph,
123
102
  config,
@@ -131,7 +110,19 @@ async function applyRuntimes({
131
110
  }) {
132
111
  let runtimes = await config.getRuntimes();
133
112
 
134
- // Sort bundles into reverse topological order
113
+ /**
114
+ * Usually, the assets returned from a runtime will go in the same bundle. It is
115
+ * possible though, for a runtime to return an asset with a `parallel` priority,
116
+ * which allows it to be moved to a separate bundle. In practice, this is
117
+ * usually used to generate application manifest files.
118
+ *
119
+ * When adding a manifest bundle (a whole new separate bundle) during a runtime,
120
+ * it needs to be added to a bundle group which will be potentially referenced
121
+ * by another bundle group. To avoid trying to reference a manifest entry which
122
+ * hasn't been created yet, we process the bundles from the bottom up (topological
123
+ * order), so that children will always be available when parents try to reference
124
+ * them.
125
+ */
135
126
  let bundles = [];
136
127
  bundleGraph.traverseBundles({
137
128
  exit(bundle) {
@@ -179,6 +170,12 @@ async function applyRuntimes({
179
170
  isSource: true
180
171
  };
181
172
  let connectionBundle = bundle;
173
+
174
+ /**
175
+ * If a runtime asset is marked with a priority of `parallel` this
176
+ * means we need to create a new bundle for the asset and add it to
177
+ * all the same bundle groups.
178
+ */
182
179
  if (priority === 'parallel' && !bundle.needsStableName) {
183
180
  let bundleGroups = bundleGraph.getBundleGroupsContainingBundle(bundle);
184
181
  connectionBundle = (0, _nullthrows().default)(bundleGraph.createBundle({
@@ -193,6 +190,9 @@ async function applyRuntimes({
193
190
  bundleGraph.addBundleToBundleGroup(connectionBundle, bundleGroup);
194
191
  }
195
192
  bundleGraph.createBundleReference(bundle, connectionBundle);
193
+
194
+ // Ensure we name the bundle now as all other bundles have already
195
+ // been named as this point.
196
196
  nameRuntimeBundle(connectionBundle, bundle);
197
197
  }
198
198
  connectionMap.get(connectionBundle).push({
@@ -215,7 +215,22 @@ async function applyRuntimes({
215
215
  }
216
216
  }
217
217
 
218
- // Sort the bundles back into forward topological order
218
+ /**
219
+ * When merging the connections in to the bundle graph, the topological
220
+ * order can create module not found errors in some situations, often when HMR
221
+ * is enabled. To fix this, we put the connections into DFS order.
222
+ *
223
+ * Note: While DFS order seems to be the most reliable order to process the
224
+ * connections, this is likely due to it being close to the order that the bundles were
225
+ * inserted into the graph. There is a known issue where runtime assets marked
226
+ * as `isEntry` can create scenarios where there is no correct load order that
227
+ * won't error, as the entry runtime assets are added to many bundles in a
228
+ * single bundle group but their dependencies are not.
229
+ *
230
+ * This issue is almost exclusive to HMR scenarios as the two HMR runtime
231
+ * plugins (@atlaspack/runtime-browser-hmr and @atlaspack/runtime-react-refresh)
232
+ * are the only known cases where a runtime asset is marked as `isEntry`.
233
+ */
219
234
  let connections = [];
220
235
  bundleGraph.traverseBundles({
221
236
  enter(bundle) {
@@ -232,18 +247,29 @@ async function applyRuntimes({
232
247
  devDepRequests.set(`${devDepRequest.specifier}:${(0, _projectPath.fromProjectPathRelative)(devDepRequest.resolveFrom)}`, devDepRequest);
233
248
  await (0, _DevDepRequest.runDevDepRequest)(api, devDepRequest);
234
249
  }
250
+
251
+ // Create a new AssetGraph from the generated runtime assets which also runs
252
+ // transforms and resolves all dependencies.
235
253
  let {
236
254
  assetGraph: runtimesAssetGraph,
237
255
  changedAssets
238
256
  } = await reconcileNewRuntimes(api, connections, optionsRef);
239
- let runtimesGraph = _BundleGraph2.default.fromAssetGraph(runtimesAssetGraph, options.mode === 'production', bundleGraph._publicIdByAssetId, bundleGraph._assetPublicIds);
240
257
 
241
- // Merge the runtimes graph into the main bundle graph.
242
- bundleGraph.merge(runtimesGraph);
243
- for (let [assetId, publicId] of runtimesGraph._publicIdByAssetId) {
258
+ // Convert the runtime AssetGraph into a BundleGraph, this includes assigning
259
+ // the assets their public ids
260
+ let runtimesBundleGraph = _BundleGraph2.default.fromAssetGraph(runtimesAssetGraph, options.mode === 'production', bundleGraph._publicIdByAssetId, bundleGraph._assetPublicIds);
261
+
262
+ // Merge the runtimes bundle graph into the main bundle graph.
263
+ bundleGraph.merge(runtimesBundleGraph);
264
+
265
+ // Add the public id mappings from the runtumes bundlegraph to the main bundle graph
266
+ for (let [assetId, publicId] of runtimesBundleGraph._publicIdByAssetId) {
244
267
  bundleGraph._publicIdByAssetId.set(assetId, publicId);
245
268
  bundleGraph._assetPublicIds.add(publicId);
246
269
  }
270
+
271
+ // Connect each of the generated runtime assets to bundles in the main bundle
272
+ // graph. This is like a mini-bundling algorithm for runtime assets.
247
273
  for (let {
248
274
  bundle,
249
275
  assetGroup,
@@ -256,16 +282,24 @@ async function applyRuntimes({
256
282
  let runtimeNodeId = assetGroupAssetNodeIds[0];
257
283
  let runtimeNode = (0, _nullthrows().default)(runtimesAssetGraph.getNode(runtimeNodeId));
258
284
  (0, _assert().default)(runtimeNode.type === 'asset');
285
+
286
+ // Find the asset that the runtime asset should be connected from by resolving
287
+ // it's dependency.
259
288
  let resolution = dependency && bundleGraph.getResolvedAsset((0, _Dependency.dependencyToInternalDependency)(dependency), bundle);
260
- let runtimesGraphRuntimeNodeId = runtimesGraph._graph.getNodeIdByContentKey(runtimeNode.id);
289
+
290
+ // Walk all the dependencies of the runtime assets and check if they are
291
+ // already reachable from the bundle that the runtime asset is assigned to.
292
+ // If so, we add them to `duplicatedContentKeys` to be skipped when assigning
293
+ // assets to bundles.
294
+ let runtimesBundleGraphRuntimeNodeId = runtimesBundleGraph._graph.getNodeIdByContentKey(runtimeNode.id);
261
295
  let duplicatedContentKeys = new Set();
262
- runtimesGraph._graph.traverse((nodeId, _, actions) => {
263
- let node = (0, _nullthrows().default)(runtimesGraph._graph.getNode(nodeId));
296
+ runtimesBundleGraph._graph.traverse((nodeId, _, actions) => {
297
+ let node = (0, _nullthrows().default)(runtimesBundleGraph._graph.getNode(nodeId));
264
298
  if (node.type !== 'dependency') {
265
299
  return;
266
300
  }
267
- let assets = runtimesGraph._graph.getNodeIdsConnectedFrom(nodeId).map(assetNodeId => {
268
- let assetNode = (0, _nullthrows().default)(runtimesGraph._graph.getNode(assetNodeId));
301
+ let assets = runtimesBundleGraph._graph.getNodeIdsConnectedFrom(nodeId).map(assetNodeId => {
302
+ let assetNode = (0, _nullthrows().default)(runtimesBundleGraph._graph.getNode(assetNodeId));
269
303
  (0, _assert().default)(assetNode.type === 'asset');
270
304
  return assetNode.value;
271
305
  });
@@ -275,12 +309,14 @@ async function applyRuntimes({
275
309
  actions.skipChildren();
276
310
  }
277
311
  }
278
- }, runtimesGraphRuntimeNodeId);
312
+ }, runtimesBundleGraphRuntimeNodeId);
279
313
  let bundleNodeId = bundleGraph._graph.getNodeIdByContentKey(bundle.id);
280
314
  let bundleGraphRuntimeNodeId = bundleGraph._graph.getNodeIdByContentKey(runtimeNode.id); // the node id is not constant between graphs
281
315
 
282
- runtimesGraph._graph.traverse((nodeId, _, actions) => {
283
- let node = (0, _nullthrows().default)(runtimesGraph._graph.getNode(nodeId));
316
+ // Assign the runtime assets and all of it's depepdencies to the bundle unless
317
+ // we have detected it as already being reachable from this bundle via `duplicatedContentKeys`.
318
+ runtimesBundleGraph._graph.traverse((nodeId, _, actions) => {
319
+ let node = (0, _nullthrows().default)(runtimesBundleGraph._graph.getNode(nodeId));
284
320
  if (node.type === 'asset' || node.type === 'dependency') {
285
321
  if (duplicatedContentKeys.has(node.id)) {
286
322
  actions.skipChildren();
@@ -289,7 +325,7 @@ async function applyRuntimes({
289
325
  const bundleGraphNodeId = bundleGraph._graph.getNodeIdByContentKey(node.id); // the node id is not constant between graphs
290
326
  bundleGraph._graph.addEdge(bundleNodeId, bundleGraphNodeId, _BundleGraph2.bundleGraphEdgeTypes.contains);
291
327
  }
292
- }, runtimesGraphRuntimeNodeId);
328
+ }, runtimesBundleGraphRuntimeNodeId);
293
329
  if (isEntry) {
294
330
  bundleGraph._graph.addEdge(bundleNodeId, bundleGraphRuntimeNodeId);
295
331
  bundle.entryAssetIds.unshift(runtimeNode.id);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaspack/core",
3
- "version": "2.12.1-canary.3416+3bbd0a6d7",
3
+ "version": "2.12.1-canary.3418+49a56fea2",
4
4
  "license": "(MIT OR Apache-2.0)",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,20 +20,20 @@
20
20
  "check-ts": "tsc --noEmit index.d.ts"
21
21
  },
22
22
  "dependencies": {
23
- "@atlaspack/cache": "2.12.1-canary.3416+3bbd0a6d7",
24
- "@atlaspack/diagnostic": "2.12.1-canary.3416+3bbd0a6d7",
25
- "@atlaspack/events": "2.12.1-canary.3416+3bbd0a6d7",
26
- "@atlaspack/feature-flags": "2.12.1-canary.3416+3bbd0a6d7",
27
- "@atlaspack/fs": "2.12.1-canary.3416+3bbd0a6d7",
28
- "@atlaspack/graph": "3.2.1-canary.3416+3bbd0a6d7",
29
- "@atlaspack/logger": "2.12.1-canary.3416+3bbd0a6d7",
30
- "@atlaspack/package-manager": "2.12.1-canary.3416+3bbd0a6d7",
31
- "@atlaspack/plugin": "2.12.1-canary.3416+3bbd0a6d7",
32
- "@atlaspack/profiler": "2.12.1-canary.3416+3bbd0a6d7",
33
- "@atlaspack/rust": "2.12.1-canary.3416+3bbd0a6d7",
34
- "@atlaspack/types": "2.12.1-canary.3416+3bbd0a6d7",
35
- "@atlaspack/utils": "2.12.1-canary.3416+3bbd0a6d7",
36
- "@atlaspack/workers": "2.12.1-canary.3416+3bbd0a6d7",
23
+ "@atlaspack/cache": "2.12.1-canary.3418+49a56fea2",
24
+ "@atlaspack/diagnostic": "2.12.1-canary.3418+49a56fea2",
25
+ "@atlaspack/events": "2.12.1-canary.3418+49a56fea2",
26
+ "@atlaspack/feature-flags": "2.12.1-canary.3418+49a56fea2",
27
+ "@atlaspack/fs": "2.12.1-canary.3418+49a56fea2",
28
+ "@atlaspack/graph": "3.2.1-canary.3418+49a56fea2",
29
+ "@atlaspack/logger": "2.12.1-canary.3418+49a56fea2",
30
+ "@atlaspack/package-manager": "2.12.1-canary.3418+49a56fea2",
31
+ "@atlaspack/plugin": "2.12.1-canary.3418+49a56fea2",
32
+ "@atlaspack/profiler": "2.12.1-canary.3418+49a56fea2",
33
+ "@atlaspack/rust": "2.12.1-canary.3418+49a56fea2",
34
+ "@atlaspack/types": "2.12.1-canary.3418+49a56fea2",
35
+ "@atlaspack/utils": "2.12.1-canary.3418+49a56fea2",
36
+ "@atlaspack/workers": "2.12.1-canary.3418+49a56fea2",
37
37
  "@mischnic/json-sourcemap": "^0.1.0",
38
38
  "@parcel/source-map": "^2.1.1",
39
39
  "base-x": "^3.0.8",
@@ -55,5 +55,5 @@
55
55
  "browser": {
56
56
  "./src/serializerCore.js": "./src/serializerCore.browser.js"
57
57
  },
58
- "gitHead": "3bbd0a6d756f69bc7ebeb2db4c97c05617b3000d"
58
+ "gitHead": "49a56fea20468bd4cb26ecf69f9b3a200986a35b"
59
59
  }
@@ -66,30 +66,9 @@ function nameRuntimeBundle(
66
66
  * (assets created during the build that don't actually exist on disk) and then
67
67
  * linking them into the bundle graph.
68
68
  *
69
- * Usually, the assets returned from a runtime will go in the same bundle. It is
70
- * possible though, for a runtime to return an asset with a `parallel` priority,
71
- * which allows it to be moved to a separate bundle. In practice, this is
72
- * usually used to generate application manifest files.
73
- *
74
- * When adding a manifest bundle (a whole new separate bundle) during a runtime,
75
- * it needs to be added to a bundle group which will be potentially referenced
76
- * by another bundle group. To avoid trying to reference a manifest entry which
77
- * hasn't been created yet, we process the bundles from the bottom up, so that
78
- * children will always be available when parents try to reference them.
79
- *
80
- * However, when merging those connections in to the bundle graph, the reversed
81
- * order can create a situation where the child bundles thought they were coming
82
- * first and so take responsibility for loading in shared bundles. When the
83
- * parents actually load first, they're expecting bundles to be loaded which
84
- * aren't yet, creating module not found errors.
85
- *
86
- * To fix that, we restore the forward topological order once all the
87
- * connections are created.
88
- *
89
69
  * Introduction of manifest bundles: https://github.com/parcel-bundler/parcel/pull/8837
90
70
  * Introduction of reverse topology: https://github.com/parcel-bundler/parcel/pull/8981
91
71
  */
92
-
93
72
  export default async function applyRuntimes<TResult: RequestResult>({
94
73
  bundleGraph,
95
74
  config,
@@ -113,7 +92,19 @@ export default async function applyRuntimes<TResult: RequestResult>({
113
92
  |}): Promise<Map<string, Asset>> {
114
93
  let runtimes = await config.getRuntimes();
115
94
 
116
- // Sort bundles into reverse topological order
95
+ /**
96
+ * Usually, the assets returned from a runtime will go in the same bundle. It is
97
+ * possible though, for a runtime to return an asset with a `parallel` priority,
98
+ * which allows it to be moved to a separate bundle. In practice, this is
99
+ * usually used to generate application manifest files.
100
+ *
101
+ * When adding a manifest bundle (a whole new separate bundle) during a runtime,
102
+ * it needs to be added to a bundle group which will be potentially referenced
103
+ * by another bundle group. To avoid trying to reference a manifest entry which
104
+ * hasn't been created yet, we process the bundles from the bottom up (topological
105
+ * order), so that children will always be available when parents try to reference
106
+ * them.
107
+ */
117
108
  let bundles = [];
118
109
  bundleGraph.traverseBundles({
119
110
  exit(bundle) {
@@ -175,6 +166,11 @@ export default async function applyRuntimes<TResult: RequestResult>({
175
166
 
176
167
  let connectionBundle = bundle;
177
168
 
169
+ /**
170
+ * If a runtime asset is marked with a priority of `parallel` this
171
+ * means we need to create a new bundle for the asset and add it to
172
+ * all the same bundle groups.
173
+ */
178
174
  if (priority === 'parallel' && !bundle.needsStableName) {
179
175
  let bundleGroups =
180
176
  bundleGraph.getBundleGroupsContainingBundle(bundle);
@@ -198,6 +194,8 @@ export default async function applyRuntimes<TResult: RequestResult>({
198
194
  }
199
195
  bundleGraph.createBundleReference(bundle, connectionBundle);
200
196
 
197
+ // Ensure we name the bundle now as all other bundles have already
198
+ // been named as this point.
201
199
  nameRuntimeBundle(connectionBundle, bundle);
202
200
  }
203
201
 
@@ -221,9 +219,23 @@ export default async function applyRuntimes<TResult: RequestResult>({
221
219
  }
222
220
  }
223
221
 
224
- // Sort the bundles back into forward topological order
222
+ /**
223
+ * When merging the connections in to the bundle graph, the topological
224
+ * order can create module not found errors in some situations, often when HMR
225
+ * is enabled. To fix this, we put the connections into DFS order.
226
+ *
227
+ * Note: While DFS order seems to be the most reliable order to process the
228
+ * connections, this is likely due to it being close to the order that the bundles were
229
+ * inserted into the graph. There is a known issue where runtime assets marked
230
+ * as `isEntry` can create scenarios where there is no correct load order that
231
+ * won't error, as the entry runtime assets are added to many bundles in a
232
+ * single bundle group but their dependencies are not.
233
+ *
234
+ * This issue is almost exclusive to HMR scenarios as the two HMR runtime
235
+ * plugins (@atlaspack/runtime-browser-hmr and @atlaspack/runtime-react-refresh)
236
+ * are the only known cases where a runtime asset is marked as `isEntry`.
237
+ */
225
238
  let connections: Array<RuntimeConnection> = [];
226
-
227
239
  bundleGraph.traverseBundles({
228
240
  enter(bundle) {
229
241
  connections.push(...connectionMap.get(bundle));
@@ -249,23 +261,31 @@ export default async function applyRuntimes<TResult: RequestResult>({
249
261
  await runDevDepRequest(api, devDepRequest);
250
262
  }
251
263
 
264
+ // Create a new AssetGraph from the generated runtime assets which also runs
265
+ // transforms and resolves all dependencies.
252
266
  let {assetGraph: runtimesAssetGraph, changedAssets} =
253
267
  await reconcileNewRuntimes(api, connections, optionsRef);
254
268
 
255
- let runtimesGraph = InternalBundleGraph.fromAssetGraph(
269
+ // Convert the runtime AssetGraph into a BundleGraph, this includes assigning
270
+ // the assets their public ids
271
+ let runtimesBundleGraph = InternalBundleGraph.fromAssetGraph(
256
272
  runtimesAssetGraph,
257
273
  options.mode === 'production',
258
274
  bundleGraph._publicIdByAssetId,
259
275
  bundleGraph._assetPublicIds,
260
276
  );
261
277
 
262
- // Merge the runtimes graph into the main bundle graph.
263
- bundleGraph.merge(runtimesGraph);
264
- for (let [assetId, publicId] of runtimesGraph._publicIdByAssetId) {
278
+ // Merge the runtimes bundle graph into the main bundle graph.
279
+ bundleGraph.merge(runtimesBundleGraph);
280
+
281
+ // Add the public id mappings from the runtumes bundlegraph to the main bundle graph
282
+ for (let [assetId, publicId] of runtimesBundleGraph._publicIdByAssetId) {
265
283
  bundleGraph._publicIdByAssetId.set(assetId, publicId);
266
284
  bundleGraph._assetPublicIds.add(publicId);
267
285
  }
268
286
 
287
+ // Connect each of the generated runtime assets to bundles in the main bundle
288
+ // graph. This is like a mini-bundling algorithm for runtime assets.
269
289
  for (let {bundle, assetGroup, dependency, isEntry} of connections) {
270
290
  let assetGroupNode = nodeFromAssetGroup(assetGroup);
271
291
  let assetGroupAssetNodeIds = runtimesAssetGraph.getNodeIdsConnectedFrom(
@@ -276,6 +296,8 @@ export default async function applyRuntimes<TResult: RequestResult>({
276
296
  let runtimeNode = nullthrows(runtimesAssetGraph.getNode(runtimeNodeId));
277
297
  invariant(runtimeNode.type === 'asset');
278
298
 
299
+ // Find the asset that the runtime asset should be connected from by resolving
300
+ // it's dependency.
279
301
  let resolution =
280
302
  dependency &&
281
303
  bundleGraph.getResolvedAsset(
@@ -283,20 +305,25 @@ export default async function applyRuntimes<TResult: RequestResult>({
283
305
  bundle,
284
306
  );
285
307
 
286
- let runtimesGraphRuntimeNodeId = runtimesGraph._graph.getNodeIdByContentKey(
287
- runtimeNode.id,
288
- );
308
+ // Walk all the dependencies of the runtime assets and check if they are
309
+ // already reachable from the bundle that the runtime asset is assigned to.
310
+ // If so, we add them to `duplicatedContentKeys` to be skipped when assigning
311
+ // assets to bundles.
312
+ let runtimesBundleGraphRuntimeNodeId =
313
+ runtimesBundleGraph._graph.getNodeIdByContentKey(runtimeNode.id);
289
314
  let duplicatedContentKeys: Set<ContentKey> = new Set();
290
- runtimesGraph._graph.traverse((nodeId, _, actions) => {
291
- let node = nullthrows(runtimesGraph._graph.getNode(nodeId));
315
+ runtimesBundleGraph._graph.traverse((nodeId, _, actions) => {
316
+ let node = nullthrows(runtimesBundleGraph._graph.getNode(nodeId));
292
317
  if (node.type !== 'dependency') {
293
318
  return;
294
319
  }
295
320
 
296
- let assets = runtimesGraph._graph
321
+ let assets = runtimesBundleGraph._graph
297
322
  .getNodeIdsConnectedFrom(nodeId)
298
323
  .map(assetNodeId => {
299
- let assetNode = nullthrows(runtimesGraph._graph.getNode(assetNodeId));
324
+ let assetNode = nullthrows(
325
+ runtimesBundleGraph._graph.getNode(assetNodeId),
326
+ );
300
327
  invariant(assetNode.type === 'asset');
301
328
  return assetNode.value;
302
329
  });
@@ -310,15 +337,17 @@ export default async function applyRuntimes<TResult: RequestResult>({
310
337
  actions.skipChildren();
311
338
  }
312
339
  }
313
- }, runtimesGraphRuntimeNodeId);
340
+ }, runtimesBundleGraphRuntimeNodeId);
314
341
 
315
342
  let bundleNodeId = bundleGraph._graph.getNodeIdByContentKey(bundle.id);
316
343
  let bundleGraphRuntimeNodeId = bundleGraph._graph.getNodeIdByContentKey(
317
344
  runtimeNode.id,
318
345
  ); // the node id is not constant between graphs
319
346
 
320
- runtimesGraph._graph.traverse((nodeId, _, actions) => {
321
- let node = nullthrows(runtimesGraph._graph.getNode(nodeId));
347
+ // Assign the runtime assets and all of it's depepdencies to the bundle unless
348
+ // we have detected it as already being reachable from this bundle via `duplicatedContentKeys`.
349
+ runtimesBundleGraph._graph.traverse((nodeId, _, actions) => {
350
+ let node = nullthrows(runtimesBundleGraph._graph.getNode(nodeId));
322
351
  if (node.type === 'asset' || node.type === 'dependency') {
323
352
  if (duplicatedContentKeys.has(node.id)) {
324
353
  actions.skipChildren();
@@ -334,7 +363,7 @@ export default async function applyRuntimes<TResult: RequestResult>({
334
363
  bundleGraphEdgeTypes.contains,
335
364
  );
336
365
  }
337
- }, runtimesGraphRuntimeNodeId);
366
+ }, runtimesBundleGraphRuntimeNodeId);
338
367
 
339
368
  if (isEntry) {
340
369
  bundleGraph._graph.addEdge(bundleNodeId, bundleGraphRuntimeNodeId);