rsbuild-plugin-react-router 0.2.0 → 0.3.1

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.
Files changed (105) hide show
  1. package/README.md +213 -201
  2. package/dist/451.js +1103 -0
  3. package/dist/bounded-cache.d.ts +1 -0
  4. package/dist/build-manifest.d.ts +7 -4
  5. package/dist/build-output-transforms.d.ts +30 -0
  6. package/dist/concurrency.d.ts +3 -0
  7. package/dist/config-imports.d.ts +10 -0
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/dev-background-resources.d.ts +38 -0
  10. package/dist/dev-generation.d.ts +25 -0
  11. package/dist/dev-runtime-artifacts.d.ts +47 -0
  12. package/dist/dev-runtime-compilation.d.ts +47 -0
  13. package/dist/dev-runtime-controller.d.ts +14 -0
  14. package/dist/dev-runtime-session.d.ts +34 -0
  15. package/dist/dev-server.d.ts +16 -2
  16. package/dist/effect-runtime.d.ts +18 -0
  17. package/dist/export-utils.d.ts +15 -7
  18. package/dist/index.cjs +13775 -1412
  19. package/dist/index.d.ts +8 -1
  20. package/dist/index.js +9429 -1501
  21. package/dist/lazy-compilation-prewarm.d.ts +25 -0
  22. package/dist/lazy-compilation.d.ts +6 -0
  23. package/dist/manifest.d.ts +48 -10
  24. package/dist/modify-browser-manifest.d.ts +30 -13
  25. package/dist/parallel-route-transform-protocol.d.ts +25 -0
  26. package/dist/parallel-route-transform-worker.d.ts +1 -0
  27. package/dist/parallel-route-transform-worker.js +38 -0
  28. package/dist/parallel-route-transforms.d.ts +29 -0
  29. package/dist/performance.d.ts +26 -0
  30. package/dist/plugin-utils.d.ts +2 -12
  31. package/dist/prerender-build.d.ts +36 -0
  32. package/dist/prerender.d.ts +27 -2
  33. package/dist/react-router-config.d.ts +15 -5
  34. package/dist/route-artifacts.d.ts +33 -0
  35. package/dist/route-ast.d.ts +21 -0
  36. package/dist/route-chunks.d.ts +9 -1
  37. package/dist/route-component-transform.d.ts +5 -0
  38. package/dist/route-export-pruning.d.ts +6 -0
  39. package/dist/route-export-resolution.d.ts +5 -0
  40. package/dist/route-transform-tasks.d.ts +47 -0
  41. package/dist/route-watch.d.ts +27 -0
  42. package/dist/server-build-plan.d.ts +22 -0
  43. package/dist/server-build-resolution.d.ts +3 -0
  44. package/dist/server-utils.d.ts +3 -2
  45. package/dist/ssr-externals.d.ts +1 -0
  46. package/dist/templates/entry.server.cjs +3 -3
  47. package/dist/templates/entry.server.js +3 -3
  48. package/dist/typegen.d.ts +15 -0
  49. package/dist/types.d.ts +41 -14
  50. package/dist/virtual-modules.d.ts +2 -0
  51. package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
  52. package/dist/yuku.d.ts +15 -0
  53. package/package.json +24 -19
  54. package/src/bounded-cache.ts +18 -0
  55. package/src/build-manifest.ts +205 -0
  56. package/src/build-output-transforms.ts +273 -0
  57. package/src/concurrency.ts +15 -0
  58. package/src/config-imports.ts +83 -0
  59. package/src/constants.ts +91 -0
  60. package/src/dev-background-resources.ts +255 -0
  61. package/src/dev-generation.ts +690 -0
  62. package/src/dev-runtime-artifacts.ts +239 -0
  63. package/src/dev-runtime-compilation.ts +171 -0
  64. package/src/dev-runtime-controller.ts +542 -0
  65. package/src/dev-runtime-session.ts +191 -0
  66. package/src/dev-server.ts +88 -0
  67. package/src/effect-runtime.ts +130 -0
  68. package/src/export-utils.ts +278 -0
  69. package/src/index.ts +990 -0
  70. package/src/lazy-compilation-prewarm.ts +279 -0
  71. package/src/lazy-compilation.ts +101 -0
  72. package/src/manifest.ts +591 -0
  73. package/src/modify-browser-manifest.ts +246 -0
  74. package/src/parallel-route-transform-protocol.ts +35 -0
  75. package/src/parallel-route-transform-worker.ts +82 -0
  76. package/src/parallel-route-transforms.ts +458 -0
  77. package/src/performance.ts +254 -0
  78. package/src/plugin-utils.ts +82 -0
  79. package/src/prerender-build.ts +687 -0
  80. package/src/prerender.ts +349 -0
  81. package/src/react-router-config.ts +245 -0
  82. package/src/route-artifacts.ts +155 -0
  83. package/src/route-ast.ts +163 -0
  84. package/src/route-chunks.ts +857 -0
  85. package/src/route-component-transform.ts +314 -0
  86. package/src/route-config.ts +106 -0
  87. package/src/route-export-pruning.ts +668 -0
  88. package/src/route-export-resolution.ts +329 -0
  89. package/src/route-transform-tasks.ts +249 -0
  90. package/src/route-watch.ts +357 -0
  91. package/src/server-build-plan.ts +91 -0
  92. package/src/server-build-resolution.ts +131 -0
  93. package/src/server-utils.ts +111 -0
  94. package/src/ssr-externals.ts +59 -0
  95. package/src/templates/context.ts +12 -0
  96. package/src/templates/entry.client.tsx +12 -0
  97. package/src/templates/entry.server.tsx +76 -0
  98. package/src/typegen.ts +178 -0
  99. package/src/types.ts +92 -0
  100. package/src/validation/validate-plugin-order.ts +76 -0
  101. package/src/virtual-modules.ts +30 -0
  102. package/src/warnings/warn-on-client-source-maps.ts +96 -0
  103. package/src/yuku.ts +67 -0
  104. package/dist/0~rslib-runtime.js +0 -16
  105. package/dist/babel.d.ts +0 -8
