rsbuild-plugin-react-router 0.1.1 → 0.3.0

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 (99) hide show
  1. package/README.md +185 -204
  2. package/dist/451.js +1087 -0
  3. package/dist/bounded-cache.d.ts +1 -0
  4. package/dist/build-manifest.d.ts +1 -1
  5. package/dist/build-output-transforms.d.ts +30 -0
  6. package/dist/concurrency.d.ts +2 -0
  7. package/dist/config-imports.d.ts +3 -0
  8. package/dist/constants.d.ts +3 -0
  9. package/dist/dev-generation.d.ts +25 -0
  10. package/dist/dev-runtime-artifacts.d.ts +39 -0
  11. package/dist/dev-runtime-compilation.d.ts +32 -0
  12. package/dist/dev-runtime-controller.d.ts +14 -0
  13. package/dist/dev-runtime-session.d.ts +34 -0
  14. package/dist/dev-server.d.ts +11 -2
  15. package/dist/export-utils.d.ts +15 -7
  16. package/dist/index.cjs +3085 -1272
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +2039 -1299
  19. package/dist/lazy-compilation.d.ts +5 -0
  20. package/dist/manifest.d.ts +32 -9
  21. package/dist/modify-browser-manifest.d.ts +30 -13
  22. package/dist/parallel-route-transform-protocol.d.ts +25 -0
  23. package/dist/parallel-route-transform-worker.d.ts +1 -0
  24. package/dist/parallel-route-transform-worker.js +38 -0
  25. package/dist/parallel-route-transforms.d.ts +14 -0
  26. package/dist/performance.d.ts +26 -0
  27. package/dist/plugin-utils.d.ts +2 -12
  28. package/dist/prerender-build.d.ts +32 -0
  29. package/dist/prerender.d.ts +28 -2
  30. package/dist/react-router-config.d.ts +8 -1
  31. package/dist/route-artifacts.d.ts +33 -0
  32. package/dist/route-ast.d.ts +21 -0
  33. package/dist/route-chunks.d.ts +9 -1
  34. package/dist/route-component-transform.d.ts +5 -0
  35. package/dist/route-export-pruning.d.ts +6 -0
  36. package/dist/route-export-resolution.d.ts +5 -0
  37. package/dist/route-transform-tasks.d.ts +47 -0
  38. package/dist/route-watch.d.ts +27 -0
  39. package/dist/server-build-plan.d.ts +22 -0
  40. package/dist/server-utils.d.ts +3 -2
  41. package/dist/templates/entry.client.js +3 -3
  42. package/dist/templates/entry.server.cjs +11 -8
  43. package/dist/templates/entry.server.js +5 -5
  44. package/dist/typegen.d.ts +2 -0
  45. package/dist/types.d.ts +30 -12
  46. package/dist/virtual-modules.d.ts +2 -0
  47. package/dist/warnings/warn-on-client-source-maps.d.ts +1 -0
  48. package/dist/yuku.d.ts +15 -0
  49. package/package.json +26 -22
  50. package/src/bounded-cache.ts +18 -0
  51. package/src/build-manifest.ts +168 -0
  52. package/src/build-output-transforms.ts +273 -0
  53. package/src/concurrency.ts +34 -0
  54. package/src/config-imports.ts +45 -0
  55. package/src/constants.ts +91 -0
  56. package/src/dev-generation.ts +700 -0
  57. package/src/dev-runtime-artifacts.ts +207 -0
  58. package/src/dev-runtime-compilation.ts +92 -0
  59. package/src/dev-runtime-controller.ts +462 -0
  60. package/src/dev-runtime-session.ts +184 -0
  61. package/src/dev-server.ts +58 -0
  62. package/src/export-utils.ts +219 -0
  63. package/src/index.ts +1025 -0
  64. package/src/lazy-compilation.ts +94 -0
  65. package/src/manifest.ts +480 -0
  66. package/src/modify-browser-manifest.ts +245 -0
  67. package/src/parallel-route-transform-protocol.ts +35 -0
  68. package/src/parallel-route-transform-worker.ts +82 -0
  69. package/src/parallel-route-transforms.ts +332 -0
  70. package/src/performance.ts +254 -0
  71. package/src/plugin-utils.ts +82 -0
  72. package/src/prerender-build.ts +623 -0
  73. package/src/prerender.ts +367 -0
  74. package/src/react-router-config.ts +220 -0
  75. package/src/route-artifacts.ts +155 -0
  76. package/src/route-ast.ts +163 -0
  77. package/src/route-chunks.ts +857 -0
  78. package/src/route-component-transform.ts +314 -0
  79. package/src/route-config.ts +106 -0
  80. package/src/route-export-pruning.ts +668 -0
  81. package/src/route-export-resolution.ts +329 -0
  82. package/src/route-transform-tasks.ts +249 -0
  83. package/src/route-watch.ts +322 -0
  84. package/src/server-build-plan.ts +91 -0
  85. package/src/server-utils.ts +210 -0
  86. package/src/ssr-externals.ts +59 -0
  87. package/src/templates/context.ts +12 -0
  88. package/src/templates/entry.client.tsx +12 -0
  89. package/src/templates/entry.server.tsx +76 -0
  90. package/src/typegen.ts +49 -0
  91. package/src/types.ts +82 -0
  92. package/src/validation/validate-plugin-order.ts +76 -0
  93. package/src/virtual-modules.ts +30 -0
  94. package/src/warnings/warn-on-client-source-maps.ts +96 -0
  95. package/src/yuku.ts +67 -0
  96. package/dist/906.js +0 -1
  97. package/dist/946.js +0 -1
  98. package/dist/babel.d.ts +0 -8
  99. package/dist/rslib-runtime.js +0 -23
