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.
Files changed (110) hide show
  1. package/README.md +58 -208
  2. package/dist/511.js +370 -331
  3. package/dist/build-output-transforms.d.ts +30 -6
  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 -1
  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/index.cjs +3957 -2208
  17. package/dist/index.d.ts +3 -1
  18. package/dist/index.js +3470 -1793
  19. package/dist/lazy-compilation-prewarm.d.ts +8 -5
  20. package/dist/manifest.d.ts +16 -4
  21. package/dist/mode-plan.d.ts +82 -0
  22. package/dist/modify-browser-manifest.d.ts +8 -4
  23. package/dist/plugin-utils.d.ts +27 -1
  24. package/dist/prerender-build.d.ts +5 -5
  25. package/dist/prerender.d.ts +1 -5
  26. package/dist/react-router-config.d.ts +11 -3
  27. package/dist/route-artifacts.d.ts +4 -2
  28. package/dist/route-chunks.d.ts +2 -1
  29. package/dist/route-component-transform.d.ts +0 -2
  30. package/dist/route-export-pruning.d.ts +4 -1
  31. package/dist/route-imports.d.ts +18 -0
  32. package/dist/route-transform-tasks.d.ts +5 -0
  33. package/dist/route-watch.d.ts +11 -6
  34. package/dist/rsc-dev-server.d.ts +26 -0
  35. package/dist/rsc-prerender.d.ts +54 -0
  36. package/dist/rsc-route-config.d.ts +6 -0
  37. package/dist/rsc-route-exports.d.ts +15 -0
  38. package/dist/rsc-route-transform-loader.cjs +43 -0
  39. package/dist/rsc-route-transform-loader.d.ts +30 -0
  40. package/dist/rsc-route-transform-loader.js +16 -0
  41. package/dist/rsc-route-transform-registration.d.ts +12 -0
  42. package/dist/rsc-route-transforms.d.ts +21 -0
  43. package/dist/rsc-support.d.ts +23 -0
  44. package/dist/rsc-virtual-modules.d.ts +19 -0
  45. package/dist/server-build-plan.d.ts +2 -1
  46. package/dist/server-build-resolution.d.ts +1 -2
  47. package/dist/server-utils.d.ts +4 -5
  48. package/dist/ssr-asset-relocation.d.ts +98 -0
  49. package/dist/templates/entry.rsc.client.d.ts +1 -0
  50. package/dist/templates/entry.rsc.client.js +61 -0
  51. package/dist/templates/entry.rsc.d.ts +9 -0
  52. package/dist/templates/entry.rsc.js +38 -0
  53. package/dist/templates/entry.rsc.ssr.d.ts +4 -0
  54. package/dist/templates/entry.rsc.ssr.js +24 -0
  55. package/dist/typegen.d.ts +4 -2
  56. package/dist/types.d.ts +30 -1
  57. package/package.json +69 -14
  58. package/src/build-output-transforms.ts +155 -21
  59. package/src/classic-mode.ts +253 -0
  60. package/src/config-imports.ts +153 -6
  61. package/src/constants.ts +6 -2
  62. package/src/dev-background-resources.ts +46 -85
  63. package/src/dev-generation.ts +52 -33
  64. package/src/dev-hmr.ts +112 -80
  65. package/src/dev-runtime-artifacts.ts +11 -12
  66. package/src/dev-runtime-controller.ts +75 -85
  67. package/src/dev-runtime-session.ts +14 -18
  68. package/src/dev-server.ts +2 -0
  69. package/src/dev-source-maps.ts +257 -0
  70. package/src/effect-runtime.ts +105 -57
  71. package/src/entry-paths.ts +80 -0
  72. package/src/environment-output.ts +55 -0
  73. package/src/export-utils.ts +15 -11
  74. package/src/index.ts +661 -496
  75. package/src/lazy-compilation-prewarm.ts +20 -4
  76. package/src/manifest.ts +157 -82
  77. package/src/mode-plan.ts +367 -0
  78. package/src/modify-browser-manifest.ts +130 -124
  79. package/src/plugin-utils.ts +103 -39
  80. package/src/prerender-build.ts +82 -104
  81. package/src/prerender.ts +23 -24
  82. package/src/react-router-config.ts +72 -26
  83. package/src/route-artifacts.ts +96 -66
  84. package/src/route-chunks.ts +167 -51
  85. package/src/route-component-transform.ts +14 -21
  86. package/src/route-export-pruning.ts +4 -3
  87. package/src/route-imports.ts +100 -0
  88. package/src/route-transform-tasks.ts +39 -25
  89. package/src/route-watch.ts +166 -188
  90. package/src/rsc-dev-server.ts +112 -0
  91. package/src/rsc-prerender.ts +362 -0
  92. package/src/rsc-route-config.ts +175 -0
  93. package/src/rsc-route-exports.ts +67 -0
  94. package/src/rsc-route-transform-loader.ts +65 -0
  95. package/src/rsc-route-transform-registration.ts +145 -0
  96. package/src/rsc-route-transforms.ts +995 -0
  97. package/src/rsc-runtime.d.ts +143 -0
  98. package/src/rsc-support.ts +116 -0
  99. package/src/rsc-virtual-modules.ts +113 -0
  100. package/src/server-build-plan.ts +14 -3
  101. package/src/server-build-resolution.ts +37 -47
  102. package/src/server-utils.ts +21 -35
  103. package/src/ssr-asset-relocation.ts +183 -0
  104. package/src/ssr-externals.ts +8 -26
  105. package/src/templates/entry.rsc.client.tsx +168 -0
  106. package/src/templates/entry.rsc.ssr.tsx +45 -0
  107. package/src/templates/entry.rsc.tsx +80 -0
  108. package/src/typegen.ts +40 -23
  109. package/src/types.ts +39 -1
  110. package/src/warnings/warn-on-client-source-maps.ts +6 -10
