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,273 @@
1
+ import type { RsbuildPluginAPI, TransformHandler } from '@rsbuild/core';
2
+ import jsesc from 'jsesc';
3
+ import { relative } from 'pathe';
4
+ import { PLUGIN_NAME } from './constants.js';
5
+ import {
6
+ getReactRouterManifestForDev,
7
+ type ReactRouterManifestStats,
8
+ } from './manifest.js';
9
+ import type { RouteTransformExecutor } from './parallel-route-transforms.js';
10
+ import type { ReactRouterPerformanceProfiler } from './performance.js';
11
+ import { createBundlerRouteExportResolver } from './route-export-resolution.js';
12
+ import type { RouteChunkConfig } from './route-chunks.js';
13
+ import type { PluginOptions, Route } from './types.js';
14
+ import { isSourceMapEnabled } from './warnings/warn-on-client-source-maps.js';
15
+
16
+ type ReactRouterManifest = Awaited<
17
+ ReturnType<typeof getReactRouterManifestForDev>
18
+ >;
19
+
20
+ type RegisterBuildOutputTransformsOptions = {
21
+ api: RsbuildPluginAPI;
22
+ resolvedServerOutput: 'module' | 'commonjs';
23
+ performanceProfiler: ReactRouterPerformanceProfiler;
24
+ getLatestServerManifest: () => ReactRouterManifest | null;
25
+ getLatestServerManifestByBundleId: (
26
+ bundleId: string
27
+ ) => ReactRouterManifest | undefined;
28
+ routes: Record<string, Route>;
29
+ pluginOptions: PluginOptions;
30
+ getClientStats: () => ReactRouterManifestStats | undefined;
31
+ appDirectory: string;
32
+ getAssetPrefix: () => string;
33
+ routeChunkOptions: Parameters<typeof getReactRouterManifestForDev>[5];
34
+ routeTransformExecutor: RouteTransformExecutor;
35
+ routeByFilePath: Map<string, Route>;
36
+ routeChunkConfig: RouteChunkConfig;
37
+ isBuild: boolean;
38
+ splitRouteModules: boolean;
39
+ ssr: boolean;
40
+ isSpaMode: boolean;
41
+ rootRoutePath: string;
42
+ };
43
+
44
+ export const registerBuildOutputTransforms = ({
45
+ api,
46
+ resolvedServerOutput,
47
+ performanceProfiler,
48
+ getLatestServerManifest,
49
+ getLatestServerManifestByBundleId,
50
+ routes,
51
+ pluginOptions,
52
+ getClientStats,
53
+ appDirectory,
54
+ getAssetPrefix,
55
+ routeChunkOptions,
56
+ routeTransformExecutor,
57
+ routeByFilePath,
58
+ routeChunkConfig,
59
+ isBuild,
60
+ splitRouteModules,
61
+ ssr,
62
+ isSpaMode,
63
+ rootRoutePath,
64
+ }: RegisterBuildOutputTransformsOptions): void => {
65
+ const transformRouteModule = async (args: Parameters<TransformHandler>[0]) =>
66
+ performanceProfiler.record(
67
+ args.environment?.name,
68
+ 'route:module',
69
+ args.resource,
70
+ async () =>
71
+ routeTransformExecutor.run({
72
+ kind: 'routeModule',
73
+ code: args.code,
74
+ resource: args.resource,
75
+ resourcePath: args.resourcePath,
76
+ environmentName: args.environment.name,
77
+ sourceMaps: isSourceMapEnabled(
78
+ args.environment.config.output.sourceMap
79
+ ),
80
+ ssr,
81
+ isBuild,
82
+ isSpaMode,
83
+ rootRoutePath,
84
+ })
85
+ );
86
+
87
+ api.processAssets(
88
+ { stage: 'additional', targets: ['node'] },
89
+ ({ sources, compilation }) => {
90
+ const packageJsonPath = 'package.json';
91
+ const source = new sources.RawSource(
92
+ `{"type": "${resolvedServerOutput}"}`
93
+ );
94
+
95
+ if (compilation.getAsset(packageJsonPath)) {
96
+ compilation.updateAsset(packageJsonPath, source);
97
+ } else {
98
+ compilation.emitAsset(packageJsonPath, source);
99
+ }
100
+ }
101
+ );
102
+
103
+ api.transform(
104
+ {
105
+ test: /virtual\/react-router\/(browser|server)-manifest/,
106
+ },
107
+ async args =>
108
+ performanceProfiler.record(
109
+ args.environment?.name,
110
+ 'manifest:transform',
111
+ args.resource,
112
+ async () => {
113
+ if (args.environment.name === 'web') {
114
+ return {
115
+ code: `window.__reactRouterManifest = "PLACEHOLDER";`,
116
+ };
117
+ }
118
+
119
+ const bundleMatch = args.resource.match(
120
+ /virtual\/react-router\/server-manifest(?:-([^?]+))?/
121
+ );
122
+ const bundleId = bundleMatch?.[1]?.replace(/\.js$/, '');
123
+ const latestServerManifest = getLatestServerManifest();
124
+ const manifest =
125
+ (latestServerManifest
126
+ ? ((bundleId && getLatestServerManifestByBundleId(bundleId)) ??
127
+ latestServerManifest)
128
+ : null) ??
129
+ (await getReactRouterManifestForDev(
130
+ routes,
131
+ pluginOptions,
132
+ getClientStats(),
133
+ appDirectory,
134
+ getAssetPrefix(),
135
+ routeChunkOptions
136
+ ));
137
+ return {
138
+ code: `export default ${jsesc(manifest, { es6: true })};`,
139
+ };
140
+ }
141
+ )
142
+ );
143
+
144
+ api.transform(
145
+ {
146
+ resourceQuery: /__react-router-build-client-route/,
147
+ },
148
+ async args =>
149
+ performanceProfiler.record(
150
+ args.environment?.name,
151
+ 'route:client-entry',
152
+ args.resource,
153
+ async () =>
154
+ routeTransformExecutor.run({
155
+ kind: 'routeClientEntry',
156
+ code: args.code,
157
+ resourcePath: args.resourcePath,
158
+ environmentName: args.environment?.name,
159
+ isBuild,
160
+ routeChunkConfig,
161
+ })
162
+ )
163
+ );
164
+
165
+ api.transform(
166
+ {
167
+ resourceQuery: /route-chunk=/,
168
+ environments: ['web'],
169
+ },
170
+ async args =>
171
+ performanceProfiler.record(
172
+ args.environment?.name,
173
+ 'route:chunk',
174
+ args.resource,
175
+ async () =>
176
+ routeTransformExecutor.run({
177
+ kind: 'routeChunk',
178
+ code: args.code,
179
+ resource: args.resource,
180
+ resourcePath: args.resourcePath,
181
+ isBuild,
182
+ routeChunkConfig,
183
+ })
184
+ )
185
+ );
186
+
187
+ if (isBuild && splitRouteModules) {
188
+ api.transform(
189
+ {
190
+ test: path => routeByFilePath.has(path),
191
+ resourceQuery: {
192
+ not: /__react-router-build-client-route|react-router-route|route-chunk=/,
193
+ },
194
+ environments: ['web'],
195
+ },
196
+ async args =>
197
+ performanceProfiler.record(
198
+ args.environment?.name,
199
+ 'route:split-exports',
200
+ args.resource,
201
+ async () =>
202
+ routeTransformExecutor.run({
203
+ kind: 'splitRouteExports',
204
+ code: args.code,
205
+ resourcePath: args.resourcePath,
206
+ routeChunkConfig,
207
+ })
208
+ )
209
+ );
210
+ }
211
+
212
+ api.transform(
213
+ {
214
+ test: /[\\/]\.server[\\/]|\.server(\.[cm]?[jt]sx?)?$/,
215
+ environments: ['web'],
216
+ },
217
+ async args =>
218
+ performanceProfiler.record(
219
+ args.environment?.name,
220
+ 'module:server-only-guard',
221
+ args.resource,
222
+ async () => {
223
+ const relativePath = relative(process.cwd(), args.resourcePath);
224
+ throw new Error(
225
+ `[${PLUGIN_NAME}] Server-only module referenced by client: ${relativePath}`
226
+ );
227
+ }
228
+ )
229
+ );
230
+
231
+ api.transform(
232
+ {
233
+ test: /[\\/]\.client[\\/]|\.client(\.[cm]?[jt]sx?)?$/,
234
+ environments: ['node'],
235
+ },
236
+ async args =>
237
+ performanceProfiler.record(
238
+ args.environment?.name,
239
+ 'module:client-only-stub',
240
+ args.resource,
241
+ async () => {
242
+ return routeTransformExecutor.run({
243
+ kind: 'clientOnlyStub',
244
+ code: args.code,
245
+ resourcePath: args.resourcePath,
246
+ resolveExportAllModule:
247
+ typeof args.resolve === 'function'
248
+ ? createBundlerRouteExportResolver(args.resolve)
249
+ : undefined,
250
+ });
251
+ }
252
+ )
253
+ );
254
+
255
+ api.transform(
256
+ {
257
+ resourceQuery: /\?react-router-route/,
258
+ order: 'post',
259
+ },
260
+ transformRouteModule
261
+ );
262
+
263
+ api.transform(
264
+ {
265
+ test: path => routeByFilePath.has(path),
266
+ resourceQuery: {
267
+ not: /__react-router-build-client-route|react-router-route|route-chunk=/,
268
+ },
269
+ order: 'post',
270
+ },
271
+ transformRouteModule
272
+ );
273
+ };
@@ -0,0 +1,34 @@
1
+ import { availableParallelism, cpus } from 'node:os';
2
+
3
+ const DEFAULT_RESERVED_CORES = 2;
4
+
5
+ const getAvailableCpuCount = (): number =>
6
+ typeof availableParallelism === 'function'
7
+ ? availableParallelism()
8
+ : cpus().length;
9
+
10
+ export const getDefaultConcurrency = (
11
+ cpuCount: number = getAvailableCpuCount()
12
+ ): number => Math.max(0, Math.floor(cpuCount) - DEFAULT_RESERVED_CORES);
13
+
14
+ export const mapWithConcurrency = async <Item, Result>(
15
+ items: readonly Item[],
16
+ concurrency: number,
17
+ worker: (item: Item, index: number) => Promise<Result>
18
+ ): Promise<Result[]> => {
19
+ const results = new Array<Result>(items.length);
20
+ let nextIndex = 0;
21
+ const workerCount = Math.max(1, Math.min(concurrency, items.length));
22
+ await Promise.all(
23
+ Array.from({ length: workerCount }, async () => {
24
+ while (true) {
25
+ const index = nextIndex++;
26
+ if (index >= items.length) {
27
+ return;
28
+ }
29
+ results[index] = await worker(items[index], index);
30
+ }
31
+ })
32
+ );
33
+ return results;
34
+ };
@@ -0,0 +1,45 @@
1
+ import type { ModuleCache } from 'jiti';
2
+ import { resolve } from 'pathe';
3
+
4
+ const normalizePath = (filePath: string): string => resolve(filePath);
5
+
6
+ const isNodeModulePath = (filePath: string): boolean =>
7
+ filePath.split(/[\\/]/).includes('node_modules');
8
+
9
+ export const collectConfigImportWatchPaths = (
10
+ configPath: string,
11
+ moduleCache: ModuleCache,
12
+ previousCacheKeys: ReadonlySet<string>
13
+ ): string[] => {
14
+ const normalizedConfigPath = normalizePath(configPath);
15
+ const watchPaths = new Set<string>();
16
+
17
+ for (const [cacheKey, module] of Object.entries(moduleCache)) {
18
+ if (previousCacheKeys.has(cacheKey)) {
19
+ continue;
20
+ }
21
+
22
+ const importPath = normalizePath(module?.filename ?? cacheKey);
23
+ if (importPath === normalizedConfigPath || isNodeModulePath(importPath)) {
24
+ continue;
25
+ }
26
+
27
+ watchPaths.add(importPath);
28
+ }
29
+
30
+ return Array.from(watchPaths);
31
+ };
32
+
33
+ export const clearConfigImportCache = (
34
+ moduleCache: ModuleCache,
35
+ filePaths: readonly string[]
36
+ ): void => {
37
+ const normalizedFilePaths = new Set(filePaths.map(normalizePath));
38
+
39
+ for (const [cacheKey, module] of Object.entries(moduleCache)) {
40
+ const cachedPath = normalizePath(module?.filename ?? cacheKey);
41
+ if (normalizedFilePaths.has(cachedPath)) {
42
+ delete moduleCache[cacheKey];
43
+ }
44
+ }
45
+ };
@@ -0,0 +1,91 @@
1
+ export const PLUGIN_NAME = 'rsbuild:react-router';
2
+
3
+ export const JS_EXTENSIONS = [
4
+ '.tsx',
5
+ '.ts',
6
+ '.jsx',
7
+ '.js',
8
+ '.mjs',
9
+ '.mts',
10
+ ] as const;
11
+
12
+ export const JS_LOADERS = {
13
+ '.ts': 'ts',
14
+ '.tsx': 'tsx',
15
+ '.js': 'js',
16
+ '.jsx': 'jsx',
17
+ '.mjs': 'js',
18
+ '.mts': 'ts',
19
+ } as const;
20
+
21
+ export const BUILD_CLIENT_ROUTE_QUERY_STRING =
22
+ '?__react-router-build-client-route';
23
+
24
+ export const SERVER_ONLY_ROUTE_EXPORTS = [
25
+ 'loader',
26
+ 'action',
27
+ 'middleware',
28
+ 'headers',
29
+ ] as const;
30
+
31
+ export const SERVER_ONLY_ROUTE_EXPORTS_SET: ReadonlySet<string> = new Set(
32
+ SERVER_ONLY_ROUTE_EXPORTS
33
+ );
34
+
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
+ export const CLIENT_NON_COMPONENT_EXPORTS = [
38
+ 'clientAction',
39
+ 'clientLoader',
40
+ 'clientMiddleware',
41
+ 'handle',
42
+ 'meta',
43
+ 'links',
44
+ 'shouldRevalidate',
45
+ ] as const;
46
+
47
+ export const CLIENT_COMPONENT_EXPORTS = [
48
+ 'default',
49
+ 'ErrorBoundary',
50
+ 'HydrateFallback',
51
+ 'Layout',
52
+ ] as const;
53
+
54
+ export const CLIENT_ROUTE_EXPORTS: readonly (
55
+ | (typeof CLIENT_NON_COMPONENT_EXPORTS)[number]
56
+ | (typeof CLIENT_COMPONENT_EXPORTS)[number]
57
+ )[] = [...CLIENT_NON_COMPONENT_EXPORTS, ...CLIENT_COMPONENT_EXPORTS];
58
+
59
+ export const CLIENT_ROUTE_EXPORTS_SET: ReadonlySet<string> = new Set(
60
+ CLIENT_ROUTE_EXPORTS
61
+ );
62
+
63
+ export const NAMED_COMPONENT_EXPORTS = [
64
+ 'HydrateFallback',
65
+ 'ErrorBoundary',
66
+ ] as const;
67
+
68
+ export const NAMED_COMPONENT_EXPORTS_SET: ReadonlySet<string> = new Set(
69
+ NAMED_COMPONENT_EXPORTS
70
+ );
71
+
72
+ export const SERVER_EXPORTS = {
73
+ loader: 'loader',
74
+ action: 'action',
75
+ middleware: 'middleware',
76
+ headers: 'headers',
77
+ } as const;
78
+
79
+ export const CLIENT_EXPORTS = {
80
+ clientAction: 'clientAction',
81
+ clientLoader: 'clientLoader',
82
+ clientMiddleware: 'clientMiddleware',
83
+ default: 'default',
84
+ ErrorBoundary: 'ErrorBoundary',
85
+ handle: 'handle',
86
+ HydrateFallback: 'HydrateFallback',
87
+ Layout: 'Layout',
88
+ links: 'links',
89
+ meta: 'meta',
90
+ shouldRevalidate: 'shouldRevalidate',
91
+ } as const;