rsbuild-plugin-react-router 0.3.0 → 0.4.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 +88 -35
- package/dist/451.js +164 -30
- package/dist/build-manifest.d.ts +6 -3
- package/dist/build-output-transforms.d.ts +2 -1
- package/dist/concurrency.d.ts +2 -1
- package/dist/config-imports.d.ts +7 -0
- package/dist/dev-background-resources.d.ts +38 -0
- package/dist/dev-generation.d.ts +2 -2
- package/dist/dev-hmr.d.ts +45 -0
- package/dist/dev-runtime-artifacts.d.ts +9 -1
- package/dist/dev-runtime-compilation.d.ts +17 -2
- package/dist/dev-runtime-controller.d.ts +6 -1
- package/dist/dev-server.d.ts +5 -0
- package/dist/effect-runtime.d.ts +18 -0
- package/dist/export-utils.d.ts +2 -2
- package/dist/index.cjs +11852 -848
- package/dist/index.d.ts +5 -0
- package/dist/index.js +8289 -773
- package/dist/lazy-compilation-prewarm.d.ts +25 -0
- package/dist/lazy-compilation.d.ts +2 -1
- package/dist/manifest.d.ts +19 -4
- package/dist/parallel-route-transforms.d.ts +17 -2
- package/dist/prerender-build.d.ts +4 -0
- package/dist/prerender.d.ts +0 -1
- package/dist/react-router-config.d.ts +8 -5
- package/dist/route-artifacts.d.ts +10 -2
- package/dist/route-transform-tasks.d.ts +3 -0
- package/dist/server-build-resolution.d.ts +3 -0
- package/dist/ssr-externals.d.ts +1 -0
- package/dist/typegen.d.ts +14 -1
- package/dist/types.d.ts +15 -6
- package/package.json +18 -10
- package/src/build-manifest.ts +110 -73
- package/src/build-output-transforms.ts +5 -0
- package/src/concurrency.ts +3 -22
- package/src/config-imports.ts +38 -0
- package/src/dev-background-resources.ts +255 -0
- package/src/dev-generation.ts +43 -53
- package/src/dev-hmr.ts +431 -0
- package/src/dev-runtime-artifacts.ts +50 -18
- package/src/dev-runtime-compilation.ts +80 -1
- package/src/dev-runtime-controller.ts +155 -31
- package/src/dev-runtime-session.ts +18 -11
- package/src/dev-server.ts +31 -1
- package/src/effect-runtime.ts +130 -0
- package/src/export-utils.ts +82 -23
- package/src/index.ts +166 -153
- package/src/lazy-compilation-prewarm.ts +279 -0
- package/src/lazy-compilation.ts +12 -5
- package/src/manifest.ts +366 -255
- package/src/modify-browser-manifest.ts +2 -1
- package/src/parallel-route-transforms.ts +195 -69
- package/src/prerender-build.ts +147 -63
- package/src/prerender.ts +1 -19
- package/src/react-router-config.ts +98 -73
- package/src/route-artifacts.ts +122 -3
- package/src/route-export-resolution.ts +3 -3
- package/src/route-transform-tasks.ts +173 -2
- package/src/route-watch.ts +119 -84
- package/src/server-build-resolution.ts +131 -0
- package/src/server-utils.ts +7 -106
- package/src/ssr-externals.ts +1 -1
- package/src/typegen.ts +162 -33
- package/src/types.ts +16 -6
package/src/typegen.ts
CHANGED
|
@@ -1,49 +1,178 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
1
3
|
import type { RsbuildPluginAPI } from '@rsbuild/core';
|
|
2
4
|
import type { ResultPromise } from 'execa';
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js';
|
|
7
|
+
import { resolvePackageJson } from './ssr-externals.js';
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
|
|
9
|
+
// Quiet period with no dev compiles before the typegen watch starts. Long
|
|
10
|
+
// enough that route-load and HMR compile bursts (each rescheduling the task)
|
|
11
|
+
// finish before the typegen process competes for CPU on small machines.
|
|
12
|
+
const TYPEGEN_IDLE_DELAY_MS = 10_000;
|
|
13
|
+
|
|
14
|
+
type Execa = typeof import('execa').execa;
|
|
15
|
+
type LoadExeca = () => Promise<Execa>;
|
|
16
|
+
|
|
17
|
+
export type ReactRouterTypegenRunner = {
|
|
18
|
+
startWatch(): Promise<void>;
|
|
19
|
+
closeWatch(): Promise<void>;
|
|
20
|
+
runBuild(): Promise<void>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const loadDefaultExeca: LoadExeca = async () => {
|
|
24
|
+
const { execa } = await import('execa');
|
|
25
|
+
return execa;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type TypegenCommand = {
|
|
29
|
+
command: string;
|
|
30
|
+
args: string[];
|
|
31
|
+
};
|
|
6
32
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
33
|
+
// The `react-router` CLI bin is provided by `@react-router/dev`. Spawning it
|
|
34
|
+
// directly through `process.execPath` skips the npx bootstrap (npm config
|
|
35
|
+
// load and package resolution), which otherwise costs several hundred ms of
|
|
36
|
+
// CPU during dev-server startup and every production build.
|
|
37
|
+
const resolveDirectTypegenCommand = (
|
|
38
|
+
appDirectory: string
|
|
39
|
+
): TypegenCommand | undefined => {
|
|
40
|
+
const packageJsonPath = resolvePackageJson('@react-router/dev', appDirectory);
|
|
41
|
+
if (!packageJsonPath) {
|
|
42
|
+
// `@react-router/dev` is not resolvable from the app directory; the
|
|
43
|
+
// caller falls back to spawning through npx.
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {
|
|
48
|
+
bin?: string | Record<string, string>;
|
|
49
|
+
};
|
|
50
|
+
const binRelativePath =
|
|
51
|
+
typeof packageJson.bin === 'string'
|
|
52
|
+
? packageJson.bin
|
|
53
|
+
: packageJson.bin?.['react-router'];
|
|
54
|
+
if (!binRelativePath) {
|
|
55
|
+
return undefined;
|
|
10
56
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
57
|
+
return {
|
|
58
|
+
command: process.execPath,
|
|
59
|
+
args: [resolve(dirname(packageJsonPath), binRelativePath)],
|
|
60
|
+
};
|
|
61
|
+
} catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const createReactRouterTypegenRunner = (
|
|
67
|
+
loadExeca: LoadExeca = loadDefaultExeca,
|
|
68
|
+
appDirectory?: string
|
|
69
|
+
): ReactRouterTypegenRunner => {
|
|
70
|
+
let typegenProcess: ResultPromise | undefined;
|
|
71
|
+
let typegenCommand: TypegenCommand | undefined;
|
|
72
|
+
|
|
73
|
+
const getTypegenCommand = (): TypegenCommand => {
|
|
74
|
+
typegenCommand ??= (appDirectory
|
|
75
|
+
? resolveDirectTypegenCommand(appDirectory)
|
|
76
|
+
: undefined) ?? {
|
|
77
|
+
command: 'npx',
|
|
78
|
+
args: ['--yes', 'react-router'],
|
|
79
|
+
};
|
|
80
|
+
return typegenCommand;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const observeWatchExit = (process: ResultPromise): void => {
|
|
84
|
+
void process
|
|
85
|
+
.catch(() => undefined)
|
|
26
86
|
.finally(() => {
|
|
27
87
|
if (typegenProcess === process) {
|
|
28
88
|
typegenProcess = undefined;
|
|
29
89
|
}
|
|
30
90
|
});
|
|
31
|
-
}
|
|
91
|
+
};
|
|
32
92
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
93
|
+
return {
|
|
94
|
+
async startWatch(): Promise<void> {
|
|
95
|
+
if (typegenProcess) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const execa = await loadExeca();
|
|
100
|
+
const { command, args } = getTypegenCommand();
|
|
101
|
+
const process = execa(command, [...args, 'typegen', '--watch'], {
|
|
102
|
+
stdio: 'inherit',
|
|
103
|
+
detached: false,
|
|
104
|
+
cleanup: true,
|
|
105
|
+
});
|
|
106
|
+
typegenProcess = process;
|
|
107
|
+
observeWatchExit(process);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
async closeWatch(): Promise<void> {
|
|
111
|
+
const process = typegenProcess;
|
|
112
|
+
typegenProcess = undefined;
|
|
113
|
+
if (!process) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
process.kill('SIGTERM');
|
|
118
|
+
await process.catch(() => undefined);
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
async runBuild(): Promise<void> {
|
|
122
|
+
const execa = await loadExeca();
|
|
123
|
+
const { command, args } = getTypegenCommand();
|
|
124
|
+
await execa(command, [...args, 'typegen'], {
|
|
125
|
+
stdio: 'inherit',
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const registerReactRouterTypegen = (
|
|
132
|
+
api: RsbuildPluginAPI,
|
|
133
|
+
{
|
|
134
|
+
runner,
|
|
135
|
+
devWatchDelayMs = TYPEGEN_IDLE_DELAY_MS,
|
|
136
|
+
appDirectory,
|
|
137
|
+
}: {
|
|
138
|
+
runner?: ReactRouterTypegenRunner;
|
|
139
|
+
devWatchDelayMs?: number;
|
|
140
|
+
appDirectory?: string;
|
|
141
|
+
} = {}
|
|
142
|
+
): void => {
|
|
143
|
+
const resolvedRunner =
|
|
144
|
+
runner ?? createReactRouterTypegenRunner(loadDefaultExeca, appDirectory);
|
|
145
|
+
let devWatchStarted = false;
|
|
146
|
+
const devWatchTask = createDelayedPluginTask({
|
|
147
|
+
delayMs: devWatchDelayMs,
|
|
148
|
+
run: () =>
|
|
149
|
+
tryPluginPromise(() => {
|
|
150
|
+
devWatchStarted = true;
|
|
151
|
+
return resolvedRunner.startWatch();
|
|
152
|
+
}).pipe(Effect.asVoid),
|
|
153
|
+
onError(error) {
|
|
154
|
+
api.logger.warn(
|
|
155
|
+
`[react-router] Failed to start React Router typegen watch: ${error}`
|
|
156
|
+
);
|
|
157
|
+
},
|
|
41
158
|
});
|
|
42
159
|
|
|
43
|
-
api.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
160
|
+
if (api.context.action !== 'build') {
|
|
161
|
+
// Reschedule on every compile so the typegen watch only starts after a
|
|
162
|
+
// quiet period with no compiles. Starting it during the initial compile
|
|
163
|
+
// burst competes with HMR rebuilds for CPU on small machines.
|
|
164
|
+
api.onAfterDevCompile(() => {
|
|
165
|
+
if (devWatchStarted) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
devWatchTask.reschedule();
|
|
47
169
|
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
api.onCloseDevServer(async () => {
|
|
173
|
+
await devWatchTask.cancel();
|
|
174
|
+
await resolvedRunner.closeWatch();
|
|
48
175
|
});
|
|
176
|
+
|
|
177
|
+
api.onBeforeBuild(() => resolvedRunner.runBuild());
|
|
49
178
|
};
|
package/src/types.ts
CHANGED
|
@@ -20,8 +20,8 @@ export type PluginOptions = {
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* The output format for server builds.
|
|
23
|
-
* When
|
|
24
|
-
*
|
|
23
|
+
* When omitted, React Router's `serverModuleFormat` selects the emitted
|
|
24
|
+
* Rsbuild format (`"esm"` -> `"module"`, `"cjs"` -> `"commonjs"`).
|
|
25
25
|
*/
|
|
26
26
|
serverOutput?: 'module' | 'commonjs';
|
|
27
27
|
|
|
@@ -31,15 +31,25 @@ export type PluginOptions = {
|
|
|
31
31
|
federation?: boolean;
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* Rsbuild dev-only lazy compilation behavior.
|
|
35
35
|
*
|
|
36
|
-
* React Router
|
|
37
|
-
*
|
|
36
|
+
* React Router's browser manifest remains eager so initial dev requests can
|
|
37
|
+
* discover browser assets without lazy proxy delays.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
39
|
+
* Pass `false` to disable.
|
|
40
|
+
* @default true
|
|
40
41
|
*/
|
|
41
42
|
lazyCompilation?: NonNullable<RsbuildConfig['dev']>['lazyCompilation'];
|
|
42
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Prewarm Rspack lazy-compilation proxy modules after dev compiles.
|
|
46
|
+
* This depends on Rspack's generated lazy-compilation client shape and should
|
|
47
|
+
* be treated as experimental.
|
|
48
|
+
*
|
|
49
|
+
* @default false
|
|
50
|
+
*/
|
|
51
|
+
unstableLazyCompilationPrewarm?: boolean;
|
|
52
|
+
|
|
43
53
|
/**
|
|
44
54
|
* Emit structured React Router plugin timing logs.
|
|
45
55
|
* @default false
|