@@ -0,0 +1,16 @@
1
+ export default function(source, map) {
2
+ let callback = this.async(), transform = this._compiler?.__reactRouterRscRouteTransform;
3
+ transform ? transform({
4
+ code: source.toString(),
5
+ resource: this.resource,
6
+ resourcePath: this.resourcePath,
7
+ resourceQuery: this.resourceQuery,
8
+ environment: {
9
+ name: this.getOptions().environmentName
10
+ }
11
+ }).then((result)=>{
12
+ callback(null, result.code, result.map ?? map);
13
+ }).catch((error)=>{
14
+ callback(error instanceof Error ? error : Error(String(error)));
15
+ }) : callback(null, source, map);
16
+ };
@@ -0,0 +1,12 @@
1
+ import type { RsbuildPlugin } from '@rsbuild/core';
2
+ import type { RouteChunkCache, RouteChunkConfig } from './route-chunks.js';
3
+ import type { Route } from './types.js';
4
+ import type { ReactRouterPerformanceProfiler } from './performance.js';
5
+ export declare const registerReactRouterRscRouteTransforms: ({ api, isBuild, performanceProfiler, routeByFilePath, routeChunkCache, routeChunkConfig, }: {
6
+ api: Parameters<RsbuildPlugin["setup"]>[0];
7
+ isBuild: boolean;
8
+ performanceProfiler: Pick<ReactRouterPerformanceProfiler, "record">;
9
+ routeByFilePath: Map<string, Route>;
10
+ routeChunkCache: RouteChunkCache;
11
+ routeChunkConfig: RouteChunkConfig;
12
+ }) => void;
@@ -0,0 +1,21 @@
1
+ import { generate } from './yuku.js';
2
+ import { type RouteChunkCache, type RouteChunkConfig } from './route-chunks.js';
3
+ import type { Route } from './types.js';
4
+ type RscRouteTransformOptions = {
5
+ code: string;
6
+ resourcePath: string;
7
+ resourceQuery?: string;
8
+ isRootRoute: boolean;
9
+ routeId: string;
10
+ routeByFilePath?: ReadonlyMap<string, Route>;
11
+ routeChunkCache: RouteChunkCache;
12
+ routeChunkConfig: RouteChunkConfig;
13
+ isServerEnvironment: boolean;
14
+ isDev: boolean;
15
+ };
16
+ type RscRouteTransformResult = {
17
+ code: string;
18
+ map: null | ReturnType<typeof generate>['map'];
19
+ };
20
+ export declare const transformRscRouteModule: (options: RscRouteTransformOptions) => Promise<RscRouteTransformResult>;
21
+ export {};
@@ -0,0 +1,23 @@
1
+ import type { RsbuildPlugin } from '@rsbuild/core';
2
+ import type { Config } from './react-router-config.js';
3
+ import type { PluginOptions } from './types.js';
4
+ type RscPluginOptions = Exclude<NonNullable<PluginOptions['rsc']>, boolean>;
5
+ type RscUnsupportedConfig = Pick<Config, 'buildEnd' | 'future' | 'presets' | 'serverBundles' | 'subResourceIntegrity'>;
6
+ export declare const assertReactRouterRscConfigSupport: ({ pluginName, userConfig, }: {
7
+ pluginName: string;
8
+ userConfig: RscUnsupportedConfig;
9
+ }) => void;
10
+ export declare const assertReactRouterRscSupport: ({ pluginName, resolvePackagePath, }: {
11
+ pluginName: string;
12
+ resolvePackagePath: (specifier: string) => string | undefined;
13
+ }) => void;
14
+ export declare const setupReactRouterRscPlugin: ({ api, entryRscPath, entrySsrPath, pluginName, rsc, }: {
15
+ api: Parameters<RsbuildPlugin["setup"]>[0];
16
+ entryRscPath: string;
17
+ entrySsrPath: string;
18
+ pluginName: string;
19
+ rsc: RscPluginOptions;
20
+ }) => Promise<void>;
21
+ export { createReactRouterRscDevServerSetup } from './rsc-dev-server.js';
22
+ export { registerReactRouterRscRouteTransforms } from './rsc-route-transform-registration.js';
23
+ export { createReactRouterRscResolveAliases, createReactRouterRscVirtualModules, } from './rsc-virtual-modules.js';
@@ -0,0 +1,19 @@
1
+ import type { Config } from './react-router-config.js';
2
+ import type { Route } from './types.js';
3
+ type RscVirtualModulesOptions = {
4
+ allowedActionOrigins: string[] | undefined;
5
+ appDirectory: string;
6
+ basename: string;
7
+ buildDirectory: string;
8
+ isBuild: boolean;
9
+ /** Resolved web `output.distPath.js` segment, e.g. `static/js`. */
10
+ jsDistPath: string;
11
+ outputClientPath: string;
12
+ publicPath: string;
13
+ routeDiscovery: Config['routeDiscovery'];
14
+ routes: Record<string, Route>;
15
+ ssr: boolean;
16
+ };
17
+ export declare const createReactRouterRscResolveAliases: (rootPath: string) => Record<string, string>;
18
+ export declare const createReactRouterRscVirtualModules: ({ allowedActionOrigins, appDirectory, basename, buildDirectory, isBuild, jsDistPath, outputClientPath, publicPath, routeDiscovery, routes, ssr, }: RscVirtualModulesOptions) => Record<string, string>;
19
+ export {};
@@ -12,11 +12,12 @@ export declare const createReactRouterServerBuildPlan: ({ routesByServerBundleId
12
12
  serverBuildFile: string | undefined;
13
13
  defaultEntryName: string;
14
14
  }) => ReactRouterServerBuildPlan;
