@rangojs/router 0.0.0-experimental.8a4d0430 → 0.0.0-experimental.8bcfea43
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/AGENTS.md +4 -0
- package/README.md +126 -38
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +1171 -461
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +19 -16
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +28 -20
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +91 -17
- package/skills/loader/SKILL.md +88 -45
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +765 -0
- package/skills/parallel/SKILL.md +185 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/response-routes/SKILL.md +8 -0
- package/skills/route/SKILL.md +55 -0
- package/skills/router-setup/SKILL.md +87 -2
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/typesafety/SKILL.md +13 -1
- package/src/__internal.ts +1 -1
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/navigation-bridge.ts +90 -16
- package/src/browser/navigation-client.ts +167 -59
- package/src/browser/navigation-store.ts +68 -9
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +113 -17
- package/src/browser/prefetch/cache.ts +184 -16
- package/src/browser/prefetch/fetch.ts +180 -33
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +123 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/Link.tsx +81 -9
- package/src/browser/react/NavigationProvider.tsx +89 -14
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +11 -1
- package/src/browser/react/use-router.ts +29 -9
- package/src/browser/rsc-router.tsx +168 -65
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +49 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +50 -24
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +223 -74
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +455 -15
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/client.tsx +84 -230
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +6 -1
- package/src/index.ts +49 -6
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/response-utils.ts +28 -0
- package/src/reverse.ts +27 -2
- package/src/route-definition/dsl-helpers.ts +240 -40
- package/src/route-definition/helpers-types.ts +67 -19
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +155 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +101 -25
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +10 -7
- package/src/router/loader-resolution.ts +159 -21
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +31 -16
- package/src/router/match-api.ts +127 -192
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +94 -17
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +61 -5
- package/src/router/match-result.ts +104 -10
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +8 -30
- package/src/router/middleware.ts +36 -10
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +60 -9
- package/src/router/prerender-match.ts +110 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +198 -20
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +438 -300
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +10 -4
- package/src/router/types.ts +1 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +60 -8
- package/src/rsc/handler.ts +478 -374
- package/src/rsc/helpers.ts +69 -41
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +16 -2
- package/src/rsc/response-route-handler.ts +14 -1
- package/src/rsc/rsc-rendering.ts +19 -1
- package/src/rsc/server-action.ts +10 -0
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +9 -1
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +109 -23
- package/src/server/context.ts +166 -17
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +194 -60
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +137 -65
- package/src/types/loader-types.ts +41 -15
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +19 -1
- package/src/types/segments.ts +2 -0
- package/src/urls/include-helper.ts +24 -14
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +18 -16
- package/src/use-loader.tsx +77 -5
- package/src/vite/debug.ts +55 -0
- package/src/vite/discovery/bundle-postprocess.ts +30 -33
- package/src/vite/discovery/discover-routers.ts +5 -1
- package/src/vite/discovery/prerender-collection.ts +128 -74
- package/src/vite/discovery/state.ts +13 -6
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +86 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +204 -217
- package/src/vite/router-discovery.ts +335 -64
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +37 -5
- package/src/vite/utils/shared-utils.ts +3 -2
|
@@ -10,7 +10,11 @@ import type { ReactNode } from "react";
|
|
|
10
10
|
import { invariant } from "../../errors";
|
|
11
11
|
import { revalidate } from "../loader-resolution.js";
|
|
12
12
|
import { evaluateRevalidation } from "../revalidation.js";
|
|
13
|
-
import
|
|
13
|
+
import {
|
|
14
|
+
getParallelEntries,
|
|
15
|
+
getParallelSlotEntries,
|
|
16
|
+
type EntryData,
|
|
17
|
+
} from "../../server/context";
|
|
14
18
|
import type {
|
|
15
19
|
HandlerContext,
|
|
16
20
|
InternalHandlerContext,
|
|
@@ -37,7 +41,11 @@ import {
|
|
|
37
41
|
} from "./helpers.js";
|
|
38
42
|
import { getRouterContext } from "../router-context.js";
|
|
39
43
|
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
40
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
track,
|
|
46
|
+
RSCRouterContext,
|
|
47
|
+
runInsideLoaderScope,
|
|
48
|
+
} from "../../server/context.js";
|
|
41
49
|
|
|
42
50
|
// ---------------------------------------------------------------------------
|
|
43
51
|
// Telemetry helpers
|
|
@@ -228,7 +236,9 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
228
236
|
params: ctx.params,
|
|
229
237
|
loaderId: loader.$$id,
|
|
230
238
|
loaderData: deps.wrapLoaderPromise(
|
|
231
|
-
|
|
239
|
+
runInsideLoaderScope(() =>
|
|
240
|
+
resolveLoaderData(loaderEntry, ctx, ctx.pathname),
|
|
241
|
+
),
|
|
232
242
|
entry,
|
|
233
243
|
segmentId,
|
|
234
244
|
ctx.pathname,
|
|
@@ -258,26 +268,95 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
258
268
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
259
269
|
const allLoaderSegments: ResolvedSegment[] = [];
|
|
260
270
|
const allMatchedIds: string[] = [];
|
|
271
|
+
const seenIds = new Set<string>();
|
|
272
|
+
|
|
273
|
+
async function collectEntryLoaders(
|
|
274
|
+
entry: EntryData,
|
|
275
|
+
belongsToRoute: boolean,
|
|
276
|
+
shortCodeOverride?: string,
|
|
277
|
+
): Promise<void> {
|
|
278
|
+
// Skip if all loaders from this entry have already been resolved
|
|
279
|
+
// via a parent (e.g., cache boundary wrapping a layout with shared loaders).
|
|
280
|
+
const loaderEntries = entry.loader ?? [];
|
|
281
|
+
const sc = shortCodeOverride ?? entry.shortCode;
|
|
282
|
+
const allAlreadySeen =
|
|
283
|
+
loaderEntries.length > 0 &&
|
|
284
|
+
loaderEntries.every((le, i) =>
|
|
285
|
+
seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
|
|
286
|
+
);
|
|
287
|
+
if (!allAlreadySeen) {
|
|
288
|
+
const { segments, matchedIds } = await resolveLoadersWithRevalidation(
|
|
289
|
+
entry,
|
|
290
|
+
context,
|
|
291
|
+
belongsToRoute,
|
|
292
|
+
clientSegmentIds,
|
|
293
|
+
prevParams,
|
|
294
|
+
request,
|
|
295
|
+
prevUrl,
|
|
296
|
+
nextUrl,
|
|
297
|
+
routeKey,
|
|
298
|
+
deps,
|
|
299
|
+
actionContext,
|
|
300
|
+
shortCodeOverride,
|
|
301
|
+
stale,
|
|
302
|
+
);
|
|
303
|
+
for (const seg of segments) {
|
|
304
|
+
if (!seenIds.has(seg.id)) {
|
|
305
|
+
seenIds.add(seg.id);
|
|
306
|
+
allLoaderSegments.push(seg);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
allMatchedIds.push(...matchedIds);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const seenParallelEntryIds = new Set<string>();
|
|
313
|
+
for (const parallelEntry of getParallelEntries(entry.parallel)) {
|
|
314
|
+
if (seenParallelEntryIds.has(parallelEntry.id)) continue;
|
|
315
|
+
seenParallelEntryIds.add(parallelEntry.id);
|
|
316
|
+
await collectEntryLoaders(parallelEntry, belongsToRoute, entry.shortCode);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const childBelongsToRoute = belongsToRoute || entry.type === "route";
|
|
320
|
+
for (const layoutEntry of entry.layout) {
|
|
321
|
+
await collectEntryLoaders(layoutEntry, childBelongsToRoute);
|
|
322
|
+
// Inherit route loaders for orphan layouts with parallels.
|
|
323
|
+
// Resolve directly — do NOT re-enter collectEntryLoaders with the
|
|
324
|
+
// route entry, as that would re-iterate route.layout and loop.
|
|
325
|
+
if (
|
|
326
|
+
entry.type === "route" &&
|
|
327
|
+
entry.loader &&
|
|
328
|
+
entry.loader.length > 0 &&
|
|
329
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
330
|
+
) {
|
|
331
|
+
const inherited = await resolveLoadersWithRevalidation(
|
|
332
|
+
entry,
|
|
333
|
+
context,
|
|
334
|
+
childBelongsToRoute,
|
|
335
|
+
clientSegmentIds,
|
|
336
|
+
prevParams,
|
|
337
|
+
request,
|
|
338
|
+
prevUrl,
|
|
339
|
+
nextUrl,
|
|
340
|
+
routeKey,
|
|
341
|
+
deps,
|
|
342
|
+
actionContext,
|
|
343
|
+
layoutEntry.shortCode,
|
|
344
|
+
stale,
|
|
345
|
+
);
|
|
346
|
+
for (const seg of inherited.segments) {
|
|
347
|
+
if (!seenIds.has(seg.id)) {
|
|
348
|
+
seenIds.add(seg.id);
|
|
349
|
+
seg._inherited = true;
|
|
350
|
+
allLoaderSegments.push(seg);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
allMatchedIds.push(...inherited.matchedIds);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
261
357
|
|
|
262
358
|
for (const entry of entries) {
|
|
263
|
-
|
|
264
|
-
const { segments, matchedIds } = await resolveLoadersWithRevalidation(
|
|
265
|
-
entry,
|
|
266
|
-
context,
|
|
267
|
-
belongsToRoute,
|
|
268
|
-
clientSegmentIds,
|
|
269
|
-
prevParams,
|
|
270
|
-
request,
|
|
271
|
-
prevUrl,
|
|
272
|
-
nextUrl,
|
|
273
|
-
routeKey,
|
|
274
|
-
deps,
|
|
275
|
-
actionContext,
|
|
276
|
-
undefined, // shortCodeOverride
|
|
277
|
-
stale,
|
|
278
|
-
);
|
|
279
|
-
allLoaderSegments.push(...segments);
|
|
280
|
-
allMatchedIds.push(...matchedIds);
|
|
359
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
281
360
|
}
|
|
282
361
|
|
|
283
362
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
@@ -301,22 +380,20 @@ export function buildEntryRevalidateMap(
|
|
|
301
380
|
map.set(entry.shortCode, { entry, revalidate: entry.revalidate });
|
|
302
381
|
|
|
303
382
|
if (entry.type !== "parallel") {
|
|
304
|
-
for (const parallelEntry of
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
383
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
384
|
+
entry.parallel,
|
|
385
|
+
)) {
|
|
386
|
+
const parallelParentShortCode = parentShortCode ?? entry.shortCode;
|
|
387
|
+
const parallelId = `${parallelParentShortCode}.${slot}`;
|
|
388
|
+
map.set(parallelId, {
|
|
389
|
+
entry: parallelEntry,
|
|
390
|
+
revalidate: parallelEntry.revalidate,
|
|
391
|
+
});
|
|
315
392
|
}
|
|
316
393
|
}
|
|
317
394
|
|
|
318
395
|
for (const layoutEntry of entry.layout) {
|
|
319
|
-
processEntry(layoutEntry);
|
|
396
|
+
processEntry(layoutEntry, entry.shortCode);
|
|
320
397
|
}
|
|
321
398
|
}
|
|
322
399
|
|
|
@@ -348,7 +425,10 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
348
425
|
const segments: ResolvedSegment[] = [];
|
|
349
426
|
const matchedIds: string[] = [];
|
|
350
427
|
|
|
351
|
-
|
|
428
|
+
const resolvedParallelEntries = new Set<string>();
|
|
429
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
430
|
+
entry.parallel,
|
|
431
|
+
)) {
|
|
352
432
|
invariant(
|
|
353
433
|
parallelEntry.type === "parallel",
|
|
354
434
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
@@ -359,141 +439,61 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
359
439
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
360
440
|
| ReactNode
|
|
361
441
|
>;
|
|
442
|
+
// In production, static handler bodies are evicted and the slot value
|
|
443
|
+
// may be undefined. The static store holds the pre-rendered component.
|
|
444
|
+
// We defer the handler check until after tryStaticSlot.
|
|
445
|
+
const handler = slots[slot];
|
|
446
|
+
|
|
447
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
448
|
+
|
|
449
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
450
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
451
|
+
if (
|
|
452
|
+
isFullRefetch ||
|
|
453
|
+
clientSegmentIds.has(parallelId) ||
|
|
454
|
+
belongsToRoute ||
|
|
455
|
+
isNewParent
|
|
456
|
+
) {
|
|
457
|
+
matchedIds.push(parallelId);
|
|
458
|
+
}
|
|
362
459
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
clientSegmentIds.has(parallelId) ||
|
|
376
|
-
belongsToRoute ||
|
|
377
|
-
isNewParent
|
|
378
|
-
) {
|
|
379
|
-
matchedIds.push(parallelId);
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
const shouldResolve = await (async () => {
|
|
383
|
-
if (isFullRefetch) {
|
|
384
|
-
if (isTraceActive()) {
|
|
385
|
-
pushRevalidationTraceEntry({
|
|
386
|
-
segmentId: parallelId,
|
|
387
|
-
segmentType: "parallel",
|
|
388
|
-
belongsToRoute,
|
|
389
|
-
source: "parallel",
|
|
390
|
-
defaultShouldRevalidate: true,
|
|
391
|
-
finalShouldRevalidate: true,
|
|
392
|
-
reason: "full-refetch",
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
return true;
|
|
396
|
-
}
|
|
397
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
398
|
-
const result = belongsToRoute || isNewParent;
|
|
399
|
-
if (isTraceActive()) {
|
|
400
|
-
pushRevalidationTraceEntry({
|
|
401
|
-
segmentId: parallelId,
|
|
402
|
-
segmentType: "parallel",
|
|
403
|
-
belongsToRoute,
|
|
404
|
-
source: "parallel",
|
|
405
|
-
defaultShouldRevalidate: result,
|
|
406
|
-
finalShouldRevalidate: result,
|
|
407
|
-
reason: result ? "new-segment" : "skip-parent-chain",
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
return result;
|
|
460
|
+
const shouldResolve = await (async () => {
|
|
461
|
+
if (isFullRefetch) {
|
|
462
|
+
if (isTraceActive()) {
|
|
463
|
+
pushRevalidationTraceEntry({
|
|
464
|
+
segmentId: parallelId,
|
|
465
|
+
segmentType: "parallel",
|
|
466
|
+
belongsToRoute,
|
|
467
|
+
source: "parallel",
|
|
468
|
+
defaultShouldRevalidate: true,
|
|
469
|
+
finalShouldRevalidate: true,
|
|
470
|
+
reason: "full-refetch",
|
|
471
|
+
});
|
|
411
472
|
}
|
|
412
|
-
|
|
413
|
-
const dummySegment: ResolvedSegment = {
|
|
414
|
-
id: parallelId,
|
|
415
|
-
namespace: parallelEntry.id,
|
|
416
|
-
type: "parallel",
|
|
417
|
-
index: 0,
|
|
418
|
-
component: null as any,
|
|
419
|
-
params,
|
|
420
|
-
slot,
|
|
421
|
-
belongsToRoute,
|
|
422
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
423
|
-
...(parallelEntry.mountPath
|
|
424
|
-
? { mountPath: parallelEntry.mountPath }
|
|
425
|
-
: {}),
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
return await evaluateRevalidation({
|
|
429
|
-
segment: dummySegment,
|
|
430
|
-
prevParams,
|
|
431
|
-
getPrevSegment: null,
|
|
432
|
-
request,
|
|
433
|
-
prevUrl,
|
|
434
|
-
nextUrl,
|
|
435
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
436
|
-
name: `revalidate${i}`,
|
|
437
|
-
fn,
|
|
438
|
-
})),
|
|
439
|
-
routeKey,
|
|
440
|
-
context,
|
|
441
|
-
actionContext,
|
|
442
|
-
stale,
|
|
443
|
-
traceSource: "parallel",
|
|
444
|
-
});
|
|
445
|
-
})();
|
|
446
|
-
emitRevalidationDecision(
|
|
447
|
-
parallelId,
|
|
448
|
-
context.pathname,
|
|
449
|
-
routeKey,
|
|
450
|
-
shouldResolve,
|
|
451
|
-
);
|
|
452
|
-
|
|
453
|
-
let component: ReactNode | undefined;
|
|
454
|
-
if (shouldResolve) {
|
|
455
|
-
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
473
|
+
return true;
|
|
456
474
|
}
|
|
457
|
-
if (
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
segmentType: "parallel",
|
|
470
|
-
});
|
|
471
|
-
observeStreamedHandler(
|
|
472
|
-
tracked,
|
|
473
|
-
parallelId,
|
|
474
|
-
"parallel",
|
|
475
|
-
context.pathname,
|
|
476
|
-
routeKey,
|
|
477
|
-
params,
|
|
478
|
-
);
|
|
479
|
-
component = tracked as ReactNode;
|
|
480
|
-
} else {
|
|
481
|
-
component = result as ReactNode;
|
|
482
|
-
}
|
|
483
|
-
} else {
|
|
484
|
-
component =
|
|
485
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
475
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
476
|
+
const result = belongsToRoute || isNewParent;
|
|
477
|
+
if (isTraceActive()) {
|
|
478
|
+
pushRevalidationTraceEntry({
|
|
479
|
+
segmentId: parallelId,
|
|
480
|
+
segmentType: "parallel",
|
|
481
|
+
belongsToRoute,
|
|
482
|
+
source: "parallel",
|
|
483
|
+
defaultShouldRevalidate: result,
|
|
484
|
+
finalShouldRevalidate: result,
|
|
485
|
+
reason: result ? "new-segment" : "skip-parent-chain",
|
|
486
|
+
});
|
|
486
487
|
}
|
|
488
|
+
return result;
|
|
487
489
|
}
|
|
488
490
|
|
|
489
|
-
|
|
491
|
+
const dummySegment: ResolvedSegment = {
|
|
490
492
|
id: parallelId,
|
|
491
493
|
namespace: parallelEntry.id,
|
|
492
494
|
type: "parallel",
|
|
493
495
|
index: 0,
|
|
494
|
-
component,
|
|
495
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
496
|
-
transition: parallelEntry.transition,
|
|
496
|
+
component: null as any,
|
|
497
497
|
params,
|
|
498
498
|
slot,
|
|
499
499
|
belongsToRoute,
|
|
@@ -501,28 +501,111 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
501
501
|
...(parallelEntry.mountPath
|
|
502
502
|
? { mountPath: parallelEntry.mountPath }
|
|
503
503
|
: {}),
|
|
504
|
-
}
|
|
505
|
-
}
|
|
504
|
+
};
|
|
506
505
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
parallelEntry,
|
|
510
|
-
context,
|
|
511
|
-
belongsToRoute,
|
|
512
|
-
clientSegmentIds,
|
|
506
|
+
return await evaluateRevalidation({
|
|
507
|
+
segment: dummySegment,
|
|
513
508
|
prevParams,
|
|
509
|
+
getPrevSegment: null,
|
|
514
510
|
request,
|
|
515
511
|
prevUrl,
|
|
516
512
|
nextUrl,
|
|
513
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
514
|
+
name: `revalidate${i}`,
|
|
515
|
+
fn,
|
|
516
|
+
})),
|
|
517
517
|
routeKey,
|
|
518
|
-
|
|
518
|
+
context,
|
|
519
519
|
actionContext,
|
|
520
|
-
entry.shortCode,
|
|
521
520
|
stale,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
521
|
+
traceSource: "parallel",
|
|
522
|
+
});
|
|
523
|
+
})();
|
|
524
|
+
emitRevalidationDecision(
|
|
525
|
+
parallelId,
|
|
526
|
+
context.pathname,
|
|
527
|
+
routeKey,
|
|
528
|
+
shouldResolve,
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
let component: ReactNode | undefined;
|
|
532
|
+
if (shouldResolve) {
|
|
533
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
534
|
+
}
|
|
535
|
+
if (component === undefined) {
|
|
536
|
+
const hasLoadingFallback =
|
|
537
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
538
|
+
if (!shouldResolve) {
|
|
539
|
+
component = null;
|
|
540
|
+
} else if (handler === undefined) {
|
|
541
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
542
|
+
// Nothing to render — use null so the client keeps its cached version.
|
|
543
|
+
component = null;
|
|
544
|
+
} else if (hasLoadingFallback) {
|
|
545
|
+
const result =
|
|
546
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
547
|
+
if (result instanceof Promise) {
|
|
548
|
+
const tracked = deps.trackHandler(result, {
|
|
549
|
+
segmentId: parallelId,
|
|
550
|
+
segmentType: "parallel",
|
|
551
|
+
});
|
|
552
|
+
observeStreamedHandler(
|
|
553
|
+
tracked,
|
|
554
|
+
parallelId,
|
|
555
|
+
"parallel",
|
|
556
|
+
context.pathname,
|
|
557
|
+
routeKey,
|
|
558
|
+
params,
|
|
559
|
+
);
|
|
560
|
+
component = tracked as ReactNode;
|
|
561
|
+
} else {
|
|
562
|
+
component = result as ReactNode;
|
|
563
|
+
}
|
|
564
|
+
} else {
|
|
565
|
+
component =
|
|
566
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
567
|
+
}
|
|
525
568
|
}
|
|
569
|
+
|
|
570
|
+
segments.push({
|
|
571
|
+
id: parallelId,
|
|
572
|
+
namespace: parallelEntry.id,
|
|
573
|
+
type: "parallel",
|
|
574
|
+
index: 0,
|
|
575
|
+
component,
|
|
576
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
577
|
+
transition: parallelEntry.transition,
|
|
578
|
+
params,
|
|
579
|
+
slot,
|
|
580
|
+
belongsToRoute,
|
|
581
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
582
|
+
...(parallelEntry.mountPath
|
|
583
|
+
? { mountPath: parallelEntry.mountPath }
|
|
584
|
+
: {}),
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
if (resolvedParallelEntries.has(parallelEntry.id)) {
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
592
|
+
parallelEntry,
|
|
593
|
+
context,
|
|
594
|
+
belongsToRoute,
|
|
595
|
+
clientSegmentIds,
|
|
596
|
+
prevParams,
|
|
597
|
+
request,
|
|
598
|
+
prevUrl,
|
|
599
|
+
nextUrl,
|
|
600
|
+
routeKey,
|
|
601
|
+
deps,
|
|
602
|
+
actionContext,
|
|
603
|
+
entry.shortCode,
|
|
604
|
+
stale,
|
|
605
|
+
);
|
|
606
|
+
segments.push(...loaderResult.segments);
|
|
607
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
608
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
526
609
|
}
|
|
527
610
|
|
|
528
611
|
return { segments, matchedIds };
|
|
@@ -608,6 +691,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
691
|
context,
|
|
609
692
|
actionContext,
|
|
610
693
|
stale,
|
|
694
|
+
traceSource:
|
|
695
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
696
|
});
|
|
612
697
|
emitRevalidationDecision(
|
|
613
698
|
entry.shortCode,
|
|
@@ -636,13 +721,20 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
636
721
|
return staticComponent;
|
|
637
722
|
}
|
|
638
723
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
724
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
725
|
+
// the build handler. At build time (context.build === true), always
|
|
726
|
+
// use the build handler from routeEntry.handler.
|
|
727
|
+
const handler =
|
|
728
|
+
!context.build && routeEntry.liveHandler
|
|
729
|
+
? routeEntry.liveHandler
|
|
730
|
+
: routeEntry.handler;
|
|
639
731
|
if (!routeEntry.loading) {
|
|
640
|
-
const result = handleHandlerResult(await
|
|
732
|
+
const result = handleHandlerResult(await handler(context));
|
|
641
733
|
doneHandler();
|
|
642
734
|
return result;
|
|
643
735
|
}
|
|
644
736
|
if (!actionContext) {
|
|
645
|
-
const result = handleHandlerResult(
|
|
737
|
+
const result = handleHandlerResult(handler(context));
|
|
646
738
|
if (result instanceof Promise) {
|
|
647
739
|
result.finally(doneHandler).catch(() => {});
|
|
648
740
|
const tracked = deps.trackHandler(result, {
|
|
@@ -665,9 +757,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
665
757
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
666
758
|
entryId: entry.id,
|
|
667
759
|
});
|
|
668
|
-
const actionResult = handleHandlerResult(
|
|
669
|
-
await routeEntry.handler(context),
|
|
670
|
-
);
|
|
760
|
+
const actionResult = handleHandlerResult(await handler(context));
|
|
671
761
|
doneHandler();
|
|
672
762
|
return {
|
|
673
763
|
content: Promise.resolve(actionResult),
|
|
@@ -676,10 +766,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
766
|
() => null,
|
|
677
767
|
);
|
|
678
768
|
|
|
769
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
770
|
+
// component === null checks work consistently for both void and explicit null.
|
|
679
771
|
const resolvedComponent =
|
|
680
772
|
component && typeof component === "object" && "content" in component
|
|
681
|
-
? (component as { content: ReactNode }).content
|
|
682
|
-
: component;
|
|
773
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
774
|
+
: (component ?? null);
|
|
683
775
|
|
|
684
776
|
const segment: ResolvedSegment = {
|
|
685
777
|
id: entry.shortCode,
|
|
@@ -781,6 +873,7 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
781
873
|
deps,
|
|
782
874
|
actionContext,
|
|
783
875
|
stale,
|
|
876
|
+
entry,
|
|
784
877
|
);
|
|
785
878
|
segments.push(...orphanResult.segments);
|
|
786
879
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -892,6 +985,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
892
985
|
deps: SegmentResolutionDeps<TEnv>,
|
|
893
986
|
actionContext?: ActionContext,
|
|
894
987
|
stale?: boolean,
|
|
988
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
989
|
+
parentRouteEntry?: EntryData,
|
|
895
990
|
): Promise<SegmentRevalidationResult> {
|
|
896
991
|
invariant(
|
|
897
992
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -919,6 +1014,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
919
1014
|
segments.push(...loaderResult.segments);
|
|
920
1015
|
matchedIds.push(...loaderResult.matchedIds);
|
|
921
1016
|
|
|
1017
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
1018
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
1019
|
+
if (
|
|
1020
|
+
parentRouteEntry &&
|
|
1021
|
+
parentRouteEntry.loader &&
|
|
1022
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1023
|
+
Object.keys(orphan.parallel).length > 0
|
|
1024
|
+
) {
|
|
1025
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1026
|
+
parentRouteEntry,
|
|
1027
|
+
context,
|
|
1028
|
+
belongsToRoute,
|
|
1029
|
+
clientSegmentIds,
|
|
1030
|
+
prevParams,
|
|
1031
|
+
request,
|
|
1032
|
+
prevUrl,
|
|
1033
|
+
nextUrl,
|
|
1034
|
+
routeKey,
|
|
1035
|
+
deps,
|
|
1036
|
+
actionContext,
|
|
1037
|
+
orphan.shortCode,
|
|
1038
|
+
stale,
|
|
1039
|
+
);
|
|
1040
|
+
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
1041
|
+
for (const s of inheritedResult.segments) {
|
|
1042
|
+
s._inherited = true;
|
|
1043
|
+
}
|
|
1044
|
+
segments.push(...inheritedResult.segments);
|
|
1045
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
922
1048
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
1049
|
// so ctx.set() values are visible to parallel children.
|
|
924
1050
|
matchedIds.push(orphan.shortCode);
|
|
@@ -995,143 +1121,73 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
995
1121
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
1122
|
});
|
|
997
1123
|
|
|
998
|
-
|
|
1124
|
+
const resolvedParallelEntries = new Set<string>();
|
|
1125
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
1126
|
+
orphan.parallel,
|
|
1127
|
+
)) {
|
|
999
1128
|
invariant(
|
|
1000
1129
|
parallelEntry.type === "parallel",
|
|
1001
1130
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
1002
1131
|
);
|
|
1003
1132
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1133
|
+
if (!resolvedParallelEntries.has(parallelEntry.id)) {
|
|
1134
|
+
// shortCodeOverride must match the parent layout, not the parallel entry.
|
|
1135
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
1136
|
+
parallelEntry,
|
|
1137
|
+
context,
|
|
1138
|
+
belongsToRoute,
|
|
1139
|
+
clientSegmentIds,
|
|
1140
|
+
prevParams,
|
|
1141
|
+
request,
|
|
1142
|
+
prevUrl,
|
|
1143
|
+
nextUrl,
|
|
1144
|
+
routeKey,
|
|
1145
|
+
deps,
|
|
1146
|
+
actionContext,
|
|
1147
|
+
orphan.shortCode,
|
|
1148
|
+
stale,
|
|
1149
|
+
);
|
|
1150
|
+
segments.push(...loaderResult.segments);
|
|
1151
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
1152
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
1153
|
+
}
|
|
1021
1154
|
|
|
1022
1155
|
const slots = parallelEntry.handler as Record<
|
|
1023
1156
|
`@${string}`,
|
|
1024
1157
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
1025
1158
|
| ReactNode
|
|
1026
1159
|
>;
|
|
1160
|
+
// Handler may be undefined in production after static handler eviction.
|
|
1161
|
+
const handler = slots[slot];
|
|
1027
1162
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
matchedIds.push(parallelId);
|
|
1163
|
+
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1164
|
+
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1165
|
+
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1166
|
+
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1167
|
+
matchedIds.push(parallelId);
|
|
1034
1168
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
}
|
|
1048
|
-
return true;
|
|
1049
|
-
}
|
|
1050
|
-
|
|
1051
|
-
const dummySegment: ResolvedSegment = {
|
|
1052
|
-
id: parallelId,
|
|
1053
|
-
namespace: parallelEntry.id,
|
|
1054
|
-
type: "parallel",
|
|
1055
|
-
index: 0,
|
|
1056
|
-
component: null as any,
|
|
1057
|
-
params,
|
|
1058
|
-
slot,
|
|
1059
|
-
belongsToRoute,
|
|
1060
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1061
|
-
...(parallelEntry.mountPath
|
|
1062
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1063
|
-
: {}),
|
|
1064
|
-
};
|
|
1065
|
-
|
|
1066
|
-
return await evaluateRevalidation({
|
|
1067
|
-
segment: dummySegment,
|
|
1068
|
-
prevParams,
|
|
1069
|
-
getPrevSegment: null,
|
|
1070
|
-
request,
|
|
1071
|
-
prevUrl,
|
|
1072
|
-
nextUrl,
|
|
1073
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1074
|
-
name: `revalidate${i}`,
|
|
1075
|
-
fn,
|
|
1076
|
-
})),
|
|
1077
|
-
routeKey,
|
|
1078
|
-
context,
|
|
1079
|
-
actionContext,
|
|
1080
|
-
stale,
|
|
1081
|
-
traceSource: "parallel",
|
|
1082
|
-
});
|
|
1083
|
-
})();
|
|
1084
|
-
emitRevalidationDecision(
|
|
1085
|
-
parallelId,
|
|
1086
|
-
context.pathname,
|
|
1087
|
-
routeKey,
|
|
1088
|
-
shouldResolve,
|
|
1089
|
-
);
|
|
1090
|
-
|
|
1091
|
-
let component: ReactNode | undefined;
|
|
1092
|
-
if (shouldResolve) {
|
|
1093
|
-
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1094
|
-
}
|
|
1095
|
-
if (component === undefined) {
|
|
1096
|
-
const hasLoadingFallback =
|
|
1097
|
-
parallelEntry.loading !== undefined &&
|
|
1098
|
-
parallelEntry.loading !== false;
|
|
1099
|
-
if (!shouldResolve) {
|
|
1100
|
-
component = null;
|
|
1101
|
-
} else if (hasLoadingFallback) {
|
|
1102
|
-
const result =
|
|
1103
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
1104
|
-
if (result instanceof Promise) {
|
|
1105
|
-
const tracked = deps.trackHandler(result, {
|
|
1106
|
-
segmentId: parallelId,
|
|
1107
|
-
segmentType: "parallel",
|
|
1108
|
-
});
|
|
1109
|
-
observeStreamedHandler(
|
|
1110
|
-
tracked,
|
|
1111
|
-
parallelId,
|
|
1112
|
-
"parallel",
|
|
1113
|
-
context.pathname,
|
|
1114
|
-
routeKey,
|
|
1115
|
-
params,
|
|
1116
|
-
);
|
|
1117
|
-
component = tracked as ReactNode;
|
|
1118
|
-
} else {
|
|
1119
|
-
component = result as ReactNode;
|
|
1120
|
-
}
|
|
1121
|
-
} else {
|
|
1122
|
-
component =
|
|
1123
|
-
typeof handler === "function" ? await handler(context) : handler;
|
|
1169
|
+
const shouldResolve = await (async () => {
|
|
1170
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
1171
|
+
if (isTraceActive()) {
|
|
1172
|
+
pushRevalidationTraceEntry({
|
|
1173
|
+
segmentId: parallelId,
|
|
1174
|
+
segmentType: "parallel",
|
|
1175
|
+
belongsToRoute,
|
|
1176
|
+
source: "parallel",
|
|
1177
|
+
defaultShouldRevalidate: true,
|
|
1178
|
+
finalShouldRevalidate: true,
|
|
1179
|
+
reason: "new-segment",
|
|
1180
|
+
});
|
|
1124
1181
|
}
|
|
1182
|
+
return true;
|
|
1125
1183
|
}
|
|
1126
1184
|
|
|
1127
|
-
|
|
1185
|
+
const dummySegment: ResolvedSegment = {
|
|
1128
1186
|
id: parallelId,
|
|
1129
1187
|
namespace: parallelEntry.id,
|
|
1130
1188
|
type: "parallel",
|
|
1131
1189
|
index: 0,
|
|
1132
|
-
component,
|
|
1133
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1134
|
-
transition: parallelEntry.transition,
|
|
1190
|
+
component: null as any,
|
|
1135
1191
|
params,
|
|
1136
1192
|
slot,
|
|
1137
1193
|
belongsToRoute,
|
|
@@ -1139,8 +1195,87 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1139
1195
|
...(parallelEntry.mountPath
|
|
1140
1196
|
? { mountPath: parallelEntry.mountPath }
|
|
1141
1197
|
: {}),
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
return await evaluateRevalidation({
|
|
1201
|
+
segment: dummySegment,
|
|
1202
|
+
prevParams,
|
|
1203
|
+
getPrevSegment: null,
|
|
1204
|
+
request,
|
|
1205
|
+
prevUrl,
|
|
1206
|
+
nextUrl,
|
|
1207
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1208
|
+
name: `revalidate${i}`,
|
|
1209
|
+
fn,
|
|
1210
|
+
})),
|
|
1211
|
+
routeKey,
|
|
1212
|
+
context,
|
|
1213
|
+
actionContext,
|
|
1214
|
+
stale,
|
|
1215
|
+
traceSource: "parallel",
|
|
1142
1216
|
});
|
|
1217
|
+
})();
|
|
1218
|
+
emitRevalidationDecision(
|
|
1219
|
+
parallelId,
|
|
1220
|
+
context.pathname,
|
|
1221
|
+
routeKey,
|
|
1222
|
+
shouldResolve,
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
let component: ReactNode | undefined;
|
|
1226
|
+
if (shouldResolve) {
|
|
1227
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1228
|
+
}
|
|
1229
|
+
if (component === undefined) {
|
|
1230
|
+
const hasLoadingFallback =
|
|
1231
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
1232
|
+
if (!shouldResolve) {
|
|
1233
|
+
component = null;
|
|
1234
|
+
} else if (handler === undefined) {
|
|
1235
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
1236
|
+
component = null;
|
|
1237
|
+
} else if (hasLoadingFallback) {
|
|
1238
|
+
const result =
|
|
1239
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
1240
|
+
if (result instanceof Promise) {
|
|
1241
|
+
const tracked = deps.trackHandler(result, {
|
|
1242
|
+
segmentId: parallelId,
|
|
1243
|
+
segmentType: "parallel",
|
|
1244
|
+
});
|
|
1245
|
+
observeStreamedHandler(
|
|
1246
|
+
tracked,
|
|
1247
|
+
parallelId,
|
|
1248
|
+
"parallel",
|
|
1249
|
+
context.pathname,
|
|
1250
|
+
routeKey,
|
|
1251
|
+
params,
|
|
1252
|
+
);
|
|
1253
|
+
component = tracked as ReactNode;
|
|
1254
|
+
} else {
|
|
1255
|
+
component = result as ReactNode;
|
|
1256
|
+
}
|
|
1257
|
+
} else {
|
|
1258
|
+
component =
|
|
1259
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
1260
|
+
}
|
|
1143
1261
|
}
|
|
1262
|
+
|
|
1263
|
+
segments.push({
|
|
1264
|
+
id: parallelId,
|
|
1265
|
+
namespace: parallelEntry.id,
|
|
1266
|
+
type: "parallel",
|
|
1267
|
+
index: 0,
|
|
1268
|
+
component,
|
|
1269
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1270
|
+
transition: parallelEntry.transition,
|
|
1271
|
+
params,
|
|
1272
|
+
slot,
|
|
1273
|
+
belongsToRoute,
|
|
1274
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1275
|
+
...(parallelEntry.mountPath
|
|
1276
|
+
? { mountPath: parallelEntry.mountPath }
|
|
1277
|
+
: {}),
|
|
1278
|
+
});
|
|
1144
1279
|
}
|
|
1145
1280
|
|
|
1146
1281
|
return { segments, matchedIds };
|
|
@@ -1165,6 +1300,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1165
1300
|
localRouteName: string,
|
|
1166
1301
|
pathname: string,
|
|
1167
1302
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1303
|
+
stale?: boolean,
|
|
1168
1304
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1305
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1306
|
const matchedIds: string[] = [];
|
|
@@ -1191,6 +1327,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1191
1327
|
}
|
|
1192
1328
|
|
|
1193
1329
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1330
|
+
if (entry.type === "cache") {
|
|
1331
|
+
const store = RSCRouterContext.getStore();
|
|
1332
|
+
if (store) store.insideCacheScope = true;
|
|
1333
|
+
}
|
|
1194
1334
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
1335
|
const resolved = await resolveWithErrorBoundary(
|
|
1196
1336
|
nonParallelEntry,
|
|
@@ -1209,13 +1349,11 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1209
1349
|
loaderPromises,
|
|
1210
1350
|
deps,
|
|
1211
1351
|
actionContext,
|
|
1212
|
-
|
|
1352
|
+
stale,
|
|
1213
1353
|
),
|
|
1214
1354
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1355
|
deps,
|
|
1216
|
-
telemetry
|
|
1217
|
-
? { request, url: context.url, routeKey, isPartial: true, telemetry }
|
|
1218
|
-
: undefined,
|
|
1356
|
+
{ request, url: context.url, routeKey, isPartial: true, telemetry },
|
|
1219
1357
|
pathname,
|
|
1220
1358
|
);
|
|
1221
1359
|
doneEntry();
|