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.
Files changed (126) hide show
  1. package/README.md +72 -240
  2. package/dist/{451.js → 511.js} +461 -331
  3. package/dist/build-output-transforms.d.ts +32 -7
  4. package/dist/classic-mode.d.ts +56 -0
  5. package/dist/config-imports.d.ts +8 -2
  6. package/dist/constants.d.ts +3 -0
  7. package/dist/dev-background-resources.d.ts +3 -3
  8. package/dist/dev-generation.d.ts +2 -3
  9. package/dist/dev-hmr.d.ts +8 -2
  10. package/dist/dev-runtime-controller.d.ts +6 -1
  11. package/dist/dev-source-maps.d.ts +4 -0
  12. package/dist/effect-runtime.d.ts +17 -5
  13. package/dist/entry-paths.d.ts +16 -0
  14. package/dist/environment-output.d.ts +6 -0
  15. package/dist/export-utils.d.ts +2 -1
  16. package/dist/federation.d.ts +2 -0
  17. package/dist/index.cjs +3675 -2107
  18. package/dist/index.d.ts +3 -1
  19. package/dist/index.js +3614 -2214
  20. package/dist/lazy-compilation-prewarm.d.ts +8 -5
  21. package/dist/manifest.d.ts +16 -4
  22. package/dist/mode-plan.d.ts +82 -0
  23. package/dist/modify-browser-manifest.d.ts +8 -4
  24. package/dist/performance.d.ts +4 -1
  25. package/dist/plugin-utils.d.ts +27 -1
  26. package/dist/prerender-build.d.ts +5 -5
  27. package/dist/prerender.d.ts +1 -5
  28. package/dist/react-router-config.d.ts +11 -3
  29. package/dist/route-artifacts.d.ts +4 -2
  30. package/dist/route-chunks.d.ts +2 -1
  31. package/dist/route-component-transform.d.ts +0 -2
  32. package/dist/route-export-pruning.d.ts +4 -1
  33. package/dist/route-imports.d.ts +18 -0
  34. package/dist/route-module-transform-loader.d.ts +14 -0
  35. package/dist/route-module-transform-loader.js +44 -0
  36. package/dist/route-module-transform-rules.d.ts +14 -0
  37. package/dist/route-transform-tasks.d.ts +6 -0
  38. package/dist/route-watch.d.ts +11 -6
  39. package/dist/rsc-dev-server.d.ts +26 -0
  40. package/dist/rsc-prerender.d.ts +54 -0
  41. package/dist/rsc-route-config.d.ts +6 -0
  42. package/dist/rsc-route-exports.d.ts +15 -0
  43. package/dist/rsc-route-transform-loader.cjs +43 -0
  44. package/dist/rsc-route-transform-loader.d.ts +30 -0
  45. package/dist/rsc-route-transform-loader.js +16 -0
  46. package/dist/rsc-route-transform-registration.d.ts +12 -0
  47. package/dist/rsc-route-transforms.d.ts +21 -0
  48. package/dist/rsc-support.d.ts +23 -0
  49. package/dist/rsc-virtual-modules.d.ts +19 -0
  50. package/dist/server-build-plan.d.ts +2 -1
  51. package/dist/server-build-resolution.d.ts +1 -2
  52. package/dist/server-utils.d.ts +4 -5
  53. package/dist/ssr-asset-relocation.d.ts +98 -0
  54. package/dist/templates/entry.rsc.client.d.ts +1 -0
  55. package/dist/templates/entry.rsc.client.js +61 -0
  56. package/dist/templates/entry.rsc.d.ts +9 -0
  57. package/dist/templates/entry.rsc.js +38 -0
  58. package/dist/templates/entry.rsc.ssr.d.ts +4 -0
  59. package/dist/templates/entry.rsc.ssr.js +24 -0
  60. package/dist/typegen.d.ts +4 -2
  61. package/dist/types.d.ts +34 -6
  62. package/package.json +76 -22
  63. package/src/build-output-transforms.ts +181 -43
  64. package/src/classic-mode.ts +253 -0
  65. package/src/config-imports.ts +153 -6
  66. package/src/constants.ts +6 -2
  67. package/src/dev-background-resources.ts +46 -100
  68. package/src/dev-generation.ts +52 -33
  69. package/src/dev-hmr.ts +112 -80
  70. package/src/dev-runtime-artifacts.ts +11 -12
  71. package/src/dev-runtime-controller.ts +75 -85
  72. package/src/dev-runtime-session.ts +14 -18
  73. package/src/dev-server.ts +2 -0
  74. package/src/dev-source-maps.ts +257 -0
  75. package/src/effect-runtime.ts +105 -57
  76. package/src/entry-paths.ts +80 -0
  77. package/src/environment-output.ts +55 -0
  78. package/src/export-utils.ts +15 -11
  79. package/src/federation.ts +38 -0
  80. package/src/index.ts +666 -519
  81. package/src/lazy-compilation-prewarm.ts +20 -4
  82. package/src/manifest.ts +157 -82
  83. package/src/mode-plan.ts +367 -0
  84. package/src/modify-browser-manifest.ts +130 -124
  85. package/src/performance.ts +11 -2
  86. package/src/plugin-utils.ts +103 -39
  87. package/src/prerender-build.ts +82 -104
  88. package/src/prerender.ts +23 -24
  89. package/src/react-router-config.ts +72 -26
  90. package/src/route-artifacts.ts +96 -66
  91. package/src/route-chunks.ts +167 -51
  92. package/src/route-component-transform.ts +14 -21
  93. package/src/route-export-pruning.ts +4 -3
  94. package/src/route-imports.ts +100 -0
  95. package/src/route-module-transform-loader.ts +149 -0
  96. package/src/route-module-transform-rules.ts +115 -0
  97. package/src/route-transform-tasks.ts +44 -29
  98. package/src/route-watch.ts +166 -188
  99. package/src/rsc-dev-server.ts +112 -0
  100. package/src/rsc-prerender.ts +362 -0
  101. package/src/rsc-route-config.ts +175 -0
  102. package/src/rsc-route-exports.ts +67 -0
  103. package/src/rsc-route-transform-loader.ts +65 -0
  104. package/src/rsc-route-transform-registration.ts +145 -0
  105. package/src/rsc-route-transforms.ts +995 -0
  106. package/src/rsc-runtime.d.ts +143 -0
  107. package/src/rsc-support.ts +116 -0
  108. package/src/rsc-virtual-modules.ts +113 -0
  109. package/src/server-build-plan.ts +14 -3
  110. package/src/server-build-resolution.ts +37 -47
  111. package/src/server-utils.ts +21 -35
  112. package/src/ssr-asset-relocation.ts +183 -0
  113. package/src/ssr-externals.ts +8 -26
  114. package/src/templates/entry.rsc.client.tsx +168 -0
  115. package/src/templates/entry.rsc.ssr.tsx +45 -0
  116. package/src/templates/entry.rsc.tsx +80 -0
  117. package/src/typegen.ts +40 -23
  118. package/src/types.ts +43 -6
  119. package/src/warnings/warn-on-client-source-maps.ts +6 -10
  120. package/dist/parallel-route-transform-protocol.d.ts +0 -25
  121. package/dist/parallel-route-transform-worker.d.ts +0 -1
  122. package/dist/parallel-route-transform-worker.js +0 -38
  123. package/dist/parallel-route-transforms.d.ts +0 -29
  124. package/src/parallel-route-transform-protocol.ts +0 -35
  125. package/src/parallel-route-transform-worker.ts +0 -82
  126. package/src/parallel-route-transforms.ts +0 -458