15
- export declare const createReactRouterNodeEntries: ({ hasServerApp, isBuild, serverAppPath, entryServerPath, defaultEntryName, serverBundleEntries, }: {
15
+ export declare const createReactRouterNodeEntries: ({ hasServerApp, isBuild, serverAppPath, entryServerPath, serverBuildFile, defaultEntryName, serverBundleEntries, }: {
16
16
  hasServerApp: boolean;
17
17
  isBuild: boolean;
18
18
  serverAppPath: string;
19
19
  entryServerPath: string;
20
+ serverBuildFile: string | undefined;
20
21
  defaultEntryName: string;
21
22
  serverBundleEntries: readonly ReactRouterServerBundleEntry[];
22
23
  }) => Record<string, string>;
@@ -1,3 +1,2 @@
1
- import * as Effect from 'effect/Effect';
2
1
  import type { ServerBuild } from 'react-router';
3
- export declare function resolveServerBuildModuleEffect(buildModule: unknown, source: string): Effect.Effect<ServerBuild, Error, never>;
2
+ export declare function resolveServerBuildModule(buildModule: unknown, source: string): Promise<ServerBuild>;
@@ -1,4 +1,5 @@
1
1
  import type { ServerBuild } from 'react-router';
2
+ import { resolveServerBuildModule } from './server-build-resolution.js';
2
3
  import type { Route } from './types.js';
3
4
  /**
4
5
  * Generates the server build template string.
@@ -13,6 +14,7 @@ interface ServerBuildOptions {
13
14
  basename: string;
14
15
  appDirectory: string;
15
16
  ssr: boolean;
17
+ isSpaMode: boolean;
16
18
  future?: unknown;
17
19
  allowedActionOrigins?: string[];
18
20
  prerender?: string[];
@@ -31,9 +33,6 @@ interface ServerBuildOptions {
31
33
  * @param options Build options
32
34
  * @returns The generated module content as a string
33
35
  */
34
- declare function generateServerBuild(routes: Record<string, Route>, options: ServerBuildOptions & {
35
- federation?: boolean;
36
- }): string;
37
- export declare function resolveServerBuildModule(buildModule: unknown, source: string): Promise<ServerBuild>;
36
+ export declare function generateServerBuild(routes: Record<string, Route>, options: ServerBuildOptions): string;
37
+ export { resolveServerBuildModule };
38
38
  export declare function resolveReactRouterServerBuild(buildModule: unknown): Promise<ServerBuild>;
39
- export { generateServerBuild };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Relocation of server-only static assets to the client build.
3
+ *
4
+ * In React Router framework mode a route's server-only code (a loader or a
5
+ * `.server` module) can import an asset — for example
6
+ * `import txtUrl from "./file.txt?url"` or a `.css?url` file. The imported URL
7
+ * is returned to the client (e.g. as loader data) and fetched from the client
8
+ * build directory at runtime, so the asset file must exist under
9
+ * `build/client` even though only the node/server compilation referenced it.
10
+ *
11
+ * Rspack emits these assets into the server output (`build/server/static/...`)
12
+ * and never into the client output. This module mirrors upstream React
13
+ * Router's Vite plugin, which internally enables `ssrEmitAssets` and then, in
14
+ * the SSR `writeBundle` hook, moves server-emitted static assets into the
15
+ * client assets directory and strips them from the server build
16
+ * (`react-router-dev/vite/plugin.ts`). Here the equivalent runs inside the
17
+ * node compilation's `processAssets` hook: static assets are written into the
18
+ * client output at the same public path and deleted from the server
19
+ * compilation so the server build never ships duplicate static files.
20
+ */
21
+ /**
22
+ * The subset of an Rspack asset info that identifies static assets emitted by
23
+ * asset modules (`asset/resource`) and the CSS pipeline.
24
+ */
25
+ export interface RelocatableAssetInfo {
26
+ /**
27
+ * Set by Rspack for assets produced from a source module (asset modules and
28
+ * extracted CSS). JavaScript chunks and generated files such as
29
+ * `package.json` do not carry a `sourceFilename`.
30
+ */
31
+ sourceFilename?: string;
32
+ /**
33
+ * Set for JavaScript chunk assets. Static assets never set this, so it is
34
+ * used to keep code-split JS in the server build.
35
+ */
36
+ javascriptModule?: boolean;
37
+ }
38
+ /**
39
+ * The minimal compilation surface needed to collect and relocate assets. Kept
40
+ * narrow so the logic can be unit-tested without a real Rspack compilation.
41
+ */
42
+ export interface RelocatableAssetCompilation {
43
+ assets: Record<string, unknown>;
44
+ getAsset(name: string): {
45
+ name: string;
46
+ source: {
47
+ buffer(): Buffer;
48
+ };
49
+ info: RelocatableAssetInfo;
50
+ } | undefined | void;
51
+ deleteAsset(name: string): void;
52
+ }
53
+ export interface CollectedServerAsset {
54
+ /** The public path of the asset, relative to the compilation output root. */
55
+ name: string;
56
+ source: {
57
+ buffer(): Buffer;
58
+ };
59
+ }
60
+ /**
61
+ * Determine whether an emitted asset is a static asset that must live in the
62
+ * client build. Static assets (asset modules and CSS) carry a `sourceFilename`
63
+ * and are not JavaScript chunks. Code-split JavaScript is intentionally
64
+ * excluded so it remains in the server build.
65
+ */
66
+ export declare const isRelocatableServerAsset: (info: RelocatableAssetInfo | undefined) => boolean;
67
+ /**
68
+ * Collect the server-only static assets from a node compilation. Returns them
69
+ * in stable (sorted) order so callers and tests see deterministic output.
70
+ */
71
+ export declare const collectRelocatableServerAssets: (compilation: RelocatableAssetCompilation) => CollectedServerAsset[];
72
+ export interface RelocateServerAssetsResult {
73
+ /** Assets written into the client output because they were missing or stale. */
74
+ written: string[];
75
+ /**
76
+ * Assets that already existed in the client output. They are still removed
77
+ * from the server build but not rewritten.
78
+ */
79
+ skipped: string[];
80
+ }
81
+ export declare const relocateServerAssetsToClient: ({ compilation, outputClientPath, relocatedDestinations, existsSyncFn, mkdirFn, readFileFn, renameFn, rmFn, writeFileFn, }: {
82
+ compilation: RelocatableAssetCompilation;
83
+ outputClientPath: string;
84
+ /**
85
+ * Destination content digests already handled by a previous compilation
86
+ * (dev rebuilds, serverBundles). Stable dev filenames can carry changed
87
+ * bytes, so the cache is keyed by path and digest rather than path alone.
88
+ */
89
+ relocatedDestinations?: Map<string, string>;
90
+ existsSyncFn?: (path: string) => boolean;
91
+ mkdirFn?: (dir: string) => Promise<unknown>;
92
+ readFileFn?: (path: string) => Promise<Buffer>;
93
+ renameFn?: (oldPath: string, newPath: string) => Promise<void>;
94
+ rmFn?: (path: string, options: {
95
+ force: true;
96
+ }) => Promise<void>;
97
+ writeFileFn?: (path: string, data: Buffer) => Promise<void>;
98
+ }) => Promise<RelocateServerAssetsResult>;
@@ -0,0 +1 @@
1
+ import 'virtual/react-router/unstable_rsc/inject-hmr-runtime';
@@ -0,0 +1,61 @@
1
+ import "virtual/react-router/unstable_rsc/inject-hmr-runtime";
2
+ import { hydrateRoot } from "react-dom/client";
3
+ import { unstable_RSCHydratedRouter, unstable_createCallServer, unstable_getRSCStream } from "react-router/dom";
4
+ import { createFromReadableStream, createTemporaryReferenceSet, encodeReply, setServerCallback } from "react-server-dom-rspack/client.browser";
5
+ import * as __rspack_external_react from "react";
6
+ let hydrationWindow = window, InitialRscDataLoadGuard = ({ active, children })=>(__rspack_external_react.useEffect(()=>{
7
+ let frame;
8
+ if (!active) return;
9
+ let clearGuardOnceDataLoadStarts = ()=>{
10
+ let router = hydrationWindow.__reactRouterDataRouter;
11
+ if (router && (router.state.initialized || 'idle' !== router.state.navigation.state)) {
12
+ hydrationWindow.__reactRouterHdrActive = !1;
13
+ return;
14
+ }
15
+ frame = requestAnimationFrame(clearGuardOnceDataLoadStarts);
16
+ };
17
+ return frame = requestAnimationFrame(clearGuardOnceDataLoadStarts), ()=>cancelAnimationFrame(frame);
18
+ }, [
19
+ active
20
+ ]), children);
21
+ setServerCallback(unstable_createCallServer({
22
+ createFromReadableStream: createFromReadableStream,
23
+ createTemporaryReferenceSet: createTemporaryReferenceSet,
24
+ encodeReply: encodeReply
25
+ }));
26
+ let hydrate = ()=>{
27
+ createFromReadableStream(unstable_getRSCStream()).then((payload)=>{
28
+ (0, __rspack_external_react.startTransition)(async ()=>{
29
+ let formState = 'render' === payload.type ? await payload.formState : void 0, needsInitialDataLoadGuard = 'render' === payload.type && payload.matches.some((match)=>null != match.clientLoader && !match.hasLoader && null != match.hydrateFallbackElement);
30
+ needsInitialDataLoadGuard && (hydrationWindow.__reactRouterHdrActive = !0), hydrateRoot(document, __rspack_external_react.createElement(__rspack_external_react.StrictMode, null, __rspack_external_react.createElement(InitialRscDataLoadGuard, {
31
+ active: needsInitialDataLoadGuard
32
+ }, __rspack_external_react.createElement(unstable_RSCHydratedRouter, {
33
+ createFromReadableStream: createFromReadableStream,
34
+ payload
35
+ }))), {
36
+ formState
37
+ });
38
+ });
39
+ }, (error)=>{
40
+ setTimeout(()=>{
41
+ throw error;
42
+ });
43
+ });
44
+ };
45
+ 'loading' === document.readyState ? document.addEventListener('DOMContentLoaded', hydrate, {
46
+ once: !0
47
+ }) : hydrate();
48
+ let hot = import.meta.webpackHot;
49
+ hot?.on('rsc:update', (data)=>{
50
+ requestAnimationFrame(()=>{
51
+ let router = window.__reactRouterDataRouter;
52
+ if (data?.revalidate) return void router?.revalidate();
53
+ if (router?.navigate) {
54
+ let basename = router.basename || '/', pathname = window.location.pathname;
55
+ '/' !== basename && pathname.startsWith(basename) && '/' !== (pathname = pathname.slice(basename.length) || '/')[0] && (pathname = '/' + pathname), router.navigate(pathname + window.location.search + window.location.hash, {
56
+ replace: !0,
57
+ preventScrollReset: !0
58
+ });
59
+ }
60
+ });
61
+ });
@@ -0,0 +1,9 @@
1
+ import { RouterContextProvider } from 'react-router';
2
+ import unstable_reactRouterServeConfig from 'virtual/react-router/unstable_rsc/react-router-serve-config';
3
+ export { unstable_reactRouterServeConfig };
4
+ type RscRequestHandler = {
5
+ fetch(request: Request, requestContext?: RouterContextProvider): Promise<Response>;
6
+ };
7
+ export declare function fetchServer(request: Request, requestContext?: RouterContextProvider): Promise<Response>;
8
+ declare const handler: RscRequestHandler;
9
+ export default handler;
@@ -0,0 +1,38 @@
1
+ import { createTemporaryReferenceSet, decodeAction, decodeFormState, decodeReply, loadServerAction, renderToReadableStream } from "react-server-dom-rspack/server.node";
2
+ import { RouterContextProvider, unstable_matchRSCServerRequest } from "react-router";
3
+ import routes from "virtual/react-router/unstable_rsc/routes";
4
+ import route_discovery from "virtual/react-router/unstable_rsc/route-discovery";
5
+ import basename from "virtual/react-router/unstable_rsc/basename";
6
+ import allowed_action_origins from "virtual/react-router/unstable_rsc/allowed-action-origins";
7
+ import client_version from "virtual/react-router/unstable_rsc/client-version";
8
+ import server_manifest from "virtual/react-router/unstable_rsc/server-manifest";
9
+ import bootstrap_scripts from "virtual/react-router/unstable_rsc/bootstrap-scripts";
10
+ import { generateHTML } from "./entry.rsc.ssr.js";
11
+ function fetchServer(request, requestContext) {
12
+ return unstable_matchRSCServerRequest({
13
+ allowedActionOrigins: allowed_action_origins,
14
+ basename: basename,
15
+ clientVersion: client_version,
16
+ createTemporaryReferenceSet: createTemporaryReferenceSet,
17
+ decodeAction: (body)=>decodeAction(body, server_manifest()),
18
+ decodeFormState: async (actionResult, body)=>await decodeFormState(actionResult, body, server_manifest()) ?? void 0,
19
+ decodeReply: decodeReply,
20
+ loadServerAction: (id)=>Promise.resolve(loadServerAction(id)),
21
+ request,
22
+ requestContext,
23
+ routes: routes,
24
+ routeDiscovery: route_discovery,
25
+ generateResponse: (match, options)=>new Response(renderToReadableStream(match.payload, options), {
26
+ status: match.statusCode,
27
+ headers: match.headers
28
+ })
29
+ });
30
+ }
31
+ import.meta.webpackHot?.accept();
32
+ export { default as unstable_reactRouterServeConfig } from "virtual/react-router/unstable_rsc/react-router-serve-config";
33
+ export default {
34
+ fetch: async (request, requestContext)=>(!requestContext || requestContext instanceof RouterContextProvider || (requestContext = void 0), generateHTML(request, await fetchServer(request, requestContext), {
35
+ bootstrapScripts: bootstrap_scripts
36
+ }))
37
+ };
38
+ export { fetchServer };
@@ -0,0 +1,4 @@
1
+ export declare function generateHTML(request: Request, serverResponse: Response, options?: {
2
+ bootstrapScripts?: string[];
3
+ bootstrapModules?: string[];
4
+ }): Promise<Response>;
@@ -0,0 +1,24 @@
1
+ import { renderToReadableStream } from "react-dom/server";
2
+ import { unstable_RSCStaticRouter, unstable_routeRSCServerRequest } from "react-router";
3
+ import { createFromReadableStream } from "react-server-dom-rspack/client.node";
4
+ import * as __rspack_external_react from "react";
5
+ async function generateHTML(request, serverResponse, options = {}) {
6
+ return unstable_routeRSCServerRequest({
7
+ request,
8
+ serverResponse,
9
+ createFromReadableStream: createFromReadableStream,
10
+ async renderHTML (getPayload, renderOptions) {
11
+ let payloadPromise = getPayload();
12
+ return payloadPromise.formState ??= payloadPromise.then((payload)=>'render' === payload.type ? payload.formState : void 0), renderToReadableStream(__rspack_external_react.createElement(unstable_RSCStaticRouter, {
13
+ getPayload: getPayload
14
+ }), {
15
+ ...renderOptions,
16
+ bootstrapModules: options.bootstrapModules,
17
+ bootstrapScripts: options.bootstrapScripts,
18
+ formState: await payloadPromise.formState,
19
+ signal: request.signal
20
+ });
21
+ }
22
+ });
23
+ }
24
+ export { generateHTML };
package/dist/typegen.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { RsbuildPluginAPI } from '@rsbuild/core';
2
+ import { type PluginEffectRuntime } from './effect-runtime.js';
2
3
  type Execa = typeof import('execa').execa;
3
4
  type LoadExeca = () => Promise<Execa>;
4
5
  export type ReactRouterTypegenRunner = {
@@ -7,9 +8,10 @@ export type ReactRouterTypegenRunner = {
7
8
  runBuild(): Promise<void>;
8
9
  };
9
10
  export declare const createReactRouterTypegenRunner: (loadExeca?: LoadExeca, appDirectory?: string) => ReactRouterTypegenRunner;
10
- export declare const registerReactRouterTypegen: (api: RsbuildPluginAPI, { runner, devWatchDelayMs, appDirectory, }?: {
11
+ export declare const registerReactRouterTypegen: (api: RsbuildPluginAPI, { runtime, runner, devWatchDelayMs, appDirectory, }: {
12
+ runtime: PluginEffectRuntime;
11
13
  runner?: ReactRouterTypegenRunner;
12
14
  devWatchDelayMs?: number;
13
15
  appDirectory?: string;
14
- }) => void;
16
+ }) => Promise<void>;
15
17
  export {};
package/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { RsbuildConfig } from '@rsbuild/core';
1
+ import type { RsbuildConfig, Rspack } from '@rsbuild/core';
2
2
  export type Route = {
3
3
  id: string;
4
4
  parentId?: string;
@@ -25,6 +25,20 @@ export type PluginOptions = {
25
25
  * Federation mode configuration
26
26
  */
27
27
  federation?: boolean;
28
+ /**
29
+ * Enable experimental React Router RSC framework mode.
30
+ * This composes `rsbuild-plugin-rsc` with React Router's Rsbuild
31
+ * environments. Environment names are managed by this plugin.
32
+ * Requires `react-router >=7.18.0 || >=8.0.0`, `rsbuild-plugin-rsc`,
33
+ * and `react-server-dom-rspack`.
34
+ * @default false
35
+ */
36
+ rsc?: boolean | {
37
+ layers?: {
38
+ rsc?: Rspack.RuleSetCondition;
39
+ ssr?: Rspack.RuleSetCondition;
40
+ };
41
+ };
28
42
  /**
29
43
  * Rsbuild dev-only lazy compilation behavior.
30
44
  *
@@ -63,6 +77,21 @@ export type PluginOptions = {
63
77
  */
64
78
  onRouteTopologyChange?: () => void | Promise<void>;
65
79
  };
80
+ export type ReactRouterRSCPluginOptions = Omit<PluginOptions, 'rsc'> & {
81
+ /**
82
+ * Optional overrides forwarded to `rsbuild-plugin-rsc`.
83
+ * Environment names are managed by this plugin.
84
+ */
85
+ rsc?: Exclude<NonNullable<PluginOptions['rsc']>, boolean>;
86
+ };
87
+ export type PrerenderPathsConfig = boolean | string[] | ((args: {
88
+ getStaticPaths: () => string[];
89
+ }) => boolean | string[] | Promise<boolean | string[]>);
90
+ export type PrerenderConfigObject = {
91
+ paths: PrerenderPathsConfig;
92
+ concurrency?: number;
93
+ unstable_concurrency?: number;
94
+ };
66
95
  export type RouteManifestItem = Omit<Route, 'file' | 'children'> & {
67
96
  module: string;
68
97
  clientActionModule?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rsbuild-plugin-react-router",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "React Router plugin for Rsbuild",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,6 +37,18 @@
37
37
  "types": "./dist/templates/entry.server.d.ts",
38
38
  "import": "./dist/templates/entry.server.js",
39
39
  "require": "./dist/templates/entry.server.cjs"
40
+ },
41
+ "./templates/entry.rsc": {
42
+ "types": "./dist/templates/entry.rsc.d.ts",
43
+ "import": "./dist/templates/entry.rsc.js"
44
+ },
45
+ "./templates/entry.rsc.client": {
46
+ "types": "./dist/templates/entry.rsc.client.d.ts",
47
+ "import": "./dist/templates/entry.rsc.client.js"
48
+ },
49
+ "./templates/entry.rsc.ssr": {
50
+ "types": "./dist/templates/entry.rsc.ssr.d.ts",
51
+ "import": "./dist/templates/entry.rsc.ssr.js"
40
52
  }
41
53
  },
42
54
  "main": "./dist/index.cjs",
@@ -50,8 +62,8 @@
50
62
  },
51
63
  "dependencies": {
52
64
  "@jridgewell/remapping": "^2.3.5",
53
- "@react-router/node": "^7.13.0",
54
- "@remix-run/node-fetch-server": "^0.13.0",
65
+ "@react-router/node": "^7.13.0 || ^8.0.0",
66
+ "@remix-run/node-fetch-server": "^0.13.3",
55
67
  "@rspack/plugin-react-refresh": "^2.0.2",
56
68
  "execa": "^9.6.1",
57
69
  "fs-extra": "11.3.3",
@@ -68,40 +80,76 @@
68
80
  "@changesets/cli": "^2.29.8",
69
81
  "@codspeed/vitest-plugin": "^5.7.1",
70
82
  "@jridgewell/trace-mapping": "^0.3.31",
83
+ "@playwright/test": "^1.61.1",
71
84
  "@react-router/dev": "^8.0.1",
72
- "@rsbuild/core": "2.1.5",
85
+ "@react-router/express": "^8.0.1",
86
+ "@react-router/fs-routes": "^8.0.1",
87
+ "@react-router/remix-routes-option-adapter": "^8.0.1",
88
+ "@react-router/serve": "^8.0.1",
89
+ "@rsbuild/core": "2.2.0",
90
+ "@rsbuild/plugin-less": "1.6.4",
91
+ "@rsbuild/plugin-mdx": "^1.1.3",
73
92
  "@rsbuild/plugin-react": "2.1.0",
93
+ "@rsbuild/plugin-sass": "1.5.3",
94
+ "@rsbuild/plugin-svgr": "2.0.4",
95
+ "@rsbuild/plugin-tailwindcss": "2.0.3",
74
96
  "@rslib/core": "^0.23.2",
75
- "@rspack/core": "2.1.3",
76
- "@rstest/core": "^0.11.0",
77
- "@rstest/coverage-istanbul": "^0.11.0",
97
+ "@rspack/core": "2.2.0",
98
+ "@rstest/core": "^0.11.4",
99
+ "@rstest/coverage-istanbul": "^0.11.4",
78
100
  "@swc/helpers": "^0.5.23",
79
101
  "@types/fs-extra": "11.0.4",
80
102
  "@types/jsesc": "^3.0.3",
81
103
  "@types/node": "^25.0.10",
82
104
  "@types/react": "^19.2.10",
83
105
  "@types/react-dom": "^19.2.3",
84
- "effect": "^3.21.4",
106
+ "@types/webpack-env": "^1.18.8",
107
+ "@vanilla-extract/css": "^1.20.1",
108
+ "cheerio": "^1.2.0",
109
+ "cross-spawn": "^7.0.6",
110
+ "dedent": "^1.7.2",
111
+ "effect": "^3.22.0",
85
112
  "es-module-lexer": "1.7.0",
113
+ "express": "^4.22.2",
114
+ "fast-glob": "^3.3.3",
115
+ "get-port": "7.1.0",
86
116
  "kill-port": "^2.0.1",
87
117
  "pkg-pr-new": "^0.0.75",
88
118
  "playwright": "^1.61.1",
89
119
  "prettier": "3.8.1",
90
120
  "react": "^19.2.4",
91
121
  "react-dom": "^19.2.4",
92
- "react-router": "^7.13.0",
93
- "react-router-dom": "^7.13.0",
122
+ "react-router": "^7.18.0",
123
+ "react-router-dom": "^7.18.0",
124
+ "react-server-dom-rspack": "0.1.0",
125
+ "rsbuild-plugin-rsc": "^0.1.1",
126
+ "semver": "^7.8.5",
127
+ "shelljs": "^0.10.0",
128
+ "strip-ansi": "^7.2.0",
129
+ "strip-indent": "^4.1.1",
130
+ "tailwindcss": "4.3.2",
131
+ "type-fest": "^5.7.0",
94
132
  "typescript": "^5.9.3",
95
133
  "vitest": "^4.1.10",
134
+ "wait-on": "^9.0.10",
96
135
  "@rsbuild/config": "1.0.1"
97
136
  },
98
137
  "peerDependencies": {
99
138
  "@react-router/dev": "^7.13.0 || ^8.0.0",
100
- "@rsbuild/core": "^2.0.0"
139
+ "@rsbuild/core": "^2.2.0",
140
+ "react-router": "^7.13.0 || ^8.0.0",
141
+ "react-server-dom-rspack": "0.1.0",
142
+ "rsbuild-plugin-rsc": "^0.1.1"
101
143
  },
102
144
  "peerDependenciesMeta": {
103
145
  "@react-router/dev": {
104
146
  "optional": true
147
+ },
148
+ "react-server-dom-rspack": {
149
+ "optional": true
150
+ },
151
+ "rsbuild-plugin-rsc": {
152
+ "optional": true
105
153
  }
106
154
  },
107
155
  "publishConfig": {
@@ -113,15 +161,22 @@
113
161
  "build": "rslib build",
114
162
  "bench": "pnpm build && node benchmarks/run.mts",
115
163
  "bench:smoke": "pnpm build && node benchmarks/run.mts --iterations 1 --warmup 0",
116
- "bench:codspeed": "pnpm build && vitest bench --run --config vitest.benchmark.config.mts",
117
- "e2e": "pnpm build && pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only}' test:e2e",
164
+ "bench:codspeed": "pnpm build && vitest bench --run --config vitest.benchmark.config.mts -t build-256-ssr",
165
+ "e2e": "pnpm build && pnpm e2e:no-build",
166
+ "e2e:no-build": "pnpm test:package-interop && pnpm --filter './examples/{default-template,spa-mode,prerender,custom-node-server,cloudflare,client-only,react-router-8,rsc-mode}' test:e2e",
118
167
  "dev": "rslib build --watch",
119
- "test": "rstest run",
168
+ "test": "pnpm typecheck && rstest run",
169
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json && pnpm --filter integration typecheck && pnpm --filter integration-rsbuild-template typecheck && pnpm --filter integration-rsc-framework typecheck && pnpm --filter integration-rsc-preview typecheck",
120
170
  "test:watch": "rstest watch",
121
171
  "test:coverage": "rstest run --coverage",
122
172
  "test:core": "rstest run -c ./rstest.config.ts",
123
173
  "test:core:watch": "rstest watch -c ./rstest.config.ts",
124
174
  "test:package-interop": "node scripts/test-package-interop.mts",
175
+ "test:react-router-framework": "pnpm build && playwright test --config tests/react-router-framework/integration/playwright.config.ts",
176
+ "test:react-router-framework:failfast": "pnpm build && pnpm test:react-router-framework:failfast:no-build",
177
+ "test:react-router-framework:failfast:no-build": "RR_FRAMEWORK_MAX_WORKERS=2 playwright test --config tests/react-router-framework/integration/playwright.config.ts tests/react-router-framework/integration/hmr-hdr-test.ts --workers=2 --max-failures=1 --retries=0 --reporter=line && RR_FRAMEWORK_MAX_WORKERS=2 playwright test --config tests/react-router-framework/integration/playwright.config.ts tests/react-router-framework/integration/hmr-hdr-rsc-test.ts --workers=2 --max-failures=1 --retries=0 --reporter=line && RR_FRAMEWORK_MAX_WORKERS=4 playwright test --config tests/react-router-framework/integration/playwright.config.ts --grep-invert 'HMR & HDR' --workers=4 --max-failures=1 --retries=0 --reporter=line",
178
+ "test:react-router-framework:smoke": "pnpm build && playwright test --config tests/react-router-framework/integration/playwright.config.ts use-route-test.ts loader-test.ts",
179
+ "check:react-router-framework-upstream": "node scripts/check-react-router-framework-upstream.mjs",
125
180
  "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx}\"",
126
181
  "format:check": "prettier --check \"src/**/*.{js,jsx,ts,tsx}\"",
127
182
  "changeset": "changeset",