vinext 1.4.1 → 1.4.2
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 +1 -1
- package/dist/build/action-owner-manifest.js +1 -0
- package/dist/check.js +1 -1
- package/dist/config/config-matchers.js +2 -2
- package/dist/config/destination-params.js +5 -2
- package/dist/config/next-config.d.ts +16 -1
- package/dist/config/next-config.js +31 -1
- package/dist/entries/app-rsc-entry.js +41 -18
- package/dist/entries/app-rsc-manifest.js +4 -0
- package/dist/entries/pages-server-entry.js +2 -0
- package/dist/index.js +15 -12
- package/dist/init-cloudflare.js +18 -2
- package/dist/plugins/css-target.d.ts +18 -0
- package/dist/plugins/css-target.js +93 -0
- package/dist/routing/app-route-graph.d.ts +10 -0
- package/dist/routing/app-route-graph.js +15 -1
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -5
- package/dist/server/app-action-forwarding.js +3 -1
- package/dist/server/app-browser-entry.js +21 -4
- package/dist/server/app-elements.d.ts +3 -0
- package/dist/server/app-elements.js +14 -1
- package/dist/server/app-fallback-renderer.js +2 -2
- package/dist/server/app-optimistic-routing.d.ts +12 -1
- package/dist/server/app-optimistic-routing.js +38 -16
- package/dist/server/app-page-boundary-render.js +2 -2
- package/dist/server/app-page-cache-render.js +1 -1
- package/dist/server/app-page-dispatch.d.ts +10 -1
- package/dist/server/app-page-dispatch.js +73 -30
- package/dist/server/app-page-element-builder.d.ts +9 -20
- package/dist/server/app-page-element-builder.js +37 -49
- package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
- package/dist/server/app-page-intercept-segment-config.js +90 -0
- package/dist/server/app-page-navigation-params.d.ts +23 -0
- package/dist/server/app-page-navigation-params.js +49 -0
- package/dist/server/app-page-render.d.ts +5 -2
- package/dist/server/app-page-render.js +17 -3
- package/dist/server/app-page-request.d.ts +24 -2
- package/dist/server/app-page-request.js +10 -10
- package/dist/server/app-page-route-wiring.d.ts +10 -0
- package/dist/server/app-page-route-wiring.js +16 -12
- package/dist/server/app-page-segment-plan.js +9 -1
- package/dist/server/app-page-segment-state.d.ts +9 -0
- package/dist/server/app-page-segment-state.js +54 -1
- package/dist/server/app-response-stage-entry.js +1 -0
- package/dist/server/app-route-module-loader.d.ts +2 -0
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-rsc-handler.d.ts +10 -1
- package/dist/server/app-rsc-handler.js +75 -43
- package/dist/server/app-rsc-response-finalizer.d.ts +3 -0
- package/dist/server/app-rsc-response-finalizer.js +11 -1
- package/dist/server/app-rsc-route-matching.d.ts +14 -0
- package/dist/server/app-rsc-route-matching.js +42 -18
- package/dist/server/app-server-action-execution.js +16 -10
- package/dist/server/cache-control.d.ts +2 -0
- package/dist/server/cache-control.js +3 -1
- package/dist/server/image-local-patterns.d.ts +21 -0
- package/dist/server/image-local-patterns.js +59 -0
- package/dist/server/image-optimization.d.ts +23 -22
- package/dist/server/image-optimization.js +12 -2
- package/dist/server/metadata-route-response.js +24 -6
- package/dist/server/nitro-public-files-plugin.js +20 -8
- package/dist/server/pages-data-route.d.ts +5 -1
- package/dist/server/pages-data-route.js +14 -6
- package/dist/server/pages-request-pipeline.js +6 -5
- package/dist/server/pages-request-stage-entry.js +1 -0
- package/dist/server/prod-server.js +17 -9
- package/dist/server/request-pipeline.d.ts +5 -0
- package/dist/server/request-pipeline.js +11 -1
- package/dist/server/response-stage-cacheability.d.ts +7 -0
- package/dist/server/response-stage-cacheability.js +6 -5
- package/dist/server/worker-image-optimization.d.ts +2 -2
- package/dist/server/worker-image-optimization.js +18 -3
- package/dist/server/worker-utils.d.ts +8 -0
- package/dist/server/worker-utils.js +19 -2
- package/dist/shims/image-props.d.ts +6 -0
- package/dist/shims/image-props.js +39 -3
- package/dist/shims/image.js +3 -3
- package/dist/shims/slot.d.ts +3 -0
- package/dist/shims/slot.js +67 -9
- package/dist/utils/base-path.d.ts +7 -0
- package/dist/utils/base-path.js +11 -1
- package/package.json +1 -1
|
@@ -116,24 +116,6 @@ export type BuildPageElementsOptions<TModule extends AppPageModule = AppPageModu
|
|
|
116
116
|
htmlLimitedBots?: string;
|
|
117
117
|
scriptNonce?: string;
|
|
118
118
|
};
|
|
119
|
-
type AppPageNavigationParamModule = {
|
|
120
|
-
default?: unknown;
|
|
121
|
-
};
|
|
122
|
-
type AppPageNavigationParamSlot = {
|
|
123
|
-
default?: AppPageNavigationParamModule | null;
|
|
124
|
-
page?: AppPageNavigationParamModule | null;
|
|
125
|
-
slotPatternParts?: readonly string[] | null;
|
|
126
|
-
slotParamNames?: readonly string[] | null;
|
|
127
|
-
};
|
|
128
|
-
type AppPageNavigationParamRoute = {
|
|
129
|
-
params?: readonly string[] | null;
|
|
130
|
-
slots?: Readonly<Record<string, AppPageNavigationParamSlot>> | null;
|
|
131
|
-
};
|
|
132
|
-
type AppPageNavigationParamInterceptOptions = {
|
|
133
|
-
interceptPage?: unknown;
|
|
134
|
-
interceptParams?: AppPageParams | null;
|
|
135
|
-
interceptSlotKey?: string | null;
|
|
136
|
-
};
|
|
137
119
|
/**
|
|
138
120
|
* Build the App Router element tree for a matched route.
|
|
139
121
|
*
|
|
@@ -151,6 +133,15 @@ type AppPageNavigationParamInterceptOptions = {
|
|
|
151
133
|
* {@link https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/create-metadata.tsx|create-metadata.tsx}.
|
|
152
134
|
*/
|
|
153
135
|
export declare function buildPageElements<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule>(options: BuildPageElementsOptions<TModule, TErrorModule>): Promise<AppElements>;
|
|
136
|
+
/**
|
|
137
|
+
* The state key of each segment a sibling intercept's levels retain, from the
|
|
138
|
+
* app root down to the segment its element renders, so its Activity lists key
|
|
139
|
+
* like Next.js's for the intercepting route: the marker segment carries its
|
|
140
|
+
* marker, and a dynamic marker segment keys per value. The intercepting
|
|
141
|
+
* route's own layouts and boundaries render inside its page element, so the
|
|
142
|
+
* levels stop above the first of them.
|
|
143
|
+
*/
|
|
144
|
+
export declare function resolveSiblingInterceptSegmentKeys(sourcePageSegments: readonly string[] | null | undefined, routeSegments: readonly string[] | null | undefined, opts: AppPageInterceptOptions | null | undefined, routeParams: AppPageParams, interceptParams: AppPageParams): readonly string[] | null;
|
|
154
145
|
export declare const APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS: readonly [{
|
|
155
146
|
readonly prefix: "(...)";
|
|
156
147
|
readonly levels: number;
|
|
@@ -172,7 +163,5 @@ export declare const APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS: readonly [{
|
|
|
172
163
|
*/
|
|
173
164
|
export declare function resolveInterceptedSlotIdentitySegments(sourcePageSegments: readonly string[] | null | undefined, slotKey: string): readonly AppPageSemanticSegment[] | null;
|
|
174
165
|
export declare function resolveInterceptedSlotSegments(sourcePageSegments: readonly string[] | null | undefined, slotKey: string): readonly string[] | null;
|
|
175
|
-
export declare function resolveSlotParamOverrides(route: AppPageNavigationParamRoute, routePath: string): Readonly<Record<string, AppPageParams>> | null;
|
|
176
|
-
export declare function resolveAppPageNavigationParams(route: AppPageNavigationParamRoute, routeParams: AppPageParams, routePath: string, opts?: AppPageNavigationParamInterceptOptions | null): AppPageParams;
|
|
177
166
|
//#endregion
|
|
178
167
|
export type { AppPageErrorModule, AppPageRouteWiringRoute };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { isPromiseLike } from "../utils/promise.js";
|
|
2
2
|
import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
|
|
3
|
-
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
4
3
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
5
4
|
import "./app-elements.js";
|
|
6
|
-
import { APP_PAGE_SEGMENT_KEY, isAppPageRouteGroupSegment, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey } from "./app-page-segment-state.js";
|
|
5
|
+
import { APP_PAGE_SEGMENT_KEY, isAppPageRouteGroupSegment, resolveAppPagePatternStateKey, resolveAppPageRouteStateKey, resolveAppPageSemanticSegmentStateKey } from "./app-page-segment-state.js";
|
|
7
6
|
import { ErrorBoundary, ForbiddenBoundary, NotFoundBoundary, UnauthorizedBoundary } from "../shims/error-boundary.js";
|
|
8
7
|
import { withUseCacheLayoutMarker, withUseCachePageMarker } from "../shims/internal/app-page-props-cache-key.js";
|
|
9
8
|
import { makeThenableParams } from "../shims/thenable-params.js";
|
|
@@ -12,15 +11,16 @@ import { createAppPageMetadataSearchParamsObserver, makeObservedAppPageSearchPar
|
|
|
12
11
|
import { resolveAppPageBranchParams } from "./app-page-params.js";
|
|
13
12
|
import { collectAppPageSearchParams, hasAppPageDynamicMetadata, prepareAppPageHead, resolveActiveParallelRouteHeadInputs, resolveInterceptLayoutsBesidePage, slotInterceptKeepsSourceHead } from "./app-page-head.js";
|
|
14
13
|
import { createRouteNotFoundHeadPlanOptions, recoverAppPageRouteHead } from "./app-page-http-access-fallback-metadata.js";
|
|
15
|
-
import { ClientPageRoot } from "../shims/client-page-root.js";
|
|
16
14
|
import "./app-rsc-route-matching.js";
|
|
15
|
+
import { resolveSlotParamOverrides } from "./app-page-navigation-params.js";
|
|
17
16
|
import { createAppPageRenderDependency, invokeAppComponent, isAppClientReference, isAppRenderSuspension, isReactOwnedAppComponent, renderAfterAppDependencies } from "./app-render-dependency.js";
|
|
18
17
|
import { sanitizeErrorForClient } from "./app-rsc-errors.js";
|
|
19
18
|
import { resolveAppPageModuleTraceSegment, traceCreateComponentTree, traceGetLayoutOrPageModule } from "./app-page-tracing.js";
|
|
20
19
|
import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath, resolveAppPageLoadingModuleAtOrAbove } from "./app-page-route-wiring.js";
|
|
21
20
|
import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
|
|
22
|
-
import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
|
|
23
21
|
import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
|
|
22
|
+
import { ClientPageRoot } from "../shims/client-page-root.js";
|
|
23
|
+
import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
|
|
24
24
|
import { Fragment, Suspense, cache, createElement } from "react";
|
|
25
25
|
//#region src/server/app-page-element-builder.ts
|
|
26
26
|
/**
|
|
@@ -429,6 +429,7 @@ async function buildPageElements(options) {
|
|
|
429
429
|
routePath,
|
|
430
430
|
semanticPageIdentity,
|
|
431
431
|
semanticInterceptionTargetRouteId: opts?.interceptTargetRouteGraphId ?? null,
|
|
432
|
+
siblingInterceptSegmentKeys: isSiblingIntercept ? resolveSiblingInterceptSegmentKeys(sourcePageSegments, route.routeSegments, opts, params, effectiveParams) : null,
|
|
432
433
|
sourcePageSegments,
|
|
433
434
|
rootNotFoundModule: rootNotFoundModule ?? null,
|
|
434
435
|
rootForbiddenModule: rootForbiddenModule ?? null,
|
|
@@ -487,6 +488,37 @@ function buildSlotOverrides(route, routeParams, routePath, opts, interceptMetada
|
|
|
487
488
|
}
|
|
488
489
|
return Object.keys(overrides).length > 0 ? overrides : null;
|
|
489
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* The state key of each segment a sibling intercept's levels retain, from the
|
|
493
|
+
* app root down to the segment its element renders, so its Activity lists key
|
|
494
|
+
* like Next.js's for the intercepting route: the marker segment carries its
|
|
495
|
+
* marker, and a dynamic marker segment keys per value. The intercepting
|
|
496
|
+
* route's own layouts and boundaries render inside its page element, so the
|
|
497
|
+
* levels stop above the first of them.
|
|
498
|
+
*/
|
|
499
|
+
function resolveSiblingInterceptSegmentKeys(sourcePageSegments, routeSegments, opts, routeParams, interceptParams) {
|
|
500
|
+
if (!sourcePageSegments || !routeSegments) return null;
|
|
501
|
+
const markerIndex = sourcePageSegments.findIndex((segment) => APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS.some(({ prefix }) => segment.startsWith(prefix)));
|
|
502
|
+
if (markerIndex < 0) return null;
|
|
503
|
+
if (routeSegments.length !== markerIndex || routeSegments.some((segment, index) => segment !== sourcePageSegments[index])) return null;
|
|
504
|
+
const branchTreePositions = [
|
|
505
|
+
...(opts?.interceptLayouts ?? []).flatMap((layoutModule, index) => layoutModule?.default ? [opts?.interceptLayoutSegments?.[index]?.length ?? 0] : []),
|
|
506
|
+
...opts?.interceptLoadingTreePositions ?? [],
|
|
507
|
+
...opts?.interceptNotFoundTreePositions ?? [],
|
|
508
|
+
...opts?.interceptForbiddenTreePositions ?? [],
|
|
509
|
+
...opts?.interceptUnauthorizedTreePositions ?? [],
|
|
510
|
+
...opts?.interceptErrorTreePositions ?? []
|
|
511
|
+
];
|
|
512
|
+
const segmentCount = Math.max(markerIndex, Math.min(sourcePageSegments.length, markerIndex + Math.min(...branchTreePositions) - 1));
|
|
513
|
+
return sourcePageSegments.slice(0, segmentCount).map((segment, index) => {
|
|
514
|
+
const marker = index === markerIndex ? APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS.find(({ prefix }) => segment.startsWith(prefix)) : void 0;
|
|
515
|
+
return resolveAppPageSemanticSegmentStateKey({
|
|
516
|
+
marker: marker?.prefix ?? null,
|
|
517
|
+
paramSource: "route",
|
|
518
|
+
segment: marker ? segment.slice(marker.prefix.length) : segment
|
|
519
|
+
}, index < markerIndex ? routeParams : interceptParams);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
490
522
|
const APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS = [
|
|
491
523
|
{
|
|
492
524
|
prefix: "(...)",
|
|
@@ -581,49 +613,5 @@ function resolveInterceptedSlotSegments(sourcePageSegments, slotKey) {
|
|
|
581
613
|
routeSegments.push(...sourcePageSegments.slice(markerIndex + 1).filter(isVisibleInterceptedSlotSegment));
|
|
582
614
|
return routeSegments;
|
|
583
615
|
}
|
|
584
|
-
function resolveSlotParamOverrides(route, routePath) {
|
|
585
|
-
const overrides = {};
|
|
586
|
-
const slots = route.slots;
|
|
587
|
-
if (slots) {
|
|
588
|
-
let urlParts = null;
|
|
589
|
-
const routeParamSet = collectParamNameSet(route.params);
|
|
590
|
-
for (const [slotKey, slot] of Object.entries(slots)) {
|
|
591
|
-
const patternParts = slot.slotPatternParts;
|
|
592
|
-
const paramNames = slot.slotParamNames;
|
|
593
|
-
if (!patternParts || patternParts.length === 0) continue;
|
|
594
|
-
if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;
|
|
595
|
-
if (urlParts === null) urlParts = routePath.split("/").filter(Boolean);
|
|
596
|
-
const matched = matchRoutePattern(urlParts, patternParts);
|
|
597
|
-
if (!matched) continue;
|
|
598
|
-
overrides[slotKey] = matched;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
return Object.keys(overrides).length > 0 ? overrides : null;
|
|
602
|
-
}
|
|
603
|
-
function mergeAppPageParams(target, source) {
|
|
604
|
-
for (const [key, value] of Object.entries(source)) target[key] = value;
|
|
605
|
-
}
|
|
606
|
-
function isDefaultExportModule(module) {
|
|
607
|
-
return typeof module === "object" && module !== null;
|
|
608
|
-
}
|
|
609
|
-
function hasDefaultExport(module) {
|
|
610
|
-
if (!isDefaultExportModule(module)) return false;
|
|
611
|
-
return module?.default !== null && module?.default !== void 0;
|
|
612
|
-
}
|
|
613
|
-
function resolveAppPageNavigationParams(route, routeParams, routePath, opts) {
|
|
614
|
-
const navigationParams = { ...routeParams };
|
|
615
|
-
const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
|
|
616
|
-
for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
|
|
617
|
-
const isInterceptedSlot = opts?.interceptSlotKey === slotKey && opts.interceptSlotKey !== "__vinext_page_intercept" && hasDefaultExport(opts.interceptPage);
|
|
618
|
-
if (!isInterceptedSlot && !hasDefaultExport(slot.page) && !hasDefaultExport(slot.default)) continue;
|
|
619
|
-
mergeAppPageParams(navigationParams, isInterceptedSlot ? opts?.interceptParams ?? routeParams : slotParamOverrides?.[slotKey] ?? routeParams);
|
|
620
|
-
}
|
|
621
|
-
return navigationParams;
|
|
622
|
-
}
|
|
623
|
-
function collectParamNameSet(params) {
|
|
624
|
-
const set = /* @__PURE__ */ new Set();
|
|
625
|
-
if (params) for (const name of params) set.add(name);
|
|
626
|
-
return set;
|
|
627
|
-
}
|
|
628
616
|
//#endregion
|
|
629
|
-
export { APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS, buildPageElements,
|
|
617
|
+
export { APP_PAGE_INTERCEPTION_MARKER_TRAVERSALS, buildPageElements, resolveInterceptedSlotIdentitySegments, resolveInterceptedSlotSegments, resolveSiblingInterceptSegmentKeys };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { FetchCacheMode } from "../shims/fetch-cache-state.js";
|
|
2
|
+
import "../shims/fetch-cache.js";
|
|
3
|
+
//#region src/server/app-page-intercept-segment-config.d.ts
|
|
4
|
+
type SegmentModule = Record<string, unknown>;
|
|
5
|
+
type AppPageInterceptSegmentConfigSlot = {
|
|
6
|
+
configLayouts?: readonly (SegmentModule | null | undefined)[] | null;
|
|
7
|
+
configLayoutTreePositions?: readonly number[] | null;
|
|
8
|
+
default?: SegmentModule | null;
|
|
9
|
+
layout?: SegmentModule | null;
|
|
10
|
+
layoutIndex?: number;
|
|
11
|
+
name?: string;
|
|
12
|
+
ownerTreePosition?: number | null;
|
|
13
|
+
page?: SegmentModule | null;
|
|
14
|
+
routeSegments?: readonly string[] | null;
|
|
15
|
+
};
|
|
16
|
+
/** The parts of the route an interception renders under that its config reads. */
|
|
17
|
+
export type AppPageInterceptSegmentConfigRoute = {
|
|
18
|
+
layouts?: readonly (SegmentModule | null | undefined)[];
|
|
19
|
+
layoutTreePositions?: readonly number[];
|
|
20
|
+
routeSegments?: readonly string[];
|
|
21
|
+
slots?: Readonly<Record<string, AppPageInterceptSegmentConfigSlot>> | null;
|
|
22
|
+
};
|
|
23
|
+
/** The parts of the interception's options its config reads. */
|
|
24
|
+
export type AppPageInterceptSegmentConfigOptions = {
|
|
25
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
26
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
27
|
+
interceptLayouts?: readonly unknown[] | null;
|
|
28
|
+
interceptPage?: unknown;
|
|
29
|
+
interceptSlotKey: string;
|
|
30
|
+
interceptSourcePageSegments?: readonly string[] | null;
|
|
31
|
+
};
|
|
32
|
+
export type AppPageInterceptSegmentConfig = {
|
|
33
|
+
dynamicConfig: string | undefined;
|
|
34
|
+
dynamicStaleTimeSeconds: number | undefined;
|
|
35
|
+
fetchCacheMode: FetchCacheMode | null;
|
|
36
|
+
/** Whether any segment of the interception exports `generateStaticParams`. */
|
|
37
|
+
hasAnyGenerateStaticParams: boolean;
|
|
38
|
+
/** Whether one sits at or below the interception's last dynamic segment. */
|
|
39
|
+
hasGenerateStaticParams: boolean;
|
|
40
|
+
/** Whether the interception's own segments choose the edge runtime. */
|
|
41
|
+
isEdgeRuntime: boolean;
|
|
42
|
+
isStaticEligible: boolean;
|
|
43
|
+
isStaticGenerationEdgeRuntime: boolean;
|
|
44
|
+
revalidateSeconds: number | null;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Next.js renders an interception as its own route, whose loader tree holds
|
|
48
|
+
* the owner's layouts and the intercepting page, never the source's page, so
|
|
49
|
+
* its segment config comes from those modules alone. A sibling intercept's
|
|
50
|
+
* branch takes the children position below its owner folder; a slot
|
|
51
|
+
* intercept's branch takes its slot, beside the other slots of the owner's
|
|
52
|
+
* layouts, and children renders the owner's default.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveAppPageInterceptSegmentConfig(route: AppPageInterceptSegmentConfigRoute, interceptOpts: AppPageInterceptSegmentConfigOptions): AppPageInterceptSegmentConfig;
|
|
55
|
+
//#endregion
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { collectAppPageStaticGenerationRuntimes, collectAppPageStaticParamsWalkSegments, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "./app-segment-config.js";
|
|
2
|
+
import { SIBLING_PAGE_INTERCEPT_SLOT_KEY } from "./app-rsc-route-matching.js";
|
|
3
|
+
//#region src/server/app-page-intercept-segment-config.ts
|
|
4
|
+
/**
|
|
5
|
+
* Next.js renders an interception as its own route, whose loader tree holds
|
|
6
|
+
* the owner's layouts and the intercepting page, never the source's page, so
|
|
7
|
+
* its segment config comes from those modules alone. A sibling intercept's
|
|
8
|
+
* branch takes the children position below its owner folder; a slot
|
|
9
|
+
* intercept's branch takes its slot, beside the other slots of the owner's
|
|
10
|
+
* layouts, and children renders the owner's default.
|
|
11
|
+
*/
|
|
12
|
+
function resolveAppPageInterceptSegmentConfig(route, interceptOpts) {
|
|
13
|
+
const routeLayouts = route.layouts ?? [];
|
|
14
|
+
const routeSegments = route.routeSegments ?? [];
|
|
15
|
+
const isSiblingIntercept = interceptOpts.interceptSlotKey === SIBLING_PAGE_INTERCEPT_SLOT_KEY;
|
|
16
|
+
const interceptedSlot = isSiblingIntercept ? void 0 : route.slots?.[interceptOpts.interceptSlotKey];
|
|
17
|
+
const branchSegments = interceptOpts.interceptBranchSegments ?? [];
|
|
18
|
+
const interceptLayouts = interceptOpts.interceptLayouts ?? [];
|
|
19
|
+
const interceptLayoutPositions = (interceptOpts.interceptLayoutSegments ?? []).map((segments) => segments.length);
|
|
20
|
+
const ownerPosition = isSiblingIntercept ? interceptOpts.interceptSourcePageSegments ? interceptOpts.interceptSourcePageSegments.length - branchSegments.length : routeSegments.length : interceptedSlot?.ownerTreePosition ?? route.layoutTreePositions?.[interceptedSlot?.layoutIndex ?? -1] ?? routeSegments.length;
|
|
21
|
+
const ownerLayoutIndexes = routeLayouts.flatMap((_, index) => (route.layoutTreePositions?.[index] ?? 0) <= ownerPosition ? [index] : []);
|
|
22
|
+
const ownerLayouts = ownerLayoutIndexes.map((index) => routeLayouts[index]);
|
|
23
|
+
const ownerLayoutPositions = ownerLayoutIndexes.map((index) => route.layoutTreePositions?.[index] ?? 0);
|
|
24
|
+
const ownerSegments = routeSegments.slice(0, ownerPosition);
|
|
25
|
+
const page = interceptOpts.interceptPage;
|
|
26
|
+
const retainedBranches = Object.entries(route.slots ?? {}).flatMap(([slotKey, slot]) => slotKey !== interceptOpts.interceptSlotKey && (slot.ownerTreePosition ?? route.layoutTreePositions?.[slot.layoutIndex ?? 0] ?? 0) <= ownerPosition ? [{
|
|
27
|
+
configLayouts: slot.page ? slot.configLayouts : null,
|
|
28
|
+
configLayoutTreePositions: slot.page ? slot.configLayoutTreePositions : null,
|
|
29
|
+
isDefault: !slot.page,
|
|
30
|
+
layout: slot.page ? slot.layout : null,
|
|
31
|
+
name: slot.name,
|
|
32
|
+
ownerTreePosition: slot.ownerTreePosition,
|
|
33
|
+
page: slot.page ?? slot.default,
|
|
34
|
+
routeSegments: slot.routeSegments
|
|
35
|
+
}] : []);
|
|
36
|
+
const tree = isSiblingIntercept ? {
|
|
37
|
+
layouts: [...ownerLayouts, ...interceptLayouts],
|
|
38
|
+
layoutTreePositions: [...ownerLayoutPositions, ...interceptLayoutPositions.map((position) => ownerPosition + position)],
|
|
39
|
+
page,
|
|
40
|
+
parallelBranches: retainedBranches,
|
|
41
|
+
routeSegments: [...ownerSegments, ...branchSegments]
|
|
42
|
+
} : {
|
|
43
|
+
layouts: ownerLayouts,
|
|
44
|
+
layoutTreePositions: ownerLayoutPositions,
|
|
45
|
+
page: null,
|
|
46
|
+
parallelBranches: [...retainedBranches, {
|
|
47
|
+
configLayouts: interceptLayouts,
|
|
48
|
+
configLayoutTreePositions: interceptLayoutPositions,
|
|
49
|
+
layout: interceptedSlot?.layout,
|
|
50
|
+
name: interceptedSlot?.name,
|
|
51
|
+
ownerTreePosition: ownerPosition,
|
|
52
|
+
page,
|
|
53
|
+
routeSegments: branchSegments
|
|
54
|
+
}],
|
|
55
|
+
routeSegments: ownerSegments
|
|
56
|
+
};
|
|
57
|
+
const config = resolveAppPageSegmentConfig({
|
|
58
|
+
layouts: [
|
|
59
|
+
...ownerLayouts,
|
|
60
|
+
...isSiblingIntercept ? [] : [interceptedSlot?.layout],
|
|
61
|
+
...interceptLayouts
|
|
62
|
+
],
|
|
63
|
+
page,
|
|
64
|
+
parallelBranches: retainedBranches,
|
|
65
|
+
parallelPages: retainedBranches.map((branch) => branch.page)
|
|
66
|
+
});
|
|
67
|
+
const walkSegments = collectAppPageStaticParamsWalkSegments(tree);
|
|
68
|
+
const hasGenerateStaticParams = hasAppPageGenerateStaticParamsAtLastDynamicSegment(tree);
|
|
69
|
+
const isDynamicRoute = walkSegments.some((segment) => segment.dynamic);
|
|
70
|
+
const isStaticGenerationEdgeRuntime = isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes(tree)));
|
|
71
|
+
return {
|
|
72
|
+
dynamicConfig: config.dynamicConfig,
|
|
73
|
+
dynamicStaleTimeSeconds: config.dynamicStaleTimeSeconds,
|
|
74
|
+
fetchCacheMode: config.fetchCache ?? null,
|
|
75
|
+
hasAnyGenerateStaticParams: walkSegments.some((segment) => segment.generateStaticParams),
|
|
76
|
+
hasGenerateStaticParams,
|
|
77
|
+
isEdgeRuntime: isEdgeRuntime(config.runtime),
|
|
78
|
+
isStaticEligible: isAppPageStaticEligible({
|
|
79
|
+
dynamicConfig: config.dynamicConfig,
|
|
80
|
+
hasGenerateStaticParams,
|
|
81
|
+
isDynamicRoute,
|
|
82
|
+
isStaticGenerationEdgeRuntime,
|
|
83
|
+
revalidateSeconds: config.revalidateSeconds
|
|
84
|
+
}),
|
|
85
|
+
isStaticGenerationEdgeRuntime,
|
|
86
|
+
revalidateSeconds: config.revalidateSeconds
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { resolveAppPageInterceptSegmentConfig };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { AppPageParams } from "./app-page-boundary.js";
|
|
2
|
+
//#region src/server/app-page-navigation-params.d.ts
|
|
3
|
+
export type AppPageNavigationParamSlot = {
|
|
4
|
+
__loadDefault?: unknown;
|
|
5
|
+
__loadPage?: unknown;
|
|
6
|
+
default?: unknown;
|
|
7
|
+
page?: unknown;
|
|
8
|
+
slotPatternParts?: readonly string[] | null;
|
|
9
|
+
slotParamNames?: readonly string[] | null;
|
|
10
|
+
};
|
|
11
|
+
type AppPageNavigationParamRoute = {
|
|
12
|
+
params?: readonly string[] | null;
|
|
13
|
+
slots?: Readonly<Record<string, AppPageNavigationParamSlot>> | null;
|
|
14
|
+
};
|
|
15
|
+
type AppPageNavigationParamInterceptOptions = {
|
|
16
|
+
interceptPage?: unknown;
|
|
17
|
+
interceptParams?: AppPageParams | null;
|
|
18
|
+
interceptSlotKey?: string | null;
|
|
19
|
+
};
|
|
20
|
+
export declare function resolveSlotParamOverrides(route: AppPageNavigationParamRoute, routePath: string): Readonly<Record<string, AppPageParams>> | null;
|
|
21
|
+
export declare function resolveAppPageNavigationParams(route: AppPageNavigationParamRoute, routeParams: AppPageParams, routePath: string, opts?: AppPageNavigationParamInterceptOptions | null): AppPageParams;
|
|
22
|
+
//#endregion
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
2
|
+
import "./app-rsc-route-matching.js";
|
|
3
|
+
//#region src/server/app-page-navigation-params.ts
|
|
4
|
+
function resolveSlotParamOverrides(route, routePath) {
|
|
5
|
+
const overrides = {};
|
|
6
|
+
const slots = route.slots;
|
|
7
|
+
if (slots) {
|
|
8
|
+
let urlParts = null;
|
|
9
|
+
const routeParamSet = collectParamNameSet(route.params);
|
|
10
|
+
for (const [slotKey, slot] of Object.entries(slots)) {
|
|
11
|
+
const patternParts = slot.slotPatternParts;
|
|
12
|
+
const paramNames = slot.slotParamNames;
|
|
13
|
+
if (!patternParts || patternParts.length === 0) continue;
|
|
14
|
+
if (paramNames && paramNames.every((name) => routeParamSet.has(name))) continue;
|
|
15
|
+
if (urlParts === null) urlParts = routePath.split("/").filter(Boolean);
|
|
16
|
+
const matched = matchRoutePattern(urlParts, patternParts);
|
|
17
|
+
if (!matched) continue;
|
|
18
|
+
overrides[slotKey] = matched;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return Object.keys(overrides).length > 0 ? overrides : null;
|
|
22
|
+
}
|
|
23
|
+
function mergeAppPageParams(target, source) {
|
|
24
|
+
for (const [key, value] of Object.entries(source)) target[key] = value;
|
|
25
|
+
}
|
|
26
|
+
function isDefaultExportModule(module) {
|
|
27
|
+
return typeof module === "object" && module !== null;
|
|
28
|
+
}
|
|
29
|
+
function hasDefaultExport(module) {
|
|
30
|
+
if (!isDefaultExportModule(module)) return false;
|
|
31
|
+
return module?.default !== null && module?.default !== void 0;
|
|
32
|
+
}
|
|
33
|
+
function resolveAppPageNavigationParams(route, routeParams, routePath, opts) {
|
|
34
|
+
const navigationParams = { ...routeParams };
|
|
35
|
+
const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
|
|
36
|
+
for (const [slotKey, slot] of Object.entries(route.slots ?? {})) {
|
|
37
|
+
const isInterceptedSlot = opts?.interceptSlotKey === slotKey && opts.interceptSlotKey !== "__vinext_page_intercept" && hasDefaultExport(opts.interceptPage);
|
|
38
|
+
if (!isInterceptedSlot && !hasDefaultExport(slot.page) && !hasDefaultExport(slot.default) && !slot.__loadPage && !slot.__loadDefault) continue;
|
|
39
|
+
mergeAppPageParams(navigationParams, isInterceptedSlot ? opts?.interceptParams ?? routeParams : slotParamOverrides?.[slotKey] ?? routeParams);
|
|
40
|
+
}
|
|
41
|
+
return navigationParams;
|
|
42
|
+
}
|
|
43
|
+
function collectParamNameSet(params) {
|
|
44
|
+
const set = /* @__PURE__ */ new Set();
|
|
45
|
+
if (params) for (const name of params) set.add(name);
|
|
46
|
+
return set;
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
export { resolveAppPageNavigationParams, resolveSlotParamOverrides };
|
|
@@ -150,9 +150,12 @@ type RenderAppPageLifecycleOptions = RenderAppPageLifecycleOptionsBase & ({
|
|
|
150
150
|
* static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
|
|
151
151
|
* before the response left the render. Responses that leave the render before
|
|
152
152
|
* its response policy, such as error boundaries and special errors, get the
|
|
153
|
-
* same header as the normal render.
|
|
153
|
+
* same header as the normal render. One not yet known dynamic is never stored
|
|
154
|
+
* either, and its body can still reach a dynamic API after its headers leave,
|
|
155
|
+
* so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
|
|
156
|
+
* for the responses that aren't).
|
|
154
157
|
*/
|
|
155
|
-
export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
|
|
158
|
+
export declare function applyIneligibleRouteCachePolicy(response: Response, options: Pick<RenderAppPageLifecycleOptions, "dynamicStaleTimeSeconds" | "isDraftMode" | "isDynamicError" | "isForceDynamic" | "isForceStatic" | "isPrerender" | "isProduction" | "isProgressiveActionRender" | "isRscRequest" | "isStaticEligible" | "middlewareContext" | "peekDynamicUsage" | "revalidateSeconds" | "scriptNonce">, requestCacheLife?: AppPageRequestCacheLife | null): Response;
|
|
156
159
|
export declare function renderAppPageLifecycle(options: RenderAppPageLifecycleOptions): Promise<Response>;
|
|
157
160
|
//#endregion
|
|
158
161
|
export {};
|
|
@@ -24,10 +24,10 @@ import { appendRscCompletionMetadata } from "./rsc-completion-metadata.js";
|
|
|
24
24
|
import { isBailoutToCSRError } from "../shims/navigation-errors.js";
|
|
25
25
|
import { applyClientStaleTimeHeader, applyEdgeRuntimeHeader, buildAppPageHtmlResponse, buildAppPageRscResponse, resolveAppPageHtmlResponsePolicy, resolveAppPageRscResponsePolicy, resolveUncacheableCacheControl } from "./app-page-response.js";
|
|
26
26
|
import { buildAppPageFontLinkHeader, readAppPageBinaryStream, resolveAppPageCaughtNotFoundBoundaries, resolveAppPageShellSpecialError, resolveAppPageSpecialError, resolveAppPageSpecialErrorStoredHeaders, teeAppPageRscStreamForCapture } from "./app-page-execution.js";
|
|
27
|
+
import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
|
|
27
28
|
import { getAppPageLayoutGate } from "./app-page-layout-gate.js";
|
|
28
29
|
import { hasDigest } from "./app-rsc-errors.js";
|
|
29
30
|
import { recordAppPageRenderError, traceAppPageRender } from "./app-page-tracing.js";
|
|
30
|
-
import { buildAppPageHtmlDocumentResponse, buildAppPageLinkHeader, createAppPageFontData, createAppPageRscErrorTracker, createAppPageSsrErrorHandler, renderAppPageHtmlStream, renderAppPageHtmlStreamWithRecovery } from "./app-page-stream.js";
|
|
31
31
|
import { createAppPageHtmlOutputScope, createAppPageRenderObservation, createAppPageRscOutputScope } from "./app-page-render-observation.js";
|
|
32
32
|
import { createAppPageRscRenderStatusResolver } from "./app-page-rsc-render-status.js";
|
|
33
33
|
import { finalizeAppPageCacheabilityEvaluationResponse, finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse } from "./app-page-cache-finalizer.js";
|
|
@@ -70,11 +70,14 @@ function applyRequestCacheLife(options) {
|
|
|
70
70
|
* static, draft mode, `force-dynamic`, `revalidate = 0`, or a dynamic API read
|
|
71
71
|
* before the response left the render. Responses that leave the render before
|
|
72
72
|
* its response policy, such as error boundaries and special errors, get the
|
|
73
|
-
* same header as the normal render.
|
|
73
|
+
* same header as the normal render. One not yet known dynamic is never stored
|
|
74
|
+
* either, and its body can still reach a dynamic API after its headers leave,
|
|
75
|
+
* so it is sent like a dynamic render (see resolveUnstoredEarlyResponseCacheControl
|
|
76
|
+
* for the responses that aren't).
|
|
74
77
|
*/
|
|
75
78
|
function applyIneligibleRouteCachePolicy(response, options, requestCacheLife = null) {
|
|
76
|
-
const cacheControl = resolveEarlyResponseCacheControl(options);
|
|
77
79
|
const isRscRedirect = options.isRscRequest && response.headers.has("X-Vinext-Rsc-Redirect");
|
|
80
|
+
const cacheControl = resolveEarlyResponseCacheControl(options) ?? (isRscRedirect ? null : resolveUnstoredEarlyResponseCacheControl(response, options));
|
|
78
81
|
if (!cacheControl) {
|
|
79
82
|
const staleTimeSeconds = isRscRedirect ? resolveClientStaleTimeSeconds(requestCacheLife) : void 0;
|
|
80
83
|
if (staleTimeSeconds === void 0) return response;
|
|
@@ -108,6 +111,17 @@ function resolveEarlyResponseCacheControl(options) {
|
|
|
108
111
|
const ignoresDynamicUsage = (options.isForceStatic || options.isDynamicError) && (options.revalidateSeconds === null || options.revalidateSeconds === Infinity);
|
|
109
112
|
return options.revalidateSeconds === 0 || !ignoresDynamicUsage && (options.peekDynamicUsage?.() ?? peekDynamicUsage()) ? uncacheable : null;
|
|
110
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* The normal render can still become a cache entry, so it sends its pending
|
|
116
|
+
* policy until the stream proves it static. An early response never does, and
|
|
117
|
+
* nothing checks it again, so one with a body takes the never-cache header
|
|
118
|
+
* unless it already carries a policy, such as middleware's. A force-static or
|
|
119
|
+
* dynamic = "error" render can't read the request, and dev and the build's
|
|
120
|
+
* prerender, which reads `no-store` as a dynamic render, keep no policy.
|
|
121
|
+
*/
|
|
122
|
+
function resolveUnstoredEarlyResponseCacheControl(response, options) {
|
|
123
|
+
return options.isProduction && options.isPrerender !== true && !options.isForceStatic && !options.isDynamicError && response.body !== null && !response.headers.has("cache-control") ? NEVER_CACHE_CONTROL : null;
|
|
124
|
+
}
|
|
111
125
|
function resolveAppPageCacheWriteRevalidateSeconds(options) {
|
|
112
126
|
if (!options.isStaticEligible) return null;
|
|
113
127
|
if (options.revalidateSeconds === null && (options.isForceStatic || options.isDynamicError)) return Infinity;
|
|
@@ -100,6 +100,25 @@ type AppPageInterceptMatch<TPage = unknown> = {
|
|
|
100
100
|
targetPatternParts?: readonly string[];
|
|
101
101
|
targetRouteGraphId?: string | null;
|
|
102
102
|
};
|
|
103
|
+
export type AppPageInterceptState<TRoute, TPage> = {
|
|
104
|
+
kind: "none";
|
|
105
|
+
} | {
|
|
106
|
+
kind: "current-route";
|
|
107
|
+
intercept: AppPageInterceptMatch<TPage>;
|
|
108
|
+
} | {
|
|
109
|
+
kind: "source-route";
|
|
110
|
+
intercept: AppPageInterceptMatch<TPage>;
|
|
111
|
+
sourceRoute: TRoute;
|
|
112
|
+
};
|
|
113
|
+
type AppPageInterceptStateLookup<TRoute, TPage> = {
|
|
114
|
+
cleanPathname: string;
|
|
115
|
+
currentRoute: TRoute;
|
|
116
|
+
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
117
|
+
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
118
|
+
/** The state this request already resolved, which is then reused. */
|
|
119
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
120
|
+
isRscRequest: boolean;
|
|
121
|
+
};
|
|
103
122
|
type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts> = {
|
|
104
123
|
cleanPathname: string;
|
|
105
124
|
currentParams: AppPageParams;
|
|
@@ -107,6 +126,7 @@ type ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOp
|
|
|
107
126
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
108
127
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
109
128
|
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
129
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
110
130
|
isRscRequest: boolean;
|
|
111
131
|
toInterceptOpts: (intercept: AppPageInterceptMatch<TPage>) => TInterceptOpts;
|
|
112
132
|
};
|
|
@@ -128,10 +148,11 @@ type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
|
|
|
128
148
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
129
149
|
getRouteParamNames: (route: TRoute) => readonly string[];
|
|
130
150
|
getSourceRoute: (sourceRouteIndex: number) => Awaitable<TRoute | undefined>;
|
|
151
|
+
interceptState?: AppPageInterceptState<TRoute, TPage>;
|
|
131
152
|
isRscRequest: boolean;
|
|
132
153
|
resolveNavigationParams: (route: TRoute, params: AppPageParams, pathname: string, interceptOpts: TInterceptOpts) => AppPageParams;
|
|
133
|
-
renderInterceptResponse: (route: TRoute, element: TElement) => Promise<Response> | Response;
|
|
134
|
-
resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams) => Awaitable<URLSearchParams>;
|
|
154
|
+
renderInterceptResponse: (route: TRoute, element: TElement, navigationParams: AppPageParams) => Promise<Response> | Response;
|
|
155
|
+
resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams, interceptOpts: TInterceptOpts) => Awaitable<URLSearchParams>;
|
|
135
156
|
searchParams: URLSearchParams;
|
|
136
157
|
setNavigationContext: (context: {
|
|
137
158
|
params: AppPageParams;
|
|
@@ -146,6 +167,7 @@ type ResolveAppPageInterceptResult<TInterceptOpts> = {
|
|
|
146
167
|
};
|
|
147
168
|
export declare function resolveAppPageGenerateStaticParamsSources(options: ResolveAppPageGenerateStaticParamsSourcesOptions): GenerateStaticParamsSource[];
|
|
148
169
|
export declare function validateAppPageDynamicParams(options: ValidateAppPageDynamicParamsOptions): Promise<Response | null>;
|
|
170
|
+
export declare function resolveAppPageInterceptState<TRoute, TPage>(options: AppPageInterceptStateLookup<TRoute, TPage>): Promise<AppPageInterceptState<TRoute, TPage>>;
|
|
149
171
|
export declare function resolveAppPageInterceptionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
150
172
|
export declare function resolveAppPageActionRerenderTarget<TRoute, TPage, TInterceptOpts>(options: ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>): Promise<ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts>>;
|
|
151
173
|
export declare function resolveAppPageIntercept<TRoute, TPage, TInterceptOpts, TElement>(options: ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement>): Promise<ResolveAppPageInterceptResult<TInterceptOpts>>;
|
|
@@ -278,6 +278,7 @@ async function validateAppPageDynamicParams(options) {
|
|
|
278
278
|
return null;
|
|
279
279
|
}
|
|
280
280
|
async function resolveAppPageInterceptState(options) {
|
|
281
|
+
if (options.interceptState) return options.interceptState;
|
|
281
282
|
if (!options.isRscRequest) return { kind: "none" };
|
|
282
283
|
const intercept = options.findIntercept(options.cleanPathname);
|
|
283
284
|
if (!intercept) return { kind: "none" };
|
|
@@ -301,10 +302,9 @@ async function resolveAppPageInterceptionRerenderTarget(options) {
|
|
|
301
302
|
cleanPathname: options.cleanPathname,
|
|
302
303
|
currentRoute: options.currentRoute,
|
|
303
304
|
findIntercept: options.findIntercept,
|
|
304
|
-
getRouteParamNames: options.getRouteParamNames,
|
|
305
305
|
getSourceRoute: options.getSourceRoute,
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
interceptState: options.interceptState,
|
|
307
|
+
isRscRequest: options.isRscRequest
|
|
308
308
|
});
|
|
309
309
|
if (interceptState.kind === "source-route") {
|
|
310
310
|
const sourceMatchedParams = interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams;
|
|
@@ -333,10 +333,9 @@ async function resolveAppPageIntercept(options) {
|
|
|
333
333
|
cleanPathname: options.cleanPathname,
|
|
334
334
|
currentRoute: options.currentRoute,
|
|
335
335
|
findIntercept: options.findIntercept,
|
|
336
|
-
getRouteParamNames: options.getRouteParamNames,
|
|
337
336
|
getSourceRoute: options.getSourceRoute,
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
interceptState: options.interceptState,
|
|
338
|
+
isRscRequest: options.isRscRequest
|
|
340
339
|
});
|
|
341
340
|
if (interceptState.kind === "source-route") {
|
|
342
341
|
const renderRoute = interceptState.sourceRoute;
|
|
@@ -346,17 +345,18 @@ async function resolveAppPageIntercept(options) {
|
|
|
346
345
|
...sourceMatchedParams,
|
|
347
346
|
...interceptState.intercept.matchedParams
|
|
348
347
|
};
|
|
349
|
-
const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams) : options.searchParams;
|
|
348
|
+
const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams, interceptOpts) : options.searchParams;
|
|
350
349
|
const renderParams = pickRouteParams(sourceMatchedParams, options.getRouteParamNames(interceptState.sourceRoute));
|
|
350
|
+
const resolvedNavigationParams = options.resolveNavigationParams(renderRoute, navigationParams, options.cleanPathname, interceptOpts);
|
|
351
351
|
options.setNavigationContext({
|
|
352
|
-
params:
|
|
352
|
+
params: resolvedNavigationParams,
|
|
353
353
|
pathname: options.cleanPathname,
|
|
354
354
|
searchParams: renderSearchParams
|
|
355
355
|
});
|
|
356
356
|
const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts, renderSearchParams);
|
|
357
357
|
return {
|
|
358
358
|
interceptOpts: void 0,
|
|
359
|
-
response: await options.renderInterceptResponse(renderRoute, interceptElement)
|
|
359
|
+
response: await options.renderInterceptResponse(renderRoute, interceptElement, resolvedNavigationParams)
|
|
360
360
|
};
|
|
361
361
|
}
|
|
362
362
|
return {
|
|
@@ -385,4 +385,4 @@ async function buildAppPageElement(options) {
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
//#endregion
|
|
388
|
-
export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
|
|
388
|
+
export { buildAppPageElement, resolveAppPageActionRerenderTarget, resolveAppPageGenerateStaticParamsSources, resolveAppPageIntercept, resolveAppPageInterceptState, resolveAppPageInterceptionRerenderTarget, validateAppPageDynamicParams };
|
|
@@ -35,6 +35,8 @@ export type AppPageRouteWiringSlot<TModule extends AppPageModule = AppPageModule
|
|
|
35
35
|
configLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
36
36
|
configLayoutTreePositions?: readonly number[] | null;
|
|
37
37
|
error?: TErrorModule | null;
|
|
38
|
+
errors?: readonly (TErrorModule | null | undefined)[] | null;
|
|
39
|
+
errorTreePositions?: readonly number[] | null;
|
|
38
40
|
layout?: TModule | null;
|
|
39
41
|
layoutIndex: number;
|
|
40
42
|
loading?: TModule | null;
|
|
@@ -99,6 +101,8 @@ export type AppPageRouteWiringRoute<TModule extends AppPageModule = AppPageModul
|
|
|
99
101
|
id: string;
|
|
100
102
|
ownerTreePath: string;
|
|
101
103
|
state: AppElementsSlotBinding["state"];
|
|
104
|
+
/** A slot sub-route whose children its route segments don't describe. */
|
|
105
|
+
synthetic?: boolean;
|
|
102
106
|
} | null;
|
|
103
107
|
/**
|
|
104
108
|
* Static sibling segment names at each dynamic URL level for this route. Used
|
|
@@ -241,6 +245,12 @@ type BuildAppPageElementsOptions<TModule extends AppPageModule = AppPageModule,
|
|
|
241
245
|
sourceRouteGraphId: string;
|
|
242
246
|
}> | null;
|
|
243
247
|
semanticInterceptionTargetRouteId?: string | null;
|
|
248
|
+
/**
|
|
249
|
+
* A sibling intercept's segment state keys, app root first, down to the
|
|
250
|
+
* segment its page element renders. Its BFCache segment levels follow these
|
|
251
|
+
* rather than the source route's segments.
|
|
252
|
+
*/
|
|
253
|
+
siblingInterceptSegmentKeys?: readonly string[] | null;
|
|
244
254
|
sourcePageSegments?: readonly string[] | null;
|
|
245
255
|
};
|
|
246
256
|
export type AppPageTemplateEntry<TModule extends AppPageModule = AppPageModule> = {
|