@rangojs/router 0.0.0-experimental.ea6d5eec → 0.0.0-experimental.ede38110
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 +130 -47
- package/dist/vite/index.js +719 -240
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +8 -0
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +53 -43
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +764 -0
- package/skills/parallel/SKILL.md +185 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/route/SKILL.md +55 -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 +37 -5
- package/src/browser/navigation-client.ts +107 -75
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/partial-update.ts +51 -6
- package/src/browser/prefetch/cache.ts +22 -12
- package/src/browser/prefetch/fetch.ts +81 -20
- package/src/browser/prefetch/queue.ts +61 -29
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +67 -8
- package/src/browser/react/NavigationProvider.tsx +13 -4
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-navigation.ts +11 -10
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +45 -3
- package/src/browser/scroll-restoration.ts +10 -8
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +27 -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 +211 -72
- package/src/build/route-types/scan-filter.ts +8 -1
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +46 -5
- package/src/cache/document-cache.ts +17 -7
- 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 +3 -1
- package/src/index.ts +46 -6
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +25 -1
- package/src/route-definition/dsl-helpers.ts +224 -37
- package/src/route-definition/helpers-types.ts +67 -19
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +9 -1
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-types.ts +18 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/handler-context.ts +82 -23
- package/src/router/intercept-resolution.ts +9 -4
- package/src/router/lazy-includes.ts +7 -6
- package/src/router/loader-resolution.ts +156 -21
- package/src/router/logging.ts +1 -1
- package/src/router/manifest.ts +28 -15
- 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 +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 +60 -5
- package/src/router/match-result.ts +104 -10
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +6 -8
- package/src/router/middleware.ts +2 -5
- 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 +1 -0
- 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 +433 -296
- package/src/router/types.ts +1 -0
- package/src/router.ts +55 -6
- package/src/rsc/handler.ts +472 -372
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +14 -2
- package/src/rsc/rsc-rendering.ts +10 -1
- package/src/rsc/server-action.ts +8 -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 +175 -15
- 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 +12 -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 +16 -6
- package/src/use-loader.tsx +77 -5
- 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 -4
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +60 -5
- 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 +88 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/rango.ts +19 -2
- package/src/vite/router-discovery.ts +178 -37
- package/src/vite/utils/banner.ts +3 -3
- 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 };
|
|
@@ -638,13 +721,20 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
638
721
|
return staticComponent;
|
|
639
722
|
}
|
|
640
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;
|
|
641
731
|
if (!routeEntry.loading) {
|
|
642
|
-
const result = handleHandlerResult(await
|
|
732
|
+
const result = handleHandlerResult(await handler(context));
|
|
643
733
|
doneHandler();
|
|
644
734
|
return result;
|
|
645
735
|
}
|
|
646
736
|
if (!actionContext) {
|
|
647
|
-
const result = handleHandlerResult(
|
|
737
|
+
const result = handleHandlerResult(handler(context));
|
|
648
738
|
if (result instanceof Promise) {
|
|
649
739
|
result.finally(doneHandler).catch(() => {});
|
|
650
740
|
const tracked = deps.trackHandler(result, {
|
|
@@ -667,9 +757,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
667
757
|
debugLog("segment.action", "resolving action route with awaited value", {
|
|
668
758
|
entryId: entry.id,
|
|
669
759
|
});
|
|
670
|
-
const actionResult = handleHandlerResult(
|
|
671
|
-
await routeEntry.handler(context),
|
|
672
|
-
);
|
|
760
|
+
const actionResult = handleHandlerResult(await handler(context));
|
|
673
761
|
doneHandler();
|
|
674
762
|
return {
|
|
675
763
|
content: Promise.resolve(actionResult),
|
|
@@ -678,10 +766,12 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
678
766
|
() => null,
|
|
679
767
|
);
|
|
680
768
|
|
|
769
|
+
// Normalize void handlers (undefined) to null so the reconciler's
|
|
770
|
+
// component === null checks work consistently for both void and explicit null.
|
|
681
771
|
const resolvedComponent =
|
|
682
772
|
component && typeof component === "object" && "content" in component
|
|
683
|
-
? (component as { content: ReactNode }).content
|
|
684
|
-
: component;
|
|
773
|
+
? ((component as { content: ReactNode }).content ?? null)
|
|
774
|
+
: (component ?? null);
|
|
685
775
|
|
|
686
776
|
const segment: ResolvedSegment = {
|
|
687
777
|
id: entry.shortCode,
|
|
@@ -783,6 +873,7 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
783
873
|
deps,
|
|
784
874
|
actionContext,
|
|
785
875
|
stale,
|
|
876
|
+
entry,
|
|
786
877
|
);
|
|
787
878
|
segments.push(...orphanResult.segments);
|
|
788
879
|
matchedIds.push(...orphanResult.matchedIds);
|
|
@@ -894,6 +985,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
894
985
|
deps: SegmentResolutionDeps<TEnv>,
|
|
895
986
|
actionContext?: ActionContext,
|
|
896
987
|
stale?: boolean,
|
|
988
|
+
/** Parent route entry — its loaders are inherited so parallel slots can access them. */
|
|
989
|
+
parentRouteEntry?: EntryData,
|
|
897
990
|
): Promise<SegmentRevalidationResult> {
|
|
898
991
|
invariant(
|
|
899
992
|
orphan.type === "layout" || orphan.type === "cache",
|
|
@@ -921,6 +1014,37 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
921
1014
|
segments.push(...loaderResult.segments);
|
|
922
1015
|
matchedIds.push(...loaderResult.matchedIds);
|
|
923
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
|
+
|
|
924
1048
|
// Handler-first: resolve orphan layout handler before its parallels
|
|
925
1049
|
// so ctx.set() values are visible to parallel children.
|
|
926
1050
|
matchedIds.push(orphan.shortCode);
|
|
@@ -997,143 +1121,73 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
997
1121
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
998
1122
|
});
|
|
999
1123
|
|
|
1000
|
-
|
|
1124
|
+
const resolvedParallelEntries = new Set<string>();
|
|
1125
|
+
for (const { slot, entry: parallelEntry } of getParallelSlotEntries(
|
|
1126
|
+
orphan.parallel,
|
|
1127
|
+
)) {
|
|
1001
1128
|
invariant(
|
|
1002
1129
|
parallelEntry.type === "parallel",
|
|
1003
1130
|
`Expected parallel entry, got: ${parallelEntry.type}`,
|
|
1004
1131
|
);
|
|
1005
1132
|
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
+
}
|
|
1023
1154
|
|
|
1024
1155
|
const slots = parallelEntry.handler as Record<
|
|
1025
1156
|
`@${string}`,
|
|
1026
1157
|
| ((ctx: HandlerContext<any, TEnv>) => ReactNode | Promise<ReactNode>)
|
|
1027
1158
|
| ReactNode
|
|
1028
1159
|
>;
|
|
1160
|
+
// Handler may be undefined in production after static handler eviction.
|
|
1161
|
+
const handler = slots[slot];
|
|
1029
1162
|
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
matchedIds.push(parallelId);
|
|
1036
|
-
|
|
1037
|
-
const shouldResolve = await (async () => {
|
|
1038
|
-
if (!clientSegmentIds.has(parallelId)) {
|
|
1039
|
-
if (isTraceActive()) {
|
|
1040
|
-
pushRevalidationTraceEntry({
|
|
1041
|
-
segmentId: parallelId,
|
|
1042
|
-
segmentType: "parallel",
|
|
1043
|
-
belongsToRoute,
|
|
1044
|
-
source: "parallel",
|
|
1045
|
-
defaultShouldRevalidate: true,
|
|
1046
|
-
finalShouldRevalidate: true,
|
|
1047
|
-
reason: "new-segment",
|
|
1048
|
-
});
|
|
1049
|
-
}
|
|
1050
|
-
return true;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
const dummySegment: ResolvedSegment = {
|
|
1054
|
-
id: parallelId,
|
|
1055
|
-
namespace: parallelEntry.id,
|
|
1056
|
-
type: "parallel",
|
|
1057
|
-
index: 0,
|
|
1058
|
-
component: null as any,
|
|
1059
|
-
params,
|
|
1060
|
-
slot,
|
|
1061
|
-
belongsToRoute,
|
|
1062
|
-
parallelName: `${parallelEntry.id}.${slot}`,
|
|
1063
|
-
...(parallelEntry.mountPath
|
|
1064
|
-
? { mountPath: parallelEntry.mountPath }
|
|
1065
|
-
: {}),
|
|
1066
|
-
};
|
|
1067
|
-
|
|
1068
|
-
return await evaluateRevalidation({
|
|
1069
|
-
segment: dummySegment,
|
|
1070
|
-
prevParams,
|
|
1071
|
-
getPrevSegment: null,
|
|
1072
|
-
request,
|
|
1073
|
-
prevUrl,
|
|
1074
|
-
nextUrl,
|
|
1075
|
-
revalidations: parallelEntry.revalidate.map((fn, i) => ({
|
|
1076
|
-
name: `revalidate${i}`,
|
|
1077
|
-
fn,
|
|
1078
|
-
})),
|
|
1079
|
-
routeKey,
|
|
1080
|
-
context,
|
|
1081
|
-
actionContext,
|
|
1082
|
-
stale,
|
|
1083
|
-
traceSource: "parallel",
|
|
1084
|
-
});
|
|
1085
|
-
})();
|
|
1086
|
-
emitRevalidationDecision(
|
|
1087
|
-
parallelId,
|
|
1088
|
-
context.pathname,
|
|
1089
|
-
routeKey,
|
|
1090
|
-
shouldResolve,
|
|
1091
|
-
);
|
|
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);
|
|
1092
1168
|
|
|
1093
|
-
|
|
1094
|
-
if (
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
typeof handler === "function" ? handler(context) : handler;
|
|
1106
|
-
if (result instanceof Promise) {
|
|
1107
|
-
const tracked = deps.trackHandler(result, {
|
|
1108
|
-
segmentId: parallelId,
|
|
1109
|
-
segmentType: "parallel",
|
|
1110
|
-
});
|
|
1111
|
-
observeStreamedHandler(
|
|
1112
|
-
tracked,
|
|
1113
|
-
parallelId,
|
|
1114
|
-
"parallel",
|
|
1115
|
-
context.pathname,
|
|
1116
|
-
routeKey,
|
|
1117
|
-
params,
|
|
1118
|
-
);
|
|
1119
|
-
component = tracked as ReactNode;
|
|
1120
|
-
} else {
|
|
1121
|
-
component = result as ReactNode;
|
|
1122
|
-
}
|
|
1123
|
-
} else {
|
|
1124
|
-
component =
|
|
1125
|
-
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
|
+
});
|
|
1126
1181
|
}
|
|
1182
|
+
return true;
|
|
1127
1183
|
}
|
|
1128
1184
|
|
|
1129
|
-
|
|
1185
|
+
const dummySegment: ResolvedSegment = {
|
|
1130
1186
|
id: parallelId,
|
|
1131
1187
|
namespace: parallelEntry.id,
|
|
1132
1188
|
type: "parallel",
|
|
1133
1189
|
index: 0,
|
|
1134
|
-
component,
|
|
1135
|
-
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
1136
|
-
transition: parallelEntry.transition,
|
|
1190
|
+
component: null as any,
|
|
1137
1191
|
params,
|
|
1138
1192
|
slot,
|
|
1139
1193
|
belongsToRoute,
|
|
@@ -1141,8 +1195,87 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1141
1195
|
...(parallelEntry.mountPath
|
|
1142
1196
|
? { mountPath: parallelEntry.mountPath }
|
|
1143
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",
|
|
1144
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);
|
|
1145
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
|
+
}
|
|
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
|
+
});
|
|
1146
1279
|
}
|
|
1147
1280
|
|
|
1148
1281
|
return { segments, matchedIds };
|
|
@@ -1194,6 +1327,10 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1194
1327
|
}
|
|
1195
1328
|
|
|
1196
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
|
+
}
|
|
1197
1334
|
const doneEntry = track(`segment:${entry.id}`, 1);
|
|
1198
1335
|
const resolved = await resolveWithErrorBoundary(
|
|
1199
1336
|
nonParallelEntry,
|