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
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Rspack } from '@rsbuild/core';
|
|
2
2
|
import type {
|
|
3
|
+
DevCompileAttemptIdentity,
|
|
3
4
|
DevCompilationIdentity,
|
|
5
|
+
DevRuntimeStats,
|
|
4
6
|
DevGraphChanges,
|
|
5
7
|
DevGraphIdentity,
|
|
6
8
|
} from './dev-runtime-artifacts.js';
|
|
@@ -10,13 +12,16 @@ export type DevCompilerPair = {
|
|
|
10
12
|
node: Rspack.Compiler;
|
|
11
13
|
settledCompilations: WeakSet<Rspack.Compilation>;
|
|
12
14
|
pendingAttempt?: PendingDevCompilation;
|
|
15
|
+
currentAttemptIdentity?: DevCompileAttemptIdentity;
|
|
13
16
|
latestCompletedWebIdentity?: DevCompilationIdentity;
|
|
17
|
+
latestCompletedWebStats?: Rspack.Stats;
|
|
18
|
+
latestCompletedNodeStats?: Rspack.Stats;
|
|
14
19
|
latestWebStart?: CompilationStart;
|
|
15
20
|
latestNodeStart?: CompilationStart;
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
export type PendingDevCompilation = {
|
|
19
|
-
stats:
|
|
24
|
+
stats: DevRuntimeStats;
|
|
20
25
|
changes: DevGraphChanges;
|
|
21
26
|
identity: DevGraphIdentity;
|
|
22
27
|
webCompilation: Rspack.Compilation;
|
|
@@ -27,6 +32,30 @@ export type CompilationStart =
|
|
|
27
32
|
| { status: 'pending' }
|
|
28
33
|
| { status: 'started'; identity: DevCompilationIdentity };
|
|
29
34
|
|
|
35
|
+
type CompilerPairStartSide = 'latestWebStart' | 'latestNodeStart';
|
|
36
|
+
|
|
37
|
+
export const createDevCompilerPair = ({
|
|
38
|
+
web,
|
|
39
|
+
node,
|
|
40
|
+
}: {
|
|
41
|
+
web: Rspack.Compiler;
|
|
42
|
+
node: Rspack.Compiler;
|
|
43
|
+
}): DevCompilerPair => ({
|
|
44
|
+
web,
|
|
45
|
+
node,
|
|
46
|
+
settledCompilations: new WeakSet(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
export const resetDevCompilerPair = (pair: DevCompilerPair): void => {
|
|
50
|
+
pair.pendingAttempt = undefined;
|
|
51
|
+
pair.currentAttemptIdentity = undefined;
|
|
52
|
+
pair.latestCompletedWebIdentity = undefined;
|
|
53
|
+
pair.latestCompletedWebStats = undefined;
|
|
54
|
+
pair.latestCompletedNodeStats = undefined;
|
|
55
|
+
pair.latestWebStart = undefined;
|
|
56
|
+
pair.latestNodeStart = undefined;
|
|
57
|
+
};
|
|
58
|
+
|
|
30
59
|
export const isLatestStartedCompilation = (
|
|
31
60
|
identity: DevCompilationIdentity | undefined,
|
|
32
61
|
start: CompilationStart | undefined
|
|
@@ -37,6 +66,32 @@ export const hasPendingCompilation = (pair: DevCompilerPair): boolean =>
|
|
|
37
66
|
pair.latestWebStart?.status === 'pending' ||
|
|
38
67
|
pair.latestNodeStart?.status === 'pending';
|
|
39
68
|
|
|
69
|
+
export const beginDevCompilerAttempt = (pair: DevCompilerPair): void => {
|
|
70
|
+
pair.pendingAttempt = undefined;
|
|
71
|
+
pair.currentAttemptIdentity = Symbol();
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const markDevCompilerPending = (
|
|
75
|
+
pair: DevCompilerPair,
|
|
76
|
+
side: CompilerPairStartSide
|
|
77
|
+
): boolean => {
|
|
78
|
+
const attemptAlreadyPending = hasPendingCompilation(pair);
|
|
79
|
+
pair[side] = { status: 'pending' };
|
|
80
|
+
pair.pendingAttempt = undefined;
|
|
81
|
+
if (!attemptAlreadyPending) {
|
|
82
|
+
pair.currentAttemptIdentity = Symbol();
|
|
83
|
+
}
|
|
84
|
+
return !attemptAlreadyPending;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const clearDevCompilerStart = (
|
|
88
|
+
pair: DevCompilerPair,
|
|
89
|
+
side: CompilerPairStartSide
|
|
90
|
+
): void => {
|
|
91
|
+
pair[side] = undefined;
|
|
92
|
+
pair.pendingAttempt = undefined;
|
|
93
|
+
};
|
|
94
|
+
|
|
40
95
|
export type CompilationIdentityTracker = {
|
|
41
96
|
getCompilationIdentity(
|
|
42
97
|
compilation: Rspack.Compilation
|
|
@@ -44,6 +99,13 @@ export type CompilationIdentityTracker = {
|
|
|
44
99
|
getWebIdentityForNodeCompilation(
|
|
45
100
|
compilation: Rspack.Compilation
|
|
46
101
|
): DevCompilationIdentity | undefined;
|
|
102
|
+
getAttemptIdentityForCompilation(
|
|
103
|
+
compilation: Rspack.Compilation
|
|
104
|
+
): DevCompileAttemptIdentity | undefined;
|
|
105
|
+
setAttemptIdentityForCompilation(
|
|
106
|
+
compilation: Rspack.Compilation,
|
|
107
|
+
identity: DevCompileAttemptIdentity
|
|
108
|
+
): void;
|
|
47
109
|
setWebIdentityForNodeCompilation(
|
|
48
110
|
compilation: Rspack.Compilation,
|
|
49
111
|
identity: DevCompilationIdentity
|
|
@@ -60,6 +122,10 @@ export const createCompilationIdentityTracker =
|
|
|
60
122
|
Rspack.Compilation,
|
|
61
123
|
DevCompilationIdentity
|
|
62
124
|
>();
|
|
125
|
+
const attemptIdentityByCompilation = new WeakMap<
|
|
126
|
+
Rspack.Compilation,
|
|
127
|
+
DevCompileAttemptIdentity
|
|
128
|
+
>();
|
|
63
129
|
|
|
64
130
|
return {
|
|
65
131
|
getCompilationIdentity(
|
|
@@ -82,6 +148,19 @@ export const createCompilationIdentityTracker =
|
|
|
82
148
|
return webIdentityByNodeCompilation.get(compilation);
|
|
83
149
|
},
|
|
84
150
|
|
|
151
|
+
getAttemptIdentityForCompilation(
|
|
152
|
+
compilation: Rspack.Compilation
|
|
153
|
+
): DevCompileAttemptIdentity | undefined {
|
|
154
|
+
return attemptIdentityByCompilation.get(compilation);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
setAttemptIdentityForCompilation(
|
|
158
|
+
compilation: Rspack.Compilation,
|
|
159
|
+
identity: DevCompileAttemptIdentity
|
|
160
|
+
): void {
|
|
161
|
+
attemptIdentityByCompilation.set(compilation, identity);
|
|
162
|
+
},
|
|
163
|
+
|
|
85
164
|
setWebIdentityForNodeCompilation(
|
|
86
165
|
compilation: Rspack.Compilation,
|
|
87
166
|
identity: DevCompilationIdentity
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { RsbuildConfig, RsbuildPluginAPI, Rspack } from '@rsbuild/core';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
2
3
|
import type { ServerBuild } from 'react-router';
|
|
3
4
|
import { PLUGIN_NAME } from './constants.js';
|
|
4
5
|
import {
|
|
6
|
+
beginDevCompilerAttempt,
|
|
7
|
+
clearDevCompilerStart,
|
|
5
8
|
createCompilationIdentityTracker,
|
|
9
|
+
createDevCompilerPair,
|
|
6
10
|
hasPendingCompilation,
|
|
7
11
|
isLatestStartedCompilation,
|
|
12
|
+
markDevCompilerPending,
|
|
13
|
+
resetDevCompilerPair,
|
|
8
14
|
type DevCompilerPair,
|
|
9
15
|
} from './dev-runtime-compilation.js';
|
|
10
16
|
import {
|
|
@@ -16,6 +22,8 @@ import {
|
|
|
16
22
|
import {
|
|
17
23
|
getEnvironmentStats,
|
|
18
24
|
snapshotDevChangedFiles,
|
|
25
|
+
type DevGraphIdentity,
|
|
26
|
+
type DevRuntimeStats,
|
|
19
27
|
type ReactRouterDevBuildPlan,
|
|
20
28
|
type ReactRouterDevManifestSet,
|
|
21
29
|
} from './dev-runtime-artifacts.js';
|
|
@@ -23,6 +31,12 @@ import {
|
|
|
23
31
|
createDevRuntimeSessionManager,
|
|
24
32
|
type RuntimeBinding,
|
|
25
33
|
} from './dev-runtime-session.js';
|
|
34
|
+
import {
|
|
35
|
+
normalizeEffectError,
|
|
36
|
+
runPluginEffect,
|
|
37
|
+
tryPluginPromise,
|
|
38
|
+
tryPluginSync,
|
|
39
|
+
} from './effect-runtime.js';
|
|
26
40
|
|
|
27
41
|
type ServerSetup = Exclude<
|
|
28
42
|
NonNullable<NonNullable<RsbuildConfig['server']>['setup']>,
|
|
@@ -107,10 +121,7 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
107
121
|
reloadAfterCssAssetOwnershipRemoval = false;
|
|
108
122
|
const pair = binding.compilers;
|
|
109
123
|
if (pair) {
|
|
110
|
-
pair
|
|
111
|
-
pair.latestCompletedWebIdentity = undefined;
|
|
112
|
-
pair.latestWebStart = undefined;
|
|
113
|
-
pair.latestNodeStart = undefined;
|
|
124
|
+
resetDevCompilerPair(pair);
|
|
114
125
|
}
|
|
115
126
|
binding.compilers = undefined;
|
|
116
127
|
binding.runtime.close(error);
|
|
@@ -121,6 +132,46 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
121
132
|
const compilationIdentities = createCompilationIdentityTracker();
|
|
122
133
|
const { getCompilationIdentity } = compilationIdentities;
|
|
123
134
|
|
|
135
|
+
const finishRuntimeAttemptEffect = (
|
|
136
|
+
binding: RuntimeBinding,
|
|
137
|
+
pair: DevCompilerPair,
|
|
138
|
+
stats: DevRuntimeStats,
|
|
139
|
+
changes: Parameters<RuntimeBinding['runtime']['finishAttempt']>[1],
|
|
140
|
+
identity: Parameters<RuntimeBinding['runtime']['finishAttempt']>[2]
|
|
141
|
+
): Effect.Effect<void, Error, never> =>
|
|
142
|
+
tryPluginPromise(() =>
|
|
143
|
+
binding.runtime.finishAttempt(stats, changes, identity)
|
|
144
|
+
).pipe(
|
|
145
|
+
Effect.flatMap(result =>
|
|
146
|
+
tryPluginSync(() => {
|
|
147
|
+
if (
|
|
148
|
+
result === 'retry-node' &&
|
|
149
|
+
sessions.getActiveBinding()?.id === binding.id
|
|
150
|
+
) {
|
|
151
|
+
pair.node.watching?.invalidate();
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
),
|
|
155
|
+
Effect.catchAll(cause =>
|
|
156
|
+
tryPluginSync(() => {
|
|
157
|
+
if (sessions.getActiveBinding()?.id === binding.id) {
|
|
158
|
+
binding.runtime.failAttempt(normalizeEffectError(cause));
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
)
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const finishRuntimeAttempt = (
|
|
165
|
+
binding: RuntimeBinding,
|
|
166
|
+
pair: DevCompilerPair,
|
|
167
|
+
stats: DevRuntimeStats,
|
|
168
|
+
changes: Parameters<RuntimeBinding['runtime']['finishAttempt']>[1],
|
|
169
|
+
identity: Parameters<RuntimeBinding['runtime']['finishAttempt']>[2]
|
|
170
|
+
): Promise<void> =>
|
|
171
|
+
runPluginEffect(
|
|
172
|
+
finishRuntimeAttemptEffect(binding, pair, stats, changes, identity)
|
|
173
|
+
);
|
|
174
|
+
|
|
124
175
|
const flushSettledAttempt = (
|
|
125
176
|
binding: RuntimeBinding,
|
|
126
177
|
pair: DevCompilerPair
|
|
@@ -141,15 +192,13 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
141
192
|
) {
|
|
142
193
|
return;
|
|
143
194
|
}
|
|
144
|
-
void
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
});
|
|
195
|
+
void finishRuntimeAttempt(
|
|
196
|
+
binding,
|
|
197
|
+
pair,
|
|
198
|
+
pending.stats,
|
|
199
|
+
pending.changes,
|
|
200
|
+
pending.identity
|
|
201
|
+
);
|
|
153
202
|
};
|
|
154
203
|
|
|
155
204
|
const rejectUnsupportedCompiler = (reason: string): void => {
|
|
@@ -247,7 +296,7 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
247
296
|
if (!binding || !pair || hasPendingCompilation(pair)) {
|
|
248
297
|
return;
|
|
249
298
|
}
|
|
250
|
-
pair
|
|
299
|
+
beginDevCompilerAttempt(pair);
|
|
251
300
|
binding.runtime.beginAttempt();
|
|
252
301
|
},
|
|
253
302
|
});
|
|
@@ -267,22 +316,17 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
267
316
|
if (!binding) {
|
|
268
317
|
return;
|
|
269
318
|
}
|
|
270
|
-
const pair: DevCompilerPair = {
|
|
271
|
-
web,
|
|
272
|
-
node,
|
|
273
|
-
settledCompilations: new WeakSet(),
|
|
274
|
-
};
|
|
319
|
+
const pair: DevCompilerPair = createDevCompilerPair({ web, node });
|
|
275
320
|
binding.compilers = pair;
|
|
276
321
|
const sessionId = binding.id;
|
|
277
322
|
const runtime = binding.runtime;
|
|
278
323
|
const failCurrentAttempt = (side: 'web' | 'node', error: Error): void => {
|
|
279
324
|
if (sessions.getActiveBinding()?.id === sessionId) {
|
|
280
325
|
if (side === 'web') {
|
|
281
|
-
pair
|
|
326
|
+
clearDevCompilerStart(pair, 'latestWebStart');
|
|
282
327
|
} else {
|
|
283
|
-
pair
|
|
328
|
+
clearDevCompilerStart(pair, 'latestNodeStart');
|
|
284
329
|
}
|
|
285
|
-
pair.pendingAttempt = undefined;
|
|
286
330
|
runtime.failAttempt(error);
|
|
287
331
|
}
|
|
288
332
|
};
|
|
@@ -293,12 +337,9 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
293
337
|
sessions.getActiveBinding()?.id === sessionId &&
|
|
294
338
|
pair[side]?.status !== 'pending'
|
|
295
339
|
) {
|
|
296
|
-
const attemptAlreadyPending = hasPendingCompilation(pair);
|
|
297
340
|
// Invalidation can arrive before the aggregate before-compile hook.
|
|
298
341
|
// Supersede any evaluation that could resolve in that gap immediately.
|
|
299
|
-
pair
|
|
300
|
-
pair.pendingAttempt = undefined;
|
|
301
|
-
if (!attemptAlreadyPending) {
|
|
342
|
+
if (markDevCompilerPending(pair, side)) {
|
|
302
343
|
runtime.beginAttempt();
|
|
303
344
|
}
|
|
304
345
|
if (side === 'latestWebStart' && reloadAfterCssAssetOwnershipRemoval) {
|
|
@@ -322,12 +363,27 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
322
363
|
pair.latestCompletedWebIdentity = getCompilationIdentity(
|
|
323
364
|
stats.compilation
|
|
324
365
|
);
|
|
366
|
+
pair.latestCompletedWebStats = stats;
|
|
367
|
+
}
|
|
368
|
+
);
|
|
369
|
+
node.hooks.done.tap(
|
|
370
|
+
{ name: `${PLUGIN_NAME}:dev-node-complete`, stage: -1000 },
|
|
371
|
+
stats => {
|
|
372
|
+
if (sessions.getActiveBinding()?.id === sessionId) {
|
|
373
|
+
pair.latestCompletedNodeStats = stats;
|
|
374
|
+
}
|
|
325
375
|
}
|
|
326
376
|
);
|
|
327
377
|
web.hooks.thisCompilation.tap(
|
|
328
378
|
`${PLUGIN_NAME}:dev-web-compilation`,
|
|
329
379
|
compilation => {
|
|
330
380
|
if (sessions.getActiveBinding()?.id === sessionId) {
|
|
381
|
+
if (pair.currentAttemptIdentity) {
|
|
382
|
+
compilationIdentities.setAttemptIdentityForCompilation(
|
|
383
|
+
compilation,
|
|
384
|
+
pair.currentAttemptIdentity
|
|
385
|
+
);
|
|
386
|
+
}
|
|
331
387
|
pair.latestWebStart = {
|
|
332
388
|
status: 'started',
|
|
333
389
|
identity: getCompilationIdentity(compilation),
|
|
@@ -349,6 +405,12 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
349
405
|
status: 'started',
|
|
350
406
|
identity: getCompilationIdentity(compilation),
|
|
351
407
|
};
|
|
408
|
+
if (pair.currentAttemptIdentity) {
|
|
409
|
+
compilationIdentities.setAttemptIdentityForCompilation(
|
|
410
|
+
compilation,
|
|
411
|
+
pair.currentAttemptIdentity
|
|
412
|
+
);
|
|
413
|
+
}
|
|
352
414
|
if (pair.latestCompletedWebIdentity) {
|
|
353
415
|
compilationIdentities.setWebIdentityForNodeCompilation(
|
|
354
416
|
compilation,
|
|
@@ -386,8 +448,10 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
386
448
|
if (!binding || !pair) {
|
|
387
449
|
return;
|
|
388
450
|
}
|
|
389
|
-
const webStats =
|
|
390
|
-
|
|
451
|
+
const webStats =
|
|
452
|
+
getEnvironmentStats(stats, 'web') ?? pair.latestCompletedWebStats;
|
|
453
|
+
const nodeStats =
|
|
454
|
+
getEnvironmentStats(stats, 'node') ?? pair.latestCompletedNodeStats;
|
|
391
455
|
if (
|
|
392
456
|
(webStats && webStats.compilation.compiler !== pair.web) ||
|
|
393
457
|
(nodeStats && nodeStats.compilation.compiler !== pair.node)
|
|
@@ -410,7 +474,17 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
410
474
|
web: snapshotDevChangedFiles(pair.web),
|
|
411
475
|
node: snapshotDevChangedFiles(pair.node),
|
|
412
476
|
};
|
|
413
|
-
const
|
|
477
|
+
const webAttempt = webStats
|
|
478
|
+
? compilationIdentities.getAttemptIdentityForCompilation(
|
|
479
|
+
webStats.compilation
|
|
480
|
+
)
|
|
481
|
+
: undefined;
|
|
482
|
+
const nodeAttempt = nodeStats
|
|
483
|
+
? compilationIdentities.getAttemptIdentityForCompilation(
|
|
484
|
+
nodeStats.compilation
|
|
485
|
+
)
|
|
486
|
+
: undefined;
|
|
487
|
+
const identity: DevGraphIdentity = {
|
|
414
488
|
web: webIdentity,
|
|
415
489
|
node: nodeIdentity,
|
|
416
490
|
nodeWeb: nodeStats
|
|
@@ -418,13 +492,19 @@ export const createReactRouterDevRuntimeController = ({
|
|
|
418
492
|
nodeStats.compilation
|
|
419
493
|
)
|
|
420
494
|
: undefined,
|
|
495
|
+
attempt:
|
|
496
|
+
webAttempt && nodeAttempt && webAttempt === nodeAttempt
|
|
497
|
+
? webAttempt
|
|
498
|
+
: undefined,
|
|
421
499
|
};
|
|
500
|
+
const finishStats: DevRuntimeStats =
|
|
501
|
+
webStats && nodeStats ? { web: webStats, node: nodeStats } : stats;
|
|
422
502
|
if (!webStats || !nodeStats) {
|
|
423
|
-
await binding
|
|
503
|
+
await finishRuntimeAttempt(binding, pair, finishStats, changes, identity);
|
|
424
504
|
return;
|
|
425
505
|
}
|
|
426
506
|
pair.pendingAttempt = {
|
|
427
|
-
stats,
|
|
507
|
+
stats: finishStats,
|
|
428
508
|
changes,
|
|
429
509
|
identity,
|
|
430
510
|
webCompilation: webStats.compilation,
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { RsbuildDevServer } from '@rsbuild/core';
|
|
2
|
+
import * as Effect from 'effect/Effect';
|
|
2
3
|
import { PLUGIN_NAME } from './constants.js';
|
|
3
4
|
import type { ReactRouterDevRuntime } from './dev-generation.js';
|
|
4
5
|
import type { DevCompilerPair } from './dev-runtime-compilation.js';
|
|
6
|
+
import {
|
|
7
|
+
runPluginEffect,
|
|
8
|
+
tryPluginPromise,
|
|
9
|
+
tryPluginSync,
|
|
10
|
+
} from './effect-runtime.js';
|
|
5
11
|
|
|
6
12
|
export type RuntimeBinding = {
|
|
7
13
|
id: number;
|
|
@@ -109,18 +115,19 @@ export const createDevRuntimeSessionManager = (
|
|
|
109
115
|
if (observation.promise) {
|
|
110
116
|
return observation.promise;
|
|
111
117
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
observation.promise = runPluginEffect(
|
|
119
|
+
tryPluginPromise(close).pipe(
|
|
120
|
+
Effect.tap(() =>
|
|
121
|
+
tryPluginSync(() => applyCloseOutcome(observation, { ok: true }))
|
|
122
|
+
),
|
|
123
|
+
Effect.catchAll(cause =>
|
|
124
|
+
tryPluginSync(() =>
|
|
125
|
+
applyCloseOutcome(observation, { ok: false, cause })
|
|
126
|
+
).pipe(Effect.zipRight(Effect.fail(cause)))
|
|
127
|
+
)
|
|
128
|
+
)
|
|
122
129
|
);
|
|
123
|
-
return
|
|
130
|
+
return observation.promise;
|
|
124
131
|
};
|
|
125
132
|
closeObservationByServer.set(server, observation);
|
|
126
133
|
return observation;
|
package/src/dev-server.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import type { RsbuildConfig } from '@rsbuild/core';
|
|
2
3
|
import type { ServerBuild } from 'react-router';
|
|
3
4
|
|
|
5
|
+
export type ServerSetup = Exclude<
|
|
6
|
+
NonNullable<NonNullable<RsbuildConfig['server']>['setup']>,
|
|
7
|
+
unknown[]
|
|
8
|
+
>;
|
|
9
|
+
|
|
4
10
|
export type DevServerMiddleware = (
|
|
5
11
|
req: IncomingMessage,
|
|
6
12
|
res: ServerResponse,
|
|
@@ -42,11 +48,18 @@ export const createDevServerMiddleware = (
|
|
|
42
48
|
dependencies.loadBuild,
|
|
43
49
|
'development'
|
|
44
50
|
);
|
|
45
|
-
return createRequestListener(requestHandler);
|
|
51
|
+
return createRequestListener(request => requestHandler(request));
|
|
46
52
|
})();
|
|
47
53
|
return listenerPromise;
|
|
48
54
|
};
|
|
49
55
|
|
|
56
|
+
// Warm the handler imports now so the first request does not pay them.
|
|
57
|
+
// On failure, reset so the first real request retries and surfaces the
|
|
58
|
+
// error through the middleware's own error path.
|
|
59
|
+
void getListener().catch(() => {
|
|
60
|
+
listenerPromise = undefined;
|
|
61
|
+
});
|
|
62
|
+
|
|
50
63
|
return async (req, res, next): Promise<void> => {
|
|
51
64
|
try {
|
|
52
65
|
const listener = await getListener();
|
|
@@ -56,3 +69,20 @@ export const createDevServerMiddleware = (
|
|
|
56
69
|
}
|
|
57
70
|
};
|
|
58
71
|
};
|
|
72
|
+
|
|
73
|
+
export const createReactRouterDevServerSetup = ({
|
|
74
|
+
loadBuild,
|
|
75
|
+
}: {
|
|
76
|
+
loadBuild: BuildProvider;
|
|
77
|
+
}): ServerSetup =>
|
|
78
|
+
function reactRouterDevServerSetup(context) {
|
|
79
|
+
if (context.action !== 'dev') {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// The returned callback runs after Rsbuild registers its built-in
|
|
83
|
+
// middlewares, so static assets are served before the React Router
|
|
84
|
+
// request handler.
|
|
85
|
+
return () => {
|
|
86
|
+
context.server.middlewares.use(createDevServerMiddleware({ loadBuild }));
|
|
87
|
+
};
|
|
88
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as Cause from 'effect/Cause';
|
|
2
|
+
import * as Duration from 'effect/Duration';
|
|
3
|
+
import * as Effect from 'effect/Effect';
|
|
4
|
+
import * as Exit from 'effect/Exit';
|
|
5
|
+
import * as Fiber from 'effect/Fiber';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
7
|
+
|
|
8
|
+
export const DEV_BACKGROUND_STARTUP_DELAY_MS = 3_000;
|
|
9
|
+
|
|
10
|
+
export const normalizeEffectError = (cause: unknown): Error =>
|
|
11
|
+
cause instanceof Error ? cause : new Error(String(cause));
|
|
12
|
+
|
|
13
|
+
const normalizeEffectCause = <E>(cause: Cause.Cause<E>): Error => {
|
|
14
|
+
const failure = Cause.failureOption(cause);
|
|
15
|
+
return normalizeEffectError(
|
|
16
|
+
Option.isSome(failure) ? failure.value : Cause.squash(cause)
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const runPluginEffect = async <A, E>(
|
|
21
|
+
effect: Effect.Effect<A, E, never>
|
|
22
|
+
): Promise<A> => {
|
|
23
|
+
const exit = await Effect.runPromiseExit(effect);
|
|
24
|
+
if (Exit.isSuccess(exit)) {
|
|
25
|
+
return exit.value;
|
|
26
|
+
}
|
|
27
|
+
throw normalizeEffectCause(exit.cause);
|
|
28
|
+
};
|
|
29
|
+
|
|
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
|
+
export const tryPluginPromise = <A>(
|
|
39
|
+
evaluate: () => PromiseLike<A> | A
|
|
40
|
+
): Effect.Effect<A, Error, never> =>
|
|
41
|
+
Effect.tryPromise({
|
|
42
|
+
try: () => Promise.resolve(evaluate()),
|
|
43
|
+
catch: normalizeEffectError,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
type DelayedPluginTask = {
|
|
47
|
+
schedule(): void;
|
|
48
|
+
reschedule(): void;
|
|
49
|
+
cancelEffect(): Effect.Effect<void, Error, never>;
|
|
50
|
+
cancel(): Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const createDelayedPluginTask = ({
|
|
54
|
+
delayMs,
|
|
55
|
+
run,
|
|
56
|
+
onError,
|
|
57
|
+
}: {
|
|
58
|
+
delayMs: number;
|
|
59
|
+
run: () => Effect.Effect<void, Error, never>;
|
|
60
|
+
onError: (error: Error) => void;
|
|
61
|
+
}): 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) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let fiber: ReturnType<typeof Effect.runFork>;
|
|
87
|
+
fiber = Effect.runFork(
|
|
88
|
+
Effect.sleep(Duration.millis(delayMs)).pipe(
|
|
89
|
+
Effect.zipRight(Effect.suspend(run)),
|
|
90
|
+
Effect.catchAll(error =>
|
|
91
|
+
Effect.sync(() => {
|
|
92
|
+
onError(error);
|
|
93
|
+
})
|
|
94
|
+
),
|
|
95
|
+
Effect.ensuring(
|
|
96
|
+
Effect.sync(() => {
|
|
97
|
+
if (activeFiber === fiber) {
|
|
98
|
+
activeFiber = undefined;
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
activeFiber = fiber;
|
|
105
|
+
};
|
|
106
|
+
|
|
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
|
+
},
|
|
123
|
+
|
|
124
|
+
cancelEffect,
|
|
125
|
+
|
|
126
|
+
async cancel(): Promise<void> {
|
|
127
|
+
await runPluginEffect(cancelEffect());
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
};
|