@rangojs/router 0.23.0 → 0.24.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/dist/bin/rango.js +4 -3
- package/dist/types/browser/merge-segment-loaders.d.ts +5 -2
- package/dist/types/browser/partial-update.d.ts +6 -4
- package/dist/types/browser/react/NavigationProvider.d.ts +11 -7
- package/dist/types/browser/react/context.d.ts +3 -2
- package/dist/types/browser/types.d.ts +5 -1
- package/dist/types/build/route-types/scan-filter.d.ts +7 -1
- package/dist/types/cache/document-cache-policy.d.ts +24 -0
- package/dist/types/cache/document-cache.d.ts +2 -10
- package/dist/types/context-var.d.ts +12 -0
- package/dist/types/errors.d.ts +10 -0
- package/dist/types/handles/pushed-by.d.ts +8 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.rsc.d.ts +2 -0
- package/dist/types/response-utils.d.ts +7 -0
- package/dist/types/reverse.d.ts +8 -8
- package/dist/types/route-definition/rewrite.d.ts +56 -0
- package/dist/types/route-map-builder.d.ts +7 -2
- package/dist/types/router/basename.d.ts +7 -0
- package/dist/types/router/handler-context.d.ts +1 -5
- package/dist/types/router/internal-params.d.ts +8 -0
- package/dist/types/router/middleware-types.d.ts +15 -0
- package/dist/types/router/revalidation.d.ts +1 -2
- package/dist/types/router/route-trie-builder.d.ts +10 -0
- package/dist/types/router/segment-resolution/mask-nested.d.ts +4 -2
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +36 -13
- package/dist/types/rsc/full-payload.d.ts +11 -0
- package/dist/types/rsc/handler.d.ts +33 -0
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/loader-fetch.d.ts +16 -0
- package/dist/types/rsc/rewrite-passes.d.ts +74 -0
- package/dist/types/rsc/ssr-setup.d.ts +3 -1
- package/dist/types/server/request-context.d.ts +39 -2
- package/dist/types/server/rewrite-trail.d.ts +49 -0
- package/dist/types/server/slot-accessors.d.ts +29 -0
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +8 -0
- package/dist/types/testing/run-middleware.d.ts +20 -0
- package/dist/types/testing/serve-shell-request.d.ts +2 -1
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/request-scope.d.ts +7 -0
- package/dist/types/vite/discovery/state.d.ts +12 -9
- package/dist/vite/index.js +164 -103
- package/package.json +1 -1
- package/skills/caching/SKILL.md +12 -0
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +84 -0
- package/skills/hooks/SKILL.md +11 -6
- package/skills/hooks/data.md +3 -1
- package/skills/loader/SKILL.md +7 -1
- package/skills/middleware/SKILL.md +183 -2
- package/skills/ppr/SKILL.md +9 -0
- package/skills/rango/SKILL.md +1 -1
- package/skills/server-actions/SKILL.md +27 -0
- package/skills/testing/client-components.md +1 -0
- package/skills/testing/middleware.md +31 -10
- package/skills/testing/response-routes.md +1 -0
- package/skills/testing/serve-shell-request.md +19 -1
- package/skills/use-cache/SKILL.md +4 -2
- package/src/__augment-tests__/augmented.check.ts +20 -0
- package/src/browser/event-controller.ts +3 -0
- package/src/browser/merge-segment-loaders.ts +31 -18
- package/src/browser/navigation-bridge.ts +19 -3
- package/src/browser/partial-update.ts +27 -12
- package/src/browser/react/Link.tsx +7 -9
- package/src/browser/react/NavigationProvider.tsx +46 -26
- package/src/browser/react/context.ts +3 -2
- package/src/browser/react/use-router.ts +2 -4
- package/src/browser/server-action-bridge.ts +6 -0
- package/src/browser/types.ts +5 -1
- package/src/build/route-types/scan-filter.ts +11 -4
- package/src/cache/document-cache-policy.ts +131 -0
- package/src/cache/document-cache.ts +12 -119
- package/src/context-var.ts +32 -2
- package/src/errors.ts +19 -0
- package/src/handles/pushed-by.ts +26 -0
- package/src/index.rsc.ts +8 -0
- package/src/index.ts +7 -0
- package/src/response-utils.ts +19 -0
- package/src/reverse.ts +17 -14
- package/src/route-definition/redirect.ts +2 -15
- package/src/route-definition/rewrite.ts +146 -0
- package/src/route-map-builder.ts +14 -11
- package/src/router/basename.ts +20 -0
- package/src/router/handler-context.ts +3 -13
- package/src/router/intercept-resolution.ts +29 -12
- package/src/router/internal-params.ts +16 -0
- package/src/router/loader-resolution.ts +2 -0
- package/src/router/match-middleware/background-revalidation.ts +11 -9
- package/src/router/match-middleware/cache-lookup.ts +20 -15
- package/src/router/match-middleware/cache-store.ts +9 -11
- package/src/router/match-middleware/intercept-resolution.ts +4 -2
- package/src/router/middleware-types.ts +16 -0
- package/src/router/middleware.ts +127 -49
- package/src/router/revalidation.ts +1 -1
- package/src/router/route-trie-builder.ts +28 -1
- package/src/router/segment-resolution/loader-snapshot.ts +18 -4
- package/src/router/segment-resolution/mask-nested.ts +8 -4
- package/src/router/segment-resolution/prefetch-deferral.ts +57 -17
- package/src/router/segment-resolution/revalidation.ts +57 -33
- package/src/rsc/full-payload.ts +37 -5
- package/src/rsc/handler.ts +82 -55
- package/src/rsc/helpers.ts +24 -0
- package/src/rsc/loader-fetch.ts +79 -24
- package/src/rsc/response-cache-serve.ts +18 -2
- package/src/rsc/response-route-handler.ts +4 -7
- package/src/rsc/rewrite-passes.ts +203 -0
- package/src/rsc/rsc-rendering.ts +4 -2
- package/src/rsc/ssr-setup.ts +11 -5
- package/src/server/cookie-parse.ts +36 -12
- package/src/server/cookie-store.ts +7 -3
- package/src/server/handle-store.ts +48 -17
- package/src/server/request-context.ts +154 -63
- package/src/server/request-headers.ts +4 -14
- package/src/server/rewrite-trail.ts +190 -0
- package/src/server/slot-accessors.ts +57 -0
- package/src/ssr/index.tsx +118 -16
- package/src/testing/dispatch.ts +138 -67
- package/src/testing/index.ts +1 -0
- package/src/testing/render-route.tsx +16 -3
- package/src/testing/run-loader.ts +2 -0
- package/src/testing/run-middleware.ts +56 -11
- package/src/testing/serve-shell-request.ts +15 -2
- package/src/thenable-status.ts +11 -0
- package/src/types/request-scope.ts +8 -0
- package/src/vite/discovery/state.ts +21 -13
- package/src/vite/plugins/expose-internal-ids.ts +2 -3
- package/src/vite/router-discovery.ts +29 -27
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
*
|
|
14
14
|
* `nextCalled` counts how many times the terminal `next()` (the finalHandler)
|
|
15
15
|
* ran: 0 when the chain short-circuited, 1 when it ran to completion.
|
|
16
|
+
*
|
|
17
|
+
* A `throw rewrite(target)` is reported as `result.rewrite` (the resolved
|
|
18
|
+
* target) instead of failing the run: production re-dispatches the request
|
|
19
|
+
* there, which this primitive does not do.
|
|
16
20
|
*/
|
|
17
21
|
|
|
18
22
|
import {
|
|
@@ -31,6 +35,9 @@ import {
|
|
|
31
35
|
type StateCookieSeed,
|
|
32
36
|
} from "./internal/context.js";
|
|
33
37
|
import type { ThemeConfig } from "../theme/types.js";
|
|
38
|
+
import { isRewrite } from "../route-definition/rewrite.js";
|
|
39
|
+
import { resolveRewriteTarget } from "../rsc/rewrite-passes.js";
|
|
40
|
+
import { getGlobalRouteMap } from "../route-map-builder.js";
|
|
34
41
|
import type { SegmentCacheStore } from "../cache/types.js";
|
|
35
42
|
import type { CacheProfile } from "../cache/profile-registry.js";
|
|
36
43
|
|
|
@@ -94,6 +101,13 @@ export interface RunMiddlewareOptions<TEnv = any> {
|
|
|
94
101
|
stateCookie?: StateCookieSeed;
|
|
95
102
|
}
|
|
96
103
|
|
|
104
|
+
/** A rewrite the chain threw (RunMiddlewareResult.rewrite). */
|
|
105
|
+
export interface RunMiddlewareRewrite {
|
|
106
|
+
pathname: string;
|
|
107
|
+
search: string;
|
|
108
|
+
routeName?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
97
111
|
/**
|
|
98
112
|
* Result of runMiddleware.
|
|
99
113
|
*/
|
|
@@ -109,6 +123,16 @@ export interface RunMiddlewareResult<TEnv = any> {
|
|
|
109
123
|
ctx: RequestContext<TEnv>;
|
|
110
124
|
/** Number of times the terminal handler ran (0 = short-circuited, 1 = passed through). */
|
|
111
125
|
nextCalled: number;
|
|
126
|
+
/**
|
|
127
|
+
* The target of a `throw rewrite(...)` from the chain, resolved as
|
|
128
|
+
* production resolves it (a path basename-prefixed, a route name through
|
|
129
|
+
* `routeMap`): `pathname` and `search` of the URL the request would render,
|
|
130
|
+
* and `routeName` when the rewrite named a route. `undefined` when the chain
|
|
131
|
+
* did not rewrite. On a rewrite, `response` carries what the chain set
|
|
132
|
+
* before the throw (status, headers, cookies), which production keeps for
|
|
133
|
+
* the target's response.
|
|
134
|
+
*/
|
|
135
|
+
rewrite?: RunMiddlewareRewrite;
|
|
112
136
|
/**
|
|
113
137
|
* Whether the chain called `ctx.dynamic()` (the PPR shell opt-out). The public
|
|
114
138
|
* way to assert the opt-out without reading the `@internal` `ctx._dynamic`.
|
|
@@ -192,17 +216,37 @@ export async function runMiddleware<TEnv = any>(
|
|
|
192
216
|
reverse as RequestContext<TEnv>["reverse"];
|
|
193
217
|
}
|
|
194
218
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
219
|
+
let rewrite: RunMiddlewareRewrite | undefined;
|
|
220
|
+
const response = await runWithRequestContext(ctx, async () => {
|
|
221
|
+
try {
|
|
222
|
+
return await executeLoaderMiddleware<TEnv>(
|
|
223
|
+
mwArray,
|
|
224
|
+
builtRequest,
|
|
225
|
+
(opts.env ?? {}) as TEnv,
|
|
226
|
+
opts.params ?? {},
|
|
227
|
+
variables,
|
|
228
|
+
finalHandler,
|
|
229
|
+
reverse,
|
|
230
|
+
);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (!isRewrite(error)) throw error;
|
|
233
|
+
const target = resolveRewriteTarget(
|
|
234
|
+
error,
|
|
235
|
+
opts.routeMap ?? getGlobalRouteMap(),
|
|
236
|
+
new URL(builtRequest.url),
|
|
237
|
+
ctx._basename,
|
|
238
|
+
);
|
|
239
|
+
rewrite = {
|
|
240
|
+
pathname: target.pathname,
|
|
241
|
+
search: target.search,
|
|
242
|
+
...("name" in error.target && { routeName: error.target.name }),
|
|
243
|
+
};
|
|
244
|
+
return new Response(null, {
|
|
245
|
+
status: ctx.res.status,
|
|
246
|
+
headers: ctx.res.headers,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
});
|
|
206
250
|
|
|
207
251
|
const { cookies, locationState } = snapshotRunEffects(ctx);
|
|
208
252
|
const headers = headersToObject(response.headers);
|
|
@@ -210,6 +254,7 @@ export async function runMiddleware<TEnv = any>(
|
|
|
210
254
|
response,
|
|
211
255
|
ctx,
|
|
212
256
|
nextCalled,
|
|
257
|
+
...(rewrite && { rewrite }),
|
|
213
258
|
dynamic: ctx._dynamic === true,
|
|
214
259
|
cookies,
|
|
215
260
|
headers,
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
*
|
|
30
30
|
* A `Prerender` route serves its handler layer from the artifact
|
|
31
31
|
* `router.matchForPrerender` bakes for the URL (on its first request, kept
|
|
32
|
-
* until resetShellTestState), through the production prerender store
|
|
32
|
+
* until resetShellTestState), through the production prerender store; an
|
|
33
|
+
* intercept of it serves from the intercept variant the bake keeps. Its
|
|
33
34
|
* first `ppr` request is a MISS with a runtime capture, as a URL without a
|
|
34
35
|
* build-time shell is in production; build-time shells are e2e-only. An
|
|
35
36
|
* on-demand route (`Prerender(..., { onDemand })`) bakes nothing here: it
|
|
@@ -415,10 +416,22 @@ async function servePrerenderArtifacts(
|
|
|
415
416
|
const { hashParams } = await import("../prerender/param-hash.js");
|
|
416
417
|
baked.add(pathname);
|
|
417
418
|
if (result && !result.passthrough) {
|
|
419
|
+
const key = `${result.routeName}/${hashParams(result.params)}`;
|
|
418
420
|
payloads.set(
|
|
419
|
-
|
|
421
|
+
key,
|
|
420
422
|
JSON.stringify({ segments: result.segments, handles: result.handles }),
|
|
421
423
|
);
|
|
424
|
+
// The intercept variant, keyed and shaped as the build writes it
|
|
425
|
+
// (vite/discovery/prerender-collection.ts).
|
|
426
|
+
if (result.interceptSegments?.length) {
|
|
427
|
+
payloads.set(
|
|
428
|
+
`${key}/i`,
|
|
429
|
+
JSON.stringify({
|
|
430
|
+
segments: [...result.segments, ...result.interceptSegments],
|
|
431
|
+
handles: result.interceptHandles ?? "",
|
|
432
|
+
}),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
422
435
|
}
|
|
423
436
|
}
|
|
424
437
|
const [{ createPrerenderStore }, { setPrerenderStoreForTests }] =
|
package/src/thenable-status.ts
CHANGED
|
@@ -24,3 +24,14 @@ export const SETTLED_THENABLE: Promise<undefined> = Object.assign(
|
|
|
24
24
|
Promise.resolve(undefined),
|
|
25
25
|
{ status: "fulfilled", value: undefined, _debugInfo: [] },
|
|
26
26
|
);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The value React reads from `value` without suspending: `value` itself when
|
|
30
|
+
* it is not a promise, a promise's `value` once it carries React's or Flight's
|
|
31
|
+
* `status: "fulfilled"`. Undefined while it is pending or rejected.
|
|
32
|
+
*/
|
|
33
|
+
export function settledValue<T>(value: T | Promise<T>): T | undefined {
|
|
34
|
+
if (!(value instanceof Promise)) return value;
|
|
35
|
+
const thenable = value as Promise<T> & { status?: string; value?: T };
|
|
36
|
+
return thenable.status === "fulfilled" ? thenable.value : undefined;
|
|
37
|
+
}
|
|
@@ -63,6 +63,14 @@ export interface RequestScope<TEnv = DefaultEnv> {
|
|
|
63
63
|
*/
|
|
64
64
|
originalUrl: URL;
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* The visitor's URL (the address bar, search included) when middleware
|
|
68
|
+
* rewrote this request with `throw rewrite()`; `undefined` otherwise. `url`,
|
|
69
|
+
* `pathname` and `searchParams` are the rewrite target's. After several
|
|
70
|
+
* rewrites this is still the first URL. Each context gets its own copy.
|
|
71
|
+
*/
|
|
72
|
+
rewrittenFrom?: URL;
|
|
73
|
+
|
|
66
74
|
/** URL pathname (same as `url.pathname`). */
|
|
67
75
|
pathname: string;
|
|
68
76
|
|
|
@@ -196,28 +196,36 @@ export interface DiscoveryState {
|
|
|
196
196
|
buildEnvDispose?: (() => Promise<void> | void) | null;
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* Set when the most recent discovery
|
|
200
|
-
* cold
|
|
201
|
-
* discovery. While set, the route-file
|
|
202
|
-
* change as a rediscovery trigger
|
|
199
|
+
* Set when the most recent dev discovery failed: an HMR re-discovery, or the
|
|
200
|
+
* cold discovery on the Node RSC runner or the Cloudflare temp runner.
|
|
201
|
+
* Cleared on the next successful discovery. While set, the route-file
|
|
202
|
+
* watcher treats any in-scan source change as a rediscovery trigger
|
|
203
|
+
* (recovery mode).
|
|
203
204
|
*/
|
|
204
205
|
lastDiscoveryError?: { message: string; at: number } | null;
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
/** Record a failed dev discovery (lastDiscoveryError): recovery mode. */
|
|
209
|
+
export function recordDiscoveryFailure(
|
|
210
|
+
state: Pick<DiscoveryState, "lastDiscoveryError">,
|
|
211
|
+
err: unknown,
|
|
212
|
+
): void {
|
|
213
|
+
state.lastDiscoveryError = {
|
|
214
|
+
message: (err as { message?: string } | null)?.message ?? String(err),
|
|
215
|
+
at: Date.now(),
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
207
219
|
/**
|
|
208
|
-
* Whether the route-file watcher can re-run runtime discovery
|
|
209
|
-
* manifest
|
|
210
|
-
*
|
|
211
|
-
* the
|
|
220
|
+
* Whether the route-file watcher can re-run runtime discovery: a committed
|
|
221
|
+
* manifest, or a recorded failure. A failed cold discovery commits none, and
|
|
222
|
+
* without its recorded failure the dev server stayed broken until a restart,
|
|
223
|
+
* on the Node runner and on Cloudflare alike.
|
|
212
224
|
*/
|
|
213
225
|
export function canRefreshRuntimeDiscovery(
|
|
214
226
|
state: Pick<DiscoveryState, "perRouterManifests" | "lastDiscoveryError">,
|
|
215
|
-
hasMainRunner: boolean,
|
|
216
227
|
): boolean {
|
|
217
|
-
return
|
|
218
|
-
state.perRouterManifests.length > 0 ||
|
|
219
|
-
(hasMainRunner && !!state.lastDiscoveryError)
|
|
220
|
-
);
|
|
228
|
+
return state.perRouterManifests.length > 0 || !!state.lastDiscoveryError;
|
|
221
229
|
}
|
|
222
230
|
|
|
223
231
|
export function createDiscoveryState(
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
transformInlineHandlers,
|
|
13
13
|
type VirtualHandlerEntry,
|
|
14
14
|
} from "../utils/ast-handler-extract.js";
|
|
15
|
+
import { isSkippedSourceScanDir } from "../../build/route-types/scan-filter.js";
|
|
15
16
|
|
|
16
17
|
import type {
|
|
17
18
|
HandlerTransformConfig,
|
|
@@ -297,8 +298,6 @@ ${lazyImports.join(",\n")}
|
|
|
297
298
|
|
|
298
299
|
const fs = await import("node:fs/promises");
|
|
299
300
|
|
|
300
|
-
const SKIP_DIRS = new Set(["node_modules", "dist", "build", "coverage"]);
|
|
301
|
-
|
|
302
301
|
async function scanDir(dir: string): Promise<string[]> {
|
|
303
302
|
const results: string[] = [];
|
|
304
303
|
try {
|
|
@@ -306,7 +305,7 @@ ${lazyImports.join(",\n")}
|
|
|
306
305
|
for (const entry of entries) {
|
|
307
306
|
const fullPath = path.join(dir, entry.name);
|
|
308
307
|
if (entry.isDirectory()) {
|
|
309
|
-
if (!
|
|
308
|
+
if (!isSkippedSourceScanDir(entry.name)) {
|
|
310
309
|
results.push(...(await scanDir(fullPath)));
|
|
311
310
|
}
|
|
312
311
|
} else if (/\.(ts|tsx|js|jsx)$/.test(entry.name)) {
|
|
@@ -53,6 +53,7 @@ import { extractHandlerExportsFromChunk } from "./utils/bundle-analysis.js";
|
|
|
53
53
|
import {
|
|
54
54
|
canRefreshRuntimeDiscovery,
|
|
55
55
|
createDiscoveryState,
|
|
56
|
+
recordDiscoveryFailure,
|
|
56
57
|
VIRTUAL_ROUTES_MANIFEST_ID,
|
|
57
58
|
type DiscoveryState,
|
|
58
59
|
type PluginOptions,
|
|
@@ -1050,12 +1051,20 @@ export function createRouterDiscoveryPlugin(
|
|
|
1050
1051
|
acquireBuildEnv(s, viteCommand, viteMode),
|
|
1051
1052
|
);
|
|
1052
1053
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
)
|
|
1057
|
-
)
|
|
1058
|
-
|
|
1054
|
+
const temp = await timed(
|
|
1055
|
+
debugDiscovery,
|
|
1056
|
+
"getOrCreateTempServer",
|
|
1057
|
+
() => getOrCreateTempServer(),
|
|
1058
|
+
);
|
|
1059
|
+
tempRscEnv = temp.env;
|
|
1060
|
+
if (!tempRscEnv) {
|
|
1061
|
+
// Recovery mode, as in the catch below. getOrCreateTempServer
|
|
1062
|
+
// already logged why the temp runner failed.
|
|
1063
|
+
recordDiscoveryFailure(
|
|
1064
|
+
s,
|
|
1065
|
+
temp.error ?? "temp runner unavailable for discovery",
|
|
1066
|
+
);
|
|
1067
|
+
} else {
|
|
1059
1068
|
optimizerHashBefore =
|
|
1060
1069
|
tempRscEnv.depsOptimizer?.metadata?.browserHash;
|
|
1061
1070
|
await timed(debugDiscovery, "discoverRouters (cloudflare)", () =>
|
|
@@ -1070,6 +1079,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
1070
1079
|
);
|
|
1071
1080
|
}
|
|
1072
1081
|
} catch (err: any) {
|
|
1082
|
+
recordDiscoveryFailure(s, err);
|
|
1073
1083
|
emitDiscoveryFailure(
|
|
1074
1084
|
err,
|
|
1075
1085
|
optimizerHashBefore,
|
|
@@ -1140,10 +1150,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
1140
1150
|
} catch (err: any) {
|
|
1141
1151
|
// Recovery mode: the next in-scan source change re-runs discovery
|
|
1142
1152
|
// (canRefreshRuntimeDiscovery) instead of waiting for a restart.
|
|
1143
|
-
s
|
|
1144
|
-
message: err?.message ?? String(err),
|
|
1145
|
-
at: Date.now(),
|
|
1146
|
-
};
|
|
1153
|
+
recordDiscoveryFailure(s, err);
|
|
1147
1154
|
emitDiscoveryFailure(
|
|
1148
1155
|
err,
|
|
1149
1156
|
optimizerHashBefore,
|
|
@@ -1744,13 +1751,6 @@ export function createRouterDiscoveryPlugin(
|
|
|
1744
1751
|
const refreshRuntimeDiscovery = async () => {
|
|
1745
1752
|
const rscEnv = (server.environments as any)?.rsc;
|
|
1746
1753
|
const hasMainRunner = !!rscEnv?.runner;
|
|
1747
|
-
// Cloudflare HMR has no main RSC runner (workerd is a separate
|
|
1748
|
-
// runtime). When we have a populated runtime manifest from cold
|
|
1749
|
-
// start, we can re-discover via the temp Node runner — the same
|
|
1750
|
-
// mechanism getOrCreateTempServer() uses at startup. Without a
|
|
1751
|
-
// populated manifest there's nothing useful to do, so bail
|
|
1752
|
-
// before involving the gate machine at all.
|
|
1753
|
-
if (!hasMainRunner && s.perRouterManifests.length === 0) return;
|
|
1754
1754
|
let previousRouteShape = routeShapeSignature();
|
|
1755
1755
|
await gate.runRefreshCycle(async () => {
|
|
1756
1756
|
const hmrStart = performance.now();
|
|
@@ -1770,11 +1770,16 @@ export function createRouterDiscoveryPlugin(
|
|
|
1770
1770
|
// no gen-file import, so no gen write ever invalidates it.
|
|
1771
1771
|
if (recoveringColdStart) invalidateRoutesManifestModule(rscEnv);
|
|
1772
1772
|
} else {
|
|
1773
|
-
// Cloudflare HMR
|
|
1774
|
-
//
|
|
1775
|
-
//
|
|
1773
|
+
// Cloudflare HMR has no main RSC runner (workerd is a separate
|
|
1774
|
+
// runtime): re-discover on the temp Node runner. Invalidate its
|
|
1775
|
+
// RSC module graph (or close+recreate as a fallback) so the
|
|
1776
|
+
// runner re-reads the freshly edited source. Keeping the same
|
|
1776
1777
|
// Vite instance alive avoids disrupting workerd's transport
|
|
1777
|
-
// during the first post-cold-start module-fetch window.
|
|
1778
|
+
// during the first post-cold-start module-fetch window. After
|
|
1779
|
+
// a cold start whose temp runner failed there is none, and
|
|
1780
|
+
// getOrCreateTempServer creates one. A recovering cycle needs
|
|
1781
|
+
// no routes-manifest invalidation here: the worker reload below
|
|
1782
|
+
// invalidates its whole module graph.
|
|
1778
1783
|
const tempRscEnv = await timed(
|
|
1779
1784
|
debugDiscovery,
|
|
1780
1785
|
"hmr refreshTempRscEnv (cloudflare)",
|
|
@@ -1830,10 +1835,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
1830
1835
|
forceCloudflareWorkerReload(rscEnv, expectedEpoch);
|
|
1831
1836
|
}
|
|
1832
1837
|
} catch (err: any) {
|
|
1833
|
-
s
|
|
1834
|
-
message: err?.message ?? String(err),
|
|
1835
|
-
at: Date.now(),
|
|
1836
|
-
};
|
|
1838
|
+
recordDiscoveryFailure(s, err);
|
|
1837
1839
|
// An invalid transition({ when }) is a definition error the
|
|
1838
1840
|
// user must see: terminal error plus the browser's Vite overlay,
|
|
1839
1841
|
// not a warning while the last-good route tree keeps serving.
|
|
@@ -2025,7 +2027,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
2025
2027
|
// Async: re-run runtime discovery to refresh factory-generated
|
|
2026
2028
|
// routes that the static parser cannot resolve. Resolves the
|
|
2027
2029
|
// discovery gate when complete.
|
|
2028
|
-
if (canRefreshRuntimeDiscovery(s
|
|
2030
|
+
if (canRefreshRuntimeDiscovery(s)) {
|
|
2029
2031
|
// The cloudflare workerd reload fires inside refreshRuntimeDiscovery
|
|
2030
2032
|
// on the successful cycle (see forceCloudflareWorkerReload call
|
|
2031
2033
|
// there) so queued follow-up cycles also trigger it.
|
|
@@ -2228,7 +2230,7 @@ export function createRouterDiscoveryPlugin(
|
|
|
2228
2230
|
// observe a stale resolved gate from cold-start). Resolved
|
|
2229
2231
|
// by the trailing refreshRuntimeDiscovery() cycle, which runs
|
|
2230
2232
|
// under the same predicate.
|
|
2231
|
-
if (canRefreshRuntimeDiscovery(s
|
|
2233
|
+
if (canRefreshRuntimeDiscovery(s)) {
|
|
2232
2234
|
gate.noteRouteEvent();
|
|
2233
2235
|
}
|
|
2234
2236
|
scheduleRouteRegeneration();
|