rsbuild-plugin-react-router 0.5.0 → 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 +58 -208
- package/dist/511.js +370 -331
- package/dist/build-output-transforms.d.ts +30 -6
- 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 -1
- 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/index.cjs +3957 -2208
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3470 -1793
- 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/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-transform-tasks.d.ts +5 -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 +30 -1
- package/package.json +69 -14
- package/src/build-output-transforms.ts +155 -21
- 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 -85
- 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/index.ts +661 -496
- 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/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-transform-tasks.ts +39 -25
- 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 +39 -1
- package/src/warnings/warn-on-client-source-maps.ts +6 -10
package/src/mode-plan.ts
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RsbuildConfig,
|
|
3
|
+
RsbuildEntryDescription,
|
|
4
|
+
RsbuildPluginAPI,
|
|
5
|
+
Rspack,
|
|
6
|
+
} from '@rsbuild/core';
|
|
7
|
+
import { rspack } from '@rsbuild/core';
|
|
8
|
+
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
9
|
+
import { resolve } from 'pathe';
|
|
10
|
+
import type {
|
|
11
|
+
Config,
|
|
12
|
+
ResolvedReactRouterConfig,
|
|
13
|
+
} from './react-router-config.js';
|
|
14
|
+
import type { RouteChunkCache, RouteChunkConfig } from './route-chunks.js';
|
|
15
|
+
import type { DevHmrPlanOptions } from './dev-hmr.js';
|
|
16
|
+
import type { Route } from './types.js';
|
|
17
|
+
import { createReactRouterDevServerSetup } from './dev-server.js';
|
|
18
|
+
import { resolveAppPackagePath } from './plugin-utils.js';
|
|
19
|
+
import { resolvePrerenderPaths } from './prerender.js';
|
|
20
|
+
import { createReactRouterNodeEntries } from './server-build-plan.js';
|
|
21
|
+
import { getSsrExternals } from './ssr-externals.js';
|
|
22
|
+
import {
|
|
23
|
+
createClassicBuildArtifacts,
|
|
24
|
+
createClassicVirtualModules,
|
|
25
|
+
createClassicWebRouteEntries,
|
|
26
|
+
type ClassicBuildArtifacts,
|
|
27
|
+
} from './classic-mode.js';
|
|
28
|
+
import {
|
|
29
|
+
createReactRouterRscDevServerSetup,
|
|
30
|
+
createReactRouterRscResolveAliases,
|
|
31
|
+
createReactRouterRscVirtualModules,
|
|
32
|
+
} from './rsc-support.js';
|
|
33
|
+
|
|
34
|
+
type CommonModePlan = {
|
|
35
|
+
routeChunkConfig: RouteChunkConfig;
|
|
36
|
+
manifestChunkNames: Set<string>;
|
|
37
|
+
webEntries: Record<string, string | RsbuildEntryDescription>;
|
|
38
|
+
nodeEntries: Record<string, string | RsbuildEntryDescription>;
|
|
39
|
+
createVirtualModules(
|
|
40
|
+
publicPath: string,
|
|
41
|
+
jsDistPath: string
|
|
42
|
+
): Record<string, string>;
|
|
43
|
+
createResolveConfig(rootPath: string): Rspack.Configuration['resolve'];
|
|
44
|
+
server: RsbuildConfig['server'] | undefined;
|
|
45
|
+
webExternalsType: 'module' | undefined;
|
|
46
|
+
webOutput: NonNullable<Rspack.Configuration['output']>;
|
|
47
|
+
webOptimization: NonNullable<Rspack.Configuration['optimization']>;
|
|
48
|
+
nodeExternals: string[] | undefined;
|
|
49
|
+
nodeDependencies: { dependencies?: string[] };
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type ClassicModePlan = CommonModePlan & {
|
|
53
|
+
kind: 'classic';
|
|
54
|
+
artifacts: ClassicBuildArtifacts;
|
|
55
|
+
routeChunkOptions: {
|
|
56
|
+
splitRouteModules: Config['splitRouteModules'];
|
|
57
|
+
rootRouteFile: string;
|
|
58
|
+
isBuild: boolean;
|
|
59
|
+
cache: RouteChunkCache;
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export type RscModePlan = CommonModePlan & {
|
|
64
|
+
kind: 'rsc';
|
|
65
|
+
prerenderPaths: string[];
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type ReactRouterModePlan = ClassicModePlan | RscModePlan;
|
|
69
|
+
|
|
70
|
+
// Fields both mode planners need. Mode-specific inputs live on the
|
|
71
|
+
// per-mode option types below so each planner owns only what it uses.
|
|
72
|
+
type ModePlanContext = {
|
|
73
|
+
allowedActionOriginsForBuild: string[] | undefined;
|
|
74
|
+
api: RsbuildPluginAPI;
|
|
75
|
+
appDirectory: string;
|
|
76
|
+
basename: string;
|
|
77
|
+
customServer: boolean;
|
|
78
|
+
splitRouteModules: Config['splitRouteModules'];
|
|
79
|
+
isBuild: boolean;
|
|
80
|
+
isSpaMode: boolean;
|
|
81
|
+
prerenderConfig: Config['prerender'];
|
|
82
|
+
routeConfig: RouteConfigEntry[];
|
|
83
|
+
routeDiscovery: Config['routeDiscovery'];
|
|
84
|
+
routes: Record<string, Route>;
|
|
85
|
+
rootRouteFile: string;
|
|
86
|
+
ssr: boolean;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
type CreateClassicModePlanOptions = ModePlanContext & {
|
|
90
|
+
assetsBuildDirectory: string;
|
|
91
|
+
devHmr?: DevHmrPlanOptions;
|
|
92
|
+
defaultEntryName: string;
|
|
93
|
+
entryServerPath: string;
|
|
94
|
+
finalEntryClientPath: string;
|
|
95
|
+
future: Config['future'];
|
|
96
|
+
hasServerApp: boolean;
|
|
97
|
+
reactRouterConfig: ResolvedReactRouterConfig;
|
|
98
|
+
routeChunkCache: RouteChunkCache;
|
|
99
|
+
serverAppPath: string;
|
|
100
|
+
shouldDependOnWebCompiler: boolean;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
type CreateRscModePlanOptions = ModePlanContext & {
|
|
104
|
+
buildDirectory: string;
|
|
105
|
+
finalEntryRscClientPath: string;
|
|
106
|
+
finalEntryRscPath: string;
|
|
107
|
+
outputClientPath: string;
|
|
108
|
+
pluginName: string;
|
|
109
|
+
serverBuildFile: string | undefined;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
type CreateReactRouterModePlanOptions =
|
|
113
|
+
| ({ isRscMode: true } & CreateRscModePlanOptions)
|
|
114
|
+
| ({ isRscMode: false } & CreateClassicModePlanOptions);
|
|
115
|
+
|
|
116
|
+
const RSC_LAYERS = rspack.experiments.rsc.Layers;
|
|
117
|
+
|
|
118
|
+
const createReactRouterPackageAliases = (): Record<string, string> => {
|
|
119
|
+
const reactRouterPath = resolveAppPackagePath('react-router');
|
|
120
|
+
const reactRouterDomPath = resolveAppPackagePath('react-router/dom');
|
|
121
|
+
return {
|
|
122
|
+
...(reactRouterPath ? { 'react-router$': reactRouterPath } : {}),
|
|
123
|
+
...(reactRouterDomPath ? { 'react-router/dom$': reactRouterDomPath } : {}),
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const createRscModePlan = async ({
|
|
128
|
+
allowedActionOriginsForBuild,
|
|
129
|
+
api,
|
|
130
|
+
appDirectory,
|
|
131
|
+
basename,
|
|
132
|
+
buildDirectory,
|
|
133
|
+
customServer,
|
|
134
|
+
finalEntryRscClientPath,
|
|
135
|
+
finalEntryRscPath,
|
|
136
|
+
isBuild,
|
|
137
|
+
outputClientPath,
|
|
138
|
+
pluginName,
|
|
139
|
+
prerenderConfig,
|
|
140
|
+
routeConfig,
|
|
141
|
+
routeDiscovery,
|
|
142
|
+
routes,
|
|
143
|
+
rootRouteFile,
|
|
144
|
+
serverBuildFile,
|
|
145
|
+
splitRouteModules,
|
|
146
|
+
ssr,
|
|
147
|
+
}: CreateRscModePlanOptions): Promise<RscModePlan> => {
|
|
148
|
+
const rscServerEntryName = (serverBuildFile || 'index.js').replace(
|
|
149
|
+
/\.js$/,
|
|
150
|
+
''
|
|
151
|
+
);
|
|
152
|
+
const prerenderPaths = await resolvePrerenderPaths(
|
|
153
|
+
prerenderConfig,
|
|
154
|
+
ssr,
|
|
155
|
+
routeConfig,
|
|
156
|
+
{
|
|
157
|
+
logWarning: true,
|
|
158
|
+
warn: message => api.logger.warn(message),
|
|
159
|
+
}
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
kind: 'rsc',
|
|
163
|
+
prerenderPaths,
|
|
164
|
+
// RSC route chunking is content-detected; the config value only gates
|
|
165
|
+
// 'enforce' validation, matching upstream's RSC Vite plugin.
|
|
166
|
+
routeChunkConfig: {
|
|
167
|
+
splitRouteModules,
|
|
168
|
+
appDirectory,
|
|
169
|
+
rootRouteFile,
|
|
170
|
+
},
|
|
171
|
+
manifestChunkNames: new Set<string>(['index']),
|
|
172
|
+
webEntries: {
|
|
173
|
+
index: {
|
|
174
|
+
import: finalEntryRscClientPath,
|
|
175
|
+
html: false,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
nodeEntries: {
|
|
179
|
+
[rscServerEntryName]: {
|
|
180
|
+
import: finalEntryRscPath,
|
|
181
|
+
layer: RSC_LAYERS.rsc,
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
createVirtualModules: (publicPath: string, jsDistPath: string) =>
|
|
185
|
+
createReactRouterRscVirtualModules({
|
|
186
|
+
allowedActionOrigins: allowedActionOriginsForBuild,
|
|
187
|
+
appDirectory,
|
|
188
|
+
basename,
|
|
189
|
+
buildDirectory,
|
|
190
|
+
isBuild,
|
|
191
|
+
jsDistPath,
|
|
192
|
+
outputClientPath,
|
|
193
|
+
publicPath,
|
|
194
|
+
routeDiscovery,
|
|
195
|
+
routes,
|
|
196
|
+
ssr,
|
|
197
|
+
}),
|
|
198
|
+
createResolveConfig: (rootPath: string) => ({
|
|
199
|
+
modules: [resolve(rootPath, 'node_modules'), 'node_modules'],
|
|
200
|
+
alias: createReactRouterRscResolveAliases(rootPath),
|
|
201
|
+
}),
|
|
202
|
+
server: !customServer
|
|
203
|
+
? {
|
|
204
|
+
setup: createReactRouterRscDevServerSetup({
|
|
205
|
+
entryName: rscServerEntryName,
|
|
206
|
+
pluginName,
|
|
207
|
+
}),
|
|
208
|
+
}
|
|
209
|
+
: undefined,
|
|
210
|
+
webExternalsType: undefined,
|
|
211
|
+
webOutput: {
|
|
212
|
+
chunkFormat: 'array-push',
|
|
213
|
+
chunkLoading: 'jsonp',
|
|
214
|
+
workerChunkLoading: 'import-scripts',
|
|
215
|
+
wasmLoading: 'fetch',
|
|
216
|
+
module: false,
|
|
217
|
+
},
|
|
218
|
+
webOptimization: {
|
|
219
|
+
splitChunks: false,
|
|
220
|
+
// Flight resolves client references dynamically by export name. Preserve
|
|
221
|
+
// every export and its public name in production so the manifest can
|
|
222
|
+
// resolve route components, data exports, and React Router boundaries.
|
|
223
|
+
...(isBuild ? { mangleExports: false, usedExports: false } : {}),
|
|
224
|
+
runtimeChunk: false,
|
|
225
|
+
},
|
|
226
|
+
nodeExternals: undefined,
|
|
227
|
+
nodeDependencies: {},
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const createClassicModePlan = async ({
|
|
232
|
+
api,
|
|
233
|
+
allowedActionOriginsForBuild,
|
|
234
|
+
appDirectory,
|
|
235
|
+
assetsBuildDirectory,
|
|
236
|
+
basename,
|
|
237
|
+
customServer,
|
|
238
|
+
defaultEntryName,
|
|
239
|
+
devHmr,
|
|
240
|
+
entryServerPath,
|
|
241
|
+
finalEntryClientPath,
|
|
242
|
+
future,
|
|
243
|
+
hasServerApp,
|
|
244
|
+
isBuild,
|
|
245
|
+
isSpaMode,
|
|
246
|
+
prerenderConfig,
|
|
247
|
+
reactRouterConfig,
|
|
248
|
+
routeChunkCache,
|
|
249
|
+
routeConfig,
|
|
250
|
+
routeDiscovery,
|
|
251
|
+
routes,
|
|
252
|
+
rootRouteFile,
|
|
253
|
+
serverAppPath,
|
|
254
|
+
shouldDependOnWebCompiler,
|
|
255
|
+
splitRouteModules,
|
|
256
|
+
ssr,
|
|
257
|
+
}: CreateClassicModePlanOptions): Promise<ClassicModePlan> => {
|
|
258
|
+
const routeChunkConfig: RouteChunkConfig = {
|
|
259
|
+
splitRouteModules,
|
|
260
|
+
appDirectory,
|
|
261
|
+
rootRouteFile,
|
|
262
|
+
};
|
|
263
|
+
const routeChunkOptions = {
|
|
264
|
+
splitRouteModules,
|
|
265
|
+
rootRouteFile,
|
|
266
|
+
isBuild,
|
|
267
|
+
cache: routeChunkCache,
|
|
268
|
+
};
|
|
269
|
+
const { manifestChunkNames, webRouteEntries } = createClassicWebRouteEntries({
|
|
270
|
+
appDirectory,
|
|
271
|
+
isBuild,
|
|
272
|
+
routes,
|
|
273
|
+
splitRouteModules: Boolean(splitRouteModules),
|
|
274
|
+
});
|
|
275
|
+
const artifacts = await createClassicBuildArtifacts({
|
|
276
|
+
api,
|
|
277
|
+
defaultEntryName,
|
|
278
|
+
isBuild,
|
|
279
|
+
prerenderConfig,
|
|
280
|
+
reactRouterConfig,
|
|
281
|
+
routeConfig,
|
|
282
|
+
routes,
|
|
283
|
+
rootDirectory: process.cwd(),
|
|
284
|
+
ssr,
|
|
285
|
+
devHmr,
|
|
286
|
+
});
|
|
287
|
+
const reactRouterAliases = createReactRouterPackageAliases();
|
|
288
|
+
return {
|
|
289
|
+
kind: 'classic',
|
|
290
|
+
artifacts,
|
|
291
|
+
routeChunkConfig,
|
|
292
|
+
routeChunkOptions,
|
|
293
|
+
manifestChunkNames,
|
|
294
|
+
webEntries: {
|
|
295
|
+
// `html: false` prevents rsbuild from emitting a stray entry.client.html
|
|
296
|
+
// into build/client; React Router renders HTML itself.
|
|
297
|
+
'entry.client': { import: finalEntryClientPath, html: false },
|
|
298
|
+
'virtual/react-router/browser-manifest': {
|
|
299
|
+
import: 'virtual/react-router/browser-manifest',
|
|
300
|
+
html: false,
|
|
301
|
+
},
|
|
302
|
+
...webRouteEntries,
|
|
303
|
+
},
|
|
304
|
+
nodeEntries: createReactRouterNodeEntries({
|
|
305
|
+
hasServerApp,
|
|
306
|
+
isBuild,
|
|
307
|
+
serverAppPath,
|
|
308
|
+
entryServerPath,
|
|
309
|
+
serverBuildFile: reactRouterConfig.serverBuildFile,
|
|
310
|
+
defaultEntryName,
|
|
311
|
+
serverBundleEntries: artifacts.serverBundleEntries,
|
|
312
|
+
}),
|
|
313
|
+
createVirtualModules: (publicPath: string, _jsDistPath: string) =>
|
|
314
|
+
createClassicVirtualModules({
|
|
315
|
+
allowedActionOrigins: allowedActionOriginsForBuild,
|
|
316
|
+
appDirectory,
|
|
317
|
+
assetsBuildDirectory,
|
|
318
|
+
basename,
|
|
319
|
+
devHmrRuntimeModule: devHmr?.runtimeModule,
|
|
320
|
+
entryServerPath,
|
|
321
|
+
future,
|
|
322
|
+
isSpaMode,
|
|
323
|
+
prerenderPaths: artifacts.prerenderPaths,
|
|
324
|
+
publicPath,
|
|
325
|
+
routeDiscovery,
|
|
326
|
+
routes,
|
|
327
|
+
routesByServerBundleId: artifacts.routesByServerBundleId,
|
|
328
|
+
ssr,
|
|
329
|
+
}),
|
|
330
|
+
createResolveConfig: () => ({ alias: reactRouterAliases }),
|
|
331
|
+
server: customServer
|
|
332
|
+
? undefined
|
|
333
|
+
: {
|
|
334
|
+
setup: [
|
|
335
|
+
createReactRouterDevServerSetup({
|
|
336
|
+
loadBuild: artifacts.devRuntime.createBuildLoader(),
|
|
337
|
+
}),
|
|
338
|
+
],
|
|
339
|
+
},
|
|
340
|
+
webExternalsType: 'module',
|
|
341
|
+
webOutput: {
|
|
342
|
+
chunkFormat: 'module',
|
|
343
|
+
chunkLoading: 'import',
|
|
344
|
+
workerChunkLoading: 'import',
|
|
345
|
+
wasmLoading: 'fetch',
|
|
346
|
+
library: { type: 'module' },
|
|
347
|
+
module: true,
|
|
348
|
+
},
|
|
349
|
+
webOptimization: {
|
|
350
|
+
avoidEntryIife: true,
|
|
351
|
+
runtimeChunk: 'single',
|
|
352
|
+
},
|
|
353
|
+
nodeExternals: Array.from(
|
|
354
|
+
new Set(['express', ...getSsrExternals(process.cwd())])
|
|
355
|
+
),
|
|
356
|
+
nodeDependencies: shouldDependOnWebCompiler
|
|
357
|
+
? { dependencies: ['web'] }
|
|
358
|
+
: {},
|
|
359
|
+
};
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
export const createReactRouterModePlan = (
|
|
363
|
+
options: CreateReactRouterModePlanOptions
|
|
364
|
+
): Promise<ReactRouterModePlan> =>
|
|
365
|
+
options.isRscMode
|
|
366
|
+
? createRscModePlan(options)
|
|
367
|
+
: createClassicModePlan(options);
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import type { Route, PluginOptions } from './types.js';
|
|
2
3
|
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
|
|
3
4
|
import {
|
|
5
|
+
createReactRouterManifestOptions,
|
|
4
6
|
createReactRouterManifestStats,
|
|
5
7
|
generateReactRouterManifestForDev,
|
|
6
8
|
getReactRouterManifestChunkNames,
|
|
7
|
-
getReactRouterManifestForDev,
|
|
8
9
|
getReactRouterManifestPath,
|
|
10
|
+
type ReactRouterManifestForDev as ReactRouterManifest,
|
|
11
|
+
type RouteChunkManifestOptions,
|
|
12
|
+
type RouteManifestModuleExports,
|
|
13
|
+
type RouteModuleAnalysisProvider,
|
|
9
14
|
} from './manifest.js';
|
|
10
15
|
import { combineURLs } from './plugin-utils.js';
|
|
11
16
|
import jsesc from 'jsesc';
|
|
@@ -21,6 +26,7 @@ type StatsWithIntegrity = {
|
|
|
21
26
|
|
|
22
27
|
type CompilationAssetWithIntegrity = {
|
|
23
28
|
name: string;
|
|
29
|
+
source?: { source(): string | Buffer };
|
|
24
30
|
info?: {
|
|
25
31
|
integrity?: unknown;
|
|
26
32
|
};
|
|
@@ -33,15 +39,14 @@ type CompilationWithIntegrityAssets =
|
|
|
33
39
|
| Pick<Rspack.Compilation, 'getAssets'>;
|
|
34
40
|
|
|
35
41
|
type ModifyBrowserManifestOptions = {
|
|
36
|
-
future?: { unstable_subResourceIntegrity?: boolean };
|
|
37
42
|
subResourceIntegrity?: boolean;
|
|
43
|
+
future?: { unstable_subResourceIntegrity?: boolean };
|
|
38
44
|
manifestChunkNames?: ReadonlySet<string>;
|
|
45
|
+
routeModuleAnalysis?: RouteModuleAnalysisProvider;
|
|
39
46
|
onManifest?: (
|
|
40
|
-
manifest:
|
|
47
|
+
manifest: ReactRouterManifest,
|
|
41
48
|
sri: Record<string, string> | true | undefined,
|
|
42
|
-
moduleExportsByRouteId:
|
|
43
|
-
ReturnType<typeof generateReactRouterManifestForDev>
|
|
44
|
-
>['moduleExportsByRouteId'],
|
|
49
|
+
moduleExportsByRouteId: RouteManifestModuleExports,
|
|
45
50
|
context: {
|
|
46
51
|
compilation: Rspack.Compilation;
|
|
47
52
|
manifestStats: ReturnType<typeof createReactRouterManifestStats>;
|
|
@@ -51,18 +56,9 @@ type ModifyBrowserManifestOptions = {
|
|
|
51
56
|
|
|
52
57
|
type ProcessAssetsApi = Pick<RsbuildPluginAPI, 'processAssets'>;
|
|
53
58
|
type AssetPrefixInput = string | (() => string);
|
|
54
|
-
type
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
type RouteManifestModuleExports = Awaited<
|
|
58
|
-
ReturnType<typeof generateReactRouterManifestForDev>
|
|
59
|
-
>['moduleExportsByRouteId'];
|
|
60
|
-
type GeneratedManifest = {
|
|
61
|
-
manifest: ReactRouterManifest;
|
|
62
|
-
moduleExportsByRouteId: RouteManifestModuleExports;
|
|
63
|
-
manifestStats: ReturnType<typeof createReactRouterManifestStats>;
|
|
64
|
-
assetPrefix: string;
|
|
65
|
-
};
|
|
59
|
+
type ManifestProcessAssetsContext = Parameters<
|
|
60
|
+
Parameters<RsbuildPluginAPI['processAssets']>[1]
|
|
61
|
+
>[0];
|
|
66
62
|
|
|
67
63
|
const BROWSER_MANIFEST_ASSET =
|
|
68
64
|
'static/js/virtual/react-router/browser-manifest.js';
|
|
@@ -85,12 +81,27 @@ const addIntegrity = (
|
|
|
85
81
|
assetName: unknown,
|
|
86
82
|
integrity: unknown
|
|
87
83
|
) => {
|
|
88
|
-
if (
|
|
84
|
+
if (
|
|
85
|
+
typeof assetName !== 'string' ||
|
|
86
|
+
!assetName.endsWith('.js') ||
|
|
87
|
+
typeof integrity !== 'string'
|
|
88
|
+
) {
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
sri[toManifestAssetUrl(assetPrefix, assetName)] = integrity;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
+
const computeSubresourceIntegrity = (
|
|
95
|
+
source: CompilationAssetWithIntegrity['source']
|
|
96
|
+
): string | undefined => {
|
|
97
|
+
if (!source) {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return `sha384-${createHash('sha384')
|
|
101
|
+
.update(source.source())
|
|
102
|
+
.digest('base64')}`;
|
|
103
|
+
};
|
|
104
|
+
|
|
94
105
|
export const collectSubresourceIntegrity = (
|
|
95
106
|
stats: StatsWithIntegrity | undefined,
|
|
96
107
|
compilation: CompilationWithIntegrityAssets | undefined,
|
|
@@ -106,7 +117,15 @@ export const collectSubresourceIntegrity = (
|
|
|
106
117
|
const assets =
|
|
107
118
|
compilation.getAssets() as readonly CompilationAssetWithIntegrity[];
|
|
108
119
|
for (const asset of assets) {
|
|
109
|
-
|
|
120
|
+
if (!asset.name.endsWith('.js')) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
addIntegrity(
|
|
124
|
+
sri,
|
|
125
|
+
assetPrefix,
|
|
126
|
+
asset.name,
|
|
127
|
+
computeSubresourceIntegrity(asset.source) ?? asset.info?.integrity
|
|
128
|
+
);
|
|
110
129
|
}
|
|
111
130
|
}
|
|
112
131
|
|
|
@@ -119,7 +138,7 @@ export function registerModifyBrowserManifestAssets(
|
|
|
119
138
|
pluginOptions: PluginOptions,
|
|
120
139
|
appDirectory: string,
|
|
121
140
|
assetPrefix: AssetPrefixInput = '/',
|
|
122
|
-
routeChunkOptions?:
|
|
141
|
+
routeChunkOptions?: RouteChunkManifestOptions,
|
|
123
142
|
options?: ModifyBrowserManifestOptions
|
|
124
143
|
): void {
|
|
125
144
|
const getAssetPrefix =
|
|
@@ -130,117 +149,104 @@ export function registerModifyBrowserManifestAssets(
|
|
|
130
149
|
routes,
|
|
131
150
|
routeChunkOptions?.splitRouteModules
|
|
132
151
|
);
|
|
152
|
+
const isBuild = Boolean(routeChunkOptions?.isBuild);
|
|
133
153
|
const finalizeSri = Boolean(
|
|
134
|
-
|
|
154
|
+
isBuild &&
|
|
135
155
|
(options?.subResourceIntegrity ??
|
|
136
156
|
options?.future?.unstable_subResourceIntegrity)
|
|
137
157
|
);
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
|
|
159
|
+
// Build the React Router manifest from the compilation's current asset names,
|
|
160
|
+
// emit the build-mode `manifest-<version>.js` asset (or replace the dev
|
|
161
|
+
// browser-manifest placeholder), and fire the `onManifest` callback.
|
|
162
|
+
const buildAndEmitManifest = async (
|
|
163
|
+
{ assets, sources, compilation }: ManifestProcessAssetsContext,
|
|
164
|
+
{ withSri }: { withSri: boolean }
|
|
165
|
+
): Promise<void> => {
|
|
166
|
+
const currentAssetPrefix = getAssetPrefix();
|
|
167
|
+
const stats = createReactRouterManifestStats(
|
|
168
|
+
compilation,
|
|
169
|
+
manifestChunkNames
|
|
170
|
+
);
|
|
171
|
+
const { manifest, moduleExportsByRouteId } =
|
|
172
|
+
await generateReactRouterManifestForDev(
|
|
173
|
+
routes,
|
|
174
|
+
pluginOptions,
|
|
175
|
+
stats,
|
|
176
|
+
appDirectory,
|
|
177
|
+
currentAssetPrefix,
|
|
178
|
+
createReactRouterManifestOptions({
|
|
179
|
+
routeChunks: routeChunkOptions,
|
|
180
|
+
routeModuleAnalysis: options?.routeModuleAnalysis,
|
|
181
|
+
})
|
|
149
182
|
);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const serializedManifest = jsesc(manifestForBrowser, { es6: true });
|
|
167
|
-
const newSource = originalSource.replace(
|
|
168
|
-
/["'`]PLACEHOLDER["'`]/,
|
|
169
|
-
() => serializedManifest
|
|
170
|
-
);
|
|
171
|
-
compilation.updateAsset(
|
|
172
|
-
BROWSER_MANIFEST_ASSET,
|
|
173
|
-
new sources.RawSource(newSource)
|
|
174
|
-
);
|
|
175
|
-
}
|
|
183
|
+
const browserManifest = { ...manifest };
|
|
184
|
+
delete browserManifest.sri;
|
|
185
|
+
|
|
186
|
+
const browserManifestAsset = assets[BROWSER_MANIFEST_ASSET];
|
|
187
|
+
if (browserManifestAsset) {
|
|
188
|
+
const originalSource = browserManifestAsset.source().toString();
|
|
189
|
+
const serializedManifest = jsesc(browserManifest, { es6: true });
|
|
190
|
+
const newSource = originalSource.replace(
|
|
191
|
+
/["'`]PLACEHOLDER["'`]/,
|
|
192
|
+
() => serializedManifest
|
|
193
|
+
);
|
|
194
|
+
compilation.updateAsset(
|
|
195
|
+
BROWSER_MANIFEST_ASSET,
|
|
196
|
+
new sources.RawSource(newSource)
|
|
197
|
+
);
|
|
198
|
+
}
|
|
176
199
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
200
|
+
if (isBuild) {
|
|
201
|
+
const entryAssets = stats?.assetsByChunkName?.['entry.client'];
|
|
202
|
+
const entryJsAssets =
|
|
203
|
+
entryAssets?.filter(asset => asset.endsWith('.js')) || [];
|
|
204
|
+
const manifestPath = getReactRouterManifestPath({
|
|
205
|
+
version: manifest.version,
|
|
206
|
+
isBuild: true,
|
|
207
|
+
entryModulePath: entryJsAssets[0],
|
|
208
|
+
});
|
|
209
|
+
const manifestSource = `window.__reactRouterManifest=${jsesc(
|
|
210
|
+
browserManifest,
|
|
211
|
+
{ es6: true }
|
|
212
|
+
)};`;
|
|
213
|
+
const source = new sources.RawSource(manifestSource);
|
|
214
|
+
if (compilation.getAsset(manifestPath)) {
|
|
215
|
+
compilation.updateAsset(manifestPath, source);
|
|
216
|
+
} else {
|
|
217
|
+
compilation.emitAsset(manifestPath, source);
|
|
196
218
|
}
|
|
219
|
+
}
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
221
|
+
// Rspack's SRI stats are finalized by a later report-stage hook. Hash the
|
|
222
|
+
// finalized JavaScript sources here as a fallback, matching React Router's
|
|
223
|
+
// Vite integration. Browser manifests intentionally omit `sri`; hydration
|
|
224
|
+
// receives it from the server-rendered import map.
|
|
225
|
+
const sri = withSri
|
|
226
|
+
? (collectSubresourceIntegrity(
|
|
227
|
+
compilation.getStats().toJson({
|
|
228
|
+
all: false,
|
|
229
|
+
assets: true,
|
|
230
|
+
}) as StatsWithIntegrity,
|
|
231
|
+
compilation,
|
|
232
|
+
currentAssetPrefix
|
|
233
|
+
) ?? true)
|
|
234
|
+
: undefined;
|
|
235
|
+
|
|
236
|
+
options?.onManifest?.(manifest, sri, moduleExportsByRouteId, {
|
|
237
|
+
compilation,
|
|
238
|
+
manifestStats: stats,
|
|
239
|
+
});
|
|
240
|
+
};
|
|
207
241
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
242
|
+
// In production, Rspack renames content-hashed assets during
|
|
243
|
+
// `realContentHash` (PROCESS_ASSETS_STAGE_OPTIMIZE_HASH = 2500). Reading
|
|
244
|
+
// asset names before that stage yields pre-rename hashes that never appear
|
|
245
|
+
// in the output, so CSS/JS URLs in the manifest 404. In dev, CSS extraction
|
|
246
|
+
// can also attach entry CSS after `additions`. Defer manifest generation and
|
|
247
|
+
// emission to the `report` stage (PROCESS_ASSETS_STAGE_REPORT = 5000), which
|
|
248
|
+
// runs after the rename, late CSS attachment, and SRI integrity finalization.
|
|
249
|
+
api.processAssets({ stage: 'report', environments: ['web'] }, context =>
|
|
250
|
+
buildAndEmitManifest(context, { withSri: finalizeSri })
|
|
213
251
|
);
|
|
214
|
-
|
|
215
|
-
if (generatedManifests) {
|
|
216
|
-
api.processAssets(
|
|
217
|
-
{ stage: 'report', environments: ['web'] },
|
|
218
|
-
({ compilation }) => {
|
|
219
|
-
const generatedManifest = generatedManifests.get(compilation);
|
|
220
|
-
if (!generatedManifest) {
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
generatedManifests.delete(compilation);
|
|
225
|
-
const sri =
|
|
226
|
-
collectSubresourceIntegrity(
|
|
227
|
-
undefined,
|
|
228
|
-
compilation,
|
|
229
|
-
generatedManifest.assetPrefix
|
|
230
|
-
) ?? true;
|
|
231
|
-
options?.onManifest?.(
|
|
232
|
-
{
|
|
233
|
-
...generatedManifest.manifest,
|
|
234
|
-
sri,
|
|
235
|
-
},
|
|
236
|
-
sri,
|
|
237
|
-
generatedManifest.moduleExportsByRouteId,
|
|
238
|
-
{
|
|
239
|
-
compilation,
|
|
240
|
-
manifestStats: generatedManifest.manifestStats,
|
|
241
|
-
}
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
252
|
}
|