rsbuild-plugin-react-router 0.4.1 → 0.6.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.
- package/README.md +72 -240
- package/dist/{451.js → 511.js} +461 -331
- package/dist/build-output-transforms.d.ts +32 -7
- package/dist/classic-mode.d.ts +56 -0
- package/dist/config-imports.d.ts +8 -2
- package/dist/constants.d.ts +3 -0
- package/dist/dev-background-resources.d.ts +3 -3
- package/dist/dev-generation.d.ts +2 -3
- package/dist/dev-hmr.d.ts +8 -2
- package/dist/dev-runtime-controller.d.ts +6 -1
- package/dist/dev-source-maps.d.ts +4 -0
- package/dist/effect-runtime.d.ts +17 -5
- package/dist/entry-paths.d.ts +16 -0
- package/dist/environment-output.d.ts +6 -0
- package/dist/export-utils.d.ts +2 -1
- package/dist/federation.d.ts +2 -0
- package/dist/index.cjs +3675 -2107
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3614 -2214
- package/dist/lazy-compilation-prewarm.d.ts +8 -5
- package/dist/manifest.d.ts +16 -4
- package/dist/mode-plan.d.ts +82 -0
- package/dist/modify-browser-manifest.d.ts +8 -4
- package/dist/performance.d.ts +4 -1
- package/dist/plugin-utils.d.ts +27 -1
- package/dist/prerender-build.d.ts +5 -5
- package/dist/prerender.d.ts +1 -5
- package/dist/react-router-config.d.ts +11 -3
- package/dist/route-artifacts.d.ts +4 -2
- package/dist/route-chunks.d.ts +2 -1
- package/dist/route-component-transform.d.ts +0 -2
- package/dist/route-export-pruning.d.ts +4 -1
- package/dist/route-imports.d.ts +18 -0
- package/dist/route-module-transform-loader.d.ts +14 -0
- package/dist/route-module-transform-loader.js +44 -0
- package/dist/route-module-transform-rules.d.ts +14 -0
- package/dist/route-transform-tasks.d.ts +6 -0
- package/dist/route-watch.d.ts +11 -6
- package/dist/rsc-dev-server.d.ts +26 -0
- package/dist/rsc-prerender.d.ts +54 -0
- package/dist/rsc-route-config.d.ts +6 -0
- package/dist/rsc-route-exports.d.ts +15 -0
- package/dist/rsc-route-transform-loader.cjs +43 -0
- package/dist/rsc-route-transform-loader.d.ts +30 -0
- package/dist/rsc-route-transform-loader.js +16 -0
- package/dist/rsc-route-transform-registration.d.ts +12 -0
- package/dist/rsc-route-transforms.d.ts +21 -0
- package/dist/rsc-support.d.ts +23 -0
- package/dist/rsc-virtual-modules.d.ts +19 -0
- package/dist/server-build-plan.d.ts +2 -1
- package/dist/server-build-resolution.d.ts +1 -2
- package/dist/server-utils.d.ts +4 -5
- package/dist/ssr-asset-relocation.d.ts +98 -0
- package/dist/templates/entry.rsc.client.d.ts +1 -0
- package/dist/templates/entry.rsc.client.js +61 -0
- package/dist/templates/entry.rsc.d.ts +9 -0
- package/dist/templates/entry.rsc.js +38 -0
- package/dist/templates/entry.rsc.ssr.d.ts +4 -0
- package/dist/templates/entry.rsc.ssr.js +24 -0
- package/dist/typegen.d.ts +4 -2
- package/dist/types.d.ts +34 -6
- package/package.json +76 -22
- package/src/build-output-transforms.ts +181 -43
- package/src/classic-mode.ts +253 -0
- package/src/config-imports.ts +153 -6
- package/src/constants.ts +6 -2
- package/src/dev-background-resources.ts +46 -100
- package/src/dev-generation.ts +52 -33
- package/src/dev-hmr.ts +112 -80
- package/src/dev-runtime-artifacts.ts +11 -12
- package/src/dev-runtime-controller.ts +75 -85
- package/src/dev-runtime-session.ts +14 -18
- package/src/dev-server.ts +2 -0
- package/src/dev-source-maps.ts +257 -0
- package/src/effect-runtime.ts +105 -57
- package/src/entry-paths.ts +80 -0
- package/src/environment-output.ts +55 -0
- package/src/export-utils.ts +15 -11
- package/src/federation.ts +38 -0
- package/src/index.ts +666 -519
- package/src/lazy-compilation-prewarm.ts +20 -4
- package/src/manifest.ts +157 -82
- package/src/mode-plan.ts +367 -0
- package/src/modify-browser-manifest.ts +130 -124
- package/src/performance.ts +11 -2
- package/src/plugin-utils.ts +103 -39
- package/src/prerender-build.ts +82 -104
- package/src/prerender.ts +23 -24
- package/src/react-router-config.ts +72 -26
- package/src/route-artifacts.ts +96 -66
- package/src/route-chunks.ts +167 -51
- package/src/route-component-transform.ts +14 -21
- package/src/route-export-pruning.ts +4 -3
- package/src/route-imports.ts +100 -0
- package/src/route-module-transform-loader.ts +149 -0
- package/src/route-module-transform-rules.ts +115 -0
- package/src/route-transform-tasks.ts +44 -29
- package/src/route-watch.ts +166 -188
- package/src/rsc-dev-server.ts +112 -0
- package/src/rsc-prerender.ts +362 -0
- package/src/rsc-route-config.ts +175 -0
- package/src/rsc-route-exports.ts +67 -0
- package/src/rsc-route-transform-loader.ts +65 -0
- package/src/rsc-route-transform-registration.ts +145 -0
- package/src/rsc-route-transforms.ts +995 -0
- package/src/rsc-runtime.d.ts +143 -0
- package/src/rsc-support.ts +116 -0
- package/src/rsc-virtual-modules.ts +113 -0
- package/src/server-build-plan.ts +14 -3
- package/src/server-build-resolution.ts +37 -47
- package/src/server-utils.ts +21 -35
- package/src/ssr-asset-relocation.ts +183 -0
- package/src/ssr-externals.ts +8 -26
- package/src/templates/entry.rsc.client.tsx +168 -0
- package/src/templates/entry.rsc.ssr.tsx +45 -0
- package/src/templates/entry.rsc.tsx +80 -0
- package/src/typegen.ts +40 -23
- package/src/types.ts +43 -6
- package/src/warnings/warn-on-client-source-maps.ts +6 -10
- package/dist/parallel-route-transform-protocol.d.ts +0 -25
- package/dist/parallel-route-transform-worker.d.ts +0 -1
- package/dist/parallel-route-transform-worker.js +0 -38
- package/dist/parallel-route-transforms.d.ts +0 -29
- package/src/parallel-route-transform-protocol.ts +0 -35
- package/src/parallel-route-transform-worker.ts +0 -82
- package/src/parallel-route-transforms.ts +0 -458
package/src/performance.ts
CHANGED
|
@@ -54,9 +54,15 @@ export const roundMs = (value: number): number => Math.round(value * 10) / 10;
|
|
|
54
54
|
export type ReactRouterPerformanceReport = {
|
|
55
55
|
environment: string;
|
|
56
56
|
compilerLifecycleMs?: number;
|
|
57
|
+
partial?: boolean;
|
|
58
|
+
workerId?: string;
|
|
57
59
|
operations: Record<string, OperationTiming>;
|
|
58
60
|
};
|
|
59
61
|
|
|
62
|
+
export const formatReactRouterPerformanceReport = (
|
|
63
|
+
report: ReactRouterPerformanceReport
|
|
64
|
+
): string => `[react-router:performance] ${JSON.stringify(report)}`;
|
|
65
|
+
|
|
60
66
|
export type ReactRouterPerformanceProfiler = {
|
|
61
67
|
record<T>(
|
|
62
68
|
environment: string | undefined,
|
|
@@ -72,7 +78,10 @@ export type ReactRouterPerformanceProfiler = {
|
|
|
72
78
|
): T;
|
|
73
79
|
flush(
|
|
74
80
|
environment: string | undefined,
|
|
75
|
-
details?: Pick<
|
|
81
|
+
details?: Pick<
|
|
82
|
+
ReactRouterPerformanceReport,
|
|
83
|
+
'compilerLifecycleMs' | 'partial' | 'workerId'
|
|
84
|
+
>
|
|
76
85
|
): void;
|
|
77
86
|
};
|
|
78
87
|
|
|
@@ -247,7 +256,7 @@ export const createReactRouterPerformanceProfiler = ({
|
|
|
247
256
|
...details,
|
|
248
257
|
operations,
|
|
249
258
|
};
|
|
250
|
-
log(
|
|
259
|
+
log(formatReactRouterPerformanceReport(report));
|
|
251
260
|
timingsByEnvironment.delete(resolvedEnvironment);
|
|
252
261
|
},
|
|
253
262
|
};
|
package/src/plugin-utils.ts
CHANGED
|
@@ -1,7 +1,71 @@
|
|
|
1
|
-
import { normalize } from 'pathe';
|
|
2
|
-
import { existsSync } from 'node:fs';
|
|
1
|
+
import { normalize, resolve } from 'pathe';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
3
4
|
import { JS_EXTENSIONS } from './constants.js';
|
|
4
5
|
|
|
6
|
+
const requireFromApp = createRequire(resolve(process.cwd(), 'package.json'));
|
|
7
|
+
|
|
8
|
+
export const resolveAppPackagePath = (
|
|
9
|
+
specifier: string
|
|
10
|
+
): string | undefined => {
|
|
11
|
+
try {
|
|
12
|
+
return requireFromApp.resolve(specifier);
|
|
13
|
+
} catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const parseVersionMajorMinor = (
|
|
19
|
+
version: string | undefined
|
|
20
|
+
): { major: number; minor: number } | undefined => {
|
|
21
|
+
const match = version?.match(/^(\d+)\.(\d+)\./);
|
|
22
|
+
if (!match) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
return { major: Number(match[1]), minor: Number(match[2]) };
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const packageVersionCache = new Map<string, string | undefined>();
|
|
29
|
+
|
|
30
|
+
export const getPackageVersion = (
|
|
31
|
+
packageName: string,
|
|
32
|
+
resolvePackagePath: (
|
|
33
|
+
specifier: string
|
|
34
|
+
) => string | undefined = resolveAppPackagePath
|
|
35
|
+
): string | undefined => {
|
|
36
|
+
// Only default resolution is cached; injected resolvers (tests) stay live.
|
|
37
|
+
const cacheable = resolvePackagePath === resolveAppPackagePath;
|
|
38
|
+
if (cacheable && packageVersionCache.has(packageName)) {
|
|
39
|
+
return packageVersionCache.get(packageName);
|
|
40
|
+
}
|
|
41
|
+
const packageJsonPath = resolvePackagePath(`${packageName}/package.json`);
|
|
42
|
+
if (!packageJsonPath) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
|
|
47
|
+
version?: unknown;
|
|
48
|
+
};
|
|
49
|
+
const version =
|
|
50
|
+
typeof packageJson.version === 'string' ? packageJson.version : undefined;
|
|
51
|
+
if (cacheable) {
|
|
52
|
+
packageVersionCache.set(packageName, version);
|
|
53
|
+
}
|
|
54
|
+
return version;
|
|
55
|
+
} catch {
|
|
56
|
+
if (cacheable) {
|
|
57
|
+
packageVersionCache.set(packageName, undefined);
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const escapeHtml = (value: string): string =>
|
|
64
|
+
value
|
|
65
|
+
.replaceAll('&', '&')
|
|
66
|
+
.replaceAll('<', '<')
|
|
67
|
+
.replaceAll('>', '>');
|
|
68
|
+
|
|
5
69
|
export function combineURLs(baseURL: string, relativeURL: string): string {
|
|
6
70
|
return relativeURL
|
|
7
71
|
? `${baseURL.replace(/\/+$/, '')}/${relativeURL.replace(/^\/+/, '')}`
|
|
@@ -15,12 +79,38 @@ export function normalizeAssetPrefix(assetPrefix?: string): string {
|
|
|
15
79
|
return assetPrefix.endsWith('/') ? assetPrefix : `${assetPrefix}/`;
|
|
16
80
|
}
|
|
17
81
|
|
|
18
|
-
|
|
19
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Resolve the asset prefix Rsbuild applies to emitted asset URLs for the given
|
|
84
|
+
* mode. In development the effective prefix is `dev.assetPrefix` (which Rsbuild
|
|
85
|
+
* defaults from `server.base`, falling back to `output.assetPrefix`); in a
|
|
86
|
+
* production build it is `output.assetPrefix`. Both fields are already resolved
|
|
87
|
+
* on the normalized config, so this mirrors Rsbuild's own precedence rather than
|
|
88
|
+
* re-deriving it from `server.base`.
|
|
89
|
+
*
|
|
90
|
+
* `dev.assetPrefix` may be a boolean on the raw config (`false` disables it);
|
|
91
|
+
* boolean/`'auto'`/empty values normalize to the root prefix `'/'`.
|
|
92
|
+
*/
|
|
93
|
+
export function resolveEffectiveAssetPrefix(config: {
|
|
94
|
+
dev?: { assetPrefix?: unknown };
|
|
95
|
+
output?: { assetPrefix?: unknown };
|
|
96
|
+
isBuild: boolean;
|
|
97
|
+
}): string {
|
|
98
|
+
const outputPrefix =
|
|
99
|
+
typeof config.output?.assetPrefix === 'string'
|
|
100
|
+
? config.output.assetPrefix
|
|
101
|
+
: undefined;
|
|
102
|
+
if (config.isBuild) {
|
|
103
|
+
return normalizeAssetPrefix(outputPrefix);
|
|
104
|
+
}
|
|
105
|
+
const devPrefix =
|
|
106
|
+
typeof config.dev?.assetPrefix === 'string'
|
|
107
|
+
? config.dev.assetPrefix
|
|
108
|
+
: undefined;
|
|
109
|
+
return normalizeAssetPrefix(devPrefix ?? outputPrefix);
|
|
20
110
|
}
|
|
21
111
|
|
|
22
112
|
export function createRouteId(file: string): string {
|
|
23
|
-
return normalize(
|
|
113
|
+
return normalize(file.replace(/\.[^/.]+$/, ''));
|
|
24
114
|
}
|
|
25
115
|
|
|
26
116
|
export function findEntryFile(basePath: string): string {
|
|
@@ -35,41 +125,15 @@ export function findEntryFile(basePath: string): string {
|
|
|
35
125
|
|
|
36
126
|
export function generateWithProps() {
|
|
37
127
|
return `
|
|
38
|
-
import {
|
|
39
|
-
|
|
128
|
+
import {
|
|
129
|
+
UNSAFE_withComponentProps,
|
|
130
|
+
UNSAFE_withErrorBoundaryProps,
|
|
131
|
+
UNSAFE_withHydrateFallbackProps,
|
|
132
|
+
} from "react-router";
|
|
40
133
|
|
|
41
|
-
export
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
params: useParams(),
|
|
45
|
-
loaderData: useLoaderData(),
|
|
46
|
-
actionData: useActionData(),
|
|
47
|
-
matches: useMatches(),
|
|
48
|
-
};
|
|
49
|
-
return h(Component, props);
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function withHydrateFallbackProps(HydrateFallback) {
|
|
54
|
-
return function Wrapped() {
|
|
55
|
-
const props = {
|
|
56
|
-
params: useParams(),
|
|
57
|
-
};
|
|
58
|
-
return h(HydrateFallback, props);
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function withErrorBoundaryProps(ErrorBoundary) {
|
|
63
|
-
return function Wrapped() {
|
|
64
|
-
const props = {
|
|
65
|
-
params: useParams(),
|
|
66
|
-
loaderData: useLoaderData(),
|
|
67
|
-
actionData: useActionData(),
|
|
68
|
-
error: useRouteError(),
|
|
69
|
-
};
|
|
70
|
-
return h(ErrorBoundary, props);
|
|
71
|
-
};
|
|
72
|
-
}
|
|
134
|
+
export const withComponentProps = UNSAFE_withComponentProps;
|
|
135
|
+
export const withHydrateFallbackProps = UNSAFE_withHydrateFallbackProps;
|
|
136
|
+
export const withErrorBoundaryProps = UNSAFE_withErrorBoundaryProps;
|
|
73
137
|
`;
|
|
74
138
|
}
|
|
75
139
|
|
package/src/prerender-build.ts
CHANGED
|
@@ -10,12 +10,15 @@ import {
|
|
|
10
10
|
type ServerBuild,
|
|
11
11
|
} from 'react-router';
|
|
12
12
|
import { dirname, relative, resolve } from 'pathe';
|
|
13
|
-
import { PLUGIN_NAME } from './constants.js';
|
|
13
|
+
import { PLUGIN_NAME, SPA_FALLBACK_HTML_FILE } from './constants.js';
|
|
14
14
|
import { getBuildManifest } from './build-manifest.js';
|
|
15
15
|
import {
|
|
16
|
+
createReactRouterManifestOptions,
|
|
16
17
|
generateReactRouterManifestForDev,
|
|
17
|
-
|
|
18
|
+
type ReactRouterManifestForDev as ReactRouterManifest,
|
|
18
19
|
type ReactRouterManifestStats,
|
|
20
|
+
type RouteChunkManifestOptions,
|
|
21
|
+
type RouteModuleAnalysisProvider,
|
|
19
22
|
type RouteManifestModuleExports,
|
|
20
23
|
} from './manifest.js';
|
|
21
24
|
import {
|
|
@@ -32,10 +35,6 @@ import { resolveServerBuildModule } from './server-utils.js';
|
|
|
32
35
|
import type { PluginOptions, Route } from './types.js';
|
|
33
36
|
import { runPluginEffect, tryPluginPromise } from './effect-runtime.js';
|
|
34
37
|
|
|
35
|
-
type ReactRouterManifest = Awaited<
|
|
36
|
-
ReturnType<typeof getReactRouterManifestForDev>
|
|
37
|
-
>;
|
|
38
|
-
|
|
39
38
|
type BuildRouteModule = {
|
|
40
39
|
loader?: unknown;
|
|
41
40
|
default?: unknown;
|
|
@@ -73,9 +72,10 @@ type RunReactRouterPrerenderBuildOptions = {
|
|
|
73
72
|
pluginOptions: PluginOptions;
|
|
74
73
|
appDirectory: string;
|
|
75
74
|
assetPrefix: string;
|
|
76
|
-
routeChunkOptions:
|
|
75
|
+
routeChunkOptions: RouteChunkManifestOptions | undefined;
|
|
76
|
+
routeModuleAnalysis?: RouteModuleAnalysisProvider;
|
|
77
77
|
buildManifest: Awaited<ReturnType<typeof getBuildManifest>>;
|
|
78
|
-
|
|
78
|
+
buildEndReactRouterConfig: ResolvedReactRouterConfig;
|
|
79
79
|
buildEnd: Config['buildEnd'];
|
|
80
80
|
};
|
|
81
81
|
|
|
@@ -160,7 +160,7 @@ export const withBuildRequest = <T>(
|
|
|
160
160
|
handle: (request: Request) => Promise<T>
|
|
161
161
|
): Promise<T> => runPluginEffect(createBuildRequestEffect(input, init, handle));
|
|
162
162
|
|
|
163
|
-
const
|
|
163
|
+
const prerenderDataEffect = ({
|
|
164
164
|
handler,
|
|
165
165
|
prerenderPath,
|
|
166
166
|
onlyRoutes,
|
|
@@ -178,7 +178,7 @@ const prerenderData = async ({
|
|
|
178
178
|
trailingSlashAwareDataRequests: boolean;
|
|
179
179
|
api: PrerenderBuildApi;
|
|
180
180
|
requestInit?: RequestInit;
|
|
181
|
-
})
|
|
181
|
+
}) => {
|
|
182
182
|
const dataOutputPath = createDataRequestPath(
|
|
183
183
|
prerenderPath,
|
|
184
184
|
trailingSlashAwareDataRequests
|
|
@@ -199,7 +199,7 @@ const prerenderData = async ({
|
|
|
199
199
|
url.searchParams.set('_routes', onlyRoutes.join(','));
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
return
|
|
202
|
+
return createBuildRequestEffect(url, requestInit, async request => {
|
|
203
203
|
const response = await handler(request);
|
|
204
204
|
const data = await response.text();
|
|
205
205
|
|
|
@@ -227,7 +227,7 @@ const prerenderData = async ({
|
|
|
227
227
|
});
|
|
228
228
|
};
|
|
229
229
|
|
|
230
|
-
const
|
|
230
|
+
const prerenderRouteEffect = ({
|
|
231
231
|
handler,
|
|
232
232
|
prerenderPath,
|
|
233
233
|
clientBuildDir,
|
|
@@ -241,9 +241,9 @@ const prerenderRoute = async ({
|
|
|
241
241
|
basename: string;
|
|
242
242
|
api: PrerenderBuildApi;
|
|
243
243
|
requestInit?: RequestInit;
|
|
244
|
-
})
|
|
244
|
+
}) => {
|
|
245
245
|
const normalizedPath = `${basename}${prerenderPath}/`.replace(/\/\/+/g, '/');
|
|
246
|
-
|
|
246
|
+
return createBuildRequestEffect(
|
|
247
247
|
`http://localhost${normalizedPath}`,
|
|
248
248
|
requestInit,
|
|
249
249
|
async request => {
|
|
@@ -290,7 +290,7 @@ const prerenderRoute = async ({
|
|
|
290
290
|
);
|
|
291
291
|
};
|
|
292
292
|
|
|
293
|
-
const
|
|
293
|
+
const prerenderResourceRouteEffect = ({
|
|
294
294
|
handler,
|
|
295
295
|
prerenderPath,
|
|
296
296
|
clientBuildDir,
|
|
@@ -304,11 +304,11 @@ const prerenderResourceRoute = async ({
|
|
|
304
304
|
basename: string;
|
|
305
305
|
api: PrerenderBuildApi;
|
|
306
306
|
requestInit?: RequestInit;
|
|
307
|
-
})
|
|
307
|
+
}) => {
|
|
308
308
|
const normalizedPath = `${basename}${prerenderPath}/`
|
|
309
309
|
.replace(/\/\/+/g, '/')
|
|
310
310
|
.replace(/\/$/g, '');
|
|
311
|
-
|
|
311
|
+
return createBuildRequestEffect(
|
|
312
312
|
`http://localhost${normalizedPath}`,
|
|
313
313
|
requestInit,
|
|
314
314
|
async request => {
|
|
@@ -361,7 +361,7 @@ const handleSpaMode = async ({
|
|
|
361
361
|
const html = await response.text();
|
|
362
362
|
const isPrerenderSpaFallback = build.prerender?.includes('/');
|
|
363
363
|
const filename = isPrerenderSpaFallback
|
|
364
|
-
?
|
|
364
|
+
? SPA_FALLBACK_HTML_FILE
|
|
365
365
|
: 'index.html';
|
|
366
366
|
|
|
367
367
|
if (response.status !== 200) {
|
|
@@ -374,7 +374,7 @@ const handleSpaMode = async ({
|
|
|
374
374
|
}
|
|
375
375
|
throw new Error(
|
|
376
376
|
`SPA Mode: Received a ${response.status} status code from ` +
|
|
377
|
-
|
|
377
|
+
'`entry.server.tsx` while prerendering your SPA Fallback HTML file.\n' +
|
|
378
378
|
html
|
|
379
379
|
);
|
|
380
380
|
}
|
|
@@ -443,10 +443,10 @@ const validatePrerenderPathMatches = (
|
|
|
443
443
|
}
|
|
444
444
|
};
|
|
445
445
|
|
|
446
|
-
export const createBoundedPrerenderTasksEffect = (
|
|
447
|
-
prerenderPaths:
|
|
446
|
+
export const createBoundedPrerenderTasksEffect = <T>(
|
|
447
|
+
prerenderPaths: T[],
|
|
448
448
|
concurrency: number,
|
|
449
|
-
renderPath: (path:
|
|
449
|
+
renderPath: (path: T) => Effect.Effect<void, Error, never>
|
|
450
450
|
): Effect.Effect<void, Error, never> =>
|
|
451
451
|
Effect.forEach(prerenderPaths, renderPath, { concurrency, discard: true });
|
|
452
452
|
|
|
@@ -480,27 +480,23 @@ const createPrerenderPathEffect = ({
|
|
|
480
480
|
|
|
481
481
|
if (isResourceRoute) {
|
|
482
482
|
if (manifestRoute.loader && routeId) {
|
|
483
|
-
yield*
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
basename,
|
|
501
|
-
api,
|
|
502
|
-
})
|
|
503
|
-
);
|
|
483
|
+
yield* prerenderDataEffect({
|
|
484
|
+
handler: requestHandler,
|
|
485
|
+
prerenderPath: path,
|
|
486
|
+
onlyRoutes: [routeId],
|
|
487
|
+
clientBuildDir,
|
|
488
|
+
basename,
|
|
489
|
+
trailingSlashAwareDataRequests:
|
|
490
|
+
future.unstable_trailingSlashAwareDataRequests,
|
|
491
|
+
api,
|
|
492
|
+
});
|
|
493
|
+
yield* prerenderResourceRouteEffect({
|
|
494
|
+
handler: requestHandler,
|
|
495
|
+
prerenderPath: path,
|
|
496
|
+
clientBuildDir,
|
|
497
|
+
basename,
|
|
498
|
+
api,
|
|
499
|
+
});
|
|
504
500
|
} else {
|
|
505
501
|
yield* Effect.sync(() => {
|
|
506
502
|
api.logger.warn(
|
|
@@ -519,30 +515,26 @@ const createPrerenderPathEffect = ({
|
|
|
519
515
|
return build.assets?.routes?.[matchedRouteId]?.hasLoader;
|
|
520
516
|
});
|
|
521
517
|
const data = hasLoaders
|
|
522
|
-
? yield*
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
})
|
|
533
|
-
)
|
|
518
|
+
? yield* prerenderDataEffect({
|
|
519
|
+
handler: requestHandler,
|
|
520
|
+
prerenderPath: path,
|
|
521
|
+
onlyRoutes: null,
|
|
522
|
+
clientBuildDir,
|
|
523
|
+
basename,
|
|
524
|
+
trailingSlashAwareDataRequests:
|
|
525
|
+
future.unstable_trailingSlashAwareDataRequests,
|
|
526
|
+
api,
|
|
527
|
+
})
|
|
534
528
|
: undefined;
|
|
535
529
|
|
|
536
|
-
yield*
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
})
|
|
545
|
-
);
|
|
530
|
+
yield* prerenderRouteEffect({
|
|
531
|
+
handler: requestHandler,
|
|
532
|
+
prerenderPath: path,
|
|
533
|
+
clientBuildDir,
|
|
534
|
+
basename,
|
|
535
|
+
api,
|
|
536
|
+
requestInit: data ? createPrerenderDataRequestInit(data) : undefined,
|
|
537
|
+
});
|
|
546
538
|
});
|
|
547
539
|
|
|
548
540
|
const createPrerenderDataRequestInit = (
|
|
@@ -558,35 +550,6 @@ const createPrerenderDataRequestInit = (
|
|
|
558
550
|
: undefined;
|
|
559
551
|
};
|
|
560
552
|
|
|
561
|
-
const runPrerenderPaths = async ({
|
|
562
|
-
build,
|
|
563
|
-
requestHandler,
|
|
564
|
-
clientBuildDir,
|
|
565
|
-
options,
|
|
566
|
-
}: {
|
|
567
|
-
build: PrerenderServerBuild;
|
|
568
|
-
requestHandler: (request: Request) => Promise<Response>;
|
|
569
|
-
clientBuildDir: string;
|
|
570
|
-
options: RunReactRouterPrerenderBuildOptions;
|
|
571
|
-
}): Promise<void> => {
|
|
572
|
-
const { prerenderConfig, prerenderPaths } = options;
|
|
573
|
-
const buildRoutes = createPrerenderRoutes(build.routes);
|
|
574
|
-
const concurrency = getPrerenderConcurrency(prerenderConfig);
|
|
575
|
-
|
|
576
|
-
await runPluginEffect(
|
|
577
|
-
createBoundedPrerenderTasksEffect(prerenderPaths, concurrency, path =>
|
|
578
|
-
createPrerenderPathEffect({
|
|
579
|
-
path,
|
|
580
|
-
build,
|
|
581
|
-
buildRoutes,
|
|
582
|
-
requestHandler,
|
|
583
|
-
clientBuildDir,
|
|
584
|
-
options,
|
|
585
|
-
})
|
|
586
|
-
)
|
|
587
|
-
);
|
|
588
|
-
};
|
|
589
|
-
|
|
590
553
|
export const runReactRouterPrerenderBuild = async (
|
|
591
554
|
options: RunReactRouterPrerenderBuildOptions
|
|
592
555
|
): Promise<void> => {
|
|
@@ -600,6 +563,7 @@ export const runReactRouterPrerenderBuild = async (
|
|
|
600
563
|
serverBuildFile,
|
|
601
564
|
ssr,
|
|
602
565
|
isPrerenderEnabled,
|
|
566
|
+
prerenderConfig,
|
|
603
567
|
prerenderPaths,
|
|
604
568
|
routes,
|
|
605
569
|
latestBrowserManifest,
|
|
@@ -609,8 +573,9 @@ export const runReactRouterPrerenderBuild = async (
|
|
|
609
573
|
appDirectory,
|
|
610
574
|
assetPrefix,
|
|
611
575
|
routeChunkOptions,
|
|
576
|
+
routeModuleAnalysis,
|
|
612
577
|
buildManifest,
|
|
613
|
-
|
|
578
|
+
buildEndReactRouterConfig,
|
|
614
579
|
buildEnd,
|
|
615
580
|
basename,
|
|
616
581
|
} = options;
|
|
@@ -652,7 +617,10 @@ export const runReactRouterPrerenderBuild = async (
|
|
|
652
617
|
clientStats,
|
|
653
618
|
appDirectory,
|
|
654
619
|
assetPrefix,
|
|
655
|
-
|
|
620
|
+
createReactRouterManifestOptions({
|
|
621
|
+
routeChunks: routeChunkOptions,
|
|
622
|
+
routeModuleAnalysis,
|
|
623
|
+
})
|
|
656
624
|
);
|
|
657
625
|
assertValidSsrFalsePrerenderExports({
|
|
658
626
|
routes,
|
|
@@ -671,12 +639,22 @@ export const runReactRouterPrerenderBuild = async (
|
|
|
671
639
|
);
|
|
672
640
|
}
|
|
673
641
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
642
|
+
const buildRoutes = createPrerenderRoutes(build.routes);
|
|
643
|
+
await runPluginEffect(
|
|
644
|
+
createBoundedPrerenderTasksEffect(
|
|
645
|
+
prerenderPaths,
|
|
646
|
+
getPrerenderConcurrency(prerenderConfig),
|
|
647
|
+
path =>
|
|
648
|
+
createPrerenderPathEffect({
|
|
649
|
+
path,
|
|
650
|
+
build,
|
|
651
|
+
buildRoutes,
|
|
652
|
+
requestHandler,
|
|
653
|
+
clientBuildDir,
|
|
654
|
+
options,
|
|
655
|
+
})
|
|
656
|
+
)
|
|
657
|
+
);
|
|
680
658
|
}
|
|
681
659
|
|
|
682
660
|
if (!ssr) {
|
|
@@ -700,8 +678,8 @@ export const runReactRouterPrerenderBuild = async (
|
|
|
700
678
|
if (buildEnd) {
|
|
701
679
|
await buildEnd({
|
|
702
680
|
buildManifest,
|
|
703
|
-
reactRouterConfig:
|
|
704
|
-
|
|
681
|
+
reactRouterConfig: buildEndReactRouterConfig,
|
|
682
|
+
rsbuildConfig: api.getNormalizedConfig(),
|
|
705
683
|
});
|
|
706
684
|
}
|
|
707
685
|
};
|
package/src/prerender.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Config } from './react-router-config.js';
|
|
2
|
+
import type { PrerenderConfigObject, PrerenderPathsConfig } from './types.js';
|
|
2
3
|
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
3
4
|
import { matchRoutes } from 'react-router';
|
|
4
5
|
|
|
@@ -6,20 +7,6 @@ type ReactRouterPrerenderConfig = Config['prerender'];
|
|
|
6
7
|
type MatchRouteObject =
|
|
7
8
|
Parameters<typeof matchRoutes>[0] extends Array<infer R> ? R : never;
|
|
8
9
|
|
|
9
|
-
type PrerenderPathsConfig =
|
|
10
|
-
| boolean
|
|
11
|
-
| string[]
|
|
12
|
-
| ((args: {
|
|
13
|
-
getStaticPaths: () => string[];
|
|
14
|
-
}) => boolean | string[] | Promise<boolean | string[]>);
|
|
15
|
-
|
|
16
|
-
type PrerenderConfigObject = Extract<
|
|
17
|
-
NonNullable<ReactRouterPrerenderConfig>,
|
|
18
|
-
{ paths: unknown }
|
|
19
|
-
> & {
|
|
20
|
-
unstable_concurrency?: number;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
10
|
type PrerenderConfig = ReactRouterPrerenderConfig | PrerenderConfigObject;
|
|
24
11
|
type PrerenderConcurrencyConfig =
|
|
25
12
|
| {
|
|
@@ -78,7 +65,11 @@ export const createPrerenderRoutes = (
|
|
|
78
65
|
grouped: Record<string, any[]> = groupRoutesByParentId(manifest)
|
|
79
66
|
): MatchRouteObject[] => {
|
|
80
67
|
return (grouped[parentId] || []).map(route => {
|
|
81
|
-
const common = {
|
|
68
|
+
const common = {
|
|
69
|
+
id: route.id,
|
|
70
|
+
path: route.path,
|
|
71
|
+
caseSensitive: route.caseSensitive,
|
|
72
|
+
};
|
|
82
73
|
if (route.index) {
|
|
83
74
|
return { index: true, ...common } as MatchRouteObject;
|
|
84
75
|
}
|
|
@@ -120,10 +111,9 @@ export const getSsrFalsePrerenderExportErrors = ({
|
|
|
120
111
|
const errors: string[] = [];
|
|
121
112
|
for (const [routeId, route] of Object.entries(manifestRoutes)) {
|
|
122
113
|
const exports = routeExports[routeId] ?? [];
|
|
123
|
-
const invalidApis
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
if (exports.includes('action')) invalidApis.push('action');
|
|
114
|
+
const invalidApis = ['headers', 'action', 'middleware'].filter(api =>
|
|
115
|
+
exports.includes(api)
|
|
116
|
+
);
|
|
127
117
|
|
|
128
118
|
if (invalidApis.length > 0) {
|
|
129
119
|
errors.push(
|
|
@@ -143,11 +133,17 @@ export const getSsrFalsePrerenderExportErrors = ({
|
|
|
143
133
|
);
|
|
144
134
|
}
|
|
145
135
|
|
|
136
|
+
// A loader on an ancestor route is only invalid when the ancestor is not
|
|
137
|
+
// itself fully prerendered. The root route is exempt: its loader always
|
|
138
|
+
// runs to render the app shell and it is always part of the prerender set
|
|
139
|
+
// whenever any path is prerendered, so upstream never flags it here.
|
|
146
140
|
let parentRoute =
|
|
147
|
-
route.parentId &&
|
|
141
|
+
route.parentId &&
|
|
142
|
+
route.parentId !== 'root' &&
|
|
143
|
+
manifestRoutes[route.parentId]
|
|
148
144
|
? manifestRoutes[route.parentId]
|
|
149
145
|
: null;
|
|
150
|
-
while (parentRoute) {
|
|
146
|
+
while (parentRoute && parentRoute.id !== 'root') {
|
|
151
147
|
if (parentRoute.hasLoader && !parentRoute.hasClientLoader) {
|
|
152
148
|
errors.push(
|
|
153
149
|
`Prerender: 1 invalid route export in \`${parentRoute.id}\` when ` +
|
|
@@ -156,7 +152,9 @@ export const getSsrFalsePrerenderExportErrors = ({
|
|
|
156
152
|
);
|
|
157
153
|
}
|
|
158
154
|
parentRoute =
|
|
159
|
-
parentRoute.parentId &&
|
|
155
|
+
parentRoute.parentId &&
|
|
156
|
+
parentRoute.parentId !== 'root' &&
|
|
157
|
+
manifestRoutes[parentRoute.parentId]
|
|
160
158
|
? manifestRoutes[parentRoute.parentId]
|
|
161
159
|
: null;
|
|
162
160
|
}
|
|
@@ -247,8 +245,9 @@ export const resolvePrerenderPaths = async (
|
|
|
247
245
|
});
|
|
248
246
|
warn(
|
|
249
247
|
[
|
|
250
|
-
'
|
|
251
|
-
|
|
248
|
+
'⚠️ Paths with dynamic/splat params cannot be prerendered when ' +
|
|
249
|
+
'using `prerender: true`. You may want to use the `prerender()` ' +
|
|
250
|
+
'API to prerender the following paths:',
|
|
252
251
|
...paramRoutes.map(path => ` - ${path.replace(/^\/(?=[:*])/, '')}`),
|
|
253
252
|
].join('\n')
|
|
254
253
|
);
|