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
@@ -1,9 +1,80 @@
1
1
  import * as Cause from 'effect/Cause';
2
+ import * as Context from 'effect/Context';
2
3
  import * as Duration from 'effect/Duration';
3
4
  import * as Effect from 'effect/Effect';
5
+ import * as ExecutionStrategy from 'effect/ExecutionStrategy';
4
6
  import * as Exit from 'effect/Exit';
5
7
  import * as Fiber from 'effect/Fiber';
8
+ import * as FiberSet from 'effect/FiberSet';
9
+ import * as Layer from 'effect/Layer';
10
+ import * as ManagedRuntime from 'effect/ManagedRuntime';
6
11
  import * as Option from 'effect/Option';
12
+ import * as Scope from 'effect/Scope';
13
+
14
+ const PluginFibers = Context.GenericTag<FiberSet.FiberSet>(
15
+ 'rsbuild-plugin-react-router/PluginFibers'
16
+ );
17
+ type PluginResources = { readonly scope: Scope.CloseableScope };
18
+ const PluginResources: Context.Tag<PluginResources, PluginResources> =
19
+ Context.GenericTag<PluginResources>(
20
+ 'rsbuild-plugin-react-router/PluginResources'
21
+ );
22
+
23
+ type PluginRuntimeContext = Scope.Scope | FiberSet.FiberSet | PluginResources;
24
+
25
+ const PluginRuntimeLive = Layer.scopedContext(
26
+ Effect.gen(function* () {
27
+ const scope = yield* Effect.scope;
28
+ const resources = yield* Scope.fork(scope, ExecutionStrategy.sequential);
29
+ const fiberScope = yield* Scope.fork(scope, ExecutionStrategy.sequential);
30
+ const fibers = yield* FiberSet.make().pipe(
31
+ Effect.provideService(Scope.Scope, fiberScope)
32
+ );
33
+ return Context.make(Scope.Scope, resources).pipe(
34
+ Context.add(PluginFibers, fibers),
35
+ Context.add(PluginResources, { scope: resources })
36
+ );
37
+ })
38
+ );
39
+
40
+ export type PluginEffectRuntime = Pick<
41
+ ManagedRuntime.ManagedRuntime<PluginRuntimeContext, never>,
42
+ 'runFork' | 'runPromise'
43
+ > & {
44
+ readonly dispose: () => Promise<void>;
45
+ };
46
+
47
+ export const createPluginEffectRuntime = (): PluginEffectRuntime => {
48
+ const runtime = ManagedRuntime.make(PluginRuntimeLive);
49
+ let fiberRunFork: typeof runtime.runFork | undefined;
50
+
51
+ let disposePromise: Promise<void> | undefined;
52
+
53
+ return {
54
+ runPromise: runtime.runPromise,
55
+ runFork: (effect, options) => {
56
+ if (disposePromise) return Effect.runFork(Effect.interrupt);
57
+ return (fiberRunFork ??= runtime.runSync(
58
+ Effect.flatMap(PluginFibers, fibers =>
59
+ FiberSet.runtime(fibers)<PluginRuntimeContext>()
60
+ )
61
+ ))(effect, options);
62
+ },
63
+ // Resource finalizers cancel their owned fibers before runtime disposal
64
+ // closes the fiber scope and interrupts any stragglers. Deferring by one
65
+ // microtask also lets a managed fiber request its own shutdown safely.
66
+ dispose: (): Promise<void> =>
67
+ (disposePromise ??= Promise.resolve()
68
+ .then(() =>
69
+ runtime.runPromise(
70
+ Effect.flatMap(PluginResources, ({ scope }) =>
71
+ Scope.close(scope, Exit.void)
72
+ )
73
+ )
74
+ )
75
+ .finally(() => runtime.dispose())),
76
+ };
77
+ };
7
78
 
8
79
  export const DEV_BACKGROUND_STARTUP_DELAY_MS = 3_000;
9
80
 
@@ -27,14 +98,6 @@ export const runPluginEffect = async <A, E>(
27
98
  throw normalizeEffectCause(exit.cause);
28
99
  };
29
100
 
30
- export const tryPluginSync = <A>(
31
- evaluate: () => A
32
- ): Effect.Effect<A, Error, never> =>
33
- Effect.try({
34
- try: evaluate,
35
- catch: normalizeEffectError,
36
- });
37
-
38
101
  export const tryPluginPromise = <A>(
39
102
  evaluate: () => PromiseLike<A> | A
40
103
  ): Effect.Effect<A, Error, never> =>
