rsbuild-plugin-react-router 0.7.2 → 0.8.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 +14 -2
- package/dist/468.js +48 -0
- package/dist/511.js +60 -86
- package/dist/819.js +64 -0
- package/dist/build-output-transforms.d.ts +3 -1
- package/dist/constants.d.ts +1 -0
- package/dist/dev-hdr-channel.d.ts +9 -0
- package/dist/dev-hmr.d.ts +1 -22
- package/dist/dev-runtime-controller.d.ts +1 -6
- package/dist/dev-server.d.ts +3 -1
- package/dist/index.cjs +5417 -5080
- package/dist/index.js +1701 -1441
- package/dist/manifest-assets.d.ts +34 -0
- package/dist/manifest-snapshot.d.ts +17 -0
- package/dist/manifest-state.d.ts +12 -0
- package/dist/manifest.d.ts +2 -22
- package/dist/modify-browser-manifest.d.ts +2 -0
- package/dist/node-only-manifest.d.ts +8 -0
- package/dist/plugin-utils.d.ts +1 -1
- package/dist/rsc-prerender.d.ts +3 -2
- package/dist/server-build-worker-client.d.ts +20 -0
- package/dist/server-build-worker-protocol.d.ts +84 -0
- package/dist/server-build-worker.d.ts +1 -0
- package/dist/server-build-worker.js +123 -0
- package/dist/server-utils.d.ts +1 -2
- package/dist/types.d.ts +6 -0
- package/package.json +4 -4
- package/src/build-output-transforms.ts +22 -1
- package/src/classic-mode.ts +0 -1
- package/src/constants.ts +3 -0
- package/src/dev-hdr-channel.ts +38 -0
- package/src/dev-hmr.ts +57 -100
- package/src/dev-runtime-controller.ts +23 -18
- package/src/dev-server.ts +24 -4
- package/src/index.ts +229 -144
- package/src/lazy-compilation.ts +7 -2
- package/src/manifest-assets.ts +228 -0
- package/src/manifest-snapshot.ts +80 -0
- package/src/manifest-state.ts +71 -0
- package/src/manifest.ts +23 -161
- package/src/mode-plan.ts +12 -4
- package/src/modify-browser-manifest.ts +47 -18
- package/src/node-only-manifest.ts +52 -0
- package/src/plugin-utils.ts +6 -2
- package/src/prerender-build.ts +76 -86
- package/src/route-chunks.ts +152 -63
- package/src/rsc-prerender.ts +7 -35
- package/src/server-build-resolution.ts +1 -2
- package/src/server-build-worker-client.ts +221 -0
- package/src/server-build-worker-protocol.ts +69 -0
- package/src/server-build-worker.ts +192 -0
- package/src/server-utils.ts +0 -2
- package/src/types.ts +7 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type ReactRouterManifestStats = {
|
|
2
|
+
assetsByChunkName?: Record<string, string[]>;
|
|
3
|
+
entrypointFilesByName?: Record<string, string[]>;
|
|
4
|
+
assetTypesByName?: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
type ManifestStatsChunk = {
|
|
7
|
+
files?: Iterable<string>;
|
|
8
|
+
};
|
|
9
|
+
type ManifestStatsEntrypoint = {
|
|
10
|
+
getFiles?: () => Iterable<string>;
|
|
11
|
+
};
|
|
12
|
+
type ManifestStatsLookup<T> = Iterable<[string, T | null | undefined]> & {
|
|
13
|
+
get?: (name: string) => T | null | undefined;
|
|
14
|
+
};
|
|
15
|
+
type ManifestStatsCompilation = {
|
|
16
|
+
namedChunks: ManifestStatsLookup<ManifestStatsChunk>;
|
|
17
|
+
entrypoints?: ManifestStatsLookup<ManifestStatsEntrypoint>;
|
|
18
|
+
getAsset?: (name: string) => {
|
|
19
|
+
name?: string;
|
|
20
|
+
info?: {
|
|
21
|
+
assetType?: string;
|
|
22
|
+
javascriptModule?: boolean;
|
|
23
|
+
};
|
|
24
|
+
} | void;
|
|
25
|
+
};
|
|
26
|
+
export declare const stripAssetQuery: (name: string) => string;
|
|
27
|
+
export declare const getManifestAssetType: (name: string, assetTypesByName?: Readonly<Record<string, string>>) => string | undefined;
|
|
28
|
+
export declare const createReactRouterManifestStats: (compilation: ManifestStatsCompilation | undefined, chunkNames?: ReadonlySet<string>) => ReactRouterManifestStats | undefined;
|
|
29
|
+
type ChunkAssets = {
|
|
30
|
+
js: string[];
|
|
31
|
+
css: string[];
|
|
32
|
+
};
|
|
33
|
+
export declare const createChunkAssetResolver: (clientStats: ReactRouterManifestStats | undefined, includeEntrypointJavaScript: boolean) => ((chunkName: string) => ChunkAssets);
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactRouterManifestForDev, RouteManifestModuleExports } from './manifest.js';
|
|
2
|
+
import type { ReactRouterServerBuildPlan } from './server-build-plan.js';
|
|
3
|
+
import type { Route } from './types.js';
|
|
4
|
+
export type ReactRouterManifestSnapshot = {
|
|
5
|
+
browser: ReactRouterManifestForDev;
|
|
6
|
+
moduleExportsByRouteId: RouteManifestModuleExports;
|
|
7
|
+
server: ReactRouterManifestForDev;
|
|
8
|
+
serverByBundleId: Readonly<Record<string, ReactRouterManifestForDev>>;
|
|
9
|
+
serverByEntryName: Readonly<Record<string, ReactRouterManifestForDev>>;
|
|
10
|
+
};
|
|
11
|
+
export declare const createReactRouterManifestSnapshot: ({ manifest, sri, moduleExportsByRouteId, serverBuildPlan, routesByServerBundleId, }: {
|
|
12
|
+
manifest: ReactRouterManifestForDev;
|
|
13
|
+
sri: ReactRouterManifestForDev["sri"];
|
|
14
|
+
moduleExportsByRouteId: RouteManifestModuleExports;
|
|
15
|
+
serverBuildPlan: Pick<ReactRouterServerBuildPlan, "defaultEntryName" | "serverBundleEntries">;
|
|
16
|
+
routesByServerBundleId: Readonly<Record<string, Readonly<Record<string, Route>>>>;
|
|
17
|
+
}) => ReactRouterManifestSnapshot;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RsbuildPluginAPI, Rspack } from '@rsbuild/core';
|
|
2
|
+
import type { ReactRouterManifestSnapshot } from './manifest-snapshot.js';
|
|
3
|
+
type ReactRouterManifestState = {
|
|
4
|
+
stage(compilation: Rspack.Compilation, snapshot: ReactRouterManifestSnapshot): void;
|
|
5
|
+
read(): ReactRouterManifestSnapshot | null;
|
|
6
|
+
};
|
|
7
|
+
export declare const createReactRouterManifestState: ({ api, isBuild, onPublish, }: {
|
|
8
|
+
api: Pick<RsbuildPluginAPI, "onBeforeEnvironmentCompile" | "onAfterEnvironmentCompile">;
|
|
9
|
+
isBuild: boolean;
|
|
10
|
+
onPublish: (compilation: Rspack.Compilation, snapshot: ReactRouterManifestSnapshot) => void;
|
|
11
|
+
}) => ReactRouterManifestState;
|
|
12
|
+
export {};
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type ReactRouterManifestStats } from './manifest-assets.js';
|
|
2
|
+
export { createReactRouterManifestStats, type ReactRouterManifestStats, } from './manifest-assets.js';
|
|
1
3
|
import type { Route, PluginOptions, RouteManifestItem } from './types.js';
|
|
2
4
|
import { type RouteChunkCache } from './route-chunks.js';
|
|
3
5
|
import { type RouteModuleAnalysis } from './export-utils.js';
|
|
@@ -44,26 +46,6 @@ export type ReactRouterManifestForDev = {
|
|
|
44
46
|
sri?: Record<string, string> | true;
|
|
45
47
|
routes: Record<string, RouteManifestItem>;
|
|
46
48
|
};
|
|
47
|
-
export type ReactRouterManifestStats = {
|
|
48
|
-
assetsByChunkName?: Record<string, string[]>;
|
|
49
|
-
entrypointFilesByName?: Record<string, string[]>;
|
|
50
|
-
};
|
|
51
|
-
type ReactRouterManifestStatsChunk = {
|
|
52
|
-
files?: Iterable<string>;
|
|
53
|
-
};
|
|
54
|
-
type ReactRouterManifestStatsEntrypoint = {
|
|
55
|
-
getFiles?: () => Iterable<string>;
|
|
56
|
-
};
|
|
57
|
-
type ReactRouterManifestStatsLookup<T> = Iterable<[
|
|
58
|
-
string,
|
|
59
|
-
T | null | undefined
|
|
60
|
-
]> & {
|
|
61
|
-
get?: (name: string) => T | null | undefined;
|
|
62
|
-
};
|
|
63
|
-
type ReactRouterManifestStatsCompilation = {
|
|
64
|
-
namedChunks: ReactRouterManifestStatsLookup<ReactRouterManifestStatsChunk>;
|
|
65
|
-
entrypoints?: ReactRouterManifestStatsLookup<ReactRouterManifestStatsEntrypoint>;
|
|
66
|
-
};
|
|
67
49
|
export declare const isManifestJsAsset: (asset: string) => boolean;
|
|
68
50
|
export declare const isManifestCssAsset: (asset: string) => boolean;
|
|
69
51
|
/**
|
|
@@ -95,7 +77,6 @@ export type RscScriptAssetChunk = {
|
|
|
95
77
|
* emits it, so function templates and `tools.rspack` overrides are covered.
|
|
96
78
|
*/
|
|
97
79
|
export declare const collectUnsupportedRscScriptAssets: (compilation: RscScriptAssetCompilation) => string[];
|
|
98
|
-
export declare const createReactRouterManifestStats: (compilation: ReactRouterManifestStatsCompilation | undefined, chunkNames?: ReadonlySet<string>) => ReactRouterManifestStats | undefined;
|
|
99
80
|
export type RouteManifestModuleExports = Record<string, readonly string[]>;
|
|
100
81
|
export type ReactRouterManifestGenerationResult = {
|
|
101
82
|
manifest: ReactRouterManifestForDev;
|
|
@@ -109,4 +90,3 @@ export declare const getReactRouterManifestPath: ({ version, isBuild, entryModul
|
|
|
109
90
|
export declare const getReactRouterManifestChunkNames: (routes: Record<string, Route>, appDirectory: string, splitRouteModules?: boolean | "enforce") => Set<string>;
|
|
110
91
|
export declare function generateReactRouterManifestForDev(routes: Record<string, Route>, _options: PluginOptions, clientStats: ReactRouterManifestStats | undefined, context: string, assetPrefix?: string, manifestOptions?: ReactRouterManifestOptions): Promise<ReactRouterManifestGenerationResult>;
|
|
111
92
|
export declare function getReactRouterManifestForDev(...args: Parameters<typeof generateReactRouterManifestForDev>): Promise<ReactRouterManifestForDev>;
|
|
112
|
-
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type RsbuildPluginAPI } from '@rsbuild/core';
|
|
2
|
+
import type { ReactRouterManifestSnapshot } from './manifest-snapshot.js';
|
|
3
|
+
import type { Route } from './types.js';
|
|
4
|
+
export declare const registerNodeOnlyManifestValidation: ({ api, routeByFilePath, getSnapshot, }: {
|
|
5
|
+
api: RsbuildPluginAPI;
|
|
6
|
+
routeByFilePath: ReadonlyMap<string, Route>;
|
|
7
|
+
getSnapshot: () => ReactRouterManifestSnapshot | null;
|
|
8
|
+
}) => void;
|
package/dist/plugin-utils.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const resolveAppPackagePath: (specifier: string) => string | undefined;
|
|
1
|
+
export declare const resolveAppPackagePath: (specifier: string, rootPath?: string) => string | undefined;
|
|
2
2
|
export declare const parseVersionMajorMinor: (version: string | undefined) => {
|
|
3
3
|
major: number;
|
|
4
4
|
minor: number;
|
package/dist/rsc-prerender.d.ts
CHANGED
|
@@ -13,8 +13,9 @@ import type { Config } from './react-router-config.js';
|
|
|
13
13
|
* inline `__FLIGHT_DATA` scripts, served for client-side navigations
|
|
14
14
|
*
|
|
15
15
|
* Instead of an HTTP round-trip through a preview server, the RSC server
|
|
16
|
-
* bundle's default-exported `fetch` handler is invoked in
|
|
17
|
-
* how classic mode prerenders through
|
|
16
|
+
* bundle's default-exported `fetch` handler is invoked directly (in the
|
|
17
|
+
* server build worker), matching how classic mode prerenders through
|
|
18
|
+
* `createRequestHandler`.
|
|
18
19
|
*/
|
|
19
20
|
export declare const SPA_FALLBACK_REQUEST_PATH: string;
|
|
20
21
|
type RscPrerenderBuildApi = Pick<RsbuildPluginAPI, 'logger'>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ServerBuildDescription, type ServerBuildWorkerData } from './server-build-worker-protocol.js';
|
|
2
|
+
export type ServerBuildWorker = {
|
|
3
|
+
/** Plain-data view of the classic server build (routes, assets, prerender). */
|
|
4
|
+
description: ServerBuildDescription | undefined;
|
|
5
|
+
/** Runs the request against the server build in the worker. */
|
|
6
|
+
handler(request: Request): Promise<Response>;
|
|
7
|
+
/** Terminates the worker, and with it any handle the server graph opened. */
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Evaluate a built server bundle in a worker thread and proxy requests to it.
|
|
12
|
+
* Build-time rendering used to `import()` the bundle into the build process;
|
|
13
|
+
* a module-scope handle in the app's server graph then kept `rsbuild build`
|
|
14
|
+
* alive forever (#135). The worker is terminated by `close()`.
|
|
15
|
+
*
|
|
16
|
+
* The worker's `exit` is its final event, so any exit (including one between
|
|
17
|
+
* requests, e.g. the app calling `process.exit`) is terminal: outstanding and
|
|
18
|
+
* later requests reject instead of waiting for a reply that cannot come.
|
|
19
|
+
*/
|
|
20
|
+
export declare const startServerBuildWorker: (data: ServerBuildWorkerData, workerPath?: string) => Promise<ServerBuildWorker>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** Headers as a structured-cloneable list (the DOM lib's Headers is not iterable here). */
|
|
2
|
+
export declare const headerEntries: (headers: Headers) => [string, string][];
|
|
3
|
+
export type ServerBuildWorkerData = {
|
|
4
|
+
serverBuildPath: string;
|
|
5
|
+
mode: 'classic' | 'rsc';
|
|
6
|
+
};
|
|
7
|
+
export type ServerBuildWorkerRequest = {
|
|
8
|
+
type: 'close';
|
|
9
|
+
} | {
|
|
10
|
+
id: number;
|
|
11
|
+
type: 'request';
|
|
12
|
+
url: string;
|
|
13
|
+
method: string;
|
|
14
|
+
headers: [string, string][];
|
|
15
|
+
body?: Uint8Array<ArrayBuffer>;
|
|
16
|
+
}
|
|
17
|
+
/** Consume a response body only when the parent asks for it. */
|
|
18
|
+
| {
|
|
19
|
+
id: number;
|
|
20
|
+
type: 'read';
|
|
21
|
+
}
|
|
22
|
+
/** The parent released the request, possibly without reading its body. */
|
|
23
|
+
| {
|
|
24
|
+
id: number;
|
|
25
|
+
type: 'abort';
|
|
26
|
+
};
|
|
27
|
+
export type SerializedResponse = {
|
|
28
|
+
status: number;
|
|
29
|
+
statusText: string;
|
|
30
|
+
headers: [string, string][];
|
|
31
|
+
hasBody: boolean;
|
|
32
|
+
};
|
|
33
|
+
export type SerializedError = {
|
|
34
|
+
message: string;
|
|
35
|
+
stack?: string;
|
|
36
|
+
name?: string;
|
|
37
|
+
};
|
|
38
|
+
export type ServerBuildWorkerResponse = {
|
|
39
|
+
type: 'closed';
|
|
40
|
+
}
|
|
41
|
+
/** Sent once the bundle is evaluated; carries the classic build description. */
|
|
42
|
+
| {
|
|
43
|
+
type: 'ready';
|
|
44
|
+
description?: ServerBuildDescription;
|
|
45
|
+
} | {
|
|
46
|
+
type: 'reply';
|
|
47
|
+
id: number;
|
|
48
|
+
ok: true;
|
|
49
|
+
response: SerializedResponse;
|
|
50
|
+
} | {
|
|
51
|
+
type: 'body';
|
|
52
|
+
id: number;
|
|
53
|
+
ok: true;
|
|
54
|
+
body: Uint8Array<ArrayBuffer>;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'reply';
|
|
57
|
+
id: number;
|
|
58
|
+
ok: false;
|
|
59
|
+
error: SerializedError;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* The parts of a classic React Router server build that build-time rendering
|
|
63
|
+
* reads, as plain data: route module exports are reported by presence only.
|
|
64
|
+
*/
|
|
65
|
+
export type ServerBuildDescription = {
|
|
66
|
+
prerender?: string[];
|
|
67
|
+
routes: Record<string, {
|
|
68
|
+
id?: string;
|
|
69
|
+
parentId?: string;
|
|
70
|
+
path?: string;
|
|
71
|
+
index?: boolean;
|
|
72
|
+
caseSensitive?: boolean;
|
|
73
|
+
module: {
|
|
74
|
+
default: boolean;
|
|
75
|
+
ErrorBoundary: boolean;
|
|
76
|
+
loader: boolean;
|
|
77
|
+
};
|
|
78
|
+
}>;
|
|
79
|
+
assets: {
|
|
80
|
+
routes: Record<string, {
|
|
81
|
+
hasLoader?: boolean;
|
|
82
|
+
}>;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
let description, handler;
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { createRequestHandler } from "react-router";
|
|
5
|
+
import { PLUGIN_NAME } from "./468.js";
|
|
6
|
+
import { resolveServerBuildModule, headerEntries } from "./819.js";
|
|
7
|
+
if (!parentPort) throw Error('server-build-worker must run as a worker thread');
|
|
8
|
+
let { serverBuildPath: serverBuildPath, mode: mode } = workerData;
|
|
9
|
+
process.env.IS_RR_BUILD_REQUEST = 'yes';
|
|
10
|
+
let post = (message, transfer = [])=>{
|
|
11
|
+
parentPort.postMessage(message, transfer);
|
|
12
|
+
}, serializeError = (error)=>({
|
|
13
|
+
message: String(error?.message ?? error),
|
|
14
|
+
stack: 'string' == typeof error?.stack ? error.stack : void 0,
|
|
15
|
+
name: 'string' == typeof error?.name ? error.name : void 0
|
|
16
|
+
}), server_build_worker_buildModule = await import(pathToFileURL(serverBuildPath).href);
|
|
17
|
+
if ('classic' === mode) {
|
|
18
|
+
let build = await resolveServerBuildModule(server_build_worker_buildModule, `Server build ${JSON.stringify(serverBuildPath)}`);
|
|
19
|
+
description = {
|
|
20
|
+
prerender: build.prerender,
|
|
21
|
+
routes: Object.fromEntries(Object.entries(build.routes).flatMap(([id, route])=>route ? [
|
|
22
|
+
[
|
|
23
|
+
id,
|
|
24
|
+
{
|
|
25
|
+
id: route.id,
|
|
26
|
+
parentId: route.parentId,
|
|
27
|
+
path: route.path,
|
|
28
|
+
index: route.index,
|
|
29
|
+
caseSensitive: route.caseSensitive,
|
|
30
|
+
module: {
|
|
31
|
+
default: void 0 !== route.module.default,
|
|
32
|
+
ErrorBoundary: void 0 !== route.module.ErrorBoundary,
|
|
33
|
+
loader: void 0 !== route.module.loader
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
] : [])),
|
|
38
|
+
assets: {
|
|
39
|
+
routes: Object.fromEntries(Object.entries(build.assets.routes).flatMap(([id, route])=>route ? [
|
|
40
|
+
[
|
|
41
|
+
id,
|
|
42
|
+
{
|
|
43
|
+
hasLoader: route.hasLoader
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
] : []))
|
|
47
|
+
}
|
|
48
|
+
}, handler = createRequestHandler(build, 'production');
|
|
49
|
+
} else handler = ((buildModule)=>{
|
|
50
|
+
let fetch = 'function' == typeof buildModule?.default?.fetch ? buildModule.default.fetch : 'function' == typeof buildModule?.default?.default?.fetch ? buildModule.default.default.fetch : null;
|
|
51
|
+
if (!fetch) throw Error(`[${PLUGIN_NAME}] RSC server build ${JSON.stringify(serverBuildPath)} must default-export an object with a fetch function.`);
|
|
52
|
+
return fetch;
|
|
53
|
+
})(server_build_worker_buildModule);
|
|
54
|
+
let controllers = new Map(), responses = new Map(), release = (id)=>{
|
|
55
|
+
controllers.get(id)?.abort(), controllers.delete(id), responses.delete(id);
|
|
56
|
+
};
|
|
57
|
+
parentPort.on('message', async (message)=>{
|
|
58
|
+
if ('close' === message.type) {
|
|
59
|
+
for (let id of controllers.keys())release(id);
|
|
60
|
+
post({
|
|
61
|
+
type: 'closed'
|
|
62
|
+
});
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if ('abort' === message.type) {
|
|
66
|
+
responses.get(message.id)?.body?.cancel().catch(()=>{}), release(message.id);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if ('read' === message.type) {
|
|
70
|
+
try {
|
|
71
|
+
let response = responses.get(message.id);
|
|
72
|
+
if (!response) throw Error('Server build response was released');
|
|
73
|
+
let body = new Uint8Array(await response.arrayBuffer());
|
|
74
|
+
release(message.id), post({
|
|
75
|
+
type: 'body',
|
|
76
|
+
id: message.id,
|
|
77
|
+
ok: !0,
|
|
78
|
+
body
|
|
79
|
+
}, [
|
|
80
|
+
body.buffer
|
|
81
|
+
]);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
release(message.id), post({
|
|
84
|
+
type: 'reply',
|
|
85
|
+
id: message.id,
|
|
86
|
+
ok: !1,
|
|
87
|
+
error: serializeError(error)
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let controller = new AbortController();
|
|
93
|
+
controllers.set(message.id, controller);
|
|
94
|
+
try {
|
|
95
|
+
let response = await handler(new Request(message.url, {
|
|
96
|
+
method: message.method,
|
|
97
|
+
headers: message.headers,
|
|
98
|
+
body: message.body,
|
|
99
|
+
signal: controller.signal
|
|
100
|
+
}));
|
|
101
|
+
response.body ? responses.set(message.id, response) : release(message.id), post({
|
|
102
|
+
type: 'reply',
|
|
103
|
+
id: message.id,
|
|
104
|
+
ok: !0,
|
|
105
|
+
response: {
|
|
106
|
+
status: response.status,
|
|
107
|
+
statusText: response.statusText,
|
|
108
|
+
headers: headerEntries(response.headers),
|
|
109
|
+
hasBody: null !== response.body
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
} catch (error) {
|
|
113
|
+
release(message.id), post({
|
|
114
|
+
type: 'reply',
|
|
115
|
+
id: message.id,
|
|
116
|
+
ok: !1,
|
|
117
|
+
error: serializeError(error)
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}), post({
|
|
121
|
+
type: 'ready',
|
|
122
|
+
description: description
|
|
123
|
+
});
|
package/dist/server-utils.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ServerBuild } from 'react-router';
|
|
2
|
-
import { resolveServerBuildModule } from './server-build-resolution.js';
|
|
3
2
|
import type { Route } from './types.js';
|
|
4
3
|
/**
|
|
5
4
|
* Generates the server build template string.
|
|
@@ -34,5 +33,5 @@ interface ServerBuildOptions {
|
|
|
34
33
|
* @returns The generated module content as a string
|
|
35
34
|
*/
|
|
36
35
|
export declare function generateServerBuild(routes: Record<string, Route>, options: ServerBuildOptions): string;
|
|
37
|
-
export { resolveServerBuildModule };
|
|
38
36
|
export declare function resolveReactRouterServerBuild(buildModule: unknown): Promise<ServerBuild>;
|
|
37
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -9,6 +9,12 @@ export type Route = {
|
|
|
9
9
|
children?: Route[];
|
|
10
10
|
};
|
|
11
11
|
export type PluginOptions = {
|
|
12
|
+
/**
|
|
13
|
+
* Generate React Router route types during development and builds.
|
|
14
|
+
* Set to false when type generation is managed separately.
|
|
15
|
+
* @default true
|
|
16
|
+
*/
|
|
17
|
+
typegen?: boolean;
|
|
12
18
|
/**
|
|
13
19
|
* Whether to disable automatic middleware setup for custom server implementation.
|
|
14
20
|
* Use this when you want to handle server setup manually.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rsbuild-plugin-react-router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "React Router plugin for Rsbuild",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@react-router/fs-routes": "^8.3.1",
|
|
87
87
|
"@react-router/remix-routes-option-adapter": "^8.3.1",
|
|
88
88
|
"@react-router/serve": "^8.3.1",
|
|
89
|
-
"@rsbuild/core": "2.2.
|
|
89
|
+
"@rsbuild/core": "2.2.8",
|
|
90
90
|
"@rsbuild/plugin-less": "1.6.4",
|
|
91
91
|
"@rsbuild/plugin-mdx": "^1.1.3",
|
|
92
92
|
"@rsbuild/plugin-react": "2.1.0",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@rsbuild/plugin-svgr": "2.0.4",
|
|
95
95
|
"@rsbuild/plugin-tailwindcss": "2.0.3",
|
|
96
96
|
"@rslib/core": "^0.23.2",
|
|
97
|
-
"@rspack/core": "2.2.
|
|
97
|
+
"@rspack/core": "2.2.6",
|
|
98
98
|
"@rstest/core": "^0.11.4",
|
|
99
99
|
"@rstest/coverage-istanbul": "^0.11.4",
|
|
100
100
|
"@swc/helpers": "^0.5.23",
|
|
@@ -136,7 +136,7 @@
|
|
|
136
136
|
},
|
|
137
137
|
"peerDependencies": {
|
|
138
138
|
"@react-router/dev": "^7.13.0 || ^8.0.0",
|
|
139
|
-
"@rsbuild/core": "^2.2.
|
|
139
|
+
"@rsbuild/core": "^2.2.8",
|
|
140
140
|
"react-router": "^7.13.0 || ^8.0.0",
|
|
141
141
|
"react-server-dom-rspack": "0.1.0",
|
|
142
142
|
"rsbuild-plugin-rsc": "^0.1.1"
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
1
2
|
import type { RsbuildPluginAPI, TransformHandler } from '@rsbuild/core';
|
|
2
3
|
import jsesc from 'jsesc';
|
|
3
4
|
import { relative } from 'pathe';
|
|
@@ -80,6 +81,8 @@ type RegisterBuildOutputTransformsOptions = {
|
|
|
80
81
|
resolvedServerOutput: 'module' | 'commonjs';
|
|
81
82
|
performanceProfiler: ReactRouterPerformanceProfiler;
|
|
82
83
|
getLatestServerManifest: () => ReactRouterManifest | null;
|
|
84
|
+
/** File holding the captured manifests; a dependency of the server-manifest module. */
|
|
85
|
+
serverManifestStampPath: string;
|
|
83
86
|
getLatestServerManifestByBundleId: (
|
|
84
87
|
bundleId: string
|
|
85
88
|
) => ReactRouterManifest | undefined;
|
|
@@ -112,6 +115,7 @@ export const registerBuildOutputTransforms = ({
|
|
|
112
115
|
resolvedServerOutput,
|
|
113
116
|
performanceProfiler,
|
|
114
117
|
getLatestServerManifest,
|
|
118
|
+
serverManifestStampPath,
|
|
115
119
|
getLatestServerManifestByBundleId,
|
|
116
120
|
routes,
|
|
117
121
|
pluginOptions,
|
|
@@ -191,7 +195,7 @@ export const registerBuildOutputTransforms = ({
|
|
|
191
195
|
|
|
192
196
|
api.transform(
|
|
193
197
|
{
|
|
194
|
-
test: /virtual\/react-router\/(browser|server)-manifest/,
|
|
198
|
+
test: /virtual\/react-router\/((browser|server)-manifest|server-build)/,
|
|
195
199
|
},
|
|
196
200
|
async args =>
|
|
197
201
|
performanceProfiler.record(
|
|
@@ -205,11 +209,28 @@ export const registerBuildOutputTransforms = ({
|
|
|
205
209
|
};
|
|
206
210
|
}
|
|
207
211
|
|
|
212
|
+
// Cache identity for a module whose source never changes (#136);
|
|
213
|
+
// see `serverManifestStampPath` in index.ts.
|
|
214
|
+
if (existsSync(serverManifestStampPath)) {
|
|
215
|
+
args.addDependency(serverManifestStampPath);
|
|
216
|
+
} else {
|
|
217
|
+
args.addMissingDependency(serverManifestStampPath);
|
|
218
|
+
}
|
|
219
|
+
// The virtual server build contains the bundle's route table. A
|
|
220
|
+
// partition change must invalidate it alongside the asset manifest.
|
|
221
|
+
if (args.resource.includes('virtual/react-router/server-build')) {
|
|
222
|
+
return { code: args.code };
|
|
223
|
+
}
|
|
208
224
|
const bundleMatch = args.resource.match(
|
|
209
225
|
/virtual\/react-router\/server-manifest(?:-([^?]+))?/
|
|
210
226
|
);
|
|
211
227
|
const bundleId = bundleMatch?.[1]?.replace(/\.js$/, '');
|
|
212
228
|
const latestServerManifest = getLatestServerManifest();
|
|
229
|
+
if (isBuild && !latestServerManifest) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`[${PLUGIN_NAME}] Production server manifest requested before the browser manifest was finalized.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
213
234
|
const manifest =
|
|
214
235
|
(latestServerManifest
|
|
215
236
|
? ((bundleId && getLatestServerManifestByBundleId(bundleId)) ??
|
package/src/classic-mode.ts
CHANGED
|
@@ -125,7 +125,6 @@ export const createClassicBuildArtifacts = async ({
|
|
|
125
125
|
isBuild,
|
|
126
126
|
buildPlan: serverBuildPlan,
|
|
127
127
|
clientPatchesRouteMetadata: devHmr?.isEnabled,
|
|
128
|
-
onNodeRebuildCommitted: devHmr?.onNodeRebuildCommitted,
|
|
129
128
|
}),
|
|
130
129
|
prerenderPaths,
|
|
131
130
|
routesByServerBundleId,
|
package/src/constants.ts
CHANGED
|
@@ -93,3 +93,6 @@ export const CLIENT_EXPORTS = {
|
|
|
93
93
|
|
|
94
94
|
// SPA-mode prerender fallback document, served when no prerendered page matches.
|
|
95
95
|
export const SPA_FALLBACK_HTML_FILE = '__spa-fallback.html';
|
|
96
|
+
|
|
97
|
+
export const BROWSER_MANIFEST_ENTRY_NAME =
|
|
98
|
+
'virtual/react-router/browser-manifest';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { RsbuildDevServer } from '@rsbuild/core';
|
|
3
|
+
|
|
4
|
+
export const DEV_HDR_UPDATE_EVENT = 'react-router:hdr-update';
|
|
5
|
+
|
|
6
|
+
export const createDevHdrChannel = ({
|
|
7
|
+
hot,
|
|
8
|
+
isEnabled,
|
|
9
|
+
}: {
|
|
10
|
+
hot: RsbuildDevServer['environments'][string]['hot'];
|
|
11
|
+
isEnabled: () => boolean;
|
|
12
|
+
}) => {
|
|
13
|
+
const sessionId = randomUUID();
|
|
14
|
+
let revision = 0;
|
|
15
|
+
let closed = false;
|
|
16
|
+
const payload = () => ({
|
|
17
|
+
event: DEV_HDR_UPDATE_EVENT,
|
|
18
|
+
data: { sessionId, revision },
|
|
19
|
+
});
|
|
20
|
+
const unsubscribe = hot.onConnect(client => {
|
|
21
|
+
if (!closed && isEnabled() && revision > 0) {
|
|
22
|
+
client.send('custom', payload());
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
publish(): void {
|
|
28
|
+
if (closed || !isEnabled()) return;
|
|
29
|
+
revision += 1;
|
|
30
|
+
hot.send('custom', payload());
|
|
31
|
+
},
|
|
32
|
+
close(): void {
|
|
33
|
+
if (closed) return;
|
|
34
|
+
closed = true;
|
|
35
|
+
unsubscribe();
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
};
|