Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 49 additions & 22 deletions packages/vinext/src/entries/app-rsc-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,14 @@ function findIntercept(pathname) {
return null;
}

async function buildPageElements(route, params, routePath, opts, searchParams, isRscRequest, request) {
async function buildPageElements(route, params, routePath, pageRequest) {
const {
opts,
searchParams,
isRscRequest,
request,
mountedSlotsHeader,
} = pageRequest;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit (non-blocking): If the stack plans to keep extending pageRequest (PRs 2–6 mention adding more fields), it might be worth adding a brief JSDoc type comment near the destructuring or at the function signature — even just /** @typedef {{ opts, searchParams, isRscRequest, request, mountedSlotsHeader }} PageRequest */ — so future readers know the shape at a glance without scanning all call sites. Generated code doesn't benefit from TypeScript, so a JSDoc hint would be the only inline documentation.

Fine to defer to a later PR in the stack if the shape isn't stable yet.

const PageComponent = route.page?.default;
if (!PageComponent) {
const _interceptionContext = opts?.interceptionContext ?? null;
Expand Down Expand Up @@ -1052,11 +1059,12 @@ async function buildPageElements(route, params, routePath, opts, searchParams, i
// dynamic, and this avoids false positives from React internals.
if (hasSearchParams) markDynamicUsage();
}
const __mountedSlotsHeader = __normalizeMountedSlotsHeader(
request?.headers?.get("x-vinext-mounted-slots"),
);
const mountedSlotIds = __mountedSlotsHeader
? new Set(__mountedSlotsHeader.split(" "))
// mountedSlotsHeader is threaded through from the handler scope so every
// call site shares one source of truth for request-derived values. Reading
// the same header in two places invites silent drift when a future refactor
// changes only one of them.
const mountedSlotIds = mountedSlotsHeader
? new Set(mountedSlotsHeader.split(" "))
: null;

return __buildAppPageElements({
Expand Down Expand Up @@ -1412,6 +1420,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
}

const isRscRequest = pathname.endsWith(".rsc") || request.headers.get("accept")?.includes("text/x-component");
// Read mounted-slots header once at the handler scope and thread it through
// every buildPageElements call site. Previously both the handler and
// buildPageElements read and normalized it independently, which invited
// silent drift if a future refactor changed only one path.
const __mountedSlotsHeader = __normalizeMountedSlotsHeader(
request.headers.get("x-vinext-mounted-slots"),
);
const interceptionContextHeader = request.headers.get("X-Vinext-Interception-Context")?.replaceAll("\0", "") || null;
let cleanPathname = pathname.replace(/\\.rsc$/, "");

Expand Down Expand Up @@ -1815,10 +1830,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
actionRoute,
actionParams,
cleanPathname,
undefined,
url.searchParams,
isRscRequest,
request,
{
opts: undefined,
searchParams: url.searchParams,
isRscRequest,
request,
mountedSlotsHeader: __mountedSlotsHeader,
},
);
} else {
const _actionRouteId = __createAppPayloadRouteId(cleanPathname, null);
Expand Down Expand Up @@ -2153,9 +2171,6 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {

// force-dynamic: set no-store Cache-Control
const isForceDynamic = dynamicConfig === "force-dynamic";
const __mountedSlotsHeader = __normalizeMountedSlotsHeader(
request.headers.get("x-vinext-mounted-slots"),
);

// ── ISR cache read (production only) ─────────────────────────────────────
// Read from cache BEFORE generateStaticParams and all rendering work.
Expand Down Expand Up @@ -2211,10 +2226,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
route,
params,
cleanPathname,
undefined,
new URLSearchParams(),
isRscRequest,
request,
{
opts: undefined,
searchParams: new URLSearchParams(),
isRscRequest,
request,
mountedSlotsHeader: __mountedSlotsHeader,
},
);
const __revalOnError = createRscOnErrorHandler(request, cleanPathname, route.pattern);
const __revalRscStream = renderToReadableStream(__revalElement, { onError: __revalOnError });
Expand Down Expand Up @@ -2269,10 +2287,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {
interceptRoute,
interceptParams,
cleanPathname,
interceptOpts,
interceptSearchParams,
isRscRequest,
request,
{
opts: interceptOpts,
searchParams: interceptSearchParams,
isRscRequest,
request,
mountedSlotsHeader: __mountedSlotsHeader,
},
);
},
cleanPathname,
Expand Down Expand Up @@ -2326,7 +2347,13 @@ async function _handleRequest(request, __reqCtx, _mwCtx) {

const __pageBuildResult = await __buildAppPageElement({
buildPageElement() {
return buildPageElements(route, params, cleanPathname, interceptOpts, url.searchParams, isRscRequest, request);
return buildPageElements(route, params, cleanPathname, {
opts: interceptOpts,
searchParams: url.searchParams,
isRscRequest,
request,
mountedSlotsHeader: __mountedSlotsHeader,
});
},
renderErrorBoundaryPage(buildErr) {
return renderErrorBoundaryPage(route, buildErr, isRscRequest, request, params, _scriptNonce);
Expand Down
Loading
Loading