@@ -0,0 +1,149 @@
1
+ import { threadId } from 'node:worker_threads';
2
+ import remapping, { type SourceMapInput } from '@jridgewell/remapping';
3
+ import type { LoaderDefinition } from '@rspack/core';
4
+ import { createReactRouterPerformanceProfiler } from './performance.js';
5
+ import { executeRouteTransformTask } from './route-transform-tasks.js';
6
+
7
+ export type RouteModuleTransformLoaderOptions = {
8
+ environmentName: string;
9
+ performanceScopeId: string;
10
+ logPerformance: boolean;
11
+ ssr: boolean;
12
+ isBuild: boolean;
13
+ isSpaMode: boolean;
14
+ rootRoutePath: string | null;
15
+ devHmr: boolean;
16
+ };
17
+
18
+ type LoaderContextWithSourceMap = ThisParameterType<
19
+ LoaderDefinition<RouteModuleTransformLoaderOptions>
20
+ > & {
21
+ sourceMap?: boolean;
22
+ };
23
+ type LoaderInputSourceMap = Parameters<
24
+ LoaderDefinition<RouteModuleTransformLoaderOptions>
25
+ >[1];
26
+ type RawLoaderSourceMap = Exclude<LoaderInputSourceMap, string | undefined>;
27
+
28
+ const LOADER_PERFORMANCE_FLUSH_DELAY_MS = 50;
29
+ const performanceWorkerId = `process-${process.pid}:thread-${threadId}`;
30
+
31
+ const composeSourceMaps = (
32
+ generatedMap: SourceMapInput,
33
+ inputSourceMap: SourceMapInput
34
+ ): RawLoaderSourceMap =>
35
+ JSON.parse(
36
+ remapping([generatedMap, inputSourceMap], () => null).toString()
37
+ ) as RawLoaderSourceMap;
38
+
39
+ const performanceProfilersByScope = new Map<
40
+ string,
41
+ Map<string, ReturnType<typeof createReactRouterPerformanceProfiler>>
42
+ >();
43
+ let performanceFlushTimer: ReturnType<typeof setTimeout> | undefined;
44
+
45
+ const getPerformanceProfiler = (
46
+ scopeId: string,
47
+ environment: string | undefined
48
+ ): ReturnType<typeof createReactRouterPerformanceProfiler> => {
49
+ let profilers = performanceProfilersByScope.get(scopeId);
50
+ if (!profilers) {
51
+ profilers = new Map();
52
+ performanceProfilersByScope.set(scopeId, profilers);
53
+ }
54
+ const environmentKey = environment ?? 'unknown';
55
+ let profiler = profilers.get(environmentKey);
56
+ if (!profiler) {
57
+ profiler = createReactRouterPerformanceProfiler({
58
+ enabled: true,
59
+ log: message => console.info(message),
60
+ });
61
+ profilers.set(environmentKey, profiler);
62
+ }
63
+ return profiler;
64
+ };
65
+
66
+ export const flushRouteModuleTransformLoaderPerformance = (): void => {
67
+ if (performanceFlushTimer) {
68
+ clearTimeout(performanceFlushTimer);
69
+ performanceFlushTimer = undefined;
70
+ }
71
+
72
+ for (const profilers of performanceProfilersByScope.values()) {
73
+ for (const [environment, profiler] of profilers) {
74
+ profiler.flush(environment === 'unknown' ? undefined : environment, {
75
+ partial: true,
76
+ workerId: performanceWorkerId,
77
+ });
78
+ }
79
+ }
80
+ performanceProfilersByScope.clear();
81
+ };
82
+
83
+ const schedulePerformanceFlush = (): void => {
84
+ if (performanceFlushTimer) {
85
+ clearTimeout(performanceFlushTimer);
86
+ }
87
+ performanceFlushTimer = setTimeout(
88
+ flushRouteModuleTransformLoaderPerformance,
89
+ LOADER_PERFORMANCE_FLUSH_DELAY_MS
90
+ );
91
+ };
92
+
93
+ process.once('beforeExit', flushRouteModuleTransformLoaderPerformance);
94
+
95
+ const routeModuleTransformLoader: LoaderDefinition<RouteModuleTransformLoaderOptions> =
96
+ async function routeModuleTransformLoader(
97
+ this: LoaderContextWithSourceMap,
98
+ source,
99
+ inputSourceMap
100
+ ) {
101
+ const callback = this.async();
102
+ const options = this.getOptions();
103
+
104
+ const transform = () =>
105
+ executeRouteTransformTask({
106
+ kind: 'routeModule',
107
+ code: String(source),
108
+ resource: this.resource,
109
+ resourcePath: this.resourcePath,
110
+ environmentName: options.environmentName,
111
+ sourceMaps: Boolean(this.sourceMap),
112
+ ssr: options.ssr,
113
+ isBuild: options.isBuild,
114
+ isSpaMode: options.isSpaMode,
115
+ rootRoutePath: options.rootRoutePath,
116
+ devHmr: options.devHmr,
117
+ });
118
+
119
+ try {
120
+ const result = options.logPerformance
121
+ ? await getPerformanceProfiler(
122
+ options.performanceScopeId,
123
+ options.environmentName
124
+ ).record(
125
+ options.environmentName,
126
+ 'route:module',
127
+ this.resource,
128
+ transform
129
+ )
130
+ : await transform();
131
+
132
+ const outputSourceMap =
133
+ result.map && inputSourceMap
134
+ ? composeSourceMaps(
135
+ result.map as SourceMapInput,
136
+ inputSourceMap as SourceMapInput
137
+ )
138
+ : result.map;
139
+ callback(null, result.code, outputSourceMap ?? undefined);
140
+ } catch (error) {
141
+ callback(error instanceof Error ? error : new Error(String(error)));
142
+ } finally {
143
+ if (options.logPerformance) {
144
+ schedulePerformanceFlush();
145
+ }
146
+ }
147
+ };
148
+
149
+ export default routeModuleTransformLoader;
@@ -0,0 +1,115 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ import type { Rspack } from '@rsbuild/core';
3
+ import type { RouteModuleTransformLoaderOptions } from './route-module-transform-loader.js';
4
+ import type { PluginOptions, Route } from './types.js';
5
+
6
+ const routeModuleTransformLoaderPath = fileURLToPath(
7
+ new URL('./route-module-transform-loader.js', import.meta.url)
8
+ );
9
+
10
+ const getRouteModuleTransformParallel = (
11
+ parallelRouteTransform: PluginOptions['parallelRouteTransform']
12
+ ): Rspack.RuleSetLoaderWithOptions['parallel'] => {
13
+ if (
14
+ parallelRouteTransform === undefined ||
15
+ parallelRouteTransform === false
16
+ ) {
17
+ return undefined;
18
+ }
19
+ if (parallelRouteTransform === true) {
20
+ return true;
21
+ }
22
+ if (typeof parallelRouteTransform === 'number') {
23
+ if (
24
+ !Number.isInteger(parallelRouteTransform) ||
25
+ parallelRouteTransform < 1
26
+ ) {
27
+ throw new Error(
28
+ '[react-router] parallelRouteTransform must be true, false, or a positive integer.'
29
+ );
30
+ }
31
+ return { maxWorkers: parallelRouteTransform };
32
+ }
33
+ throw new Error(
34
+ '[react-router] parallelRouteTransform must be true, false, or a positive integer.'
35
+ );
36
+ };
37
+
38
+ export const shouldUseRouteModuleTransformLoader = (
39
+ parallelRouteTransform: PluginOptions['parallelRouteTransform']
40
+ ): boolean =>
41
+ getRouteModuleTransformParallel(parallelRouteTransform) !== undefined;
42
+
43
+ const createRouteModuleTransformUse = (
44
+ options: RouteModuleTransformLoaderOptions,
45
+ parallelRouteTransform: PluginOptions['parallelRouteTransform']
46
+ ): Rspack.RuleSetLoaderWithOptions => {
47
+ const parallel = getRouteModuleTransformParallel(parallelRouteTransform);
48
+ return {
49
+ loader: routeModuleTransformLoaderPath,
50
+ options,
51
+ ...(parallel ? { parallel } : {}),
52
+ };
53
+ };
54
+
55
+ export const registerRouteModuleTransformRules = (
56
+ rspackConfig: Rspack.Configuration,
57
+ {
58
+ environmentName,
59
+ ssr,
60
+ isBuild,
61
+ isSpaMode,
62
+ rootRoutePath,
63
+ devHmr,
64
+ logPerformance,
65
+ routeByFilePath,
66
+ parallelRouteTransform,
67
+ }: {
68
+ environmentName: string;
69
+ ssr: boolean;
70
+ isBuild: boolean;
71
+ isSpaMode: boolean;
72
+ rootRoutePath: string | null;
73
+ devHmr: boolean;
74
+ logPerformance: boolean;
75
+ routeByFilePath: Map<string, Route>;
76
+ parallelRouteTransform: PluginOptions['parallelRouteTransform'];
77
+ }
78
+ ): void => {
79
+ const routeModuleTransformUse = createRouteModuleTransformUse(
80
+ {
81
+ environmentName,
82
+ performanceScopeId: [
83
+ environmentName,
84
+ isBuild ? 'build' : 'dev',
85
+ ssr ? 'ssr' : 'spa',
86
+ rootRoutePath ?? 'rootless',
87
+ ].join(':'),
88
+ logPerformance,
89
+ ssr,
90
+ isBuild,
91
+ isSpaMode,
92
+ rootRoutePath,
93
+ devHmr,
94
+ },
95
+ parallelRouteTransform
96
+ );
97
+
98
+ rspackConfig.module ??= {};
99
+ rspackConfig.module.rules ??= [];
100
+ rspackConfig.module.rules.push(
101
+ {
102
+ resourceQuery: /\?react-router-route/,
103
+ enforce: 'post',
104
+ use: [routeModuleTransformUse],
105
+ },
106
+ {
107
+ test: path => routeByFilePath.has(path),
108
+ resourceQuery: {
109
+ not: /__react-router-build-client-route|react-router-route|route-chunk=/,
110
+ },
111
+ enforce: 'post',
112
+ use: [routeModuleTransformUse],
113
+ }
114
+ );
115
+ };
@@ -24,10 +24,7 @@ import {
24
24
  type RouteChunkCache,
25
25
  type RouteChunkConfig,
26
26
  } from './route-chunks.js';
