@rangojs/router 0.0.0-experimental.1b930379 → 0.0.0-experimental.1fa245e2
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 +76 -18
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +558 -319
- package/package.json +16 -15
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +53 -43
- package/skills/middleware/SKILL.md +2 -0
- package/skills/parallel/SKILL.md +126 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/route/SKILL.md +31 -0
- package/skills/router-setup/SKILL.md +87 -2
- package/skills/typesafety/SKILL.md +10 -0
- package/src/__internal.ts +1 -1
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/navigation-bridge.ts +19 -13
- package/src/browser/navigation-client.ts +115 -58
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +80 -15
- package/src/browser/prefetch/cache.ts +57 -5
- package/src/browser/prefetch/fetch.ts +38 -23
- package/src/browser/prefetch/queue.ts +92 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +53 -9
- package/src/browser/react/NavigationProvider.tsx +40 -4
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +134 -59
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +6 -1
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +36 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- 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 +453 -11
- 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 +2 -56
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/index.rsc.ts +3 -1
- package/src/index.ts +8 -0
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +22 -1
- package/src/route-definition/dsl-helpers.ts +73 -25
- package/src/route-definition/helpers-types.ts +10 -6
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +11 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +79 -23
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +122 -10
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +9 -3
- package/src/router/match-api.ts +124 -189
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +88 -16
- 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 +22 -6
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +6 -8
- package/src/router/middleware.ts +4 -6
- package/src/router/navigation-snapshot.ts +182 -0
- 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 +183 -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 +412 -297
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/types.ts +1 -0
- package/src/router.ts +59 -6
- package/src/rsc/handler.ts +460 -368
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/rsc-rendering.ts +5 -0
- package/src/rsc/server-action.ts +2 -0
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +8 -1
- package/src/segment-system.tsx +140 -4
- package/src/server/context.ts +140 -14
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +144 -18
- 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 -33
- package/src/types/loader-types.ts +36 -9
- package/src/types/route-entry.ts +8 -1
- package/src/types/segments.ts +2 -0
- package/src/urls/path-helper-types.ts +9 -2
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +16 -6
- package/src/use-loader.tsx +73 -4
- 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 +14 -1
- 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/expose-action-id.ts +1 -3
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +163 -211
- package/src/vite/router-discovery.ts +153 -42
- package/src/vite/utils/banner.ts +3 -3
- package/src/vite/utils/prerender-utils.ts +18 -0
- 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,75 @@ 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
|
+
if (
|
|
324
|
+
entry.type === "route" &&
|
|
325
|
+
entry.loader &&
|
|
326
|
+
entry.loader.length > 0 &&
|
|
327
|
+
Object.keys(layoutEntry.parallel).length > 0
|
|
328
|
+
) {
|
|
329
|
+
await collectEntryLoaders(
|
|
330
|
+
entry,
|
|
331
|
+
childBelongsToRoute,
|
|
332
|
+
layoutEntry.shortCode,
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
261
337
|
|
|
262
338
|
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);
|
|
339
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
281
340
|
}
|
|
282
341
|
|
|
283
342
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
@@ -301,22 +360,20 @@ export function buildEntryRevalidateMap(
|
|
|
301
360
|
map.set(entry.shortCode, { entry, revalidate: entry.revalidate });
|
|
302
361
|
|
|
303
362
|
if (entry.type !== "parallel") {
|
|
304
|
-
for (const parallelEntry of
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
363
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
364
|
+
entry.parallel,
|
|
365
|
+
)) {
|
|
366
|
+
const parallelParentShortCode = parentShortCode ?? entry.shortCode;
|
|
367
|
+
const parallelId = `${parallelParentShortCode}.${slot}`;
|
|
368
|
+
map.set(parallelId, {
|
|
369
|
+
entry: parallelEntry,
|
|
370
|
+
revalidate: parallelEntry.revalidate,
|
|
371
|
+
});
|
|
315
372
|
}
|
|
316
373
|
}
|
|
317
374
|
|
|
318
375
|
for (const layoutEntry of entry.layout) {
|
|
319
|
-
processEntry(layoutEntry);
|
|
376
|
+
processEntry(layoutEntry, entry.shortCode);
|
|
320
377
|
}
|
|
321
378
|
}
|
|
322
379
|
|
|
@@ -348,7 +405,10 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
348
405
|
const segments: ResolvedSegment[] = [];
|
|
349
406
|
const matchedIds: string[] = [];
|
|
350
407
|
|
|
351
|
-
|
|
408
|
+
const resolvedParallelEntries = new Set<string>();
|
|
409
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
410
|
+
entry.parallel,
|
|
411
|
+
)) {
|
|
352
412
|
invariant(
|
|
353
413
|
parallelEntry.type === "parallel",
|
|
354
414
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
@@ -359,141 +419,61 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
359
419
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
360
420
|
| ReactNode
|
|
361
421
|
>;
|
|
422
|
+
// In production, static handler bodies are evicted and the slot value
|
|
423
|
+
// may be undefined. The static store holds the pre-rendered component.
|
|
424
|
+
// We defer the handler check until after tryStaticSlot.
|
|
425
|
+
const handler = slots[slot];
|
|
426
|
+
|
|
427
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
428
|
+
|
|
429
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
430
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
431
|
+
if (
|
|
432
|
+
isFullRefetch ||
|
|
433
|
+
clientSegmentIds.has(parallelId) ||
|
|
434
|
+
belongsToRoute ||
|
|
435
|
+
isNewParent
|
|
436
|
+
) {
|
|
437
|
+
matchedIds.push(parallelId);
|
|
438
|
+
}
|
|
362
439
|
|
|
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;
|
|
440
|
+
const shouldResolve = await (async () => {
|
|
441
|
+
if (isFullRefetch) {
|
|
442
|
+
if (isTraceActive()) {
|
|
443
|
+
pushRevalidationTraceEntry({
|
|
444
|
+
segmentId: parallelId,
|
|
445
|
+
segmentType: "parallel",
|
|
446
|
+
belongsToRoute,
|
|
447
|
+
source: "parallel",
|
|
448
|
+
defaultShouldRevalidate: true,
|
|
449
|
+
finalShouldRevalidate: true,
|
|
450
|
+
reason: "full-refetch",
|
|
451
|
+
});
|
|
411
452
|
}
|
|
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);
|
|
453
|
+
return true;
|
|
456
454
|
}
|
|
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;
|
|
455
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
456
|
+
const result = belongsToRoute || isNewParent;
|
|
457
|
+
if (isTraceActive()) {
|
|
458
|
+
pushRevalidationTraceEntry({
|
|
459
|
+
segmentId: parallelId,
|
|
460
|
+
segmentType: "parallel",
|
|
461
|
+
belongsToRoute,
|
|
462
|
+
source: "parallel",
|
|
463
|
+
defaultShouldRevalidate: result,
|
|
464
|
+
finalShouldRevalidate: result,
|
|
465
|
+
reason: result ? "new-segment" : "skip-parent-chain",
|
|
466
|
+
});
|
|
486
467
|
}
|
|
468
|
+
return result;
|
|
487
469
|
}
|
|
488
470
|
|
|
489
|
-
|
|
471
|
+
const dummySegment: ResolvedSegment = {
|
|
490
472
|
id: parallelId,
|
|
491
473
|
namespace: parallelEntry.id,
|
|
492
474
|
type: "parallel",
|
|
493
475
|
index: 0,
|
|
494
|
-
component,
|
|
495
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
496
|
-
transition: parallelEntry.transition,
|
|
476
|
+
component: null as any,
|
|
497
477
|
params,
|
|
498
478
|
slot,
|
|
499
479
|
belongsToRoute,
|
|
@@ -501,28 +481,111 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
501
481
|
...(parallelEntry.mountPath
|
|
502
482
|
? { mountPath: parallelEntry.mountPath }
|
|
503
483
|
: {}),
|
|
504
|
-
}
|
|
505
|
-
}
|
|
484
|
+
};
|
|
506
485
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
parallelEntry,
|
|
510
|
-
context,
|
|
511
|
-
belongsToRoute,
|
|
512
|
-
clientSegmentIds,
|
|
486
|
+
return await evaluateRevalidation({
|
|
487
|
+
segment: dummySegment,
|
|
513
488
|
prevParams,
|
|
489
|
+
getPrevSegment: null,
|
|
514
490
|
request,
|
|
515
491
|
prevUrl,
|
|
516
492
|
nextUrl,
|
|
493
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
494
|
+
name: `revalidate${i}`,
|
|
495
|
+
fn,
|
|
496
|
+
})),
|
|
517
497
|
routeKey,
|
|
518
|
-
|
|
498
|
+
context,
|
|
519
499
|
actionContext,
|
|
520
|
-
entry.shortCode,
|
|
521
500
|
stale,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
501
|
+
traceSource: "parallel",
|
|
502
|
+
});
|
|
503
|
+
})();
|
|
504
|
+
emitRevalidationDecision(
|
|
505
|
+
parallelId,
|
|
506
|
+
context.pathname,
|
|
507
|
+
routeKey,
|
|
508
|
+
shouldResolve,
|
|
509
|
+
);
|
|
510
|
+
|
|
511
|
+
let component: ReactNode | undefined;
|
|
512
|
+
if (shouldResolve) {
|
|
513
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
514
|
+
}
|
|
515
|
+
if (component === undefined) {
|
|
516
|
+
const hasLoadingFallback =
|
|
517
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
518
|
+
if (!shouldResolve) {
|
|
519
|
+
component = null;
|
|
520
|
+
} else if (handler === undefined) {
|
|
521
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
522
|
+
// Nothing to render — use null so the client keeps its cached version.
|
|
523
|
+
component = null;
|
|
524
|
+
} else if (hasLoadingFallback) {
|
|
525
|
+
const result =
|
|
526
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
527
|
+
if (result instanceof Promise) {
|
|
528
|
+
const tracked = deps.trackHandler(result, {
|
|
529
|
+
segmentId: parallelId,
|
|
530
|
+
segmentType: "parallel",
|
|
531
|
+
});
|
|
532
|
+
observeStreamedHandler(
|
|
533
|
+
tracked,
|
|
534
|
+
parallelId,
|
|
535
|
+
"parallel",
|
|
536
|
+
context.pathname,
|
|
537
|
+
routeKey,
|
|
538
|
+
params,
|
|
539
|
+
);
|
|
540
|
+
component = tracked as ReactNode;
|
|
541
|
+
} else {
|
|
542
|
+
component = result as ReactNode;
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
component =
|
|
546
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
547
|
+
}
|
|
525
548
|
}
|
|
549
|
+
|
|
550
|
+
segments.push({
|
|
551
|
+
id: parallelId,
|
|
552
|
+
namespace: parallelEntry.id,
|
|
553
|
+
type: "parallel",
|
|
554
|
+
index: 0,
|
|
555
|
+
component,
|
|
556
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
557
|
+
transition: parallelEntry.transition,
|
|
558
|
+
params,
|
|
559
|
+
slot,
|
|
560
|
+
belongsToRoute,
|
|
561
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
562
|
+
...(parallelEntry.mountPath
|
|
563
|
+
? { mountPath: parallelEntry.mountPath }
|
|
564
|
+
: {}),
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (resolvedParallelEntries.has(parallelEntry.id)) {
|
|
568
|
+
continue;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
572
|
+
parallelEntry,
|
|
573
|
+
context,
|
|
574
|
+
belongsToRoute,
|
|
575
|
+
clientSegmentIds,
|
|
576
|
+
prevParams,
|
|
577
|
+
request,
|
|
578
|
+
prevUrl,
|
|
579
|
+
nextUrl,
|
|
580
|
+
routeKey,
|
|
581
|
+
deps,
|
|
582
|
+
actionContext,
|
|
583
|
+
entry.shortCode,
|
|
584
|
+
stale,
|
|
585
|
+
);
|
|
586
|
+
segments.push(...loaderResult.segments);
|
|
587
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
588
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
526
589
|
}
|
|
527
590
|
|
|
528
591
|
return { segments, matchedIds };
|
|
@@ -608,6 +671,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
671
|
context,
|
|
609
672
|
actionContext,
|
|
610
673
|
stale,
|
|
674
|
+
traceSource:
|
|
675
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
676
|
});
|
|
612
677
|
emitRevalidationDecision(
|
|
613
678
|
entry.shortCode,
|
|
@@ -636,13 +701,20 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
636
701
|
return staticComponent;
|
|
637
702
|
}
|
|
638
703
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
704
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
705
|
+
// the build handler. At build time (context.build === true), always
|
|
706
|
+
// use the build handler from routeEntry.handler.
|
|
707
|
+
const handler =
|
|
708
|
+
!context.build && routeEntry.liveHandler
|
|
709
|
+
? routeEntry.liveHandler
|
|
710
|
+
: routeEntry.handler;
|
|
639
711
|
if (!routeEntry.loading) {
|
|
640
|
-
const result = handleHandlerResult(await
|
|
712
|
+
const result = handleHandlerResult(await handler(context));
|
|
641
713
|
doneHandler();
|
|
642
714
|
return result;
|
|
643
715
|
}
|
|
644
716
|
if (!actionContext) {
|
|
645
|
-
const result = handleHandlerResult(
|
|
717
|
+
const result = handleHandlerResult(handler(context));
|
|
646
718
|
if (result instanceof Promise) {
|
|
647
719
|
result.finally(doneHandler).catch(() => {});
|
|
648
720
|
const tracked = deps.trackHandler(result, {
|
|
@@ -665,9 +737,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
665
737
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
666
738
|
entryId: entry.id,
|
|
667
739
|
});
|
|
668
|
-
const actionResult = handleHandlerResult(
|
|
669
|
-
await routeEntry.handler(context),
|
|
670
|
-
);
|
|
740
|
+
const actionResult = handleHandlerResult(await handler(context));
|
|
671
741
|
doneHandler();
|
|
672
742
|
return {
|
|
673
743
|
content: Promise.resolve(actionResult),
|
|
@@ -676,10 +746,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
746
|
() => null,
|
|
677
747
|
);
|
|
678
748
|
|
|
749
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
750
|
+
// component === null checks work consistently for both void and explicit null.
|
|
679
751
|
const resolvedComponent =
|
|
680
752
|
component && typeof component === "object" && "content" in component
|
|
681
|
-
? (component as { content: ReactNode }).content
|
|
682
|
-
: component;
|
|
753
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
754
|
+
: (component ?? null);
|
|
683
755
|
|
|
684
756
|
const segment: ResolvedSegment = {
|
|
685
757
|
id: entry.shortCode,
|
|
@@ -781,6 +853,7 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
781
853
|
deps,
|
|
782
854
|
actionContext,
|
|
783
855
|
stale,
|
|
856
|
+
entry,
|
|
784
857
|
);
|
|
785
858
|
segments.push(...orphanResult.segments);
|
|
786
859
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -892,6 +965,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
892
965
|
deps: SegmentResolutionDeps<TEnv>,
|
|
893
966
|
actionContext?: ActionContext,
|
|
894
967
|
stale?: boolean,
|
|
968
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
969
|
+
parentRouteEntry?: EntryData,
|
|
895
970
|
): Promise<SegmentRevalidationResult> {
|
|
896
971
|
invariant(
|
|
897
972
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -919,6 +994,33 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
919
994
|
segments.push(...loaderResult.segments);
|
|
920
995
|
matchedIds.push(...loaderResult.matchedIds);
|
|
921
996
|
|
|
997
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
998
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
999
|
+
if (
|
|
1000
|
+
parentRouteEntry &&
|
|
1001
|
+
parentRouteEntry.loader &&
|
|
1002
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1003
|
+
Object.keys(orphan.parallel).length > 0
|
|
1004
|
+
) {
|
|
1005
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1006
|
+
parentRouteEntry,
|
|
1007
|
+
context,
|
|
1008
|
+
belongsToRoute,
|
|
1009
|
+
clientSegmentIds,
|
|
1010
|
+
prevParams,
|
|
1011
|
+
request,
|
|
1012
|
+
prevUrl,
|
|
1013
|
+
nextUrl,
|
|
1014
|
+
routeKey,
|
|
1015
|
+
deps,
|
|
1016
|
+
actionContext,
|
|
1017
|
+
orphan.shortCode,
|
|
1018
|
+
stale,
|
|
1019
|
+
);
|
|
1020
|
+
segments.push(...inheritedResult.segments);
|
|
1021
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
922
1024
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
1025
|
// so ctx.set() values are visible to parallel children.
|
|
924
1026
|
matchedIds.push(orphan.shortCode);
|
|
@@ -995,143 +1097,72 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
995
1097
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
1098
|
});
|
|
997
1099
|
|
|
998
|
-
|
|
1100
|
+
const resolvedParallelEntries = new Set<string>();
|
|
1101
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
1102
|
+
orphan.parallel,
|
|
1103
|
+
)) {
|
|
999
1104
|
invariant(
|
|
1000
1105
|
parallelEntry.type === "parallel",
|
|
1001
1106
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
1002
1107
|
);
|
|
1003
1108
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1109
|
+
if (!resolvedParallelEntries.has(parallelEntry.id)) {
|
|
1110
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
1111
|
+
parallelEntry,
|
|
1112
|
+
context,
|
|
1113
|
+
belongsToRoute,
|
|
1114
|
+
clientSegmentIds,
|
|
1115
|
+
prevParams,
|
|
1116
|
+
request,
|
|
1117
|
+
prevUrl,
|
|
1118
|
+
nextUrl,
|
|
1119
|
+
routeKey,
|
|
1120
|
+
deps,
|
|
1121
|
+
actionContext,
|
|
1122
|
+
undefined,
|
|
1123
|
+
stale,
|
|
1124
|
+
);
|
|
1125
|
+
segments.push(...loaderResult.segments);
|
|
1126
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
1127
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
1128
|
+
}
|
|
1021
1129
|
|
|
1022
1130
|
const slots = parallelEntry.handler as Record<
|
|
1023
1131
|
`@${string}`,
|
|
1024
1132
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
1025
1133
|
| ReactNode
|
|
1026
1134
|
>;
|
|
1135
|
+
// Handler may be undefined in production after static handler eviction.
|
|
1136
|
+
const handler = slots[slot];
|
|
1027
1137
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
matchedIds.push(parallelId);
|
|
1034
|
-
|
|
1035
|
-
const shouldResolve = await (async () => {
|
|
1036
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
1037
|
-
if (isTraceActive()) {
|
|
1038
|
-
pushRevalidationTraceEntry({
|
|
1039
|
-
segmentId: parallelId,
|
|
1040
|
-
segmentType: "parallel",
|
|
1041
|
-
belongsToRoute,
|
|
1042
|
-
source: "parallel",
|
|
1043
|
-
defaultShouldRevalidate: true,
|
|
1044
|
-
finalShouldRevalidate: true,
|
|
1045
|
-
reason: "new-segment",
|
|
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
|
-
);
|
|
1138
|
+
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1139
|
+
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1140
|
+
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1141
|
+
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1142
|
+
matchedIds.push(parallelId);
|
|
1090
1143
|
|
|
1091
|
-
|
|
1092
|
-
if (
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
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;
|
|
1144
|
+
const shouldResolve = await (async () => {
|
|
1145
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
1146
|
+
if (isTraceActive()) {
|
|
1147
|
+
pushRevalidationTraceEntry({
|
|
1148
|
+
segmentId: parallelId,
|
|
1149
|
+
segmentType: "parallel",
|
|
1150
|
+
belongsToRoute,
|
|
1151
|
+
source: "parallel",
|
|
1152
|
+
defaultShouldRevalidate: true,
|
|
1153
|
+
finalShouldRevalidate: true,
|
|
1154
|
+
reason: "new-segment",
|
|
1155
|
+
});
|
|
1124
1156
|
}
|
|
1157
|
+
return true;
|
|
1125
1158
|
}
|
|
1126
1159
|
|
|
1127
|
-
|
|
1160
|
+
const dummySegment: ResolvedSegment = {
|
|
1128
1161
|
id: parallelId,
|
|
1129
1162
|
namespace: parallelEntry.id,
|
|
1130
1163
|
type: "parallel",
|
|
1131
1164
|
index: 0,
|
|
1132
|
-
component,
|
|
1133
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1134
|
-
transition: parallelEntry.transition,
|
|
1165
|
+
component: null as any,
|
|
1135
1166
|
params,
|
|
1136
1167
|
slot,
|
|
1137
1168
|
belongsToRoute,
|
|
@@ -1139,8 +1170,87 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1139
1170
|
...(parallelEntry.mountPath
|
|
1140
1171
|
? { mountPath: parallelEntry.mountPath }
|
|
1141
1172
|
: {}),
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
return await evaluateRevalidation({
|
|
1176
|
+
segment: dummySegment,
|
|
1177
|
+
prevParams,
|
|
1178
|
+
getPrevSegment: null,
|
|
1179
|
+
request,
|
|
1180
|
+
prevUrl,
|
|
1181
|
+
nextUrl,
|
|
1182
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1183
|
+
name: `revalidate${i}`,
|
|
1184
|
+
fn,
|
|
1185
|
+
})),
|
|
1186
|
+
routeKey,
|
|
1187
|
+
context,
|
|
1188
|
+
actionContext,
|
|
1189
|
+
stale,
|
|
1190
|
+
traceSource: "parallel",
|
|
1142
1191
|
});
|
|
1192
|
+
})();
|
|
1193
|
+
emitRevalidationDecision(
|
|
1194
|
+
parallelId,
|
|
1195
|
+
context.pathname,
|
|
1196
|
+
routeKey,
|
|
1197
|
+
shouldResolve,
|
|
1198
|
+
);
|
|
1199
|
+
|
|
1200
|
+
let component: ReactNode | undefined;
|
|
1201
|
+
if (shouldResolve) {
|
|
1202
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1203
|
+
}
|
|
1204
|
+
if (component === undefined) {
|
|
1205
|
+
const hasLoadingFallback =
|
|
1206
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
1207
|
+
if (!shouldResolve) {
|
|
1208
|
+
component = null;
|
|
1209
|
+
} else if (handler === undefined) {
|
|
1210
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
1211
|
+
component = null;
|
|
1212
|
+
} else if (hasLoadingFallback) {
|
|
1213
|
+
const result =
|
|
1214
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
1215
|
+
if (result instanceof Promise) {
|
|
1216
|
+
const tracked = deps.trackHandler(result, {
|
|
1217
|
+
segmentId: parallelId,
|
|
1218
|
+
segmentType: "parallel",
|
|
1219
|
+
});
|
|
1220
|
+
observeStreamedHandler(
|
|
1221
|
+
tracked,
|
|
1222
|
+
parallelId,
|
|
1223
|
+
"parallel",
|
|
1224
|
+
context.pathname,
|
|
1225
|
+
routeKey,
|
|
1226
|
+
params,
|
|
1227
|
+
);
|
|
1228
|
+
component = tracked as ReactNode;
|
|
1229
|
+
} else {
|
|
1230
|
+
component = result as ReactNode;
|
|
1231
|
+
}
|
|
1232
|
+
} else {
|
|
1233
|
+
component =
|
|
1234
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
1235
|
+
}
|
|
1143
1236
|
}
|
|
1237
|
+
|
|
1238
|
+
segments.push({
|
|
1239
|
+
id: parallelId,
|
|
1240
|
+
namespace: parallelEntry.id,
|
|
1241
|
+
type: "parallel",
|
|
1242
|
+
index: 0,
|
|
1243
|
+
component,
|
|
1244
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1245
|
+
transition: parallelEntry.transition,
|
|
1246
|
+
params,
|
|
1247
|
+
slot,
|
|
1248
|
+
belongsToRoute,
|
|
1249
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1250
|
+
...(parallelEntry.mountPath
|
|
1251
|
+
? { mountPath: parallelEntry.mountPath }
|
|
1252
|
+
: {}),
|
|
1253
|
+
});
|
|
1144
1254
|
}
|
|
1145
1255
|
|
|
1146
1256
|
return { segments, matchedIds };
|
|
@@ -1165,6 +1275,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1165
1275
|
localRouteName: string,
|
|
1166
1276
|
pathname: string,
|
|
1167
1277
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1278
|
+
stale?: boolean,
|
|
1168
1279
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1280
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1281
|
const matchedIds: string[] = [];
|
|
@@ -1191,6 +1302,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1191
1302
|
}
|
|
1192
1303
|
|
|
1193
1304
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1305
|
+
if (entry.type === "cache") {
|
|
1306
|
+
const store = RSCRouterContext.getStore();
|
|
1307
|
+
if (store) store.insideCacheScope = true;
|
|
1308
|
+
}
|
|
1194
1309
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
1310
|
const resolved = await resolveWithErrorBoundary(
|
|
1196
1311
|
nonParallelEntry,
|
|
@@ -1209,7 +1324,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1209
1324
|
loaderPromises,
|
|
1210
1325
|
deps,
|
|
1211
1326
|
actionContext,
|
|
1212
|
-
|
|
1327
|
+
stale,
|
|
1213
1328
|
),
|
|
1214
1329
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1330
|
deps,
|