@@ -0,0 +1,542 @@
1
+ import type { RsbuildConfig, RsbuildPluginAPI, Rspack } from '@rsbuild/core';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { ServerBuild } from 'react-router';
4
+ import { PLUGIN_NAME } from './constants.js';
5
+ import {
6
+ beginDevCompilerAttempt,
7
+ clearDevCompilerStart,
8
+ createCompilationIdentityTracker,
9
+ createDevCompilerPair,
10
+ hasPendingCompilation,
11
+ isLatestStartedCompilation,
12
+ markDevCompilerPending,
13
+ resetDevCompilerPair,
14
+ type DevCompilerPair,
15
+ } from './dev-runtime-compilation.js';
16
+ import {
17
+ createReactRouterDevRuntime,
18
+ loadReactRouterServerBuild,
19
+ registerReactRouterDevRuntime,
20
+ unregisterReactRouterDevRuntime,
21
+ } from './dev-generation.js';
22
+ import {
23
+ getEnvironmentStats,
24
+ snapshotDevChangedFiles,
25
+ type DevGraphIdentity,
26
+ type DevRuntimeStats,
27
+ type ReactRouterDevBuildPlan,
28
+ type ReactRouterDevManifestSet,
29
+ } from './dev-runtime-artifacts.js';
30
+ import {
31
+ createDevRuntimeSessionManager,
32
+ type RuntimeBinding,
33
+ } from './dev-runtime-session.js';
34
+ import {
35
+ normalizeEffectError,
36
+ runPluginEffect,
37
+ tryPluginPromise,
38
+ tryPluginSync,
39
+ } from './effect-runtime.js';
40
+
41
+ type ServerSetup = Exclude<
42
+ NonNullable<NonNullable<RsbuildConfig['server']>['setup']>,
43
+ unknown[]
44
+ >;
45
+
46
+ export type ReactRouterDevRuntimeController = {
47
+ captureWeb: (
48
+ compilation: Rspack.Compilation,
49
+ manifestsByEntryName: ReactRouterDevManifestSet
50
+ ) => void;
51
+ createBuildLoader: (entryName?: string) => () => Promise<ServerBuild>;
52
+ };
53
+
54
+ type CreateControllerOptions = {
55
+ api: RsbuildPluginAPI;
56
+ isBuild: boolean;
57
+ buildPlan: ReactRouterDevBuildPlan;
58
+ };
59
+
60
+ const escapeHtml = (value: string): string =>
61
+ value
62
+ .replaceAll('&', '&amp;')
63
+ .replaceAll('<', '&lt;')
64
+ .replaceAll('>', '&gt;');
65
+
66
+ const CSS_SOURCE_RELOAD_DELAY_MS = 1000;
67
+
68
+ export const createReactRouterDevRuntimeController = ({
69
+ api,
70
+ isBuild,
71
+ buildPlan,
72
+ }: CreateControllerOptions): ReactRouterDevRuntimeController => {
73
+ if (isBuild) {
74
+ return {
75
+ captureWeb() {},
76
+ createBuildLoader() {
77
+ return () =>
78
+ Promise.reject(
79
+ new Error(
80
+ `[${PLUGIN_NAME}] The development server runtime is unavailable during a production build.`
81
+ )
82
+ );
83
+ },
84
+ };
85
+ }
86
+
87
+ let scheduledCssAssetOwnershipReload:
88
+ | ReturnType<typeof setTimeout>
89
+ | undefined;
90
+ let lastCssAssetOwnershipReloadAt = 0;
91
+ let reloadAfterCssAssetOwnershipRemoval = false;
92
+
93
+ const sendCssAssetOwnershipReload = (): void => {
94
+ const binding = sessions.getActiveBinding();
95
+ if (!binding) {
96
+ return;
97
+ }
98
+ lastCssAssetOwnershipReloadAt = Date.now();
99
+ binding.server.sockWrite('full-reload', { path: '*' });
100
+ };
101
+
102
+ const scheduleCssAssetOwnershipReload = (): void => {
103
+ if (scheduledCssAssetOwnershipReload) {
104
+ clearTimeout(scheduledCssAssetOwnershipReload);
105
+ }
106
+ const scheduledAt = Date.now();
107
+ scheduledCssAssetOwnershipReload = setTimeout(() => {
108
+ scheduledCssAssetOwnershipReload = undefined;
109
+ if (lastCssAssetOwnershipReloadAt > scheduledAt) {
110
+ return;
111
+ }
112
+ sendCssAssetOwnershipReload();
113
+ }, CSS_SOURCE_RELOAD_DELAY_MS);
114
+ };
115
+
116
+ const closeBinding = (binding: RuntimeBinding, error?: Error): void => {
117
+ if (scheduledCssAssetOwnershipReload) {
118
+ clearTimeout(scheduledCssAssetOwnershipReload);
119
+ scheduledCssAssetOwnershipReload = undefined;
120
+ }
121
+ reloadAfterCssAssetOwnershipRemoval = false;
122
+ const pair = binding.compilers;
123
+ if (pair) {
124
+ resetDevCompilerPair(pair);
125
+ }
126
+ binding.compilers = undefined;
127
+ binding.runtime.close(error);
128
+ unregisterReactRouterDevRuntime(binding.server, binding.runtime);
129
+ };
130
+
131
+ const sessions = createDevRuntimeSessionManager(closeBinding);
132
+ const compilationIdentities = createCompilationIdentityTracker();
133
+ const { getCompilationIdentity } = compilationIdentities;
134
+
135
+ const finishRuntimeAttemptEffect = (
136
+ binding: RuntimeBinding,
137
+ pair: DevCompilerPair,
138
+ stats: DevRuntimeStats,
139
+ changes: Parameters<RuntimeBinding['runtime']['finishAttempt']>[1],
140
+ identity: Parameters<RuntimeBinding['runtime']['finishAttempt']>[2]
141
+ ): Effect.Effect<void, Error, never> =>
142
+ tryPluginPromise(() =>
143
+ binding.runtime.finishAttempt(stats, changes, identity)
144
+ ).pipe(
145
+ Effect.flatMap(result =>
146
+ tryPluginSync(() => {
147
+ if (
148
+ result === 'retry-node' &&
149
+ sessions.getActiveBinding()?.id === binding.id
150
+ ) {
151
+ pair.node.watching?.invalidate();
152
+ }
153
+ })
154
+ ),
155
+ Effect.catchAll(cause =>
156
+ tryPluginSync(() => {
157
+ if (sessions.getActiveBinding()?.id === binding.id) {
158
+ binding.runtime.failAttempt(normalizeEffectError(cause));
159
+ }
160
+ })
161
+ )
162
+ );
163
+
164
+ const finishRuntimeAttempt = (
165
+ binding: RuntimeBinding,
166
+ pair: DevCompilerPair,
167
+ stats: DevRuntimeStats,
168
+ changes: Parameters<RuntimeBinding['runtime']['finishAttempt']>[1],
169
+ identity: Parameters<RuntimeBinding['runtime']['finishAttempt']>[2]
170
+ ): Promise<void> =>
171
+ runPluginEffect(
172
+ finishRuntimeAttemptEffect(binding, pair, stats, changes, identity)
173
+ );
174
+
175
+ const flushSettledAttempt = (
176
+ binding: RuntimeBinding,
177
+ pair: DevCompilerPair
178
+ ): void => {
179
+ const pending = pair.pendingAttempt;
180
+ if (
181
+ !pending ||
182
+ sessions.getActiveBinding()?.id !== binding.id ||
183
+ !pair.settledCompilations.has(pending.webCompilation) ||
184
+ !pair.settledCompilations.has(pending.nodeCompilation)
185
+ ) {
186
+ return;
187
+ }
188
+ pair.pendingAttempt = undefined;
189
+ if (
190
+ !isLatestStartedCompilation(pending.identity.web, pair.latestWebStart) ||
191
+ !isLatestStartedCompilation(pending.identity.node, pair.latestNodeStart)
192
+ ) {
193
+ return;
194
+ }
195
+ void finishRuntimeAttempt(
196
+ binding,
197
+ pair,
198
+ pending.stats,
199
+ pending.changes,
200
+ pending.identity
201
+ );
202
+ };
203
+
204
+ const rejectUnsupportedCompiler = (reason: string): void => {
205
+ const message = `[${PLUGIN_NAME}] Could not coordinate React Router development output because ${reason}.`;
206
+ api.logger.warn(message);
207
+ const binding = sessions.getActiveBinding();
208
+ if (!binding) {
209
+ return;
210
+ }
211
+ const error = new Error(message);
212
+ sessions.terminate(binding, error);
213
+ };
214
+
215
+ // Rsbuild runs server.setup before onBeforeStartDevServer. Prepending the
216
+ // observer here ensures setup callbacks cannot capture an unobserved close.
217
+ api.modifyRsbuildConfig({
218
+ order: 'post',
219
+ handler(config) {
220
+ const existingSetup = config.server?.setup;
221
+ const setup = existingSetup
222
+ ? Array.isArray(existingSetup)
223
+ ? existingSetup
224
+ : [existingSetup]
225
+ : [];
226
+ const observeServer: ServerSetup = context => {
227
+ if (context.action === 'dev') {
228
+ sessions.observeClose(context.server);
229
+ }
230
+ };
231
+ return {
232
+ ...config,
233
+ server: {
234
+ ...config.server,
235
+ setup: [observeServer, ...setup],
236
+ },
237
+ };
238
+ },
239
+ });
240
+
241
+ api.onBeforeStartDevServer({
242
+ order: 'pre',
243
+ async handler({ server }) {
244
+ sessions.assertCanStart();
245
+ const runtime = createReactRouterDevRuntime({
246
+ server,
247
+ buildPlan,
248
+ onEvaluationError(error) {
249
+ if (sessions.getActiveBinding()?.runtime !== runtime) {
250
+ return;
251
+ }
252
+ api.logger.error(error.message);
253
+ server.sockWrite('errors', {
254
+ text: [error.message],
255
+ html: escapeHtml(error.message),
256
+ });
257
+ },
258
+ onCssAssetOwnershipChanged(change) {
259
+ if (sessions.getActiveBinding()?.runtime !== runtime) {
260
+ return;
261
+ }
262
+ reloadAfterCssAssetOwnershipRemoval = change === 'removed';
263
+ sendCssAssetOwnershipReload();
264
+ },
265
+ onRouteManifestChanged() {
266
+ if (sessions.getActiveBinding()?.runtime !== runtime) {
267
+ return;
268
+ }
269
+ server.sockWrite('full-reload', { path: '*' });
270
+ },
271
+ onWarning: message => api.logger.warn(message),
272
+ });
273
+ const binding = sessions.createBinding(server, runtime);
274
+ registerReactRouterDevRuntime(server, runtime);
275
+ sessions.bindCloseObservation(binding);
276
+ },
277
+ });
278
+
279
+ api.onCloseDevServer({
280
+ order: 'pre',
281
+ handler() {
282
+ const binding = sessions.getActiveBinding();
283
+ if (!binding) {
284
+ return;
285
+ }
286
+ closeBinding(binding);
287
+ sessions.markClosing(binding);
288
+ },
289
+ });
290
+
291
+ api.onBeforeDevCompile({
292
+ order: 'pre',
293
+ handler() {
294
+ const binding = sessions.getActiveBinding();
295
+ const pair = binding?.compilers;
296
+ if (!binding || !pair || hasPendingCompilation(pair)) {
297
+ return;
298
+ }
299
+ beginDevCompilerAttempt(pair);
300
+ binding.runtime.beginAttempt();
301
+ },
302
+ });
303
+
304
+ api.onAfterCreateCompiler(({ compiler }) => {
305
+ if (!('compilers' in compiler)) {
306
+ rejectUnsupportedCompiler('Rsbuild did not create a multi-compiler');
307
+ return;
308
+ }
309
+ const web = compiler.compilers.find(item => item.name === 'web');
310
+ const node = compiler.compilers.find(item => item.name === 'node');
311
+ if (!web || !node) {
312
+ rejectUnsupportedCompiler('the web or node compiler was missing');
313
+ return;
314
+ }
315
+ const binding = sessions.getActiveBinding();
316
+ if (!binding) {
317
+ return;
318
+ }
319
+ const pair: DevCompilerPair = createDevCompilerPair({ web, node });
320
+ binding.compilers = pair;
321
+ const sessionId = binding.id;
322
+ const runtime = binding.runtime;
323
+ const failCurrentAttempt = (side: 'web' | 'node', error: Error): void => {
324
+ if (sessions.getActiveBinding()?.id === sessionId) {
325
+ if (side === 'web') {
326
+ clearDevCompilerStart(pair, 'latestWebStart');
327
+ } else {
328
+ clearDevCompilerStart(pair, 'latestNodeStart');
329
+ }
330
+ runtime.failAttempt(error);
331
+ }
332
+ };
333
+ const beginCompilerAttempt = (
334
+ side: 'latestWebStart' | 'latestNodeStart'
335
+ ): void => {
336
+ if (
337
+ sessions.getActiveBinding()?.id === sessionId &&
338
+ pair[side]?.status !== 'pending'
339
+ ) {
340
+ // Invalidation can arrive before the aggregate before-compile hook.
341
+ // Supersede any evaluation that could resolve in that gap immediately.
342
+ if (markDevCompilerPending(pair, side)) {
343
+ runtime.beginAttempt();
344
+ }
345
+ if (side === 'latestWebStart' && reloadAfterCssAssetOwnershipRemoval) {
346
+ reloadAfterCssAssetOwnershipRemoval = false;
347
+ scheduleCssAssetOwnershipReload();
348
+ }
349
+ }
350
+ };
351
+ web.hooks.invalid.tap(`${PLUGIN_NAME}:dev-web-invalid`, () =>
352
+ beginCompilerAttempt('latestWebStart')
353
+ );
354
+ node.hooks.invalid.tap(`${PLUGIN_NAME}:dev-node-invalid`, () =>
355
+ beginCompilerAttempt('latestNodeStart')
356
+ );
357
+ web.hooks.done.tap(
358
+ { name: `${PLUGIN_NAME}:dev-web-complete`, stage: -1000 },
359
+ stats => {
360
+ if (sessions.getActiveBinding()?.id !== sessionId) {
361
+ return;
362
+ }
363
+ pair.latestCompletedWebIdentity = getCompilationIdentity(
364
+ stats.compilation
365
+ );
366
+ pair.latestCompletedWebStats = stats;
367
+ }
368
+ );
369
+ node.hooks.done.tap(
370
+ { name: `${PLUGIN_NAME}:dev-node-complete`, stage: -1000 },
371
+ stats => {
372
+ if (sessions.getActiveBinding()?.id === sessionId) {
373
+ pair.latestCompletedNodeStats = stats;
374
+ }
375
+ }
376
+ );
377
+ web.hooks.thisCompilation.tap(
378
+ `${PLUGIN_NAME}:dev-web-compilation`,
379
+ compilation => {
380
+ if (sessions.getActiveBinding()?.id === sessionId) {
381
+ if (pair.currentAttemptIdentity) {
382
+ compilationIdentities.setAttemptIdentityForCompilation(
383
+ compilation,
384
+ pair.currentAttemptIdentity
385
+ );
386
+ }
387
+ pair.latestWebStart = {
388
+ status: 'started',
389
+ identity: getCompilationIdentity(compilation),
390
+ };
391
+ if (reloadAfterCssAssetOwnershipRemoval) {
392
+ reloadAfterCssAssetOwnershipRemoval = false;
393
+ scheduleCssAssetOwnershipReload();
394
+ }
395
+ }
396
+ }
397
+ );
398
+ node.hooks.thisCompilation.tap(
399
+ `${PLUGIN_NAME}:dev-node-web-compilation`,
400
+ compilation => {
401
+ if (sessions.getActiveBinding()?.id !== sessionId) {
402
+ return;
403
+ }
404
+ pair.latestNodeStart = {
405
+ status: 'started',
406
+ identity: getCompilationIdentity(compilation),
407
+ };
408
+ if (pair.currentAttemptIdentity) {
409
+ compilationIdentities.setAttemptIdentityForCompilation(
410
+ compilation,
411
+ pair.currentAttemptIdentity
412
+ );
413
+ }
414
+ if (pair.latestCompletedWebIdentity) {
415
+ compilationIdentities.setWebIdentityForNodeCompilation(
416
+ compilation,
417
+ pair.latestCompletedWebIdentity
418
+ );
419
+ }
420
+ }
421
+ );
422
+ const settleCompilation = (stats: Rspack.Stats): void => {
423
+ if (sessions.getActiveBinding()?.id !== sessionId) {
424
+ return;
425
+ }
426
+ pair.settledCompilations.add(stats.compilation);
427
+ flushSettledAttempt(binding, pair);
428
+ };
429
+ web.hooks.afterDone.tap(
430
+ `${PLUGIN_NAME}:dev-web-settled`,
431
+ settleCompilation
432
+ );
433
+ node.hooks.afterDone.tap(
434
+ `${PLUGIN_NAME}:dev-node-settled`,
435
+ settleCompilation
436
+ );
437
+ web.hooks.failed.tap(`${PLUGIN_NAME}:dev-web-failed`, error =>
438
+ failCurrentAttempt('web', error)
439
+ );
440
+ node.hooks.failed.tap(`${PLUGIN_NAME}:dev-node-failed`, error =>
441
+ failCurrentAttempt('node', error)
442
+ );
443
+ });
444
+
445
+ api.onAfterDevCompile(async ({ stats }) => {
446
+ const binding = sessions.getActiveBinding();
447
+ const pair = binding?.compilers;
448
+ if (!binding || !pair) {
449
+ return;
450
+ }
451
+ const webStats =
452
+ getEnvironmentStats(stats, 'web') ?? pair.latestCompletedWebStats;
453
+ const nodeStats =
454
+ getEnvironmentStats(stats, 'node') ?? pair.latestCompletedNodeStats;
455
+ if (
456
+ (webStats && webStats.compilation.compiler !== pair.web) ||
457
+ (nodeStats && nodeStats.compilation.compiler !== pair.node)
458
+ ) {
459
+ return;
460
+ }
461
+ const webIdentity = webStats
462
+ ? getCompilationIdentity(webStats.compilation)
463
+ : undefined;
464
+ const nodeIdentity = nodeStats
465
+ ? getCompilationIdentity(nodeStats.compilation)
466
+ : undefined;
467
+ if (
468
+ !isLatestStartedCompilation(webIdentity, pair.latestWebStart) ||
469
+ !isLatestStartedCompilation(nodeIdentity, pair.latestNodeStart)
470
+ ) {
471
+ return;
472
+ }
473
+ const changes = {
474
+ web: snapshotDevChangedFiles(pair.web),
475
+ node: snapshotDevChangedFiles(pair.node),
476
+ };
477
+ const webAttempt = webStats
478
+ ? compilationIdentities.getAttemptIdentityForCompilation(
479
+ webStats.compilation
480
+ )
481
+ : undefined;
482
+ const nodeAttempt = nodeStats
483
+ ? compilationIdentities.getAttemptIdentityForCompilation(
484
+ nodeStats.compilation
485
+ )
486
+ : undefined;
487
+ const identity: DevGraphIdentity = {
488
+ web: webIdentity,
489
+ node: nodeIdentity,
490
+ nodeWeb: nodeStats
491
+ ? compilationIdentities.getWebIdentityForNodeCompilation(
492
+ nodeStats.compilation
493
+ )
494
+ : undefined,
495
+ attempt:
496
+ webAttempt && nodeAttempt && webAttempt === nodeAttempt
497
+ ? webAttempt
498
+ : undefined,
499
+ };
500
+ const finishStats: DevRuntimeStats =
501
+ webStats && nodeStats ? { web: webStats, node: nodeStats } : stats;
502
+ if (!webStats || !nodeStats) {
503
+ await finishRuntimeAttempt(binding, pair, finishStats, changes, identity);
504
+ return;
505
+ }
506
+ pair.pendingAttempt = {
507
+ stats: finishStats,
508
+ changes,
509
+ identity,
510
+ webCompilation: webStats.compilation,
511
+ nodeCompilation: nodeStats.compilation,
512
+ };
513
+ flushSettledAttempt(binding, pair);
514
+ });
515
+
516
+ return {
517
+ captureWeb(compilation, manifestsByEntryName): void {
518
+ const binding = sessions.getActiveBinding();
519
+ if (binding?.compilers?.web === compilation.compiler) {
520
+ binding.runtime.captureWeb(compilation, manifestsByEntryName);
521
+ }
522
+ },
523
+
524
+ createBuildLoader(entryName?: string): () => Promise<ServerBuild> {
525
+ const server = sessions.getActiveBinding()?.server;
526
+ if (server) {
527
+ return () => loadReactRouterServerBuild(server, entryName);
528
+ }
529
+ const state = sessions.getState();
530
+ if (state.status === 'terminal') {
531
+ const { error } = state;
532
+ return () => Promise.reject(error);
533
+ }
534
+ return () =>
535
+ Promise.reject(
536
+ new Error(
537
+ `[${PLUGIN_NAME}] The development server runtime is not ready.`
538
+ )
539
+ );
540
+ },
541
+ };
542
+ };