@@ -0,0 +1,623 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import { pathToFileURL } from 'node:url';
4
+ import fsExtra from 'fs-extra';
5
+ import type { RsbuildPluginAPI } from '@rsbuild/core';
6
+ import {
7
+ createRequestHandler,
8
+ matchRoutes,
9
+ type ServerBuild,
10
+ } from 'react-router';
11
+ import { dirname, relative, resolve } from 'pathe';
12
+ import { PLUGIN_NAME } from './constants.js';
13
+ import { getBuildManifest } from './build-manifest.js';
14
+ import {
15
+ generateReactRouterManifestForDev,
16
+ getReactRouterManifestForDev,
17
+ type ReactRouterManifestStats,
18
+ type RouteManifestModuleExports,
19
+ } from './manifest.js';
20
+ import {
21
+ createPrerenderRoutes,
22
+ getPrerenderConcurrency,
23
+ getSsrFalsePrerenderExportErrors,
24
+ normalizePrerenderMatchPath,
25
+ withBuildRequest,
26
+ } from './prerender.js';
27
+ import type {
28
+ Config,
29
+ ResolvedReactRouterConfig,
30
+ } from './react-router-config.js';
31
+ import { resolveServerBuildModule } from './server-utils.js';
32
+ import type { PluginOptions, Route } from './types.js';
33
+
34
+ type ReactRouterManifest = Awaited<
35
+ ReturnType<typeof getReactRouterManifestForDev>
36
+ >;
37
+
38
+ type BuildRouteModule = {
39
+ loader?: unknown;
40
+ default?: unknown;
41
+ ErrorBoundary?: unknown;
42
+ };
43
+
44
+ type PrerenderServerBuild = ServerBuild & {
45
+ routes: Record<string, { id?: string; module?: BuildRouteModule }>;
46
+ assets?: {
47
+ routes?: Record<string, { hasLoader?: boolean }>;
48
+ };
49
+ prerender?: string[];
50
+ };
51
+
52
+ type PrerenderBuildApi = Pick<
53
+ RsbuildPluginAPI,
54
+ 'logger' | 'getNormalizedConfig'
55
+ >;
56
+
57
+ type RunReactRouterPrerenderBuildOptions = {
58
+ api: PrerenderBuildApi;
59
+ hasWebEnvironment: boolean;
60
+ buildDirectory: string;
61
+ serverBuildFile?: string;
62
+ ssr: boolean;
63
+ isPrerenderEnabled: boolean;
64
+ prerenderConfig: Config['prerender'];
65
+ prerenderPaths: string[];
66
+ basename: string;
67
+ future: ResolvedReactRouterConfig['future'];
68
+ routes: Record<string, Route>;
69
+ latestBrowserManifest: ReactRouterManifest | null;
70
+ latestBrowserManifestModuleExports: RouteManifestModuleExports;
71
+ clientStats: ReactRouterManifestStats | undefined;
72
+ pluginOptions: PluginOptions;
73
+ appDirectory: string;
74
+ assetPrefix: string;
75
+ routeChunkOptions: Parameters<typeof getReactRouterManifestForDev>[5];
76
+ buildManifest: Awaited<ReturnType<typeof getBuildManifest>>;
77
+ resolvedConfigWithRoutes: ResolvedReactRouterConfig;
78
+ buildEnd: Config['buildEnd'];
79
+ };
80
+
81
+ const redirectStatusCodes = new Set([301, 302, 303, 307, 308]);
82
+
83
+ const getServerBuildPath = async ({
84
+ buildDirectory,
85
+ serverBuildFile,
86
+ }: {
87
+ buildDirectory: string;
88
+ serverBuildFile?: string;
89
+ }): Promise<{ serverBuildDir: string; serverBuildPath: string }> => {
90
+ const serverBuildDir = resolve(buildDirectory, 'server');
91
+ const defaultServerBuildFile = 'static/js/app.js';
92
+ const configuredServerBuildFile = serverBuildFile || 'index.js';
93
+ const configuredServerBuildPath = resolve(
94
+ serverBuildDir,
95
+ configuredServerBuildFile
96
+ );
97
+ const defaultServerBuildPath = resolve(
98
+ serverBuildDir,
99
+ defaultServerBuildFile
100
+ );
101
+
102
+ if (
103
+ configuredServerBuildFile !== defaultServerBuildFile &&
104
+ existsSync(defaultServerBuildPath) &&
105
+ !existsSync(configuredServerBuildPath)
106
+ ) {
107
+ await mkdir(dirname(configuredServerBuildPath), { recursive: true });
108
+ await fsExtra.copy(defaultServerBuildPath, configuredServerBuildPath);
109
+ }
110
+
111
+ return {
112
+ serverBuildDir,
113
+ serverBuildPath: existsSync(configuredServerBuildPath)
114
+ ? configuredServerBuildPath
115
+ : defaultServerBuildPath,
116
+ };
117
+ };
118
+
119
+ const createDataRequestPath = (
120
+ prerenderPath: string,
121
+ trailingSlashAwareDataRequests: boolean
122
+ ): string => {
123
+ if (trailingSlashAwareDataRequests) {
124
+ return prerenderPath.endsWith('/')
125
+ ? `${prerenderPath}_.data`
126
+ : `${prerenderPath}.data`;
127
+ }
128
+
129
+ return prerenderPath === '/'
130
+ ? '/_root.data'
131
+ : `${prerenderPath.replace(/\/$/, '')}.data`;
132
+ };
133
+
134
+ const prerenderData = async ({
135
+ handler,
136
+ prerenderPath,
137
+ onlyRoutes,
138
+ clientBuildDir,
139
+ basename,
140
+ trailingSlashAwareDataRequests,
141
+ api,
142
+ requestInit,
143
+ }: {
144
+ handler: (request: Request) => Promise<Response>;
145
+ prerenderPath: string;
146
+ onlyRoutes: string[] | null;
147
+ clientBuildDir: string;
148
+ basename: string;
149
+ trailingSlashAwareDataRequests: boolean;
150
+ api: PrerenderBuildApi;
151
+ requestInit?: RequestInit;
152
+ }): Promise<string> => {
153
+ const dataRequestPath = createDataRequestPath(
154
+ prerenderPath,
155
+ trailingSlashAwareDataRequests
156
+ );
157
+ const normalizedPath = `${basename}${dataRequestPath}`.replace(/\/\/+/g, '/');
158
+ const url = new URL(`http://localhost${normalizedPath}`);
159
+ if (onlyRoutes?.length) {
160
+ url.searchParams.set('_routes', onlyRoutes.join(','));
161
+ }
162
+
163
+ return withBuildRequest(url, requestInit, async request => {
164
+ const response = await handler(request);
165
+ const data = await response.text();
166
+
167
+ if (response.status !== 200 && response.status !== 202) {
168
+ throw new Error(
169
+ `Prerender (data): Received a ${response.status} status code from ` +
170
+ `\`entry.server.tsx\` while prerendering the \`${prerenderPath}\` path.\n` +
171
+ `${normalizedPath}`
172
+ );
173
+ }
174
+
175
+ const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
176
+ await mkdir(dirname(outputPath), { recursive: true });
177
+ await writeFile(outputPath, data);
178
+ api.logger.info(
179
+ `Prerender (data): ${prerenderPath} -> ${relative(
180
+ process.cwd(),
181
+ outputPath
182
+ )}`
183
+ );
184
+ return data;
185
+ });
186
+ };
187
+
188
+ const prerenderRoute = async ({
189
+ handler,
190
+ prerenderPath,
191
+ clientBuildDir,
192
+ basename,
193
+ api,
194
+ requestInit,
195
+ }: {
196
+ handler: (request: Request) => Promise<Response>;
197
+ prerenderPath: string;
198
+ clientBuildDir: string;
199
+ basename: string;
200
+ api: PrerenderBuildApi;
201
+ requestInit?: RequestInit;
202
+ }): Promise<void> => {
203
+ const normalizedPath = `${basename}${prerenderPath}/`.replace(/\/\/+/g, '/');
204
+ await withBuildRequest(
205
+ `http://localhost${normalizedPath}`,
206
+ requestInit,
207
+ async request => {
208
+ const response = await handler(request);
209
+ let html = await response.text();
210
+
211
+ if (redirectStatusCodes.has(response.status)) {
212
+ const location = response.headers.get('Location');
213
+ const delay = response.status === 302 ? 2 : 0;
214
+ html = `<!doctype html>
215
+ <head>
216
+ <title>Redirecting to: ${location}</title>
217
+ <meta http-equiv="refresh" content="${delay};url=${location}">
218
+ <meta name="robots" content="noindex">
219
+ </head>
220
+ <body>
221
+ \t<a href="${location}">
222
+ Redirecting from <code>${normalizedPath}</code> to <code>${location}</code>
223
+ </a>
224
+ </body>
225
+ </html>`;
226
+ } else if (response.status !== 200) {
227
+ throw new Error(
228
+ `Prerender (html): Received a ${response.status} status code from ` +
229
+ `\`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.\n` +
230
+ html
231
+ );
232
+ }
233
+
234
+ const outputPath = resolve(
235
+ clientBuildDir,
236
+ ...normalizedPath.split('/'),
237
+ 'index.html'
238
+ );
239
+ await mkdir(dirname(outputPath), { recursive: true });
240
+ await writeFile(outputPath, html);
241
+ api.logger.info(
242
+ `Prerender (html): ${prerenderPath} -> ${relative(
243
+ process.cwd(),
244
+ outputPath
245
+ )}`
246
+ );
247
+ }
248
+ );
249
+ };
250
+
251
+ const prerenderResourceRoute = async ({
252
+ handler,
253
+ prerenderPath,
254
+ clientBuildDir,
255
+ basename,
256
+ api,
257
+ requestInit,
258
+ }: {
259
+ handler: (request: Request) => Promise<Response>;
260
+ prerenderPath: string;
261
+ clientBuildDir: string;
262
+ basename: string;
263
+ api: PrerenderBuildApi;
264
+ requestInit?: RequestInit;
265
+ }): Promise<void> => {
266
+ const normalizedPath = `${basename}${prerenderPath}/`
267
+ .replace(/\/\/+/g, '/')
268
+ .replace(/\/$/g, '');
269
+ await withBuildRequest(
270
+ `http://localhost${normalizedPath}`,
271
+ requestInit,
272
+ async request => {
273
+ const response = await handler(request);
274
+ const content = Buffer.from(await response.arrayBuffer());
275
+
276
+ if (response.status !== 200) {
277
+ throw new Error(
278
+ `Prerender (resource): Received a ${response.status} status code from ` +
279
+ `\`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.\n` +
280
+ content.toString('utf8')
281
+ );
282
+ }
283
+
284
+ const outputPath = resolve(clientBuildDir, ...normalizedPath.split('/'));
285
+ await mkdir(dirname(outputPath), { recursive: true });
286
+ await writeFile(outputPath, content);
287
+ api.logger.info(
288
+ `Prerender (resource): ${prerenderPath} -> ${relative(
289
+ process.cwd(),
290
+ outputPath
291
+ )}`
292
+ );
293
+ }
294
+ );
295
+ };
296
+
297
+ const handleSpaMode = async ({
298
+ handler,
299
+ build,
300
+ clientBuildDir,
301
+ basename,
302
+ api,
303
+ }: {
304
+ handler: (request: Request) => Promise<Response>;
305
+ build: PrerenderServerBuild;
306
+ clientBuildDir: string;
307
+ basename: string;
308
+ api: PrerenderBuildApi;
309
+ }): Promise<void> => {
310
+ await withBuildRequest(
311
+ `http://localhost${basename}`,
312
+ {
313
+ headers: {
314
+ 'X-React-Router-SPA-Mode': 'yes',
315
+ },
316
+ },
317
+ async request => {
318
+ const response = await handler(request);
319
+ const html = await response.text();
320
+ const isPrerenderSpaFallback = build.prerender?.includes('/');
321
+ const filename = isPrerenderSpaFallback
322
+ ? '__spa-fallback.html'
323
+ : 'index.html';
324
+
325
+ if (response.status !== 200) {
326
+ if (isPrerenderSpaFallback) {
327
+ throw new Error(
328
+ `Prerender: Received a ${response.status} status code from ` +
329
+ `\`entry.server.tsx\` while prerendering your \`${filename}\` file.\n` +
330
+ html
331
+ );
332
+ }
333
+ throw new Error(
334
+ `SPA Mode: Received a ${response.status} status code from ` +
335
+ `\`entry.server.tsx\` while prerendering your \`${filename}\` file.\n` +
336
+ html
337
+ );
338
+ }
339
+
340
+ if (
341
+ !html.includes('window.__reactRouterContext =') ||
342
+ !html.includes('window.__reactRouterRouteModules =')
343
+ ) {
344
+ throw new Error(
345
+ 'SPA Mode: Did you forget to include `<Scripts/>` in your root route? ' +
346
+ 'Your pre-rendered HTML cannot hydrate without `<Scripts />`.'
347
+ );
348
+ }
349
+
350
+ const outputPath = resolve(clientBuildDir, filename);
351
+ await writeFile(outputPath, html);
352
+ const prettyPath = relative(process.cwd(), outputPath);
353
+ if (build.prerender?.length) {
354
+ api.logger.info(`Prerender (html): SPA Fallback -> ${prettyPath}`);
355
+ } else {
356
+ api.logger.info(`SPA Mode: Generated ${prettyPath}`);
357
+ }
358
+ }
359
+ );
360
+ };
361
+
362
+ const assertValidSsrFalsePrerenderExports = ({
363
+ routes,
364
+ manifestRoutes,
365
+ routeExports,
366
+ prerenderPaths,
367
+ api,
368
+ }: {
369
+ routes: Record<string, Route>;
370
+ manifestRoutes: ReactRouterManifest['routes'];
371
+ routeExports: RouteManifestModuleExports;
372
+ prerenderPaths: string[];
373
+ api: PrerenderBuildApi;
374
+ }) => {
375
+ const errors = getSsrFalsePrerenderExportErrors({
376
+ routes,
377
+ manifestRoutes,
378
+ routeExports,
379
+ prerenderPaths,
380
+ });
381
+ if (errors.length > 0) {
382
+ api.logger.error(errors.join('\n'));
383
+ throw new Error(
384
+ 'Invalid route exports found when prerendering with `ssr:false`'
385
+ );
386
+ }
387
+ };
388
+
389
+ const validatePrerenderPathMatches = (
390
+ routes: Record<string, Route>,
391
+ prerenderPaths: string[]
392
+ ): void => {
393
+ const routeTree = createPrerenderRoutes(routes);
394
+ for (const path of prerenderPaths) {
395
+ const matches = matchRoutes(routeTree, normalizePrerenderMatchPath(path));
396
+ if (!matches) {
397
+ throw new Error(
398
+ `Unable to prerender path because it does not match any routes: ${path}`
399
+ );
400
+ }
401
+ }
402
+ };
403
+
404
+ const runPrerenderPaths = async ({
405
+ build,
406
+ requestHandler,
407
+ clientBuildDir,
408
+ options,
409
+ }: {
410
+ build: PrerenderServerBuild;
411
+ requestHandler: (request: Request) => Promise<Response>;
412
+ clientBuildDir: string;
413
+ options: RunReactRouterPrerenderBuildOptions;
414
+ }): Promise<void> => {
415
+ const { api, basename, future, prerenderConfig, prerenderPaths } = options;
416
+ const buildRoutes = createPrerenderRoutes(build.routes);
417
+ const concurrency = getPrerenderConcurrency(prerenderConfig);
418
+ const pending = new Set<Promise<void>>();
419
+
420
+ const enqueue = async (path: string) => {
421
+ const matches = matchRoutes(buildRoutes, normalizePrerenderMatchPath(path));
422
+ if (!matches) {
423
+ return;
424
+ }
425
+
426
+ const leafRoute = matches[matches.length - 1]?.route;
427
+ const routeId = leafRoute?.id;
428
+ const manifestRoute = routeId ? build.routes?.[routeId]?.module : null;
429
+ const isResourceRoute =
430
+ manifestRoute && !manifestRoute.default && !manifestRoute.ErrorBoundary;
431
+
432
+ if (isResourceRoute) {
433
+ if (manifestRoute.loader && routeId) {
434
+ await prerenderData({
435
+ handler: requestHandler,
436
+ prerenderPath: path,
437
+ onlyRoutes: [routeId],
438
+ clientBuildDir,
439
+ basename,
440
+ trailingSlashAwareDataRequests:
441
+ future.unstable_trailingSlashAwareDataRequests,
442
+ api,
443
+ });
444
+ await prerenderResourceRoute({
445
+ handler: requestHandler,
446
+ prerenderPath: path,
447
+ clientBuildDir,
448
+ basename,
449
+ api,
450
+ });
451
+ } else {
452
+ api.logger.warn(
453
+ `⚠️ Skipping prerendering for resource route without a loader: ${routeId}`
454
+ );
455
+ }
456
+ return;
457
+ }
458
+
459
+ const hasLoaders = matches.some(match => {
460
+ const matchedRouteId = match.route.id;
461
+ if (!matchedRouteId) {
462
+ return false;
463
+ }
464
+ return build.assets?.routes?.[matchedRouteId]?.hasLoader;
465
+ });
466
+ let data: string | undefined;
467
+ if (hasLoaders) {
468
+ data = await prerenderData({
469
+ handler: requestHandler,
470
+ prerenderPath: path,
471
+ onlyRoutes: null,
472
+ clientBuildDir,
473
+ basename,
474
+ trailingSlashAwareDataRequests:
475
+ future.unstable_trailingSlashAwareDataRequests,
476
+ api,
477
+ });
478
+ }
479
+ await prerenderRoute({
480
+ handler: requestHandler,
481
+ prerenderPath: path,
482
+ clientBuildDir,
483
+ basename,
484
+ api,
485
+ requestInit: data
486
+ ? {
487
+ headers: {
488
+ 'X-React-Router-Prerender-Data': encodeURI(data),
489
+ },
490
+ }
491
+ : undefined,
492
+ });
493
+ };
494
+
495
+ for (const path of prerenderPaths) {
496
+ const task = enqueue(path);
497
+ pending.add(task);
498
+ task.finally(() => pending.delete(task));
499
+ if (pending.size >= concurrency) {
500
+ await Promise.race(pending);
501
+ }
502
+ }
503
+ await Promise.all(pending);
504
+ };
505
+
506
+ export const runReactRouterPrerenderBuild = async (
507
+ options: RunReactRouterPrerenderBuildOptions
508
+ ): Promise<void> => {
509
+ if (!options.hasWebEnvironment) {
510
+ return;
511
+ }
512
+
513
+ const {
514
+ api,
515
+ buildDirectory,
516
+ serverBuildFile,
517
+ ssr,
518
+ isPrerenderEnabled,
519
+ prerenderPaths,
520
+ routes,
521
+ latestBrowserManifest,
522
+ latestBrowserManifestModuleExports,
523
+ clientStats,
524
+ pluginOptions,
525
+ appDirectory,
526
+ assetPrefix,
527
+ routeChunkOptions,
528
+ buildManifest,
529
+ resolvedConfigWithRoutes,
530
+ buildEnd,
531
+ basename,
532
+ } = options;
533
+ const { serverBuildDir, serverBuildPath } = await getServerBuildPath({
534
+ buildDirectory,
535
+ serverBuildFile,
536
+ });
537
+ const clientBuildDir = resolve(buildDirectory, 'client');
538
+
539
+ if (!existsSync(serverBuildPath)) {
540
+ console.warn(
541
+ `[${PLUGIN_NAME}] Server build not found at ${serverBuildPath}. ` +
542
+ 'Skipping prerendering.'
543
+ );
544
+ return;
545
+ }
546
+
547
+ await mkdir(clientBuildDir, { recursive: true });
548
+
549
+ if (!ssr || isPrerenderEnabled) {
550
+ process.env.IS_RR_BUILD_REQUEST = 'yes';
551
+ const buildModule = await import(pathToFileURL(serverBuildPath).toString());
552
+ const build = (await resolveServerBuildModule(
553
+ buildModule,
554
+ `Server build ${JSON.stringify(serverBuildPath)}`
555
+ )) as PrerenderServerBuild;
556
+ const requestHandler = createRequestHandler(build, 'production');
557
+
558
+ if (isPrerenderEnabled) {
559
+ if (!ssr) {
560
+ const generated = latestBrowserManifest
561
+ ? {
562
+ manifest: latestBrowserManifest,
563
+ moduleExportsByRouteId: latestBrowserManifestModuleExports,
564
+ }
565
+ : await generateReactRouterManifestForDev(
566
+ routes,
567
+ pluginOptions,
568
+ clientStats,
569
+ appDirectory,
570
+ assetPrefix,
571
+ routeChunkOptions
572
+ );
573
+ assertValidSsrFalsePrerenderExports({
574
+ routes,
575
+ manifestRoutes: generated.manifest.routes,
576
+ routeExports: generated.moduleExportsByRouteId,
577
+ prerenderPaths,
578
+ api,
579
+ });
580
+ }
581
+
582
+ validatePrerenderPathMatches(routes, prerenderPaths);
583
+
584
+ if (prerenderPaths.length > 0) {
585
+ api.logger.info(
586
+ `Prerender (html): ${prerenderPaths.length} path(s)...`
587
+ );
588
+ }
589
+
590
+ await runPrerenderPaths({
591
+ build,
592
+ requestHandler,
593
+ clientBuildDir,
594
+ options,
595
+ });
596
+ }
597
+
598
+ if (!ssr) {
599
+ await handleSpaMode({
600
+ handler: requestHandler,
601
+ build,
602
+ clientBuildDir,
603
+ basename,
604
+ api,
605
+ });
606
+ }
607
+ }
608
+
609
+ if (!ssr) {
610
+ await fsExtra.remove(serverBuildDir);
611
+ api.logger.info(
612
+ `[${PLUGIN_NAME}] Removed server build (static deployment)`
613
+ );
614
+ }
615
+
616
+ if (buildEnd) {
617
+ await buildEnd({
618
+ buildManifest,
619
+ reactRouterConfig: resolvedConfigWithRoutes,
620
+ viteConfig: api.getNormalizedConfig(),
621
+ });
622
+ }
623
+ };