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
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import type { RsbuildEntryDescription, RsbuildPluginAPI } from '@rsbuild/core';
|
|
3
|
+
import type { RouteConfigEntry } from '@react-router/dev/routes';
|
|
4
|
+
import { resolve } from 'pathe';
|
|
5
|
+
import {
|
|
6
|
+
getBuildManifest,
|
|
7
|
+
getRoutesByServerBundleId,
|
|
8
|
+
} from './build-manifest.js';
|
|
9
|
+
import { BUILD_CLIENT_ROUTE_QUERY_STRING } from './constants.js';
|
|
10
|
+
import {
|
|
11
|
+
DEV_HMR_RUNTIME_MODULE_ID,
|
|
12
|
+
type DevHmrPlanOptions,
|
|
13
|
+
} from './dev-hmr.js';
|
|
14
|
+
import {
|
|
15
|
+
createReactRouterDevRuntimeController,
|
|
16
|
+
type ReactRouterDevRuntimeController,
|
|
17
|
+
} from './dev-runtime-controller.js';
|
|
18
|
+
import { generateWithProps } from './plugin-utils.js';
|
|
19
|
+
import { resolvePrerenderPaths } from './prerender.js';
|
|
20
|
+
import { generateServerBuild } from './server-utils.js';
|
|
21
|
+
import {
|
|
22
|
+
createReactRouterServerBuildPlan,
|
|
23
|
+
type ReactRouterServerBundleEntry,
|
|
24
|
+
} from './server-build-plan.js';
|
|
25
|
+
import {
|
|
26
|
+
getRouteChunkEntryName,
|
|
27
|
+
getRouteChunkModuleId,
|
|
28
|
+
routeChunkExportNames,
|
|
29
|
+
} from './route-chunks.js';
|
|
30
|
+
import type { Config } from './react-router-config.js';
|
|
31
|
+
import type { Route } from './types.js';
|
|
32
|
+
|
|
33
|
+
type CreateClassicWebRouteEntriesOptions = {
|
|
34
|
+
appDirectory: string;
|
|
35
|
+
isBuild: boolean;
|
|
36
|
+
routes: Record<string, Route>;
|
|
37
|
+
splitRouteModules: boolean;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type CreateClassicVirtualModulesOptions = {
|
|
41
|
+
allowedActionOrigins: string[] | undefined;
|
|
42
|
+
appDirectory: string;
|
|
43
|
+
assetsBuildDirectory: string;
|
|
44
|
+
basename: string;
|
|
45
|
+
devHmrRuntimeModule?: string;
|
|
46
|
+
entryServerPath: string;
|
|
47
|
+
future: Config['future'];
|
|
48
|
+
isSpaMode: boolean;
|
|
49
|
+
prerenderPaths: string[];
|
|
50
|
+
publicPath: string;
|
|
51
|
+
routeDiscovery: Config['routeDiscovery'];
|
|
52
|
+
routes: Record<string, Route>;
|
|
53
|
+
routesByServerBundleId: Record<string, Record<string, Route>>;
|
|
54
|
+
ssr: boolean;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
type CreateClassicBuildArtifactsOptions = {
|
|
58
|
+
api: RsbuildPluginAPI;
|
|
59
|
+
defaultEntryName: string;
|
|
60
|
+
isBuild: boolean;
|
|
61
|
+
prerenderConfig: Config['prerender'];
|
|
62
|
+
reactRouterConfig: Required<
|
|
63
|
+
Pick<
|
|
64
|
+
Config,
|
|
65
|
+
'appDirectory' | 'buildDirectory' | 'serverBuildFile' | 'future'
|
|
66
|
+
>
|
|
67
|
+
> &
|
|
68
|
+
Pick<Config, 'serverBundles'>;
|
|
69
|
+
routeConfig: RouteConfigEntry[];
|
|
70
|
+
routes: Record<string, Route>;
|
|
71
|
+
rootDirectory: string;
|
|
72
|
+
ssr: boolean;
|
|
73
|
+
devHmr?: DevHmrPlanOptions;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type ClassicBuildArtifacts = {
|
|
77
|
+
buildManifest: Awaited<ReturnType<typeof getBuildManifest>>;
|
|
78
|
+
devRuntime: ReactRouterDevRuntimeController;
|
|
79
|
+
prerenderPaths: string[];
|
|
80
|
+
routesByServerBundleId: Record<string, Record<string, Route>>;
|
|
81
|
+
serverBundleEntries: ReactRouterServerBundleEntry[];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export const createClassicBuildArtifacts = async ({
|
|
85
|
+
api,
|
|
86
|
+
defaultEntryName,
|
|
87
|
+
isBuild,
|
|
88
|
+
prerenderConfig,
|
|
89
|
+
reactRouterConfig,
|
|
90
|
+
routeConfig,
|
|
91
|
+
routes,
|
|
92
|
+
rootDirectory,
|
|
93
|
+
ssr,
|
|
94
|
+
devHmr,
|
|
95
|
+
}: CreateClassicBuildArtifactsOptions): Promise<ClassicBuildArtifacts> => {
|
|
96
|
+
const buildManifest = await getBuildManifest({
|
|
97
|
+
reactRouterConfig,
|
|
98
|
+
routes,
|
|
99
|
+
rootDirectory,
|
|
100
|
+
});
|
|
101
|
+
const routesByServerBundleId = getRoutesByServerBundleId(
|
|
102
|
+
buildManifest,
|
|
103
|
+
routes
|
|
104
|
+
);
|
|
105
|
+
const serverBuildPlan = createReactRouterServerBuildPlan({
|
|
106
|
+
routesByServerBundleId,
|
|
107
|
+
serverBuildFile: reactRouterConfig.serverBuildFile,
|
|
108
|
+
defaultEntryName,
|
|
109
|
+
});
|
|
110
|
+
const prerenderPaths = await resolvePrerenderPaths(
|
|
111
|
+
prerenderConfig,
|
|
112
|
+
ssr,
|
|
113
|
+
routeConfig,
|
|
114
|
+
{
|
|
115
|
+
logWarning: true,
|
|
116
|
+
warn: message => api.logger.warn(message),
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
buildManifest,
|
|
122
|
+
devRuntime: createReactRouterDevRuntimeController({
|
|
123
|
+
api,
|
|
124
|
+
isBuild,
|
|
125
|
+
buildPlan: serverBuildPlan,
|
|
126
|
+
clientPatchesRouteMetadata: devHmr?.isEnabled,
|
|
127
|
+
onNodeRebuildCommitted: devHmr?.onNodeRebuildCommitted,
|
|
128
|
+
}),
|
|
129
|
+
prerenderPaths,
|
|
130
|
+
routesByServerBundleId,
|
|
131
|
+
serverBundleEntries: serverBuildPlan.serverBundleEntries,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const createClassicWebRouteEntries = ({
|
|
136
|
+
appDirectory,
|
|
137
|
+
isBuild,
|
|
138
|
+
routes,
|
|
139
|
+
splitRouteModules,
|
|
140
|
+
}: CreateClassicWebRouteEntriesOptions): {
|
|
141
|
+
manifestChunkNames: Set<string>;
|
|
142
|
+
webRouteEntries: Record<string, RsbuildEntryDescription>;
|
|
143
|
+
} => {
|
|
144
|
+
const manifestChunkNames = new Set<string>(['entry.client']);
|
|
145
|
+
const webRouteEntries = Object.values(routes).reduce(
|
|
146
|
+
(acc, route) => {
|
|
147
|
+
const entryName = route.file.slice(0, route.file.lastIndexOf('.'));
|
|
148
|
+
const routeFilePath = resolve(appDirectory, route.file);
|
|
149
|
+
manifestChunkNames.add(entryName);
|
|
150
|
+
acc[entryName] = {
|
|
151
|
+
import: `${routeFilePath}${BUILD_CLIENT_ROUTE_QUERY_STRING}`,
|
|
152
|
+
html: false,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
if (isBuild && splitRouteModules && route.id !== 'root') {
|
|
156
|
+
let source: string;
|
|
157
|
+
try {
|
|
158
|
+
source = readFileSync(routeFilePath, 'utf8');
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
161
|
+
return acc;
|
|
162
|
+
}
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
165
|
+
for (const exportName of routeChunkExportNames) {
|
|
166
|
+
if (!source.includes(exportName)) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const chunkEntryName = getRouteChunkEntryName(route.id, exportName);
|
|
170
|
+
manifestChunkNames.add(chunkEntryName);
|
|
171
|
+
acc[chunkEntryName] = {
|
|
172
|
+
import: getRouteChunkModuleId(routeFilePath, exportName),
|
|
173
|
+
html: false,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return acc;
|
|
179
|
+
},
|
|
180
|
+
{} as Record<string, RsbuildEntryDescription>
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
manifestChunkNames,
|
|
185
|
+
webRouteEntries,
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export const createClassicVirtualModules = ({
|
|
190
|
+
allowedActionOrigins,
|
|
191
|
+
appDirectory,
|
|
192
|
+
assetsBuildDirectory,
|
|
193
|
+
basename,
|
|
194
|
+
devHmrRuntimeModule,
|
|
195
|
+
entryServerPath,
|
|
196
|
+
future,
|
|
197
|
+
isSpaMode,
|
|
198
|
+
prerenderPaths,
|
|
199
|
+
publicPath,
|
|
200
|
+
routeDiscovery,
|
|
201
|
+
routes,
|
|
202
|
+
routesByServerBundleId,
|
|
203
|
+
ssr,
|
|
204
|
+
}: CreateClassicVirtualModulesOptions): Record<string, string> => {
|
|
205
|
+
const serverBuildOptions = {
|
|
206
|
+
entryServerPath,
|
|
207
|
+
assetsBuildDirectory,
|
|
208
|
+
basename,
|
|
209
|
+
appDirectory,
|
|
210
|
+
ssr,
|
|
211
|
+
isSpaMode,
|
|
212
|
+
future,
|
|
213
|
+
allowedActionOrigins,
|
|
214
|
+
prerender: prerenderPaths,
|
|
215
|
+
routeDiscovery,
|
|
216
|
+
publicPath,
|
|
217
|
+
};
|
|
218
|
+
const bundleVirtualModules = Object.fromEntries(
|
|
219
|
+
Object.entries(routesByServerBundleId).map(([bundleId, bundleRoutes]) => [
|
|
220
|
+
`virtual/react-router/server-build-${bundleId}`,
|
|
221
|
+
generateServerBuild(bundleRoutes, {
|
|
222
|
+
...serverBuildOptions,
|
|
223
|
+
serverManifestId: `virtual/react-router/server-manifest-${bundleId}`,
|
|
224
|
+
}),
|
|
225
|
+
])
|
|
226
|
+
);
|
|
227
|
+
const bundleManifestModules = Object.fromEntries(
|
|
228
|
+
Object.entries(routesByServerBundleId)
|
|
229
|
+
.filter(
|
|
230
|
+
([, bundleRoutes]) =>
|
|
231
|
+
bundleRoutes && Object.keys(bundleRoutes).length > 0
|
|
232
|
+
)
|
|
233
|
+
.map(([bundleId]) => [
|
|
234
|
+
`virtual/react-router/server-manifest-${bundleId}`,
|
|
235
|
+
'export default {};',
|
|
236
|
+
])
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
'virtual/react-router/browser-manifest': 'export default {};',
|
|
241
|
+
'virtual/react-router/server-manifest': 'export default {};',
|
|
242
|
+
'virtual/react-router/server-build': generateServerBuild(
|
|
243
|
+
routes,
|
|
244
|
+
serverBuildOptions
|
|
245
|
+
),
|
|
246
|
+
...bundleVirtualModules,
|
|
247
|
+
...bundleManifestModules,
|
|
248
|
+
'virtual/react-router/with-props': generateWithProps(),
|
|
249
|
+
...(devHmrRuntimeModule !== undefined
|
|
250
|
+
? { [DEV_HMR_RUNTIME_MODULE_ID]: devHmrRuntimeModule }
|
|
251
|
+
: {}),
|
|
252
|
+
};
|
|
253
|
+
};
|
package/src/config-imports.ts
CHANGED
|
@@ -1,14 +1,162 @@
|
|
|
1
|
-
import type { ModuleCache } from 'jiti';
|
|
1
|
+
import type { JitiOptions, ModuleCache, TransformOptions } from 'jiti';
|
|
2
2
|
import { createJiti } from 'jiti';
|
|
3
3
|
import { resolve } from 'pathe';
|
|
4
|
+
import { parse } from './yuku.js';
|
|
4
5
|
|
|
5
|
-
type ConfigImporter = Pick<ReturnType<typeof createJiti>, 'import'>;
|
|
6
|
+
export type ConfigImporter = Pick<ReturnType<typeof createJiti>, 'import'>;
|
|
7
|
+
|
|
8
|
+
type ConfigImportOptions = {
|
|
9
|
+
define?: Record<string, unknown>;
|
|
10
|
+
moduleCache?: boolean;
|
|
11
|
+
};
|
|
6
12
|
|
|
7
13
|
const normalizePath = (filePath: string): string => resolve(filePath);
|
|
8
14
|
|
|
9
15
|
const isNodeModulePath = (filePath: string): boolean =>
|
|
10
16
|
filePath.split(/[\\/]/).includes('node_modules');
|
|
11
17
|
|
|
18
|
+
type AstNode = {
|
|
19
|
+
type?: string;
|
|
20
|
+
start?: number;
|
|
21
|
+
end?: number;
|
|
22
|
+
object?: AstNode;
|
|
23
|
+
property?: AstNode;
|
|
24
|
+
meta?: AstNode;
|
|
25
|
+
name?: string;
|
|
26
|
+
value?: unknown;
|
|
27
|
+
computed?: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const normalizeDefineValue = (value: unknown): string | undefined => {
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
if (typeof value === 'string') {
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
return JSON.stringify(value);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const getMemberExpressionName = (node: AstNode | undefined): string | null => {
|
|
41
|
+
if (!node) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (node.type === 'Identifier') {
|
|
45
|
+
return node.name ?? null;
|
|
46
|
+
}
|
|
47
|
+
if (node.type === 'MetaProperty') {
|
|
48
|
+
const meta = getMemberExpressionName(node.meta);
|
|
49
|
+
const property = getMemberExpressionName(node.property);
|
|
50
|
+
return meta && property ? `${meta}.${property}` : null;
|
|
51
|
+
}
|
|
52
|
+
if (node.type !== 'MemberExpression') {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const object = getMemberExpressionName(node.object);
|
|
56
|
+
const property =
|
|
57
|
+
node.computed && typeof node.property?.value === 'string'
|
|
58
|
+
? node.property.value
|
|
59
|
+
: getMemberExpressionName(node.property);
|
|
60
|
+
return object && property ? `${object}.${property}` : null;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const collectDefineReplacements = (
|
|
64
|
+
node: unknown,
|
|
65
|
+
define: ReadonlyMap<string, string>,
|
|
66
|
+
replacements: Array<{ start: number; end: number; value: string }>
|
|
67
|
+
): void => {
|
|
68
|
+
if (!node || typeof node !== 'object') {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const astNode = node as AstNode;
|
|
73
|
+
const memberName = getMemberExpressionName(astNode);
|
|
74
|
+
const value = memberName ? define.get(memberName) : undefined;
|
|
75
|
+
if (
|
|
76
|
+
value !== undefined &&
|
|
77
|
+
typeof astNode.start === 'number' &&
|
|
78
|
+
typeof astNode.end === 'number'
|
|
79
|
+
) {
|
|
80
|
+
replacements.push({
|
|
81
|
+
start: astNode.start,
|
|
82
|
+
end: astNode.end,
|
|
83
|
+
value,
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const child of Object.values(node)) {
|
|
89
|
+
if (Array.isArray(child)) {
|
|
90
|
+
for (const item of child) {
|
|
91
|
+
collectDefineReplacements(item, define, replacements);
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
collectDefineReplacements(child, define, replacements);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const applyConfigDefines = (
|
|
100
|
+
source: string,
|
|
101
|
+
define: Record<string, unknown> | undefined
|
|
102
|
+
): string => {
|
|
103
|
+
if (!define) {
|
|
104
|
+
return source;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const normalizedDefine = new Map(
|
|
108
|
+
Object.entries(define).flatMap(([key, value]) => {
|
|
109
|
+
const normalized = normalizeDefineValue(value);
|
|
110
|
+
return normalized === undefined ? [] : [[key, normalized]];
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
if (normalizedDefine.size === 0) {
|
|
114
|
+
return source;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const replacements: Array<{ start: number; end: number; value: string }> = [];
|
|
118
|
+
collectDefineReplacements(
|
|
119
|
+
parse(source, { sourceType: 'module' }),
|
|
120
|
+
normalizedDefine,
|
|
121
|
+
replacements
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return replacements
|
|
125
|
+
.sort((left, right) => right.start - left.start)
|
|
126
|
+
.reduce(
|
|
127
|
+
(contents, replacement) =>
|
|
128
|
+
`${contents.slice(0, replacement.start)}${replacement.value}${contents.slice(
|
|
129
|
+
replacement.end
|
|
130
|
+
)}`,
|
|
131
|
+
source
|
|
132
|
+
);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const createConfigImporter = ({
|
|
136
|
+
define,
|
|
137
|
+
moduleCache = true,
|
|
138
|
+
}: ConfigImportOptions = {}): ReturnType<typeof createJiti> => {
|
|
139
|
+
const defaultJiti = createJiti(process.cwd(), { moduleCache });
|
|
140
|
+
if (!define || Object.keys(define).length === 0) {
|
|
141
|
+
return defaultJiti;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const defaultTransform = defaultJiti.transform;
|
|
145
|
+
const options = {
|
|
146
|
+
moduleCache,
|
|
147
|
+
transform(transformOptions: TransformOptions) {
|
|
148
|
+
return {
|
|
149
|
+
code: defaultTransform({
|
|
150
|
+
...transformOptions,
|
|
151
|
+
source: applyConfigDefines(transformOptions.source, define),
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
} satisfies JitiOptions;
|
|
156
|
+
|
|
157
|
+
return createJiti(process.cwd(), options);
|
|
158
|
+
};
|
|
159
|
+
|
|
12
160
|
export const collectConfigImportWatchPaths = (
|
|
13
161
|
configPath: string,
|
|
14
162
|
moduleCache: ModuleCache,
|
|
@@ -50,11 +198,10 @@ export const clearConfigImportCache = (
|
|
|
50
198
|
export const importConfigWithWatchPaths = async <T>(
|
|
51
199
|
configPath: string,
|
|
52
200
|
load: (importer: ConfigImporter) => PromiseLike<T> | T = async importer =>
|
|
53
|
-
importer.import<T>(configPath, { default: true })
|
|
201
|
+
importer.import<T>(configPath, { default: true }),
|
|
202
|
+
options?: ConfigImportOptions
|
|
54
203
|
): Promise<{ value: Awaited<T>; watchPaths: string | string[] }> => {
|
|
55
|
-
const jiti =
|
|
56
|
-
moduleCache: true,
|
|
57
|
-
});
|
|
204
|
+
const jiti = createConfigImporter({ moduleCache: true, ...options });
|
|
58
205
|
const previousCacheKeys = new Set(Object.keys(jiti.cache));
|
|
59
206
|
let importPaths: string[] = [];
|
|
60
207
|
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export const PLUGIN_NAME = 'rsbuild:react-router';
|
|
2
2
|
|
|
3
|
+
/** Default web `output.distPath.js` segment when the user has not customized it. */
|
|
4
|
+
export const DEFAULT_JS_DIST_PATH = 'static/js';
|
|
5
|
+
|
|
3
6
|
export const JS_EXTENSIONS = [
|
|
4
7
|
'.tsx',
|
|
5
8
|
'.ts',
|
|
@@ -32,8 +35,6 @@ export const SERVER_ONLY_ROUTE_EXPORTS_SET: ReadonlySet<string> = new Set(
|
|
|
32
35
|
SERVER_ONLY_ROUTE_EXPORTS
|
|
33
36
|
);
|
|
34
37
|
|
|
35
|
-
// Client route exports are split into non-component exports and component exports.
|
|
36
|
-
// This mirrors upstream React Router Vite plugin intent and is used for export filtering.
|
|
37
38
|
export const CLIENT_NON_COMPONENT_EXPORTS = [
|
|
38
39
|
'clientAction',
|
|
39
40
|
'clientLoader',
|
|
@@ -89,3 +90,6 @@ export const CLIENT_EXPORTS = {
|
|
|
89
90
|
meta: 'meta',
|
|
90
91
|
shouldRevalidate: 'shouldRevalidate',
|
|
91
92
|
} as const;
|
|
93
|
+
|
|
94
|
+
// SPA-mode prerender fallback document, served when no prerendered page matches.
|
|
95
|
+
export const SPA_FALLBACK_HTML_FILE = '__spa-fallback.html';
|
|
@@ -7,21 +7,19 @@ import {
|
|
|
7
7
|
createDelayedPluginTask,
|
|
8
8
|
DEV_BACKGROUND_STARTUP_DELAY_MS,
|
|
9
9
|
normalizeEffectError,
|
|
10
|
-
|
|
11
|
-
tryPluginPromise,
|
|
10
|
+
type PluginEffectRuntime,
|
|
12
11
|
} from './effect-runtime.js';
|
|
13
12
|
import {
|
|
14
|
-
|
|
13
|
+
acquireLazyCompilationPrewarm,
|
|
15
14
|
normalizeLazyCompilationPrewarmOptions,
|
|
16
15
|
} from './lazy-compilation-prewarm.js';
|
|
17
16
|
import {
|
|
18
17
|
configRoutesToRouteManifestEntries,
|
|
19
18
|
type ReactRouterManifestForDev,
|
|
20
19
|
} from './manifest.js';
|
|
21
|
-
import type { RouteTransformExecutor } from './parallel-route-transforms.js';
|
|
22
20
|
import {
|
|
21
|
+
acquireRouteTopologyWatcher,
|
|
23
22
|
createRouteManifestSnapshot,
|
|
24
|
-
createRouteTopologyWatcher,
|
|
25
23
|
ensureDevRestartMarker,
|
|
26
24
|
type WatchFileConfig,
|
|
27
25
|
} from './route-watch.js';
|
|
@@ -29,9 +27,9 @@ import type { PluginOptions } from './types.js';
|
|
|
29
27
|
|
|
30
28
|
type RegisterReactRouterDevBackgroundResourcesOptions = {
|
|
31
29
|
api: RsbuildPluginAPI;
|
|
30
|
+
runtime: PluginEffectRuntime;
|
|
32
31
|
isBuild: boolean;
|
|
33
32
|
lazyCompilationPrewarm: PluginOptions['unstableLazyCompilationPrewarm'];
|
|
34
|
-
routeTransformExecutor: RouteTransformExecutor;
|
|
35
33
|
routeRestartMarkerPath: string;
|
|
36
34
|
watchDirectory: string;
|
|
37
35
|
getRouteTopology: () => Promise<Set<string>>;
|
|
@@ -100,7 +98,10 @@ export const createReactRouterRouteWatchFiles = ({
|
|
|
100
98
|
watchFiles.push(
|
|
101
99
|
{
|
|
102
100
|
paths: routeConfigWatchPaths,
|
|
103
|
-
|
|
101
|
+
// Keep the current compiler alive while a route config is temporarily
|
|
102
|
+
// invalid. The topology watcher touches the dedicated restart marker
|
|
103
|
+
// once a valid edit actually changes the route graph.
|
|
104
|
+
type: 'reload-page',
|
|
104
105
|
},
|
|
105
106
|
{
|
|
106
107
|
paths: routeRestartMarkerPath,
|
|
@@ -112,38 +113,18 @@ export const createReactRouterRouteWatchFiles = ({
|
|
|
112
113
|
return watchFiles;
|
|
113
114
|
};
|
|
114
115
|
|
|
115
|
-
const
|
|
116
|
-
message: string,
|
|
117
|
-
closers: Array<() => Promise<void>>
|
|
118
|
-
): Promise<void> => {
|
|
119
|
-
const results = await Promise.allSettled(closers.map(closer => closer()));
|
|
120
|
-
const errors = results
|
|
121
|
-
.filter(
|
|
122
|
-
(result): result is PromiseRejectedResult => result.status === 'rejected'
|
|
123
|
-
)
|
|
124
|
-
.map(result => normalizeEffectError(result.reason));
|
|
125
|
-
|
|
126
|
-
if (errors.length === 1) {
|
|
127
|
-
throw errors[0];
|
|
128
|
-
}
|
|
129
|
-
if (errors.length > 1) {
|
|
130
|
-
throw new AggregateError(errors, message);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
export const registerReactRouterDevBackgroundResources = ({
|
|
116
|
+
export const registerReactRouterDevBackgroundResources = async ({
|
|
135
117
|
api,
|
|
118
|
+
runtime,
|
|
136
119
|
isBuild,
|
|
137
120
|
lazyCompilationPrewarm,
|
|
138
|
-
routeTransformExecutor,
|
|
139
121
|
routeRestartMarkerPath,
|
|
140
122
|
watchDirectory,
|
|
141
123
|
getRouteTopology,
|
|
142
124
|
initialRouteTopology,
|
|
143
125
|
onRouteTopologyChange,
|
|
144
|
-
}: RegisterReactRouterDevBackgroundResourcesOptions): ReactRouterDevBackgroundResources => {
|
|
145
|
-
let
|
|
146
|
-
let routeTopologyWatcherClosed = false;
|
|
126
|
+
}: RegisterReactRouterDevBackgroundResourcesOptions): Promise<ReactRouterDevBackgroundResources> => {
|
|
127
|
+
let routeTopologyWatcherStarted = false;
|
|
147
128
|
|
|
148
129
|
const reportRouteTopologyWatcherError = (error: unknown): void => {
|
|
149
130
|
api.logger.warn(
|
|
@@ -152,54 +133,49 @@ export const registerReactRouterDevBackgroundResources = ({
|
|
|
152
133
|
};
|
|
153
134
|
|
|
154
135
|
const routeTopologyWatcherTask = createDelayedPluginTask({
|
|
136
|
+
runtime,
|
|
155
137
|
delayMs: DEV_BACKGROUND_STARTUP_DELAY_MS,
|
|
156
138
|
run: () =>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (routeTopologyWatcherClosed) {
|
|
172
|
-
yield* tryPluginPromise(() => closeWatcher());
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
closeActiveRouteTopologyWatcher = closeWatcher;
|
|
176
|
-
}),
|
|
139
|
+
acquireRouteTopologyWatcher({
|
|
140
|
+
runtime,
|
|
141
|
+
watchDirectory,
|
|
142
|
+
getRouteTopology,
|
|
143
|
+
initialRouteTopology,
|
|
144
|
+
restartMarkerPath: routeRestartMarkerPath,
|
|
145
|
+
onRouteTopologyChange,
|
|
146
|
+
onError: reportRouteTopologyWatcherError,
|
|
147
|
+
}).pipe(
|
|
148
|
+
Effect.tap(() =>
|
|
149
|
+
Effect.sync(() => (routeTopologyWatcherStarted = true))
|
|
150
|
+
),
|
|
151
|
+
Effect.asVoid
|
|
152
|
+
),
|
|
177
153
|
onError: reportRouteTopologyWatcherError,
|
|
178
154
|
});
|
|
179
155
|
|
|
180
|
-
const scheduleRouteTopologyWatcher = (): void => {
|
|
181
|
-
if (routeTopologyWatcherClosed || closeActiveRouteTopologyWatcher) {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
routeTopologyWatcherTask.schedule();
|
|
185
|
-
};
|
|
186
|
-
|
|
187
156
|
const lazyCompilationPrewarmConfig = normalizeLazyCompilationPrewarmOptions(
|
|
188
157
|
lazyCompilationPrewarm
|
|
189
158
|
);
|
|
190
159
|
const lazyCompilationPrewarmController = lazyCompilationPrewarmConfig
|
|
191
|
-
?
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
160
|
+
? await runtime.runPromise(
|
|
161
|
+
acquireLazyCompilationPrewarm({
|
|
162
|
+
runtime,
|
|
163
|
+
config: lazyCompilationPrewarmConfig,
|
|
164
|
+
onError: error =>
|
|
165
|
+
api.logger.warn(
|
|
166
|
+
`[${PLUGIN_NAME}] Lazy compilation prewarm skipped: ${error.message}`
|
|
167
|
+
),
|
|
168
|
+
})
|
|
169
|
+
)
|
|
198
170
|
: null;
|
|
199
171
|
|
|
200
172
|
if (!isBuild) {
|
|
201
|
-
|
|
202
|
-
|
|
173
|
+
await runtime.runPromise(
|
|
174
|
+
Effect.acquireRelease(Effect.void, routeTopologyWatcherTask.cancelEffect)
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
api.onBeforeStartDevServer(async () => {
|
|
178
|
+
await ensureDevRestartMarker(routeRestartMarkerPath);
|
|
203
179
|
});
|
|
204
180
|
|
|
205
181
|
api.onAfterStartDevServer(({ port }) => {
|
|
@@ -210,43 +186,13 @@ export const registerReactRouterDevBackgroundResources = ({
|
|
|
210
186
|
});
|
|
211
187
|
|
|
212
188
|
api.onAfterDevCompile(() => {
|
|
213
|
-
|
|
189
|
+
if (!routeTopologyWatcherStarted) {
|
|
190
|
+
routeTopologyWatcherTask.schedule();
|
|
191
|
+
}
|
|
214
192
|
lazyCompilationPrewarmController?.schedule();
|
|
215
193
|
});
|
|
216
|
-
|
|
217
|
-
// Spawn transform workers now so thread startup overlaps Rsbuild's own
|
|
218
|
-
// compiler creation instead of delaying the first route transform.
|
|
219
|
-
routeTransformExecutor.prewarm();
|
|
220
194
|
}
|
|
221
195
|
|
|
222
|
-
const closeRouteTopologyWatcher = async (): Promise<void> => {
|
|
223
|
-
routeTopologyWatcherClosed = true;
|
|
224
|
-
await runPluginEffect(routeTopologyWatcherTask.cancelEffect());
|
|
225
|
-
await closeActiveRouteTopologyWatcher?.();
|
|
226
|
-
closeActiveRouteTopologyWatcher = undefined;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const closeLazyCompilationPrewarm = async (): Promise<void> => {
|
|
230
|
-
await runPluginEffect(
|
|
231
|
-
lazyCompilationPrewarmController?.cancelEffect() ?? Effect.void
|
|
232
|
-
);
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const closeRouteTransformExecutor = (): Promise<void> =>
|
|
236
|
-
routeTransformExecutor.close();
|
|
237
|
-
|
|
238
|
-
api.onCloseDevServer(() =>
|
|
239
|
-
closeAll(
|
|
240
|
-
'[rsbuild-plugin-react-router] Failed to close dev server resources.',
|
|
241
|
-
[
|
|
242
|
-
closeRouteTopologyWatcher,
|
|
243
|
-
closeLazyCompilationPrewarm,
|
|
244
|
-
closeRouteTransformExecutor,
|
|
245
|
-
]
|
|
246
|
-
)
|
|
247
|
-
);
|
|
248
|
-
api.onCloseBuild(closeRouteTransformExecutor);
|
|
249
|
-
|
|
250
196
|
return {
|
|
251
197
|
setManifest(manifest) {
|
|
252
198
|
lazyCompilationPrewarmController?.setManifest(manifest);
|