27
- import {
28
- getProgram,
29
- type AnyNode,
30
- } from './route-ast.js';
27
+ import { getProgram, type AnyNode } from './route-ast.js';
31
28
 
32
29
  export type RouteTransformResult = {
33
30
  code: string;
@@ -88,6 +85,10 @@ export type RouteTransformTaskOptions = {
88
85
  routeChunkCache?: RouteChunkCache;
89
86
  };
90
87
 
88
+ export type RouteTransformRunner = (
89
+ task: RouteTransformTask
90
+ ) => Promise<RouteTransformResult>;
91
+
91
92
  const defaultRouteChunkCache: RouteChunkCache = new Map();
92
93
 
93
94
  const getRouteChunkCache = (options?: RouteTransformTaskOptions) =>
@@ -309,6 +310,41 @@ const buildComponentRefreshRegistrations = (names: string[]): string => {
309
310
  return `\nif (typeof $RefreshReg$ === 'function') {\n${registrations}\n}\n`;
310
311
  };
311
312
 
313
+ export const validateSpaModeRouteExports = ({
314
+ exportNames,
315
+ resourcePath,
316
+ rootRoutePath,
317
+ }: {
318
+ exportNames: readonly string[];
319
+ resourcePath: string;
320
+ rootRoutePath: string | null;
321
+ }): void => {
322
+ const isRootRoute = resourcePath === rootRoutePath;
323
+ const relativePath = relative(process.cwd(), resourcePath);
324
+ const invalidServerOnly = exportNames.filter(exp => {
325
+ if (isRootRoute && exp === 'loader') return false;
326
+ return SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp);
327
+ });
328
+
329
+ if (invalidServerOnly.length > 0) {
330
+ const list = invalidServerOnly.map(exp => `\`${exp}\``).join(', ');
331
+ throw new Error(
332
+ `SPA Mode: ${invalidServerOnly.length} invalid route export(s) in ` +
333
+ `\`${relativePath}\`: ${list}. ` +
334
+ `See https://reactrouter.com/how-to/spa for more information.`
335
+ );
336
+ }
337
+
338
+ if (!isRootRoute && exportNames.includes('HydrateFallback')) {
339
+ throw new Error(
340
+ `SPA Mode: Invalid \`HydrateFallback\` export found in ` +
341
+ `\`${relativePath}\`. ` +
342
+ `\`HydrateFallback\` is only permitted on the root route in SPA Mode. ` +
343
+ `See https://reactrouter.com/how-to/spa for more information.`
344
+ );
345
+ }
346
+ };
347
+
312
348
  const transformRouteModule = async (
313
349
  task: RouteModuleTransformTask
314
350
  ): Promise<RouteTransformResult> => {
@@ -324,32 +360,11 @@ const transformRouteModule = async (
324
360
 
325
361
  const ast = parse(code, { sourceType: 'module' });
326
362
  if (task.environmentName === 'web' && !task.ssr && task.isSpaMode) {
327
- const resolvedExportNames = collectProgramExportNames(getProgram(ast));
328
- const isRootRoute = task.resourcePath === task.rootRoutePath;
329
- const relativePath = relative(process.cwd(), task.resourcePath);
330
-
331
- const invalidServerOnly = resolvedExportNames.filter(exp => {
332
- if (isRootRoute && exp === 'loader') return false;
333
- return SERVER_ONLY_ROUTE_EXPORTS_SET.has(exp);
363
+ validateSpaModeRouteExports({
364
+ exportNames: collectProgramExportNames(getProgram(ast)),
365
+ resourcePath: task.resourcePath,
366
+ rootRoutePath: task.rootRoutePath,
334
367
  });
335
-
336
- if (invalidServerOnly.length > 0) {
337
- const list = invalidServerOnly.map(e => `\`${e}\``).join(', ');
338
- throw new Error(
339
- `SPA Mode: ${invalidServerOnly.length} invalid route export(s) in ` +
340
- `\`${relativePath}\`: ${list}. ` +
341
- `See https://reactrouter.com/how-to/spa for more information.`
342
- );
343
- }
344
-
345
- if (!isRootRoute && resolvedExportNames.includes('HydrateFallback')) {
346
- throw new Error(
347
- `SPA Mode: Invalid \`HydrateFallback\` export found in ` +
348
- `\`${relativePath}\`. ` +
349
- `\`HydrateFallback\` is only permitted on the root route in SPA Mode. ` +
350
- `See https://reactrouter.com/how-to/spa for more information.`
351
- );
352
- }
353
368
  }
354
369
 
355
370
  const removedServerOnlyExports =