@qzsy/vinext 0.1.11 → 0.1.122
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/dist/check.d.ts +8 -0
- package/dist/check.js +20 -9
- package/dist/cli.js +2 -2
- package/dist/deploy.js +10 -11
- package/dist/entries/app-rsc-entry.js +37 -8
- package/dist/entries/app-rsc-manifest.js +8 -0
- package/dist/entries/pages-client-entry.js +1 -0
- package/dist/entries/pages-server-entry.js +1 -0
- package/dist/index.js +12 -8
- package/dist/init.js +2 -1
- package/dist/plugins/middleware-server-only.d.ts +8 -6
- package/dist/plugins/middleware-server-only.js +8 -7
- package/dist/routing/app-route-graph.d.ts +6 -2
- package/dist/routing/app-route-graph.js +60 -12
- package/dist/routing/app-router.d.ts +5 -0
- package/dist/routing/app-router.js +5 -0
- package/dist/routing/file-matcher.d.ts +5 -0
- package/dist/routing/file-matcher.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +2 -1
- package/dist/server/app-browser-history-controller.js +6 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -1
- package/dist/server/app-fallback-renderer.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +12 -3
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +8 -2
- package/dist/server/app-page-dispatch.d.ts +11 -3
- package/dist/server/app-page-dispatch.js +54 -15
- package/dist/server/app-page-element-builder.d.ts +5 -1
- package/dist/server/app-page-element-builder.js +55 -19
- package/dist/server/app-page-head.d.ts +12 -0
- package/dist/server/app-page-head.js +42 -19
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +8 -1
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +1 -1
- package/dist/server/app-page-render.d.ts +4 -1
- package/dist/server/app-page-render.js +8 -3
- package/dist/server/app-page-request.d.ts +8 -1
- package/dist/server/app-page-request.js +23 -11
- package/dist/server/app-page-route-wiring.d.ts +6 -1
- package/dist/server/app-page-route-wiring.js +30 -8
- package/dist/server/app-page-search-params-observation.d.ts +4 -2
- package/dist/server/app-page-search-params-observation.js +11 -7
- package/dist/server/app-route-handler-dispatch.js +1 -0
- package/dist/server/app-route-handler-execution.js +2 -1
- 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-router-entry.js +7 -6
- package/dist/server/app-rsc-errors.js +7 -1
- package/dist/server/app-rsc-handler.js +4 -1
- package/dist/server/app-rsc-route-matching.d.ts +7 -0
- package/dist/server/app-rsc-route-matching.js +36 -3
- package/dist/server/app-segment-config.d.ts +1 -0
- package/dist/server/app-segment-config.js +32 -2
- package/dist/server/app-server-action-execution.d.ts +4 -0
- package/dist/server/app-server-action-execution.js +41 -10
- package/dist/server/app-static-generation.d.ts +1 -0
- package/dist/server/app-static-generation.js +1 -0
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/prod-server.js +15 -6
- package/dist/server/worker-utils.d.ts +2 -1
- package/dist/server/worker-utils.js +7 -1
- package/dist/shims/error-boundary.d.ts +19 -1
- package/dist/shims/error-boundary.js +11 -1
- package/dist/shims/headers.d.ts +3 -1
- package/dist/shims/headers.js +16 -5
- package/dist/shims/metadata.d.ts +3 -2
- package/dist/shims/metadata.js +8 -4
- package/dist/shims/router.js +13 -2
- package/dist/typegen.js +6 -5
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +1 -1
- package/dist/utils/project.d.ts +4 -0
- package/dist/utils/project.js +5 -1
- package/package.json +23 -24
|
@@ -20,6 +20,8 @@ type AppPageBuildRoute<TModule extends AppPageModule = AppPageModule, TErrorModu
|
|
|
20
20
|
type AppPageInterceptOptions<TModule extends AppPageModule = AppPageModule> = {
|
|
21
21
|
interceptionContext?: string | null;
|
|
22
22
|
interceptLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
23
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
24
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
23
25
|
interceptPage?: TModule | null;
|
|
24
26
|
interceptParams?: AppPageParams | null;
|
|
25
27
|
interceptSlotId?: string | null;
|
|
@@ -33,7 +35,9 @@ type AppPagePageRequest<TModule extends AppPageModule = AppPageModule> = {
|
|
|
33
35
|
isRscRequest: boolean; /** The incoming HTTP request (available but unused by this module). */
|
|
34
36
|
request: Request; /** Normalized x-vinext-mounted-slots header value. */
|
|
35
37
|
mountedSlotsHeader: string | null; /** Semantic RSC payload mode for this page render. */
|
|
36
|
-
renderMode?: AppRscRenderMode;
|
|
38
|
+
renderMode?: AppRscRenderMode; /** Observe page `searchParams` access for cache-safety classification. */
|
|
39
|
+
observePageSearchParamsAccess?: boolean; /** Observe page metadata `searchParams` access for cache-safety classification. */
|
|
40
|
+
observeMetadataSearchParamsAccess?: boolean;
|
|
37
41
|
};
|
|
38
42
|
type BuildPageElementsOptions<TModule extends AppPageModule = AppPageModule, TErrorModule extends AppPageErrorModule = AppPageErrorModule> = {
|
|
39
43
|
applyFileBasedMetadata?: ApplyAppPageFileBasedMetadata;
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { matchRoutePattern } from "../routing/route-pattern.js";
|
|
2
2
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
3
|
-
import { APP_RSC_RENDER_MODE_NAVIGATION
|
|
3
|
+
import { APP_RSC_RENDER_MODE_NAVIGATION } from "./app-rsc-render-mode.js";
|
|
4
4
|
import "./app-elements.js";
|
|
5
5
|
import { createAppPageRenderIdentity } from "./app-page-render-identity.js";
|
|
6
6
|
import { makeThenableParams } from "../shims/thenable-params.js";
|
|
7
|
+
import { resolveAppPageBranchParams } from "./app-page-params.js";
|
|
7
8
|
import { resolveActiveParallelRouteHeadInputs, resolveAppPageHead } from "./app-page-head.js";
|
|
8
|
-
import { makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
|
|
9
|
+
import { createAppPageSearchParamsObserver, makeObservedAppPageSearchParamsThenable } from "./app-page-search-params-observation.js";
|
|
9
10
|
import { buildAppPageElements, createAppPageSourcePage, createAppPageTreePath } from "./app-page-route-wiring.js";
|
|
10
11
|
import { DEFAULT_GLOBAL_ERROR_MODULE } from "./default-global-error-module.js";
|
|
11
12
|
import "./app-rsc-route-matching.js";
|
|
12
13
|
import { shouldServeStreamingMetadata } from "./streaming-metadata.js";
|
|
13
14
|
import { createElement } from "react";
|
|
14
15
|
//#region src/server/app-page-element-builder.ts
|
|
16
|
+
function resolveInterceptLayoutParams(branchSegments, layoutSegments, params) {
|
|
17
|
+
return resolveAppPageBranchParams(branchSegments, layoutSegments.length, params, layoutSegments);
|
|
18
|
+
}
|
|
19
|
+
const REACT_CLIENT_REFERENCE = Symbol.for("react.client.reference");
|
|
20
|
+
function isReactOwnedPageComponent(component) {
|
|
21
|
+
if (typeof component !== "function") return true;
|
|
22
|
+
const candidate = component;
|
|
23
|
+
return candidate.$$typeof === REACT_CLIENT_REFERENCE || candidate.prototype?.isReactComponent != null;
|
|
24
|
+
}
|
|
15
25
|
/**
|
|
16
26
|
* Build the App Router element tree for a matched route.
|
|
17
27
|
*
|
|
@@ -30,7 +40,8 @@ import { createElement } from "react";
|
|
|
30
40
|
*/
|
|
31
41
|
async function buildPageElements(options) {
|
|
32
42
|
const { route, params, routePath, displayPathname = routePath, pageRequest, globalErrorModule, rootNotFoundModule, rootForbiddenModule, rootUnauthorizedModule, metadataRoutes } = options;
|
|
33
|
-
const
|
|
43
|
+
const slotParamOverrides = resolveSlotParamOverrides(route, routePath);
|
|
44
|
+
const { opts, searchParams, isRscRequest, mountedSlotsHeader, renderMode = APP_RSC_RENDER_MODE_NAVIGATION, observeMetadataSearchParamsAccess = false, observePageSearchParamsAccess = false } = pageRequest;
|
|
34
45
|
const pageModule = route.page;
|
|
35
46
|
const isSiblingIntercept = opts?.interceptSlotKey === "__vinext_page_intercept" && !!opts?.interceptPage;
|
|
36
47
|
const effectivePageModule = isSiblingIntercept ? opts.interceptPage : pageModule;
|
|
@@ -69,40 +80,63 @@ async function buildPageElements(options) {
|
|
|
69
80
|
layoutModules: route.layouts,
|
|
70
81
|
layoutTreePositions: route.layoutTreePositions,
|
|
71
82
|
metadataRoutes,
|
|
72
|
-
pageModule: effectivePageModule ?? null,
|
|
73
|
-
parallelRoutes: resolveActiveParallelRouteHeadInputs({
|
|
83
|
+
pageModule: isSiblingIntercept ? null : effectivePageModule ?? null,
|
|
84
|
+
parallelRoutes: [...resolveActiveParallelRouteHeadInputs({
|
|
85
|
+
interceptBranchSegments: opts?.interceptBranchSegments ?? null,
|
|
74
86
|
interceptLayouts: opts?.interceptLayouts ?? null,
|
|
87
|
+
interceptLayoutSegments: opts?.interceptLayoutSegments ?? null,
|
|
75
88
|
interceptPage: opts?.interceptPage ?? null,
|
|
76
89
|
interceptParams: opts?.interceptParams ?? null,
|
|
77
90
|
interceptSlotKey: opts?.interceptSlotKey ?? null,
|
|
91
|
+
layoutTreePositions: route.layoutTreePositions,
|
|
78
92
|
params,
|
|
79
93
|
routeSegments: route.routeSegments ?? [],
|
|
94
|
+
slotParams: slotParamOverrides,
|
|
80
95
|
slots: route.slots ?? null
|
|
81
|
-
}),
|
|
96
|
+
}), ...isSiblingIntercept ? [{
|
|
97
|
+
layoutModules: opts?.interceptLayouts ?? [],
|
|
98
|
+
layoutParams: (opts?.interceptLayoutSegments ?? []).map((segments) => resolveInterceptLayoutParams(opts?.interceptBranchSegments ?? segments, segments, effectiveParams)),
|
|
99
|
+
pageModule: effectivePageModule ?? null,
|
|
100
|
+
params: effectiveParams,
|
|
101
|
+
routeSegments: opts?.interceptSourcePageSegments ?? route.routeSegments ?? []
|
|
102
|
+
}] : []],
|
|
82
103
|
params: effectiveParams,
|
|
83
104
|
routePath: route.pattern,
|
|
84
105
|
routeSegments: route.routeSegments ?? null,
|
|
85
|
-
searchParams
|
|
106
|
+
searchParams,
|
|
107
|
+
searchParamsObserver: observeMetadataSearchParamsAccess ? createAppPageSearchParamsObserver() : void 0
|
|
86
108
|
});
|
|
87
109
|
const pageProps = { params: makeThenableParams(effectiveParams) };
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
const hasRequestSearchParams = Object.keys(pageSearchParams).length > 0;
|
|
111
|
+
const createPageElement = (PageComponent, props) => {
|
|
112
|
+
if (isReactOwnedPageComponent(PageComponent)) {
|
|
113
|
+
const invocationProps = { ...props };
|
|
114
|
+
if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams, { markDynamic: hasRequestSearchParams }) : makeThenableParams(pageSearchParams);
|
|
115
|
+
return createElement(PageComponent, invocationProps);
|
|
116
|
+
}
|
|
117
|
+
const ServerPageComponent = PageComponent;
|
|
118
|
+
const PageInvoker = () => {
|
|
119
|
+
const invocationProps = { ...props };
|
|
120
|
+
if (searchParams) invocationProps.searchParams = observePageSearchParamsAccess ? makeObservedAppPageSearchParamsThenable(pageSearchParams) : makeThenableParams(pageSearchParams);
|
|
121
|
+
return ServerPageComponent(invocationProps);
|
|
122
|
+
};
|
|
123
|
+
return createElement(PageInvoker);
|
|
124
|
+
};
|
|
125
|
+
const pageSearchParamsThenable = searchParams ? makeThenableParams(pageSearchParams) : void 0;
|
|
93
126
|
const mountedSlotIds = mountedSlotsHeader ? new Set(mountedSlotsHeader.split(" ")) : null;
|
|
94
127
|
const slotOverrides = buildSlotOverrides(route, params, routePath, opts);
|
|
95
128
|
const metadataPlacement = hasDynamicMetadata && shouldServeStreamingMetadata(pageRequest.request.headers.get("user-agent") ?? "", options.htmlLimitedBots) ? "body" : "head";
|
|
96
|
-
let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ?
|
|
97
|
-
if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) {
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
129
|
+
let siblingInterceptElement = isSiblingIntercept && EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null;
|
|
130
|
+
if (isSiblingIntercept && siblingInterceptElement !== null && opts?.interceptLayouts?.length) for (let i = opts.interceptLayouts.length - 1; i >= 0; i--) {
|
|
131
|
+
const LayoutComponent = opts.interceptLayouts[i]?.default;
|
|
132
|
+
if (LayoutComponent) {
|
|
133
|
+
const interceptLayoutSegments = opts.interceptLayoutSegments?.[i] ?? [];
|
|
134
|
+
siblingInterceptElement = createElement(LayoutComponent, { params: makeThenableParams(resolveInterceptLayoutParams(opts.interceptBranchSegments ?? interceptLayoutSegments, interceptLayoutSegments, effectiveParams)) }, siblingInterceptElement);
|
|
102
135
|
}
|
|
103
136
|
}
|
|
104
137
|
return buildAppPageElements({
|
|
105
|
-
element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ?
|
|
138
|
+
element: isSiblingIntercept ? siblingInterceptElement : EffectivePageComponent ? createPageElement(EffectivePageComponent, pageProps) : null,
|
|
139
|
+
createPageElement,
|
|
106
140
|
globalErrorModule: globalErrorModule ?? DEFAULT_GLOBAL_ERROR_MODULE,
|
|
107
141
|
isRscRequest,
|
|
108
142
|
layoutParamAccess: options.layoutParamAccess,
|
|
@@ -143,7 +177,9 @@ async function buildPageElements(options) {
|
|
|
143
177
|
function buildSlotOverrides(route, routeParams, routePath, opts) {
|
|
144
178
|
const overrides = {};
|
|
145
179
|
if (opts && opts.interceptSlotKey && opts.interceptPage && opts.interceptSlotKey !== "__vinext_page_intercept") overrides[opts.interceptSlotKey] = {
|
|
180
|
+
branchSegments: opts.interceptBranchSegments ?? null,
|
|
146
181
|
layoutModules: opts.interceptLayouts || null,
|
|
182
|
+
layoutSegments: opts.interceptLayoutSegments ?? null,
|
|
147
183
|
pageModule: opts.interceptPage,
|
|
148
184
|
params: opts.interceptParams || routeParams
|
|
149
185
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MetadataFileRoute } from "./metadata-routes.js";
|
|
2
2
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
3
|
+
import { ThenableParamsObserver } from "../shims/thenable-params.js";
|
|
3
4
|
import { Metadata, Viewport } from "../shims/metadata.js";
|
|
4
5
|
import { applyFileBasedMetadata } from "./file-based-metadata.js";
|
|
5
6
|
|
|
@@ -8,23 +9,33 @@ type AppPageSearchParams = Record<string, string | string[]>;
|
|
|
8
9
|
type AppPageHeadModule = Record<string, unknown>;
|
|
9
10
|
type ApplyAppPageFileBasedMetadata = typeof applyFileBasedMetadata;
|
|
10
11
|
type AppPageHeadParallelRoute<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
12
|
+
layoutParams?: readonly AppPageParams[] | null;
|
|
11
13
|
layoutModule?: TModule | null;
|
|
12
14
|
layoutModules?: readonly (TModule | null | undefined)[] | null;
|
|
15
|
+
layoutTreePositions?: readonly number[] | null;
|
|
13
16
|
pageModule?: TModule | null;
|
|
14
17
|
params?: AppPageParams | null;
|
|
15
18
|
routeSegments?: readonly string[] | null;
|
|
16
19
|
};
|
|
17
20
|
type AppPageHeadSlot<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
21
|
+
configLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
22
|
+
configLayoutTreePositions?: readonly number[] | null;
|
|
18
23
|
layout?: TModule | null;
|
|
24
|
+
layoutIndex?: number;
|
|
19
25
|
page?: TModule | null;
|
|
26
|
+
routeSegments?: readonly string[] | null;
|
|
20
27
|
};
|
|
21
28
|
type ResolveActiveParallelRouteHeadInputsOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
22
29
|
interceptLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
30
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
31
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
23
32
|
interceptPage?: TModule | null;
|
|
24
33
|
interceptParams?: AppPageParams | null;
|
|
25
34
|
interceptSlotKey?: string | null;
|
|
35
|
+
layoutTreePositions?: readonly number[] | null;
|
|
26
36
|
params: AppPageParams;
|
|
27
37
|
routeSegments: readonly string[];
|
|
38
|
+
slotParams?: Readonly<Record<string, AppPageParams>> | null;
|
|
28
39
|
slots?: Record<string, AppPageHeadSlot<TModule>> | null;
|
|
29
40
|
};
|
|
30
41
|
type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadModule> = {
|
|
@@ -46,6 +57,7 @@ type ResolveAppPageHeadOptions<TModule extends AppPageHeadModule = AppPageHeadMo
|
|
|
46
57
|
routePath: string;
|
|
47
58
|
routeSegments?: readonly string[] | null;
|
|
48
59
|
searchParams?: URLSearchParams | null;
|
|
60
|
+
searchParamsObserver?: ThenableParamsObserver;
|
|
49
61
|
};
|
|
50
62
|
type ResolveAppPageHeadResult = {
|
|
51
63
|
hasDynamicMetadata: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { runWithFetchDedupe } from "../shims/fetch-cache.js";
|
|
2
2
|
import { mergeMetadataEntries, mergeViewport, postProcessMetadata, resolveModuleMetadata as resolveModuleMetadata$1, resolveModuleViewport } from "../shims/metadata.js";
|
|
3
|
-
import { resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
3
|
+
import { resolveAppPageBranchParams, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
4
4
|
import { tagAppPageMetadataError } from "./app-page-execution.js";
|
|
5
5
|
//#region src/server/app-page-head.ts
|
|
6
6
|
/**
|
|
@@ -24,23 +24,44 @@ async function resolveModuleMetadata(...args) {
|
|
|
24
24
|
}
|
|
25
25
|
function resolveActiveParallelRouteHeadInputs(options) {
|
|
26
26
|
return Object.entries(options.slots ?? {}).map(([slotKey, slot]) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
const ownerTreePosition = options.layoutTreePositions?.[slot.layoutIndex ?? 0] ?? 0;
|
|
28
|
+
const ownerParams = resolveAppPageSegmentParams(options.routeSegments, ownerTreePosition, options.params);
|
|
29
|
+
if (options.interceptSlotKey === slotKey && options.interceptPage) {
|
|
30
|
+
const interceptLayouts = options.interceptLayouts ?? [];
|
|
31
|
+
return {
|
|
32
|
+
layoutModules: [slot.layout, ...interceptLayouts].filter(isPresent),
|
|
33
|
+
layoutParams: [...slot.layout ? [ownerParams] : [], ...interceptLayouts.filter(isPresent).map((_, index) => {
|
|
34
|
+
const segments = options.interceptLayoutSegments?.[index] ?? [];
|
|
35
|
+
return {
|
|
36
|
+
...ownerParams,
|
|
37
|
+
...resolveParallelLayoutParams(options.interceptBranchSegments ?? segments, segments.length, options.interceptParams ?? options.params)
|
|
38
|
+
};
|
|
39
|
+
})],
|
|
40
|
+
layoutTreePositions: [...slot.layout ? [0] : [], ...interceptLayouts.filter(isPresent).map(() => options.routeSegments.length)],
|
|
41
|
+
pageModule: options.interceptPage,
|
|
42
|
+
params: options.interceptParams ?? options.params,
|
|
43
|
+
routeSegments: options.routeSegments
|
|
44
|
+
};
|
|
45
|
+
}
|
|
33
46
|
return {
|
|
34
|
-
layoutModules: slot.layout
|
|
47
|
+
layoutModules: [slot.layout, ...slot.configLayouts ?? []].filter(isPresent),
|
|
48
|
+
layoutParams: [...slot.layout ? [ownerParams] : [], ...(slot.configLayoutTreePositions ?? []).map((treePosition) => ({
|
|
49
|
+
...ownerParams,
|
|
50
|
+
...resolveParallelLayoutParams(slot.routeSegments ?? options.routeSegments, treePosition, options.slotParams?.[slotKey] ?? options.params)
|
|
51
|
+
}))],
|
|
52
|
+
layoutTreePositions: [...slot.layout ? [0] : [], ...slot.configLayoutTreePositions ?? []],
|
|
35
53
|
pageModule: slot.page,
|
|
36
|
-
params: options.params,
|
|
37
|
-
routeSegments: options.routeSegments
|
|
54
|
+
params: options.slotParams?.[slotKey] ?? options.params,
|
|
55
|
+
routeSegments: slot.routeSegments ?? options.routeSegments
|
|
38
56
|
};
|
|
39
57
|
});
|
|
40
58
|
}
|
|
41
59
|
function isPresent(value) {
|
|
42
60
|
return value !== null && value !== void 0;
|
|
43
61
|
}
|
|
62
|
+
function resolveParallelLayoutParams(routeSegments, treePosition, params) {
|
|
63
|
+
return resolveAppPageBranchParams(routeSegments, treePosition, params);
|
|
64
|
+
}
|
|
44
65
|
function hasGenerateMetadata(module) {
|
|
45
66
|
return typeof module?.generateMetadata === "function";
|
|
46
67
|
}
|
|
@@ -111,7 +132,7 @@ async function resolveLayoutViewport(layoutInputs, params, routeSegments) {
|
|
|
111
132
|
return resolveModuleViewport(layoutInput.module, layoutParams);
|
|
112
133
|
}));
|
|
113
134
|
}
|
|
114
|
-
async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent) {
|
|
135
|
+
async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackRouteSegments, pageSearchParams, parent, searchParamsObserver) {
|
|
115
136
|
const params = parallelRoute.params ?? fallbackParams;
|
|
116
137
|
const routeSegments = parallelRoute.routeSegments ?? fallbackRouteSegments;
|
|
117
138
|
const metadataResults = [];
|
|
@@ -119,13 +140,15 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
|
|
|
119
140
|
const metadataSources = [];
|
|
120
141
|
let accumulatedMetadata = parent;
|
|
121
142
|
const layoutModules = [...parallelRoute.layoutModules ?? [], parallelRoute.layoutModule].filter(isPresent);
|
|
143
|
+
const layoutTreePositions = parallelRoute.layoutTreePositions ?? [];
|
|
144
|
+
const layoutParams = parallelRoute.layoutParams ?? [];
|
|
122
145
|
const hasDynamicMetadata = layoutModules.some(hasGenerateMetadata) || hasGenerateMetadata(parallelRoute.pageModule);
|
|
123
|
-
const layoutViewportPromises = layoutModules.map((layoutModule) => resolveModuleViewport(layoutModule, params));
|
|
124
|
-
const pageViewportPromise = parallelRoute.pageModule ? resolveModuleViewport(parallelRoute.pageModule, params) : Promise.resolve(null);
|
|
146
|
+
const layoutViewportPromises = layoutModules.map((layoutModule, index) => resolveModuleViewport(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params)));
|
|
147
|
+
const pageViewportPromise = parallelRoute.pageModule ? resolveModuleViewport(parallelRoute.pageModule, params, pageSearchParams, searchParamsObserver) : Promise.resolve(null);
|
|
125
148
|
for (const layoutViewportPromise of layoutViewportPromises) layoutViewportPromise.catch(() => null);
|
|
126
149
|
pageViewportPromise.catch(() => null);
|
|
127
|
-
for (const layoutModule of layoutModules) {
|
|
128
|
-
const layoutMetadata = await resolveModuleMetadata(layoutModule, params, void 0, accumulatedMetadata);
|
|
150
|
+
for (const [index, layoutModule] of layoutModules.entries()) {
|
|
151
|
+
const layoutMetadata = await resolveModuleMetadata(layoutModule, layoutParams[index] ?? resolveParallelLayoutParams(routeSegments, layoutTreePositions[index] ?? 0, params), void 0, accumulatedMetadata);
|
|
129
152
|
metadataResults.push(layoutMetadata);
|
|
130
153
|
metadataSources.push({
|
|
131
154
|
metadata: layoutMetadata,
|
|
@@ -137,7 +160,7 @@ async function resolveParallelRouteHead(parallelRoute, fallbackParams, fallbackR
|
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
162
|
if (parallelRoute.pageModule) {
|
|
140
|
-
const pageMetadata = await resolveModuleMetadata(parallelRoute.pageModule, params, pageSearchParams, accumulatedMetadata);
|
|
163
|
+
const pageMetadata = await resolveModuleMetadata(parallelRoute.pageModule, params, pageSearchParams, accumulatedMetadata, searchParamsObserver);
|
|
141
164
|
metadataResults.push(pageMetadata);
|
|
142
165
|
metadataSources.push({
|
|
143
166
|
metadata: pageMetadata,
|
|
@@ -170,9 +193,9 @@ async function resolveAppPageHeadInner(options) {
|
|
|
170
193
|
layoutMetadataResultsForParent.catch(() => null);
|
|
171
194
|
const pageParentPromise = layoutMetadataResultsForParent.then((metadataResults) => metadataResults.length > 0 ? mergeMetadataEntries(metadataResults.map((metadata) => ({ metadata }))) : {});
|
|
172
195
|
pageParentPromise.catch(() => null);
|
|
173
|
-
const pageMetadataPromise = options.pageModule ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise) : Promise.resolve(null);
|
|
174
|
-
const pageViewportPromise = options.pageModule ? resolveModuleViewport(options.pageModule, options.params) : Promise.resolve(null);
|
|
175
|
-
const parallelRouteHeadPromise = Promise.all((options.parallelRoutes ?? []).map((parallelRoute) => resolveParallelRouteHead(parallelRoute, options.params, routeSegments, pageSearchParams, pageParentPromise)));
|
|
196
|
+
const pageMetadataPromise = options.pageModule ? resolveModuleMetadata(options.pageModule, options.params, pageSearchParams, pageParentPromise, options.searchParamsObserver) : Promise.resolve(null);
|
|
197
|
+
const pageViewportPromise = options.pageModule ? resolveModuleViewport(options.pageModule, options.params, pageSearchParams, options.searchParamsObserver) : Promise.resolve(null);
|
|
198
|
+
const parallelRouteHeadPromise = Promise.all((options.parallelRoutes ?? []).map((parallelRoute) => resolveParallelRouteHead(parallelRoute, options.params, routeSegments, pageSearchParams, pageParentPromise, options.searchParamsObserver)));
|
|
176
199
|
const [layoutMetadataResults, layoutViewportResults, pageMetadata, pageViewport, parallelRouteHeads] = await Promise.all([
|
|
177
200
|
layoutMetadataPromise,
|
|
178
201
|
layoutViewportPromise,
|
|
@@ -4,5 +4,6 @@ import { AppPageParams } from "./app-page-boundary.js";
|
|
|
4
4
|
declare function getAppPageSegmentParamName(segment: string): string | null;
|
|
5
5
|
declare function resolveAppPageSegmentParamScopeKeys(routeSegments: readonly string[] | null | undefined, treePosition: number): readonly string[];
|
|
6
6
|
declare function resolveAppPageSegmentParams(routeSegments: readonly string[] | null | undefined, treePosition: number, matchedParams: AppPageParams): AppPageParams;
|
|
7
|
+
declare function resolveAppPageBranchParams(branchSegments: readonly string[], treePosition: number, matchedParams: AppPageParams, scopedSegments?: readonly string[]): AppPageParams;
|
|
7
8
|
//#endregion
|
|
8
|
-
export { getAppPageSegmentParamName, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams };
|
|
9
|
+
export { getAppPageSegmentParamName, resolveAppPageBranchParams, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams };
|
|
@@ -35,5 +35,12 @@ function resolveAppPageSegmentParams(routeSegments, treePosition, matchedParams)
|
|
|
35
35
|
}
|
|
36
36
|
return segmentParams;
|
|
37
37
|
}
|
|
38
|
+
function resolveAppPageBranchParams(branchSegments, treePosition, matchedParams, scopedSegments = branchSegments) {
|
|
39
|
+
const branchParamNames = new Set(branchSegments.map(getAppPageSegmentParamName).filter((name) => name !== null));
|
|
40
|
+
const scopedParams = {};
|
|
41
|
+
for (const [name, value] of Object.entries(matchedParams)) if (!branchParamNames.has(name)) scopedParams[name] = value;
|
|
42
|
+
Object.assign(scopedParams, resolveAppPageSegmentParams(scopedSegments, treePosition, matchedParams));
|
|
43
|
+
return scopedParams;
|
|
44
|
+
}
|
|
38
45
|
//#endregion
|
|
39
|
-
export { getAppPageSegmentParamName, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams };
|
|
46
|
+
export { getAppPageSegmentParamName, resolveAppPageBranchParams, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams };
|
|
@@ -119,6 +119,7 @@ type ProbeAppPageBeforeRenderResult = {
|
|
|
119
119
|
};
|
|
120
120
|
type ProbeAppPageBeforeRenderOptions = {
|
|
121
121
|
hasLoadingBoundary: boolean;
|
|
122
|
+
probePageBeforeRender?: boolean;
|
|
122
123
|
skipProbes?: boolean;
|
|
123
124
|
layoutCount: number;
|
|
124
125
|
probeLayoutAt: (layoutIndex: number) => unknown;
|
|
@@ -252,7 +252,7 @@ async function probeAppPageBeforeRender(options) {
|
|
|
252
252
|
layoutFlags
|
|
253
253
|
};
|
|
254
254
|
}
|
|
255
|
-
if (options.hasLoadingBoundary) return {
|
|
255
|
+
if (options.hasLoadingBoundary || options.probePageBeforeRender === false) return {
|
|
256
256
|
response: null,
|
|
257
257
|
layoutFlags
|
|
258
258
|
};
|
|
@@ -37,6 +37,7 @@ type RenderAppPageLifecycleOptions = {
|
|
|
37
37
|
cleanPathname: string;
|
|
38
38
|
clearRequestContext: () => void;
|
|
39
39
|
consumeDynamicUsage: () => boolean;
|
|
40
|
+
peekDynamicUsage?: () => boolean;
|
|
40
41
|
consumeRenderObservationState?: () => AppPageRenderObservationState; /** Read and clear any invalid dynamic usage error recorded during render (dev-only). */
|
|
41
42
|
consumeInvalidDynamicUsageError?: () => unknown;
|
|
42
43
|
createRscOnErrorHandler: (pathname: string, routePath: string) => AppPageBoundaryOnError;
|
|
@@ -60,6 +61,8 @@ type RenderAppPageLifecycleOptions = {
|
|
|
60
61
|
isProgressiveActionRender?: boolean;
|
|
61
62
|
isPrerender?: boolean;
|
|
62
63
|
isProduction: boolean;
|
|
64
|
+
probePageBeforeRender?: boolean;
|
|
65
|
+
omitPendingDynamicCacheState?: boolean;
|
|
63
66
|
isRscRequest: boolean;
|
|
64
67
|
isrDebug?: AppPageDebugLogger;
|
|
65
68
|
isrHtmlKey: (pathname: string) => string;
|
|
@@ -81,7 +84,7 @@ type RenderAppPageLifecycleOptions = {
|
|
|
81
84
|
expireSeconds?: number;
|
|
82
85
|
formState?: ReactFormState | null;
|
|
83
86
|
revalidateSeconds: number | null;
|
|
84
|
-
renderErrorBoundaryResponse: (error: unknown) => Promise<Response | null>;
|
|
87
|
+
renderErrorBoundaryResponse: (error: unknown, errorOrigin: "rsc" | "ssr") => Promise<Response | null>;
|
|
85
88
|
renderLayoutSpecialError: (specialError: AppPageSpecialError, layoutIndex: number) => Promise<Response>;
|
|
86
89
|
renderPageSpecialError: (specialError: AppPageSpecialError) => Promise<Response>;
|
|
87
90
|
renderToReadableStream: (element: ReactNode | AppOutgoingElements, options: {
|
|
@@ -248,8 +248,11 @@ function wrapRscResponseForDevErrorReporting(response, consumeInvalidDynamicUsag
|
|
|
248
248
|
});
|
|
249
249
|
}
|
|
250
250
|
async function renderAppPageLifecycle(options) {
|
|
251
|
+
const configuredProbePageBeforeRender = options.probePageBeforeRender ?? options.isRscRequest;
|
|
252
|
+
const probePageBeforeRender = options.isRscRequest || configuredProbePageBeforeRender && !(options.peekDynamicUsage?.() ?? false);
|
|
251
253
|
const preRenderResult = await probeAppPageBeforeRender({
|
|
252
254
|
hasLoadingBoundary: options.hasLoadingBoundary,
|
|
255
|
+
probePageBeforeRender,
|
|
253
256
|
skipProbes: options.pprFallbackShellSignal !== void 0,
|
|
254
257
|
layoutCount: options.layoutCount,
|
|
255
258
|
probeLayoutAt(layoutIndex) {
|
|
@@ -430,7 +433,8 @@ async function renderAppPageLifecycle(options) {
|
|
|
430
433
|
if (!options.isProduction) renderEnd = performance.now();
|
|
431
434
|
},
|
|
432
435
|
renderErrorBoundaryResponse(error) {
|
|
433
|
-
|
|
436
|
+
const capturedRscError = rscErrorTracker.getCapturedError();
|
|
437
|
+
return options.renderErrorBoundaryResponse(capturedRscError ?? error, capturedRscError === null ? "ssr" : "rsc");
|
|
434
438
|
},
|
|
435
439
|
async renderHtmlStream() {
|
|
436
440
|
const ssrHandler = await options.loadSsrHandler();
|
|
@@ -462,7 +466,7 @@ async function renderAppPageLifecycle(options) {
|
|
|
462
466
|
if (!htmlStream) throw new Error("[vinext] Expected an HTML stream when no fallback response was returned");
|
|
463
467
|
const linkHeader = buildAppPageLinkHeader(htmlRender.linkHeader, fontLinkHeader, options.reactMaxHeadersLength);
|
|
464
468
|
if (options.isPrerender === true) await htmlRender.metadataReady;
|
|
465
|
-
if (options.hasLoadingBoundary) {
|
|
469
|
+
if (options.hasLoadingBoundary || !probePageBeforeRender) {
|
|
466
470
|
const captured = rscErrorTracker.getCapturedSpecialError();
|
|
467
471
|
if (captured) {
|
|
468
472
|
const specialError = resolveAppPageSpecialError(captured);
|
|
@@ -480,8 +484,8 @@ async function renderAppPageLifecycle(options) {
|
|
|
480
484
|
revalidateSeconds
|
|
481
485
|
}));
|
|
482
486
|
}
|
|
487
|
+
let dynamicUsedDuringRender = options.consumeDynamicUsage();
|
|
483
488
|
const draftCookie = options.getDraftModeCookieHeader();
|
|
484
|
-
const dynamicUsedDuringRender = options.consumeDynamicUsage();
|
|
485
489
|
let dynamicUsedBeforeContextCleanup = dynamicUsedDuringRender;
|
|
486
490
|
const safeHtmlStream = deferUntilStreamConsumed(htmlStream, () => {
|
|
487
491
|
dynamicUsedBeforeContextCleanup = dynamicUsedBeforeContextCleanup || options.consumeDynamicUsage();
|
|
@@ -575,6 +579,7 @@ async function renderAppPageLifecycle(options) {
|
|
|
575
579
|
isrRscKey: options.isrRscKey,
|
|
576
580
|
isrSet: options.isrSet,
|
|
577
581
|
interceptionContext: options.interceptionContext,
|
|
582
|
+
omitPendingDynamicCacheState: options.omitPendingDynamicCacheState,
|
|
578
583
|
preserveClientResponseHeaders: !htmlResponsePolicy.shouldWriteToCache,
|
|
579
584
|
expireSeconds,
|
|
580
585
|
revalidateSeconds,
|
|
@@ -40,8 +40,11 @@ type BuildAppPageElementResult<TElement> = {
|
|
|
40
40
|
};
|
|
41
41
|
type AppPageInterceptMatch<TPage = unknown> = {
|
|
42
42
|
interceptLayouts?: readonly unknown[] | null;
|
|
43
|
+
interceptLayoutSegments?: readonly (readonly string[])[] | null;
|
|
44
|
+
interceptBranchSegments?: readonly string[] | null;
|
|
43
45
|
__loadInterceptLayouts?: readonly (() => Promise<unknown>)[] | null;
|
|
44
46
|
matchedParams: AppPageParams;
|
|
47
|
+
sourceMatchedParams?: AppPageParams;
|
|
45
48
|
page: TPage;
|
|
46
49
|
__pageLoader?: (() => Promise<TPage>) | null;
|
|
47
50
|
__loadState?: {
|
|
@@ -88,7 +91,10 @@ type ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts> = {
|
|
|
88
91
|
type ResolveAppPageActionRerenderTargetOptions<TRoute, TPage, TInterceptOpts> = ResolveAppPageInterceptionRerenderTargetOptions<TRoute, TPage, TInterceptOpts>;
|
|
89
92
|
type ResolveAppPageActionRerenderTargetResult<TRoute, TInterceptOpts> = ResolveAppPageInterceptionRerenderTargetResult<TRoute, TInterceptOpts>;
|
|
90
93
|
type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
|
|
91
|
-
buildPageElement: (route: TRoute, params: AppPageParams, interceptOpts: TInterceptOpts | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker
|
|
94
|
+
buildPageElement: (route: TRoute, params: AppPageParams, interceptOpts: TInterceptOpts | undefined, searchParams: URLSearchParams, layoutParamAccess?: AppLayoutParamAccessTracker, buildOptions?: {
|
|
95
|
+
observeMetadataSearchParamsAccess?: boolean;
|
|
96
|
+
observePageSearchParamsAccess?: boolean;
|
|
97
|
+
}) => Promise<TElement>;
|
|
92
98
|
cleanPathname: string;
|
|
93
99
|
currentRoute: TRoute;
|
|
94
100
|
findIntercept: (pathname: string) => AppPageInterceptMatch<TPage> | null;
|
|
@@ -98,6 +104,7 @@ type ResolveAppPageInterceptOptions<TRoute, TPage, TInterceptOpts, TElement> = {
|
|
|
98
104
|
layoutParamAccess?: AppLayoutParamAccessTracker;
|
|
99
105
|
resolveNavigationParams: (route: TRoute, params: AppPageParams, pathname: string, interceptOpts: TInterceptOpts) => AppPageParams;
|
|
100
106
|
renderInterceptResponse: (route: TRoute, element: TElement) => Promise<Response> | Response;
|
|
107
|
+
resolveSearchParams?: (route: TRoute, searchParams: URLSearchParams) => Awaitable<URLSearchParams>;
|
|
101
108
|
searchParams: URLSearchParams;
|
|
102
109
|
setNavigationContext: (context: {
|
|
103
110
|
params: AppPageParams;
|
|
@@ -97,7 +97,7 @@ async function resolveAppPageInterceptMatch(options) {
|
|
|
97
97
|
return {
|
|
98
98
|
interceptOpts: options.toInterceptOpts(interceptState.intercept),
|
|
99
99
|
matchedParams: interceptState.intercept.matchedParams,
|
|
100
|
-
sourceParams: pickRouteParams(interceptState.intercept.matchedParams, options.getRouteParamNames(interceptState.sourceRoute)),
|
|
100
|
+
sourceParams: pickRouteParams(interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams, options.getRouteParamNames(interceptState.sourceRoute)),
|
|
101
101
|
sourceRoute: interceptState.sourceRoute
|
|
102
102
|
};
|
|
103
103
|
}
|
|
@@ -145,12 +145,18 @@ async function resolveAppPageInterceptionRerenderTarget(options) {
|
|
|
145
145
|
isRscRequest: options.isRscRequest,
|
|
146
146
|
toInterceptOpts: options.toInterceptOpts
|
|
147
147
|
});
|
|
148
|
-
if (interceptState.kind === "source-route")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
148
|
+
if (interceptState.kind === "source-route") {
|
|
149
|
+
const sourceMatchedParams = interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams;
|
|
150
|
+
return {
|
|
151
|
+
interceptOpts: options.toInterceptOpts(interceptState.intercept),
|
|
152
|
+
navigationParams: {
|
|
153
|
+
...sourceMatchedParams,
|
|
154
|
+
...interceptState.intercept.matchedParams
|
|
155
|
+
},
|
|
156
|
+
params: pickRouteParams(sourceMatchedParams, options.getRouteParamNames(interceptState.sourceRoute)),
|
|
157
|
+
route: interceptState.sourceRoute
|
|
158
|
+
};
|
|
159
|
+
}
|
|
154
160
|
return {
|
|
155
161
|
interceptOpts: interceptState.kind === "current-route" ? options.toInterceptOpts(interceptState.intercept) : void 0,
|
|
156
162
|
navigationParams: options.currentParams,
|
|
@@ -174,13 +180,19 @@ async function resolveAppPageIntercept(options) {
|
|
|
174
180
|
if (interceptState.kind === "source-route") {
|
|
175
181
|
const renderRoute = interceptState.sourceRoute;
|
|
176
182
|
const interceptOpts = options.toInterceptOpts(interceptState.intercept);
|
|
177
|
-
const
|
|
183
|
+
const sourceMatchedParams = interceptState.intercept.sourceMatchedParams ?? interceptState.intercept.matchedParams;
|
|
184
|
+
const navigationParams = {
|
|
185
|
+
...sourceMatchedParams,
|
|
186
|
+
...interceptState.intercept.matchedParams
|
|
187
|
+
};
|
|
188
|
+
const renderSearchParams = options.resolveSearchParams ? await options.resolveSearchParams(renderRoute, options.searchParams) : options.searchParams;
|
|
189
|
+
const renderParams = pickRouteParams(sourceMatchedParams, options.getRouteParamNames(interceptState.sourceRoute));
|
|
178
190
|
options.setNavigationContext({
|
|
179
|
-
params: options.resolveNavigationParams(renderRoute,
|
|
191
|
+
params: options.resolveNavigationParams(renderRoute, navigationParams, options.cleanPathname, interceptOpts),
|
|
180
192
|
pathname: options.cleanPathname,
|
|
181
|
-
searchParams:
|
|
193
|
+
searchParams: renderSearchParams
|
|
182
194
|
});
|
|
183
|
-
const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts,
|
|
195
|
+
const interceptElement = await options.buildPageElement(renderRoute, renderParams, interceptOpts, renderSearchParams, options.layoutParamAccess);
|
|
184
196
|
return {
|
|
185
197
|
interceptOpts: void 0,
|
|
186
198
|
response: await options.renderInterceptResponse(renderRoute, interceptElement)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { AppRouteSemanticIds } from "../routing/app-route-graph.js";
|
|
2
2
|
import { AppElements, AppElementsInterception } from "./app-elements-wire.js";
|
|
3
3
|
import { AppPageParams } from "./app-page-boundary.js";
|
|
4
|
-
import { Metadata, Viewport } from "../shims/metadata.js";
|
|
5
4
|
import { ThenableParamsObserver } from "../shims/thenable-params.js";
|
|
5
|
+
import { Metadata, Viewport } from "../shims/metadata.js";
|
|
6
6
|
import { AppLayoutParamAccessTracker } from "./app-layout-param-observation.js";
|
|
7
7
|
import { AppRscRenderMode } from "./app-rsc-render-mode.js";
|
|
8
8
|
import { AppPageRenderIdentity } from "./app-page-render-identity.js";
|
|
@@ -31,6 +31,8 @@ type AppPageRouteWiringSlot<TModule extends AppPageModule = AppPageModule, TErro
|
|
|
31
31
|
/** Graph-owned semantic slot identity. */id?: string | null; /** Slot prop name passed to the owning layout (e.g. "modal" from @modal). */
|
|
32
32
|
name: string;
|
|
33
33
|
default?: TModule | null;
|
|
34
|
+
configLayouts?: readonly (TModule | null | undefined)[] | null;
|
|
35
|
+
configLayoutTreePositions?: readonly number[] | null;
|
|
34
36
|
error?: TErrorModule | null;
|
|
35
37
|
layout?: TModule | null;
|
|
36
38
|
layoutIndex: number;
|
|
@@ -83,6 +85,8 @@ type AppPageRouteWiringRoute<TModule extends AppPageModule = AppPageModule, TErr
|
|
|
83
85
|
templates?: readonly (TModule | null | undefined)[] | null;
|
|
84
86
|
};
|
|
85
87
|
type AppPageSlotOverride<TModule extends AppPageModule = AppPageModule> = {
|
|
88
|
+
branchSegments?: readonly string[] | null;
|
|
89
|
+
layoutSegments?: readonly (readonly string[])[] | null;
|
|
86
90
|
layoutModules?: readonly (TModule | null | undefined)[] | null;
|
|
87
91
|
/**
|
|
88
92
|
* The page module to render for this slot. Optional — when omitted, the
|
|
@@ -118,6 +122,7 @@ type BuildAppPageRouteElementOptions<TModule extends AppPageModule = AppPageModu
|
|
|
118
122
|
rootNotFoundModule?: TModule | null;
|
|
119
123
|
rootUnauthorizedModule?: TModule | null;
|
|
120
124
|
route: AppPageRouteWiringRoute<TModule, TErrorModule>;
|
|
125
|
+
createPageElement?: (component: AppPageComponent, props: Readonly<Record<string, unknown>>) => ReactNode;
|
|
121
126
|
searchParams?: unknown;
|
|
122
127
|
slotOverrides?: Readonly<Record<string, AppPageSlotOverride<TModule>>> | null;
|
|
123
128
|
};
|
|
@@ -8,7 +8,7 @@ import { AppRouterScrollTarget } from "../shims/app-router-scroll.js";
|
|
|
8
8
|
import { LayoutSegmentProvider } from "../shims/layout-segment-context.js";
|
|
9
9
|
import { MetadataHead, ViewportHead, renderMetadataToHtml } from "../shims/metadata.js";
|
|
10
10
|
import { Children as Children$1, ParallelSlot, Slot } from "../shims/slot.js";
|
|
11
|
-
import { resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
11
|
+
import { resolveAppPageBranchParams, resolveAppPageSegmentParamScopeKeys, resolveAppPageSegmentParams } from "./app-page-params.js";
|
|
12
12
|
import { probeReactServerSubtree } from "./app-page-probe.js";
|
|
13
13
|
import { resolveAppPageChildSegments, resolveAppPageRouteStateKey, resolveAppPageSegmentStateKey } from "./app-page-segment-state.js";
|
|
14
14
|
import { Fragment, Suspense } from "react";
|
|
@@ -16,6 +16,9 @@ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
//#region src/server/app-page-route-wiring.tsx
|
|
17
17
|
const APP_PAGE_LAYOUT_PROBE_CHILD = /* @__PURE__ */ jsx(Fragment, {});
|
|
18
18
|
const DEFAULT_GLOBAL_ERROR_COMPONENT = DefaultGlobalError;
|
|
19
|
+
function resolveSlotLayoutParams(routeSegments, treePosition, params) {
|
|
20
|
+
return resolveAppPageBranchParams(routeSegments, treePosition, params);
|
|
21
|
+
}
|
|
19
22
|
function getDefaultExport(module) {
|
|
20
23
|
return module?.default ?? null;
|
|
21
24
|
}
|
|
@@ -293,7 +296,9 @@ function buildAppPageElements(options) {
|
|
|
293
296
|
const slotId = resolveAppPageSlotId(slot, layoutEntries[targetIndex]?.treePath ?? "/");
|
|
294
297
|
const slotOverride = resolveSlotOverride(slotKey, slotName);
|
|
295
298
|
const slotParams = getEffectiveSlotParams(slotKey, slotName);
|
|
296
|
-
const
|
|
299
|
+
const slotRouteSegments = slot.routeSegments ?? [];
|
|
300
|
+
const slotOwnerParams = resolveAppPageSegmentParams(options.route.routeSegments, layoutEntries[targetIndex]?.treePosition ?? 0, options.matchedParams);
|
|
301
|
+
const slotResetKey = resolveAppPageRouteStateKey(slotRouteSegments, slotParams);
|
|
297
302
|
const overrideOrPageComponent = getDefaultExport(slotOverride?.pageModule) ?? getDefaultExport(slot.page);
|
|
298
303
|
const defaultComponent = getDefaultExport(slot.default);
|
|
299
304
|
if (!overrideOrPageComponent && defaultComponent && options.isRscRequest && options.mountedSlotIds?.has(slotId)) continue;
|
|
@@ -302,23 +307,40 @@ function buildAppPageElements(options) {
|
|
|
302
307
|
elements[slotId] = AppElementsWire.unmatchedSlotValue;
|
|
303
308
|
continue;
|
|
304
309
|
}
|
|
305
|
-
const
|
|
306
|
-
const slotProps = { params: slotThenableParams };
|
|
310
|
+
const slotProps = { params: options.makeThenableParams(slotParams) };
|
|
307
311
|
if (options.searchParams !== void 0) slotProps.searchParams = options.searchParams;
|
|
308
312
|
if (slotOverride?.props) Object.assign(slotProps, slotOverride.props);
|
|
309
|
-
let slotElement =
|
|
313
|
+
let slotElement = options.createPageElement ? options.createPageElement(slotComponent, slotProps) : (() => {
|
|
314
|
+
return /* @__PURE__ */ jsx(slotComponent, { ...slotProps });
|
|
315
|
+
})();
|
|
316
|
+
const hasSlotTreeOverride = slotOverride?.pageModule != null || slotOverride?.layoutModules !== void 0;
|
|
310
317
|
const interceptLayouts = slotOverride?.layoutModules ?? [];
|
|
311
318
|
for (let layoutIndex = interceptLayouts.length - 1; layoutIndex >= 0; layoutIndex--) {
|
|
312
319
|
const interceptLayoutComponent = getDefaultExport(interceptLayouts[layoutIndex]);
|
|
313
320
|
if (!interceptLayoutComponent) continue;
|
|
314
|
-
|
|
315
|
-
|
|
321
|
+
const InterceptLayoutComponent = interceptLayoutComponent;
|
|
322
|
+
const interceptLayoutParams = resolveSlotLayoutParams(slotOverride?.branchSegments ?? slotRouteSegments, slotOverride?.layoutSegments?.[layoutIndex]?.length ?? slotRouteSegments.length, slotParams);
|
|
323
|
+
slotElement = /* @__PURE__ */ jsx(InterceptLayoutComponent, {
|
|
324
|
+
params: options.makeThenableParams(interceptLayoutParams),
|
|
325
|
+
children: slotElement
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (!hasSlotTreeOverride) for (let layoutIndex = (slot.configLayouts?.length ?? 0) - 1; layoutIndex >= 0; layoutIndex--) {
|
|
329
|
+
const nestedLayoutComponent = getDefaultExport(slot.configLayouts?.[layoutIndex]);
|
|
330
|
+
if (!nestedLayoutComponent) continue;
|
|
331
|
+
const NestedLayoutComponent = nestedLayoutComponent;
|
|
332
|
+
const nestedLayoutParams = resolveSlotLayoutParams(slotRouteSegments, slot.configLayoutTreePositions?.[layoutIndex] ?? 0, slotParams);
|
|
333
|
+
slotElement = /* @__PURE__ */ jsx(NestedLayoutComponent, {
|
|
334
|
+
params: options.makeThenableParams({
|
|
335
|
+
...slotOwnerParams,
|
|
336
|
+
...nestedLayoutParams
|
|
337
|
+
}),
|
|
316
338
|
children: slotElement
|
|
317
339
|
});
|
|
318
340
|
}
|
|
319
341
|
const slotLayoutComponent = getDefaultExport(slot.layout);
|
|
320
342
|
if (slotLayoutComponent) slotElement = /* @__PURE__ */ jsx(slotLayoutComponent, {
|
|
321
|
-
params:
|
|
343
|
+
params: options.makeThenableParams(slotOwnerParams),
|
|
322
344
|
children: slotElement
|
|
323
345
|
});
|
|
324
346
|
const slotLoadingComponent = getDefaultExport(slot.loading);
|