@@ -46,45 +109,38 @@ export const tryPluginPromise = <A>(
46
109
  type DelayedPluginTask = {
47
110
  schedule(): void;
48
111
  reschedule(): void;
49
- cancelEffect(): Effect.Effect<void, Error, never>;
50
- cancel(): Promise<void>;
112
+ cancelEffect(): Effect.Effect<void>;
51
113
  };
52
114
 
53
115
  export const createDelayedPluginTask = ({
116
+ runtime,
54
117
  delayMs,
55
118
  run,
56
119
  onError,
57
120
  }: {
121
+ runtime: PluginEffectRuntime;
58
122
  delayMs: number;
59
- run: () => Effect.Effect<void, Error, never>;
123
+ run: () => Effect.Effect<void, Error, Scope.Scope>;
60
124
  onError: (error: Error) => void;
61
125
  }): DelayedPluginTask => {
62
- let activeFiber: ReturnType<typeof Effect.runFork> | undefined;
63
- let version = 0;
64
-
65
- const cancelActiveEffect = (): Effect.Effect<void, Error, never> =>
66
- Effect.sync(() => {
67
- const fiber = activeFiber;
68
- activeFiber = undefined;
69
- return fiber;
70
- }).pipe(
71
- Effect.flatMap(fiber =>
72
- fiber ? Fiber.interrupt(fiber).pipe(Effect.asVoid) : Effect.void
73
- )
74
- );
75
-
76
- const cancelEffect = (): Effect.Effect<void, Error, never> =>
77
- Effect.sync(() => {
78
- version += 1;
79
- }).pipe(Effect.zipRight(cancelActiveEffect()));
80
-
81
- const start = (taskVersion: number): void => {
82
- if (activeFiber || version !== taskVersion) {
126
+ let fiber: ReturnType<PluginEffectRuntime['runFork']> | null | undefined;
127
+
128
+ const cancelEffect = (): Effect.Effect<void> =>
129
+ Effect.suspend(() => {
130
+ const activeFiber = fiber;
131
+ fiber = null;
132
+ return activeFiber
133
+ ? Fiber.interrupt(activeFiber).pipe(Effect.asVoid)
134
+ : Effect.void;
135
+ });
136
+
137
+ const start = (): void => {
138
+ if (fiber !== undefined) {
83
139
  return;
84
140
  }
85
141
 
86
- let fiber: ReturnType<typeof Effect.runFork>;
87
- fiber = Effect.runFork(
142
+ let activeFiber: ReturnType<PluginEffectRuntime['runFork']>;
143
+ activeFiber = runtime.runFork(
88
144
  Effect.sleep(Duration.millis(delayMs)).pipe(
89
145
  Effect.zipRight(Effect.suspend(run)),
90
146
  Effect.catchAll(error =>
@@ -94,37 +150,29 @@ export const createDelayedPluginTask = ({
94
150
  ),
95
151
  Effect.ensuring(
96
152
  Effect.sync(() => {
97
- if (activeFiber === fiber) {
98
- activeFiber = undefined;
153
+ if (fiber === activeFiber) {
154
+ fiber = undefined;
99
155
  }
100
156
  })
101
157
  )
102
158
  )
103
159
  );
104
- activeFiber = fiber;
160
+ fiber = activeFiber;
105
161
  };
106
162
 
107
- return {
108
- schedule(): void {
109
- if (activeFiber) {
110
- return;
111
- }
112
- version += 1;
113
- start(version);
114
- },
115
-
116
- reschedule(): void {
117
- version += 1;
118
- const taskVersion = version;
119
- void runPluginEffect(cancelActiveEffect())
120
- .then(() => start(taskVersion))
121
- .catch(onError);
122
- },
163
+ const reschedule = (): void => {
164
+ if (fiber) {
165
+ runtime.runFork(
166
+ Fiber.interrupt(fiber).pipe(Effect.ensuring(Effect.sync(start)))
167
+ );
168
+ } else {
169
+ start();
170
+ }
171
+ };
123
172
 
173
+ return {
174
+ schedule: start,
175
+ reschedule,
124
176
  cancelEffect,
125
-
126
- async cancel(): Promise<void> {
127
- await runPluginEffect(cancelEffect());
128
- },
129
177
  };
130
178
  };
@@ -0,0 +1,80 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { resolve } from 'pathe';
3
+ import { findEntryFile } from './plugin-utils.js';
4
+
5
+ type ReactRouterEntryPathsOptions = {
6
+ appDirectory: string;
7
+ templatesDirectory: string;
8
+ };
9
+
10
+ export type ReactRouterEntryPaths = {
11
+ devServerBuildEntryName: string;
12
+ finalEntryClientPath: string;
13
+ finalEntryRscClientPath: string;
14
+ finalEntryRscPath: string;
15
+ finalEntryRscSsrPath: string;
16
+ finalEntryServerPath: string;
17
+ hasServerApp: boolean;
18
+ serverAppPath: string;
19
+ };
20
+
21
+ const resolveEntryWithTemplate = ({
22
+ appDirectory,
23
+ entryName,
24
+ templateName,
25
+ templatesDirectory,
26
+ }: ReactRouterEntryPathsOptions & {
27
+ entryName: string;
28
+ templateName: string;
29
+ }): string => {
30
+ const userEntryPath = findEntryFile(resolve(appDirectory, entryName));
31
+ return existsSync(userEntryPath)
32
+ ? userEntryPath
33
+ : resolve(templatesDirectory, templateName);
34
+ };
35
+
36
+ export const resolveReactRouterEntryPaths = ({
37
+ appDirectory,
38
+ templatesDirectory,
39
+ }: ReactRouterEntryPathsOptions): ReactRouterEntryPaths => {
40
+ const serverAppPath = findEntryFile(resolve(appDirectory, '../server/index'));
41
+ const hasServerApp = existsSync(serverAppPath);
42
+
43
+ return {
44
+ devServerBuildEntryName: hasServerApp
45
+ ? 'static/js/react-router-server-build'
46
+ : 'static/js/app',
47
+ finalEntryClientPath: resolveEntryWithTemplate({
48
+ appDirectory,
49
+ entryName: 'entry.client',
50
+ templateName: 'entry.client.js',
51
+ templatesDirectory,
52
+ }),
53
+ finalEntryServerPath: resolveEntryWithTemplate({
54
+ appDirectory,
55
+ entryName: 'entry.server',
56
+ templateName: 'entry.server.js',
57
+ templatesDirectory,
58
+ }),
59
+ finalEntryRscClientPath: resolveEntryWithTemplate({
60
+ appDirectory,
61
+ entryName: 'entry.rsc.client',
62
+ templateName: 'entry.rsc.client.js',
63
+ templatesDirectory,
64
+ }),
65
+ finalEntryRscPath: resolveEntryWithTemplate({
66
+ appDirectory,
67
+ entryName: 'entry.rsc',
68
+ templateName: 'entry.rsc.js',
69
+ templatesDirectory,
70
+ }),
71
+ finalEntryRscSsrPath: resolveEntryWithTemplate({
72
+ appDirectory,
73
+ entryName: 'entry.ssr',
74
+ templateName: 'entry.rsc.ssr.js',
75
+ templatesDirectory,
76
+ }),
77
+ hasServerApp,
78
+ serverAppPath,
79
+ };
80
+ };
@@ -0,0 +1,55 @@
1
+ import type { RsbuildPluginAPI } from '@rsbuild/core';
2
+ import { ensureFederationAsyncStartup } from './federation.js';
3
+
4
+ export const registerReactRouterEnvironmentOutput = ({
5
+ api,
6
+ federation,
7
+ resolvedServerOutput,
8
+ }: {
9
+ api: RsbuildPluginAPI;
10
+ federation: boolean | undefined;
11
+ resolvedServerOutput: 'commonjs' | 'module';
12
+ }): void => {
13
+ api.modifyEnvironmentConfig(
14
+ async (config, { name, mergeEnvironmentConfig }) => {
15
+ if (name !== 'web' && name !== 'node') {
16
+ return config;
17
+ }
18
+
19
+ return mergeEnvironmentConfig(config, {
20
+ tools: {
21
+ rspack: rspackConfig => {
22
+ if (federation) {
23
+ ensureFederationAsyncStartup(rspackConfig);
24
+ }
25
+
26
+ if (name === 'node') {
27
+ const output = rspackConfig.output;
28
+ if (output) {
29
+ const library = output.library;
30
+ const libraryOptions =
31
+ library &&
32
+ typeof library === 'object' &&
33
+ !Array.isArray(library)
34
+ ? library
35
+ : {};
36
+ rspackConfig.output = {
37
+ ...output,
38
+ library: {
39
+ ...libraryOptions,
40
+ type:
41
+ resolvedServerOutput === 'module'
42
+ ? 'module'
43
+ : 'commonjs2',
44
+ },
45
+ };
46
+ }
47
+ }
48
+
49
+ return rspackConfig;
50
+ },
51
+ },
52
+ });
53
+ }
54
+ );
55
+ };
@@ -15,7 +15,7 @@ type ExportInfo = {
15
15
  readonly exportAllModules: readonly string[];
16
16
  };
17
17
 
18
- type RouteModuleAnalysis = {
18
+ export type RouteModuleAnalysis = {
19
19
  readonly code: string;
20
20
  readonly exports: readonly string[];
21
21
  readonly exportAllModules: readonly string[];
@@ -94,10 +94,7 @@ const parseProgram = (code: string, resourcePath?: string): ProgramNode => {
94
94
  return getProgram(normalizedResult);
95
95
  };
96
96
 
97
- const getExportInfoCacheKey = (
98
- code: string,
99
- resourcePath?: string
100
- ): string => {
97
+ const getExportInfoCacheKey = (code: string, resourcePath?: string): string => {
101
98
  const lang = resourcePath
102
99
  ? langFromPath(stripResourcePathQuery(resourcePath))
103
100
  : 'inline';
@@ -236,6 +233,18 @@ export const getExportNamesAndExportAll = async (
236
233
  return trackedExportInfo;
237
234
  };
238
235
 
236
+ export const analyzeRouteModuleCode = (
237
+ code: string,
238
+ resourcePath?: string
239
+ ): RouteModuleAnalysis => {
240
+ const program = parseProgram(code, resourcePath);
241
+ return {
242
+ code,
243
+ exports: collectProgramExportNames(program),
244
+ exportAllModules: collectExportAllModules(program),
245
+ };
246
+ };
247
+
239
248
  export const getRouteModuleAnalysis = async (
240
249
  resourcePath: string
241
250
  ): Promise<RouteModuleAnalysis> => {
@@ -247,12 +256,7 @@ export const getRouteModuleAnalysis = async (
247
256
 
248
257
  const analysis = (async () => {
249
258
  const source = await readFile(resourcePath, 'utf8');
250
- const program = parseProgram(source, resourcePath);
251
- return {
252
- code: source,
253
- exports: collectProgramExportNames(program),
254
- exportAllModules: collectExportAllModules(program),
255
- };
259
+ return analyzeRouteModuleCode(source, resourcePath);
256
260
  })();
257
261
 
258
262
  let trackedAnalysis: Promise<RouteModuleAnalysis>;
@@ -0,0 +1,38 @@
1
+ import type { Rspack } from '@rsbuild/core';
2
+
3
+ type ModuleFederationPluginLike = {
4
+ name?: string;
5
+ _options?: { experiments?: { asyncStartup?: boolean } };
6
+ options?: { experiments?: { asyncStartup?: boolean } };
7
+ };
8
+
9
+ export const ensureFederationAsyncStartup = (
10
+ rspackConfig: Rspack.Configuration | undefined
11
+ ): void => {
12
+ if (!rspackConfig?.plugins?.length) {
13
+ return;
14
+ }
15
+
16
+ for (const plugin of rspackConfig.plugins) {
17
+ if (!plugin || typeof plugin !== 'object') {
18
+ continue;
19
+ }
20
+ const federationPlugin = plugin as ModuleFederationPluginLike;
21
+ if (
22
+ federationPlugin.name !== 'ModuleFederationPlugin' &&
23
+ federationPlugin.name !== 'RspackModuleFederationPlugin'
24
+ ) {
25
+ continue;
26
+ }
27
+
28
+ const pluginOptions = federationPlugin._options ?? federationPlugin.options;
29
+ if (!pluginOptions) {
30
+ continue;
31
+ }
32
+
33
+ pluginOptions.experiments = {
34
+ ...pluginOptions.experiments,
35
+ asyncStartup: true,
36
+ };
37
+ }
38
+ };