@rangojs/router 0.0.0-experimental.f788c642 → 0.0.0-experimental.fb6e2f40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +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 +17 -10
- 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 +76 -13
- 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 +193 -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 +431 -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 +156 -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,94 @@ 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
|
+
allLoaderSegments.push(seg);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
allMatchedIds.push(...inherited.matchedIds);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
261
356
|
|
|
262
357
|
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);
|
|
358
|
+
await collectEntryLoaders(entry, entry.type === "route");
|
|
281
359
|
}
|
|
282
360
|
|
|
283
361
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
@@ -301,22 +379,20 @@ export function buildEntryRevalidateMap(
|
|
|
301
379
|
map.set(entry.shortCode, { entry, revalidate: entry.revalidate });
|
|
302
380
|
|
|
303
381
|
if (entry.type !== "parallel") {
|
|
304
|
-
for (const parallelEntry of
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
382
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
383
|
+
entry.parallel,
|
|
384
|
+
)) {
|
|
385
|
+
const parallelParentShortCode = parentShortCode ?? entry.shortCode;
|
|
386
|
+
const parallelId = `${parallelParentShortCode}.${slot}`;
|
|
387
|
+
map.set(parallelId, {
|
|
388
|
+
entry: parallelEntry,
|
|
389
|
+
revalidate: parallelEntry.revalidate,
|
|
390
|
+
});
|
|
315
391
|
}
|
|
316
392
|
}
|
|
317
393
|
|
|
318
394
|
for (const layoutEntry of entry.layout) {
|
|
319
|
-
processEntry(layoutEntry);
|
|
395
|
+
processEntry(layoutEntry, entry.shortCode);
|
|
320
396
|
}
|
|
321
397
|
}
|
|
322
398
|
|
|
@@ -348,7 +424,10 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
348
424
|
const segments: ResolvedSegment[] = [];
|
|
349
425
|
const matchedIds: string[] = [];
|
|
350
426
|
|
|
351
|
-
|
|
427
|
+
const resolvedParallelEntries = new Set<string>();
|
|
428
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
429
|
+
entry.parallel,
|
|
430
|
+
)) {
|
|
352
431
|
invariant(
|
|
353
432
|
parallelEntry.type === "parallel",
|
|
354
433
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
@@ -359,141 +438,61 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
359
438
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
360
439
|
| ReactNode
|
|
361
440
|
>;
|
|
441
|
+
// In production, static handler bodies are evicted and the slot value
|
|
442
|
+
// may be undefined. The static store holds the pre-rendered component.
|
|
443
|
+
// We defer the handler check until after tryStaticSlot.
|
|
444
|
+
const handler = slots[slot];
|
|
445
|
+
|
|
446
|
+
const parallelId = `${entry.shortCode}.${slot}`;
|
|
447
|
+
|
|
448
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
449
|
+
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
450
|
+
if (
|
|
451
|
+
isFullRefetch ||
|
|
452
|
+
clientSegmentIds.has(parallelId) ||
|
|
453
|
+
belongsToRoute ||
|
|
454
|
+
isNewParent
|
|
455
|
+
) {
|
|
456
|
+
matchedIds.push(parallelId);
|
|
457
|
+
}
|
|
362
458
|
|
|
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;
|
|
459
|
+
const shouldResolve = await (async () => {
|
|
460
|
+
if (isFullRefetch) {
|
|
461
|
+
if (isTraceActive()) {
|
|
462
|
+
pushRevalidationTraceEntry({
|
|
463
|
+
segmentId: parallelId,
|
|
464
|
+
segmentType: "parallel",
|
|
465
|
+
belongsToRoute,
|
|
466
|
+
source: "parallel",
|
|
467
|
+
defaultShouldRevalidate: true,
|
|
468
|
+
finalShouldRevalidate: true,
|
|
469
|
+
reason: "full-refetch",
|
|
470
|
+
});
|
|
411
471
|
}
|
|
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);
|
|
472
|
+
return true;
|
|
456
473
|
}
|
|
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;
|
|
474
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
475
|
+
const result = belongsToRoute || isNewParent;
|
|
476
|
+
if (isTraceActive()) {
|
|
477
|
+
pushRevalidationTraceEntry({
|
|
478
|
+
segmentId: parallelId,
|
|
479
|
+
segmentType: "parallel",
|
|
480
|
+
belongsToRoute,
|
|
481
|
+
source: "parallel",
|
|
482
|
+
defaultShouldRevalidate: result,
|
|
483
|
+
finalShouldRevalidate: result,
|
|
484
|
+
reason: result ? "new-segment" : "skip-parent-chain",
|
|
485
|
+
});
|
|
486
486
|
}
|
|
487
|
+
return result;
|
|
487
488
|
}
|
|
488
489
|
|
|
489
|
-
|
|
490
|
+
const dummySegment: ResolvedSegment = {
|
|
490
491
|
id: parallelId,
|
|
491
492
|
namespace: parallelEntry.id,
|
|
492
493
|
type: "parallel",
|
|
493
494
|
index: 0,
|
|
494
|
-
component,
|
|
495
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
496
|
-
transition: parallelEntry.transition,
|
|
495
|
+
component: null as any,
|
|
497
496
|
params,
|
|
498
497
|
slot,
|
|
499
498
|
belongsToRoute,
|
|
@@ -501,28 +500,111 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
501
500
|
...(parallelEntry.mountPath
|
|
502
501
|
? { mountPath: parallelEntry.mountPath }
|
|
503
502
|
: {}),
|
|
504
|
-
}
|
|
505
|
-
}
|
|
503
|
+
};
|
|
506
504
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
parallelEntry,
|
|
510
|
-
context,
|
|
511
|
-
belongsToRoute,
|
|
512
|
-
clientSegmentIds,
|
|
505
|
+
return await evaluateRevalidation({
|
|
506
|
+
segment: dummySegment,
|
|
513
507
|
prevParams,
|
|
508
|
+
getPrevSegment: null,
|
|
514
509
|
request,
|
|
515
510
|
prevUrl,
|
|
516
511
|
nextUrl,
|
|
512
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
513
|
+
name: `revalidate${i}`,
|
|
514
|
+
fn,
|
|
515
|
+
})),
|
|
517
516
|
routeKey,
|
|
518
|
-
|
|
517
|
+
context,
|
|
519
518
|
actionContext,
|
|
520
|
-
entry.shortCode,
|
|
521
519
|
stale,
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
520
|
+
traceSource: "parallel",
|
|
521
|
+
});
|
|
522
|
+
})();
|
|
523
|
+
emitRevalidationDecision(
|
|
524
|
+
parallelId,
|
|
525
|
+
context.pathname,
|
|
526
|
+
routeKey,
|
|
527
|
+
shouldResolve,
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
let component: ReactNode | undefined;
|
|
531
|
+
if (shouldResolve) {
|
|
532
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
533
|
+
}
|
|
534
|
+
if (component === undefined) {
|
|
535
|
+
const hasLoadingFallback =
|
|
536
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
537
|
+
if (!shouldResolve) {
|
|
538
|
+
component = null;
|
|
539
|
+
} else if (handler === undefined) {
|
|
540
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
541
|
+
// Nothing to render — use null so the client keeps its cached version.
|
|
542
|
+
component = null;
|
|
543
|
+
} else if (hasLoadingFallback) {
|
|
544
|
+
const result =
|
|
545
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
546
|
+
if (result instanceof Promise) {
|
|
547
|
+
const tracked = deps.trackHandler(result, {
|
|
548
|
+
segmentId: parallelId,
|
|
549
|
+
segmentType: "parallel",
|
|
550
|
+
});
|
|
551
|
+
observeStreamedHandler(
|
|
552
|
+
tracked,
|
|
553
|
+
parallelId,
|
|
554
|
+
"parallel",
|
|
555
|
+
context.pathname,
|
|
556
|
+
routeKey,
|
|
557
|
+
params,
|
|
558
|
+
);
|
|
559
|
+
component = tracked as ReactNode;
|
|
560
|
+
} else {
|
|
561
|
+
component = result as ReactNode;
|
|
562
|
+
}
|
|
563
|
+
} else {
|
|
564
|
+
component =
|
|
565
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
566
|
+
}
|
|
525
567
|
}
|
|
568
|
+
|
|
569
|
+
segments.push({
|
|
570
|
+
id: parallelId,
|
|
571
|
+
namespace: parallelEntry.id,
|
|
572
|
+
type: "parallel",
|
|
573
|
+
index: 0,
|
|
574
|
+
component,
|
|
575
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
576
|
+
transition: parallelEntry.transition,
|
|
577
|
+
params,
|
|
578
|
+
slot,
|
|
579
|
+
belongsToRoute,
|
|
580
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
581
|
+
...(parallelEntry.mountPath
|
|
582
|
+
? { mountPath: parallelEntry.mountPath }
|
|
583
|
+
: {}),
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
if (resolvedParallelEntries.has(parallelEntry.id)) {
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
591
|
+
parallelEntry,
|
|
592
|
+
context,
|
|
593
|
+
belongsToRoute,
|
|
594
|
+
clientSegmentIds,
|
|
595
|
+
prevParams,
|
|
596
|
+
request,
|
|
597
|
+
prevUrl,
|
|
598
|
+
nextUrl,
|
|
599
|
+
routeKey,
|
|
600
|
+
deps,
|
|
601
|
+
actionContext,
|
|
602
|
+
entry.shortCode,
|
|
603
|
+
stale,
|
|
604
|
+
);
|
|
605
|
+
segments.push(...loaderResult.segments);
|
|
606
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
607
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
526
608
|
}
|
|
527
609
|
|
|
528
610
|
return { segments, matchedIds };
|
|
@@ -608,6 +690,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
690
|
context,
|
|
609
691
|
actionContext,
|
|
610
692
|
stale,
|
|
693
|
+
traceSource:
|
|
694
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
695
|
});
|
|
612
696
|
emitRevalidationDecision(
|
|
613
697
|
entry.shortCode,
|
|
@@ -636,13 +720,20 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
636
720
|
return staticComponent;
|
|
637
721
|
}
|
|
638
722
|
const routeEntry = entry as Extract<EntryData, { type: "route" }>;
|
|
723
|
+
// For Passthrough routes at runtime, use the live handler instead of
|
|
724
|
+
// the build handler. At build time (context.build === true), always
|
|
725
|
+
// use the build handler from routeEntry.handler.
|
|
726
|
+
const handler =
|
|
727
|
+
!context.build && routeEntry.liveHandler
|
|
728
|
+
? routeEntry.liveHandler
|
|
729
|
+
: routeEntry.handler;
|
|
639
730
|
if (!routeEntry.loading) {
|
|
640
|
-
const result = handleHandlerResult(await
|
|
731
|
+
const result = handleHandlerResult(await handler(context));
|
|
641
732
|
doneHandler();
|
|
642
733
|
return result;
|
|
643
734
|
}
|
|
644
735
|
if (!actionContext) {
|
|
645
|
-
const result = handleHandlerResult(
|
|
736
|
+
const result = handleHandlerResult(handler(context));
|
|
646
737
|
if (result instanceof Promise) {
|
|
647
738
|
result.finally(doneHandler).catch(() => {});
|
|
648
739
|
const tracked = deps.trackHandler(result, {
|
|
@@ -665,9 +756,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
665
756
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
666
757
|
entryId: entry.id,
|
|
667
758
|
});
|
|
668
|
-
const actionResult = handleHandlerResult(
|
|
669
|
-
await routeEntry.handler(context),
|
|
670
|
-
);
|
|
759
|
+
const actionResult = handleHandlerResult(await handler(context));
|
|
671
760
|
doneHandler();
|
|
672
761
|
return {
|
|
673
762
|
content: Promise.resolve(actionResult),
|
|
@@ -676,10 +765,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
676
765
|
() => null,
|
|
677
766
|
);
|
|
678
767
|
|
|
768
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
769
|
+
// component === null checks work consistently for both void and explicit null.
|
|
679
770
|
const resolvedComponent =
|
|
680
771
|
component && typeof component === "object" && "content" in component
|
|
681
|
-
? (component as { content: ReactNode }).content
|
|
682
|
-
: component;
|
|
772
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
773
|
+
: (component ?? null);
|
|
683
774
|
|
|
684
775
|
const segment: ResolvedSegment = {
|
|
685
776
|
id: entry.shortCode,
|
|
@@ -781,6 +872,7 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
781
872
|
deps,
|
|
782
873
|
actionContext,
|
|
783
874
|
stale,
|
|
875
|
+
entry,
|
|
784
876
|
);
|
|
785
877
|
segments.push(...orphanResult.segments);
|
|
786
878
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -892,6 +984,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
892
984
|
deps: SegmentResolutionDeps<TEnv>,
|
|
893
985
|
actionContext?: ActionContext,
|
|
894
986
|
stale?: boolean,
|
|
987
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
988
|
+
parentRouteEntry?: EntryData,
|
|
895
989
|
): Promise<SegmentRevalidationResult> {
|
|
896
990
|
invariant(
|
|
897
991
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -919,6 +1013,33 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
919
1013
|
segments.push(...loaderResult.segments);
|
|
920
1014
|
matchedIds.push(...loaderResult.matchedIds);
|
|
921
1015
|
|
|
1016
|
+
// Inherit parent route's loaders so parallel slots inside this layout
|
|
1017
|
+
// can access them via useLoader(). See resolveOrphanLayout in fresh.ts.
|
|
1018
|
+
if (
|
|
1019
|
+
parentRouteEntry &&
|
|
1020
|
+
parentRouteEntry.loader &&
|
|
1021
|
+
parentRouteEntry.loader.length > 0 &&
|
|
1022
|
+
Object.keys(orphan.parallel).length > 0
|
|
1023
|
+
) {
|
|
1024
|
+
const inheritedResult = await resolveLoadersWithRevalidation(
|
|
1025
|
+
parentRouteEntry,
|
|
1026
|
+
context,
|
|
1027
|
+
belongsToRoute,
|
|
1028
|
+
clientSegmentIds,
|
|
1029
|
+
prevParams,
|
|
1030
|
+
request,
|
|
1031
|
+
prevUrl,
|
|
1032
|
+
nextUrl,
|
|
1033
|
+
routeKey,
|
|
1034
|
+
deps,
|
|
1035
|
+
actionContext,
|
|
1036
|
+
orphan.shortCode,
|
|
1037
|
+
stale,
|
|
1038
|
+
);
|
|
1039
|
+
segments.push(...inheritedResult.segments);
|
|
1040
|
+
matchedIds.push(...inheritedResult.matchedIds);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
922
1043
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
923
1044
|
// so ctx.set() values are visible to parallel children.
|
|
924
1045
|
matchedIds.push(orphan.shortCode);
|
|
@@ -995,143 +1116,72 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
995
1116
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
996
1117
|
});
|
|
997
1118
|
|
|
998
|
-
|
|
1119
|
+
const resolvedParallelEntries = new Set<string>();
|
|
1120
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
1121
|
+
orphan.parallel,
|
|
1122
|
+
)) {
|
|
999
1123
|
invariant(
|
|
1000
1124
|
parallelEntry.type === "parallel",
|
|
1001
1125
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
1002
1126
|
);
|
|
1003
1127
|
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1128
|
+
if (!resolvedParallelEntries.has(parallelEntry.id)) {
|
|
1129
|
+
const loaderResult = await resolveLoadersWithRevalidation(
|
|
1130
|
+
parallelEntry,
|
|
1131
|
+
context,
|
|
1132
|
+
belongsToRoute,
|
|
1133
|
+
clientSegmentIds,
|
|
1134
|
+
prevParams,
|
|
1135
|
+
request,
|
|
1136
|
+
prevUrl,
|
|
1137
|
+
nextUrl,
|
|
1138
|
+
routeKey,
|
|
1139
|
+
deps,
|
|
1140
|
+
actionContext,
|
|
1141
|
+
undefined,
|
|
1142
|
+
stale,
|
|
1143
|
+
);
|
|
1144
|
+
segments.push(...loaderResult.segments);
|
|
1145
|
+
matchedIds.push(...loaderResult.matchedIds);
|
|
1146
|
+
resolvedParallelEntries.add(parallelEntry.id);
|
|
1147
|
+
}
|
|
1021
1148
|
|
|
1022
1149
|
const slots = parallelEntry.handler as Record<
|
|
1023
1150
|
`@${string}`,
|
|
1024
1151
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
1025
1152
|
| ReactNode
|
|
1026
1153
|
>;
|
|
1154
|
+
// Handler may be undefined in production after static handler eviction.
|
|
1155
|
+
const handler = slots[slot];
|
|
1027
1156
|
|
|
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
|
-
);
|
|
1157
|
+
// Use orphan.shortCode (the parent layout) to match the SSR path
|
|
1158
|
+
// (resolveParallelEntry receives parentShortCode = orphan.shortCode).
|
|
1159
|
+
// Using parallelEntry.shortCode would generate IDs the client doesn't know about.
|
|
1160
|
+
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1161
|
+
matchedIds.push(parallelId);
|
|
1090
1162
|
|
|
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;
|
|
1163
|
+
const shouldResolve = await (async () => {
|
|
1164
|
+
if (!clientSegmentIds.has(parallelId)) {
|
|
1165
|
+
if (isTraceActive()) {
|
|
1166
|
+
pushRevalidationTraceEntry({
|
|
1167
|
+
segmentId: parallelId,
|
|
1168
|
+
segmentType: "parallel",
|
|
1169
|
+
belongsToRoute,
|
|
1170
|
+
source: "parallel",
|
|
1171
|
+
defaultShouldRevalidate: true,
|
|
1172
|
+
finalShouldRevalidate: true,
|
|
1173
|
+
reason: "new-segment",
|
|
1174
|
+
});
|
|
1124
1175
|
}
|
|
1176
|
+
return true;
|
|
1125
1177
|
}
|
|
1126
1178
|
|
|
1127
|
-
|
|
1179
|
+
const dummySegment: ResolvedSegment = {
|
|
1128
1180
|
id: parallelId,
|
|
1129
1181
|
namespace: parallelEntry.id,
|
|
1130
1182
|
type: "parallel",
|
|
1131
1183
|
index: 0,
|
|
1132
|
-
component,
|
|
1133
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1134
|
-
transition: parallelEntry.transition,
|
|
1184
|
+
component: null as any,
|
|
1135
1185
|
params,
|
|
1136
1186
|
slot,
|
|
1137
1187
|
belongsToRoute,
|
|
@@ -1139,8 +1189,87 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1139
1189
|
...(parallelEntry.mountPath
|
|
1140
1190
|
? { mountPath: parallelEntry.mountPath }
|
|
1141
1191
|
: {}),
|
|
1192
|
+
};
|
|
1193
|
+
|
|
1194
|
+
return await evaluateRevalidation({
|
|
1195
|
+
segment: dummySegment,
|
|
1196
|
+
prevParams,
|
|
1197
|
+
getPrevSegment: null,
|
|
1198
|
+
request,
|
|
1199
|
+
prevUrl,
|
|
1200
|
+
nextUrl,
|
|
1201
|
+
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1202
|
+
name: `revalidate${i}`,
|
|
1203
|
+
fn,
|
|
1204
|
+
})),
|
|
1205
|
+
routeKey,
|
|
1206
|
+
context,
|
|
1207
|
+
actionContext,
|
|
1208
|
+
stale,
|
|
1209
|
+
traceSource: "parallel",
|
|
1142
1210
|
});
|
|
1211
|
+
})();
|
|
1212
|
+
emitRevalidationDecision(
|
|
1213
|
+
parallelId,
|
|
1214
|
+
context.pathname,
|
|
1215
|
+
routeKey,
|
|
1216
|
+
shouldResolve,
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
let component: ReactNode | undefined;
|
|
1220
|
+
if (shouldResolve) {
|
|
1221
|
+
component = await tryStaticSlot(parallelEntry, slot, parallelId);
|
|
1222
|
+
}
|
|
1223
|
+
if (component === undefined) {
|
|
1224
|
+
const hasLoadingFallback =
|
|
1225
|
+
parallelEntry.loading !== undefined && parallelEntry.loading !== false;
|
|
1226
|
+
if (!shouldResolve) {
|
|
1227
|
+
component = null;
|
|
1228
|
+
} else if (handler === undefined) {
|
|
1229
|
+
// Handler evicted (production static slot) but static lookup missed.
|
|
1230
|
+
component = null;
|
|
1231
|
+
} else if (hasLoadingFallback) {
|
|
1232
|
+
const result =
|
|
1233
|
+
typeof handler === "function" ? handler(context) : handler;
|
|
1234
|
+
if (result instanceof Promise) {
|
|
1235
|
+
const tracked = deps.trackHandler(result, {
|
|
1236
|
+
segmentId: parallelId,
|
|
1237
|
+
segmentType: "parallel",
|
|
1238
|
+
});
|
|
1239
|
+
observeStreamedHandler(
|
|
1240
|
+
tracked,
|
|
1241
|
+
parallelId,
|
|
1242
|
+
"parallel",
|
|
1243
|
+
context.pathname,
|
|
1244
|
+
routeKey,
|
|
1245
|
+
params,
|
|
1246
|
+
);
|
|
1247
|
+
component = tracked as ReactNode;
|
|
1248
|
+
} else {
|
|
1249
|
+
component = result as ReactNode;
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
component =
|
|
1253
|
+
typeof handler === "function" ? await handler(context) : handler;
|
|
1254
|
+
}
|
|
1143
1255
|
}
|
|
1256
|
+
|
|
1257
|
+
segments.push({
|
|
1258
|
+
id: parallelId,
|
|
1259
|
+
namespace: parallelEntry.id,
|
|
1260
|
+
type: "parallel",
|
|
1261
|
+
index: 0,
|
|
1262
|
+
component,
|
|
1263
|
+
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1264
|
+
transition: parallelEntry.transition,
|
|
1265
|
+
params,
|
|
1266
|
+
slot,
|
|
1267
|
+
belongsToRoute,
|
|
1268
|
+
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1269
|
+
...(parallelEntry.mountPath
|
|
1270
|
+
? { mountPath: parallelEntry.mountPath }
|
|
1271
|
+
: {}),
|
|
1272
|
+
});
|
|
1144
1273
|
}
|
|
1145
1274
|
|
|
1146
1275
|
return { segments, matchedIds };
|
|
@@ -1165,6 +1294,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1165
1294
|
localRouteName: string,
|
|
1166
1295
|
pathname: string,
|
|
1167
1296
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1297
|
+
stale?: boolean,
|
|
1168
1298
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1299
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1300
|
const matchedIds: string[] = [];
|
|
@@ -1191,6 +1321,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1191
1321
|
}
|
|
1192
1322
|
|
|
1193
1323
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1324
|
+
if (entry.type === "cache") {
|
|
1325
|
+
const store = RSCRouterContext.getStore();
|
|
1326
|
+
if (store) store.insideCacheScope = true;
|
|
1327
|
+
}
|
|
1194
1328
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1195
1329
|
const resolved = await resolveWithErrorBoundary(
|
|
1196
1330
|
nonParallelEntry,
|
|
@@ -1209,7 +1343,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1209
1343
|
loaderPromises,
|
|
1210
1344
|
deps,
|
|
1211
1345
|
actionContext,
|
|
1212
|
-
|
|
1346
|
+
stale,
|
|
1213
1347
|
),
|
|
1214
1348
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1349
|
deps,
|