rsbuild-plugin-react-router 0.3.0 → 0.3.1
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 +55 -24
- package/dist/451.js +36 -20
- package/dist/build-manifest.d.ts +6 -3
- 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-runtime-artifacts.d.ts +9 -1
- package/dist/dev-runtime-compilation.d.ts +17 -2
- 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 +11448 -891
- package/dist/index.d.ts +4 -0
- package/dist/index.js +7950 -758
- 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/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 +11 -9
- package/src/build-manifest.ts +110 -73
- 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-runtime-artifacts.ts +50 -18
- package/src/dev-runtime-compilation.ts +80 -1
- package/src/dev-runtime-controller.ts +111 -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 +118 -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 +125 -61
- package/src/prerender.ts +0 -18
- package/src/react-router-config.ts +98 -73
- package/src/route-artifacts.ts +2 -2
- package/src/route-export-resolution.ts +3 -3
- 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
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import * as Effect from 'effect/Effect';
|
|
2
|
+
import type { ReactRouterManifestForDev } from './manifest.js';
|
|
3
|
+
import type { RouteManifestItem } from './types.js';
|
|
4
|
+
import { createDelayedPluginTask, tryPluginPromise } from './effect-runtime.js';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_LAZY_COMPILATION_TRIGGER_PREFIX = '/_rspack/lazy/trigger';
|
|
7
|
+
const DEFAULT_PREWARM_DELAY_MS = 0;
|
|
8
|
+
const DEFAULT_ROUTE_PREWARM_LIMIT = 8;
|
|
9
|
+
const PREWARM_FETCH_CONCURRENCY = 8;
|
|
10
|
+
const PREWARM_TRIGGER_CANDIDATES = 4;
|
|
11
|
+
|
|
12
|
+
type LazyCompilationPrewarmConfig = {
|
|
13
|
+
entry: boolean;
|
|
14
|
+
routeLimit: number;
|
|
15
|
+
delayMs: number;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type LazyCompilationPrewarmController = {
|
|
19
|
+
setServerOrigin(origin: string): void;
|
|
20
|
+
setManifest(manifest: ReactRouterManifestForDev | null): void;
|
|
21
|
+
schedule(): void;
|
|
22
|
+
cancelEffect(): Effect.Effect<void, Error, never>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type RspackLazyCompilationTriggerClient = {
|
|
26
|
+
extractModuleKeys(source: string): string[];
|
|
27
|
+
trigger(
|
|
28
|
+
origin: string,
|
|
29
|
+
keys: readonly string[]
|
|
30
|
+
): Effect.Effect<void, Error, never>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const normalizeLazyCompilationPrewarmOptions = (
|
|
34
|
+
options: boolean | undefined
|
|
35
|
+
): LazyCompilationPrewarmConfig | null => {
|
|
36
|
+
if (!options) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
entry: true,
|
|
42
|
+
routeLimit: DEFAULT_ROUTE_PREWARM_LIMIT,
|
|
43
|
+
delayMs: DEFAULT_PREWARM_DELAY_MS,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const collectRouteAssets = (
|
|
48
|
+
routes: Record<string, RouteManifestItem>,
|
|
49
|
+
config: LazyCompilationPrewarmConfig
|
|
50
|
+
): string[] => {
|
|
51
|
+
if (config.routeLimit < 1) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const assets: string[] = [];
|
|
56
|
+
for (const routeId in routes) {
|
|
57
|
+
const route = routes[routeId];
|
|
58
|
+
if (route.module) {
|
|
59
|
+
assets.push(route.module);
|
|
60
|
+
}
|
|
61
|
+
if (route.clientActionModule) {
|
|
62
|
+
assets.push(route.clientActionModule);
|
|
63
|
+
}
|
|
64
|
+
if (route.clientLoaderModule) {
|
|
65
|
+
assets.push(route.clientLoaderModule);
|
|
66
|
+
}
|
|
67
|
+
if (route.clientMiddlewareModule) {
|
|
68
|
+
assets.push(route.clientMiddlewareModule);
|
|
69
|
+
}
|
|
70
|
+
if (route.hydrateFallbackModule) {
|
|
71
|
+
assets.push(route.hydrateFallbackModule);
|
|
72
|
+
}
|
|
73
|
+
if (assets.length >= config.routeLimit) {
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return assets;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const collectLazyCompilationPrewarmAssets = (
|
|
82
|
+
manifest: ReactRouterManifestForDev,
|
|
83
|
+
config: LazyCompilationPrewarmConfig
|
|
84
|
+
): string[] => {
|
|
85
|
+
const assets = [
|
|
86
|
+
...(config.entry ? [manifest.entry.module, ...manifest.entry.imports] : []),
|
|
87
|
+
...collectRouteAssets(manifest.routes, config),
|
|
88
|
+
].filter(Boolean);
|
|
89
|
+
|
|
90
|
+
return Array.from(new Set(assets));
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const toAbsoluteUrl = (origin: string, asset: string): string =>
|
|
94
|
+
new URL(asset, origin).toString();
|
|
95
|
+
|
|
96
|
+
const parseJsonStringLiteral = (value: string): string | null => {
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(value);
|
|
99
|
+
return typeof parsed === 'string' ? parsed : null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const extractLazyCompilationModuleKeys = (source: string): string[] => {
|
|
106
|
+
const keys = new Set<string>();
|
|
107
|
+
const pattern = /activate\(\{\s*data:\s*("(?:\\.|[^"\\])*")/g;
|
|
108
|
+
let match: RegExpExecArray | null;
|
|
109
|
+
|
|
110
|
+
while ((match = pattern.exec(source))) {
|
|
111
|
+
const key = parseJsonStringLiteral(match[1]);
|
|
112
|
+
if (key) {
|
|
113
|
+
keys.add(key);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return Array.from(keys);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const createRspackLazyCompilationTriggerClient = (
|
|
121
|
+
triggerPrefix: string = DEFAULT_LAZY_COMPILATION_TRIGGER_PREFIX
|
|
122
|
+
): RspackLazyCompilationTriggerClient => ({
|
|
123
|
+
extractModuleKeys: extractLazyCompilationModuleKeys,
|
|
124
|
+
trigger(origin, keys) {
|
|
125
|
+
return postLazyCompilationKeys(origin, triggerPrefix, keys);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const fetchLazyCompilationKeys = (
|
|
130
|
+
origin: string,
|
|
131
|
+
assets: readonly string[],
|
|
132
|
+
triggerClient: RspackLazyCompilationTriggerClient
|
|
133
|
+
): Effect.Effect<string[], Error, never> =>
|
|
134
|
+
Effect.forEach(
|
|
135
|
+
assets,
|
|
136
|
+
asset =>
|
|
137
|
+
tryPluginPromise(async () => {
|
|
138
|
+
const response = await fetch(toAbsoluteUrl(origin, asset));
|
|
139
|
+
if (!response.ok) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
return triggerClient.extractModuleKeys(await response.text());
|
|
143
|
+
}),
|
|
144
|
+
{ concurrency: PREWARM_FETCH_CONCURRENCY }
|
|
145
|
+
).pipe(Effect.map(results => Array.from(new Set(results.flat()))));
|
|
146
|
+
|
|
147
|
+
const getTriggerCandidates = (
|
|
148
|
+
origin: string,
|
|
149
|
+
triggerPrefix: string
|
|
150
|
+
): string[] => {
|
|
151
|
+
const candidates = [triggerPrefix];
|
|
152
|
+
for (let index = 0; index < PREWARM_TRIGGER_CANDIDATES; index += 1) {
|
|
153
|
+
candidates.push(`${triggerPrefix}__${index}`);
|
|
154
|
+
}
|
|
155
|
+
return candidates.map(candidate => toAbsoluteUrl(origin, candidate));
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const postLazyCompilationKeys = (
|
|
159
|
+
origin: string,
|
|
160
|
+
triggerPrefix: string,
|
|
161
|
+
keys: readonly string[]
|
|
162
|
+
): Effect.Effect<void, Error, never> => {
|
|
163
|
+
if (keys.length === 0) {
|
|
164
|
+
return Effect.void;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return Effect.gen(function* () {
|
|
168
|
+
const body = keys.join('\n');
|
|
169
|
+
let lastError: Error | undefined;
|
|
170
|
+
|
|
171
|
+
for (const url of getTriggerCandidates(origin, triggerPrefix)) {
|
|
172
|
+
const accepted = yield* tryPluginPromise(async () => {
|
|
173
|
+
const response = await fetch(url, {
|
|
174
|
+
method: 'POST',
|
|
175
|
+
headers: {
|
|
176
|
+
'Content-Type': 'text/plain',
|
|
177
|
+
},
|
|
178
|
+
body,
|
|
179
|
+
});
|
|
180
|
+
return response.ok;
|
|
181
|
+
}).pipe(
|
|
182
|
+
Effect.catchAll(error => {
|
|
183
|
+
lastError = error;
|
|
184
|
+
return Effect.succeed(false);
|
|
185
|
+
})
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
if (accepted) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
yield* Effect.fail(
|
|
194
|
+
lastError ??
|
|
195
|
+
new Error(
|
|
196
|
+
'[rsbuild-plugin-react-router] Lazy compilation prewarm trigger was not accepted.'
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const prewarmLazyCompilation = ({
|
|
203
|
+
manifest,
|
|
204
|
+
serverOrigin,
|
|
205
|
+
config,
|
|
206
|
+
triggerClient = createRspackLazyCompilationTriggerClient(),
|
|
207
|
+
}: {
|
|
208
|
+
manifest: ReactRouterManifestForDev;
|
|
209
|
+
serverOrigin: string;
|
|
210
|
+
config: LazyCompilationPrewarmConfig;
|
|
211
|
+
triggerClient?: RspackLazyCompilationTriggerClient;
|
|
212
|
+
}): Effect.Effect<void, Error, never> =>
|
|
213
|
+
Effect.gen(function* () {
|
|
214
|
+
const assets = collectLazyCompilationPrewarmAssets(manifest, config);
|
|
215
|
+
const keys = yield* fetchLazyCompilationKeys(
|
|
216
|
+
serverOrigin,
|
|
217
|
+
assets,
|
|
218
|
+
triggerClient
|
|
219
|
+
);
|
|
220
|
+
yield* triggerClient.trigger(serverOrigin, keys);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
export const createLazyCompilationPrewarmController = ({
|
|
224
|
+
config,
|
|
225
|
+
onError,
|
|
226
|
+
}: {
|
|
227
|
+
config: LazyCompilationPrewarmConfig;
|
|
228
|
+
onError: (error: Error) => void;
|
|
229
|
+
}): LazyCompilationPrewarmController => {
|
|
230
|
+
let serverOrigin: string | undefined;
|
|
231
|
+
let manifest: ReactRouterManifestForDev | null = null;
|
|
232
|
+
let lastPrewarmAssetsKey: string | undefined;
|
|
233
|
+
const task = createDelayedPluginTask({
|
|
234
|
+
delayMs: config.delayMs,
|
|
235
|
+
run: () =>
|
|
236
|
+
Effect.gen(function* () {
|
|
237
|
+
if (!serverOrigin || !manifest) {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
yield* prewarmLazyCompilation({
|
|
241
|
+
manifest,
|
|
242
|
+
serverOrigin,
|
|
243
|
+
config,
|
|
244
|
+
});
|
|
245
|
+
}),
|
|
246
|
+
onError,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
setServerOrigin(origin) {
|
|
251
|
+
serverOrigin = origin;
|
|
252
|
+
},
|
|
253
|
+
setManifest(nextManifest) {
|
|
254
|
+
manifest = nextManifest;
|
|
255
|
+
if (!nextManifest) {
|
|
256
|
+
lastPrewarmAssetsKey = undefined;
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const assetsKey = collectLazyCompilationPrewarmAssets(
|
|
260
|
+
nextManifest,
|
|
261
|
+
config
|
|
262
|
+
).join('\n');
|
|
263
|
+
if (assetsKey === lastPrewarmAssetsKey) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
lastPrewarmAssetsKey = assetsKey;
|
|
267
|
+
task.reschedule();
|
|
268
|
+
},
|
|
269
|
+
schedule() {
|
|
270
|
+
task.schedule();
|
|
271
|
+
},
|
|
272
|
+
cancelEffect() {
|
|
273
|
+
manifest = null;
|
|
274
|
+
serverOrigin = undefined;
|
|
275
|
+
lastPrewarmAssetsKey = undefined;
|
|
276
|
+
return task.cancelEffect();
|
|
277
|
+
},
|
|
278
|
+
};
|
|
279
|
+
};
|
package/src/lazy-compilation.ts
CHANGED
|
@@ -49,10 +49,14 @@ const matchesLazyCompilationTest = (
|
|
|
49
49
|
|
|
50
50
|
const createReactRouterHydrationModuleTest = (entryClientPath: string) => {
|
|
51
51
|
const eagerPatterns = [
|
|
52
|
-
normalizeSlashes(entryClientPath),
|
|
53
52
|
'virtual/react-router/browser-manifest',
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
...(entryClientPath
|
|
54
|
+
? [
|
|
55
|
+
normalizeSlashes(entryClientPath),
|
|
56
|
+
BUILD_CLIENT_ROUTE_QUERY_STRING,
|
|
57
|
+
'?react-router-route',
|
|
58
|
+
]
|
|
59
|
+
: []),
|
|
56
60
|
];
|
|
57
61
|
|
|
58
62
|
return (module: LazyCompilationModule): boolean =>
|
|
@@ -65,9 +69,11 @@ const createReactRouterHydrationModuleTest = (entryClientPath: string) => {
|
|
|
65
69
|
export const guardReactRouterLazyCompilation = ({
|
|
66
70
|
lazyCompilation,
|
|
67
71
|
entryClientPath,
|
|
72
|
+
prewarmReactRouterModules = false,
|
|
68
73
|
}: {
|
|
69
74
|
lazyCompilation: PluginOptions['lazyCompilation'] | undefined;
|
|
70
75
|
entryClientPath: string;
|
|
76
|
+
prewarmReactRouterModules?: boolean;
|
|
71
77
|
}): PluginOptions['lazyCompilation'] | undefined => {
|
|
72
78
|
if (lazyCompilation === undefined || lazyCompilation === false) {
|
|
73
79
|
return lazyCompilation;
|
|
@@ -78,8 +84,9 @@ export const guardReactRouterLazyCompilation = ({
|
|
|
78
84
|
? { entries: true, imports: true }
|
|
79
85
|
: lazyCompilation;
|
|
80
86
|
const userTest = options.test;
|
|
81
|
-
const isReactRouterHydrationModule =
|
|
82
|
-
createReactRouterHydrationModuleTest(
|
|
87
|
+
const isReactRouterHydrationModule = prewarmReactRouterModules
|
|
88
|
+
? createReactRouterHydrationModuleTest('')
|
|
89
|
+
: createReactRouterHydrationModuleTest(entryClientPath);
|
|
83
90
|
|
|
84
91
|
return {
|
|
85
92
|
...options,
|