securityos/node_modules/next/dist/esm/server/app-render/walk-tree-with-flight-route...

136 lines
7.5 KiB
JavaScript

import React from "react";
import { canSegmentBeOverridden, matchSegment } from "../../client/components/match-segments";
import { getCssInlinedLinkTags } from "./get-css-inlined-link-tags";
import { getPreloadableFonts } from "./get-preloadable-fonts";
import { addSearchParamsIfPageSegment, createFlightRouterStateFromLoaderTree } from "./create-flight-router-state-from-loader-tree";
import { parseLoaderTree } from "./parse-loader-tree";
import { getLayerAssets } from "./get-layer-assets";
import { hasLoadingComponentInTree } from "./has-loading-component-in-tree";
import { createComponentTree } from "./create-component-tree";
/**
* Use router state to decide at what common layout to render the page.
* This can either be the common layout between two pages or a specific place to start rendering from using the "refetch" marker in the tree.
*/ export async function walkTreeWithFlightRouterState({ createSegmentPath, loaderTreeToFilter, parentParams, isFirst, flightRouterState, parentRendered, rscPayloadHead, injectedCSS, injectedFontPreloadTags, rootLayoutIncluded, asNotFound, metadataOutlet, ctx }) {
const { renderOpts: { nextFontManifest }, query, isPrefetch, getDynamicParamFromSegment, componentMod: { tree: loaderTree } } = ctx;
const [segment, parallelRoutes, components] = loaderTreeToFilter;
const parallelRoutesKeys = Object.keys(parallelRoutes);
const { layout } = components;
const isLayout = typeof layout !== "undefined";
/**
* Checks if the current segment is a root layout.
*/ const rootLayoutAtThisLevel = isLayout && !rootLayoutIncluded;
/**
* Checks if the current segment or any level above it has a root layout.
*/ const rootLayoutIncludedAtThisLevelOrAbove = rootLayoutIncluded || rootLayoutAtThisLevel;
// Because this function walks to a deeper point in the tree to start rendering we have to track the dynamic parameters up to the point where rendering starts
const segmentParam = getDynamicParamFromSegment(segment);
const currentParams = // Handle null case where dynamic param is optional
segmentParam && segmentParam.value !== null ? {
...parentParams,
[segmentParam.param]: segmentParam.value
} : parentParams;
const actualSegment = addSearchParamsIfPageSegment(segmentParam ? segmentParam.treeSegment : segment, query);
/**
* Decide if the current segment is where rendering has to start.
*/ const renderComponentsOnThisLevel = // No further router state available
!flightRouterState || // Segment in router state does not match current segment
!matchSegment(actualSegment, flightRouterState[0]) || // Last item in the tree
parallelRoutesKeys.length === 0 || // Explicit refresh
flightRouterState[3] === "refetch";
const shouldSkipComponentTree = isPrefetch && !Boolean(components.loading) && (flightRouterState || // If there is no flightRouterState, we need to check the entire loader tree, as otherwise we'll be only checking the root
!hasLoadingComponentInTree(loaderTree));
if (!parentRendered && renderComponentsOnThisLevel) {
const overriddenSegment = flightRouterState && canSegmentBeOverridden(actualSegment, flightRouterState[0]) ? flightRouterState[0] : null;
return [
[
overriddenSegment ?? actualSegment,
createFlightRouterStateFromLoaderTree(// Create router state using the slice of the loaderTree
loaderTreeToFilter, getDynamicParamFromSegment, query),
shouldSkipComponentTree ? null : // @ts-expect-error TODO-APP: fix async component type
/*#__PURE__*/ React.createElement(async ()=>{
const { Component } = await createComponentTree(// This ensures flightRouterPath is valid and filters down the tree
{
ctx,
createSegmentPath,
loaderTree: loaderTreeToFilter,
parentParams: currentParams,
firstItem: isFirst,
injectedCSS,
injectedFontPreloadTags,
// This is intentionally not "rootLayoutIncludedAtThisLevelOrAbove" as createComponentTree starts at the current level and does a check for "rootLayoutAtThisLevel" too.
rootLayoutIncluded,
asNotFound,
metadataOutlet
});
return /*#__PURE__*/ React.createElement(Component, null);
}),
shouldSkipComponentTree ? null : (()=>{
const { layoutOrPagePath } = parseLoaderTree(loaderTreeToFilter);
const styles = getLayerAssets({
ctx,
layoutOrPagePath,
injectedCSS: new Set(injectedCSS),
injectedFontPreloadTags: new Set(injectedFontPreloadTags)
});
return /*#__PURE__*/ React.createElement(React.Fragment, null, styles, rscPayloadHead);
})()
]
];
}
// If we are not rendering on this level we need to check if the current
// segment has a layout. If so, we need to track all the used CSS to make
// the result consistent.
const layoutPath = layout == null ? void 0 : layout[1];
const injectedCSSWithCurrentLayout = new Set(injectedCSS);
const injectedFontPreloadTagsWithCurrentLayout = new Set(injectedFontPreloadTags);
if (layoutPath) {
getCssInlinedLinkTags(ctx.clientReferenceManifest, layoutPath, injectedCSSWithCurrentLayout, true);
getPreloadableFonts(nextFontManifest, layoutPath, injectedFontPreloadTagsWithCurrentLayout);
}
// Walk through all parallel routes.
const paths = (await Promise.all(parallelRoutesKeys.map(async (parallelRouteKey)=>{
// for (const parallelRouteKey of parallelRoutesKeys) {
const parallelRoute = parallelRoutes[parallelRouteKey];
const currentSegmentPath = isFirst ? [
parallelRouteKey
] : [
actualSegment,
parallelRouteKey
];
const path = await walkTreeWithFlightRouterState({
ctx,
createSegmentPath: (child)=>{
return createSegmentPath([
...currentSegmentPath,
...child
]);
},
loaderTreeToFilter: parallelRoute,
parentParams: currentParams,
flightRouterState: flightRouterState && flightRouterState[1][parallelRouteKey],
parentRendered: parentRendered || renderComponentsOnThisLevel,
isFirst: false,
rscPayloadHead,
injectedCSS: injectedCSSWithCurrentLayout,
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove,
asNotFound,
metadataOutlet
});
return path.map((item)=>{
// we don't need to send over default routes in the flight data
// because they are always ignored by the client, unless it's a refetch
if (item[0] === "__DEFAULT__" && flightRouterState && !!flightRouterState[1][parallelRouteKey][0] && flightRouterState[1][parallelRouteKey][3] !== "refetch") {
return null;
}
return [
actualSegment,
parallelRouteKey,
...item
];
}).filter(Boolean);
}))).flat();
return paths;
}
//# sourceMappingURL=walk-tree-with-flight-router-state.js.map