next
Version:
The React Framework
676 lines (675 loc) • 34.5 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React from 'react';
import { isClientReference, isUseCacheFunction } from '../../lib/client-and-server-references';
import { getLayoutOrPageModule } from '../lib/app-dir-module';
import { interopDefault } from './interop-default';
import { parseLoaderTree } from './parse-loader-tree';
import { createComponentStylesAndScripts } from './create-component-styles-and-scripts';
import { getLayerAssets } from './get-layer-assets';
import { hasLoadingComponentInTree } from './has-loading-component-in-tree';
import { validateRevalidate } from '../lib/patch-fetch';
import { PARALLEL_ROUTE_DEFAULT_PATH } from '../../client/components/parallel-route-default';
import { getTracer } from '../lib/trace/tracer';
import { NextNodeServerSpan } from '../lib/trace/constants';
import { StaticGenBailoutError } from '../../client/components/static-generation-bailout';
import { workUnitAsyncStorage } from './work-unit-async-storage.external';
import { OUTLET_BOUNDARY_NAME } from '../../lib/metadata/metadata-constants';
/**
* Use the provided loader tree to create the React Component tree.
*/ export function createComponentTree(props) {
return getTracer().trace(NextNodeServerSpan.createComponentTree, {
spanName: 'build component tree'
}, ()=>createComponentTreeInternal(props));
}
function errorMissingDefaultExport(pagePath, convention) {
const normalizedPagePath = pagePath === '/' ? '' : pagePath;
throw Object.defineProperty(new Error(`The default export is not a React Component in "${normalizedPagePath}/${convention}"`), "__NEXT_ERROR_CODE", {
value: "E45",
enumerable: false,
configurable: true
});
}
const cacheNodeKey = 'c';
async function createComponentTreeInternal({ loaderTree: tree, parentParams, rootLayoutIncluded, injectedCSS, injectedJS, injectedFontPreloadTags, getViewportReady, getMetadataReady, ctx, missingSlots, preloadCallbacks, authInterrupts, StreamingMetadata, StreamingMetadataOutlet }) {
const { renderOpts: { nextConfigOutput, experimental }, workStore, componentMod: { HTTPAccessFallbackBoundary, LayoutRouter, RenderFromTemplateContext, OutletBoundary, ClientPageRoot, ClientSegmentRoot, createServerSearchParamsForServerPage, createPrerenderSearchParamsForClientPage, createServerParamsForServerSegment, createPrerenderParamsForClientSegment, serverHooks: { DynamicServerError }, Postpone }, pagePath, getDynamicParamFromSegment, isPrefetch, query } = ctx;
const { page, layoutOrPagePath, segment, modules, parallelRoutes } = parseLoaderTree(tree);
const { layout, template, error, loading, 'not-found': notFound, forbidden, unauthorized } = modules;
const injectedCSSWithCurrentLayout = new Set(injectedCSS);
const injectedJSWithCurrentLayout = new Set(injectedJS);
const injectedFontPreloadTagsWithCurrentLayout = new Set(injectedFontPreloadTags);
const layerAssets = getLayerAssets({
preloadCallbacks,
ctx,
layoutOrPagePath,
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout,
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout
});
const [Template, templateStyles, templateScripts] = template ? await createComponentStylesAndScripts({
ctx,
filePath: template[1],
getComponent: template[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [
React.Fragment
];
const [ErrorComponent, errorStyles, errorScripts] = error ? await createComponentStylesAndScripts({
ctx,
filePath: error[1],
getComponent: error[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const [Loading, loadingStyles, loadingScripts] = loading ? await createComponentStylesAndScripts({
ctx,
filePath: loading[1],
getComponent: loading[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const isLayout = typeof layout !== 'undefined';
const isPage = typeof page !== 'undefined';
const { mod: layoutOrPageMod, modType } = await getTracer().trace(NextNodeServerSpan.getLayoutOrPageModule, {
hideSpan: !(isLayout || isPage),
spanName: 'resolve segment modules',
attributes: {
'next.segment': segment
}
}, ()=>getLayoutOrPageModule(tree));
/**
* 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;
const [NotFound, notFoundStyles] = notFound ? await createComponentStylesAndScripts({
ctx,
filePath: notFound[1],
getComponent: notFound[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const [Forbidden, forbiddenStyles] = authInterrupts && forbidden ? await createComponentStylesAndScripts({
ctx,
filePath: forbidden[1],
getComponent: forbidden[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const [Unauthorized, unauthorizedStyles] = authInterrupts && unauthorized ? await createComponentStylesAndScripts({
ctx,
filePath: unauthorized[1],
getComponent: unauthorized[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
let dynamic = layoutOrPageMod == null ? void 0 : layoutOrPageMod.dynamic;
if (nextConfigOutput === 'export') {
if (!dynamic || dynamic === 'auto') {
dynamic = 'error';
} else if (dynamic === 'force-dynamic') {
// force-dynamic is always incompatible with 'export'. We must interrupt the build
throw Object.defineProperty(new StaticGenBailoutError(`Page with \`dynamic = "force-dynamic"\` couldn't be exported. \`output: "export"\` requires all pages be renderable statically because there is no runtime server to dynamically render routes in this output format. Learn more: https://nextjs.org/docs/app/building-your-application/deploying/static-exports`), "__NEXT_ERROR_CODE", {
value: "E527",
enumerable: false,
configurable: true
});
}
}
if (typeof dynamic === 'string') {
// the nested most config wins so we only force-static
// if it's configured above any parent that configured
// otherwise
if (dynamic === 'error') {
workStore.dynamicShouldError = true;
} else if (dynamic === 'force-dynamic') {
workStore.forceDynamic = true;
// TODO: (PPR) remove this bailout once PPR is the default
if (workStore.isStaticGeneration && !experimental.isRoutePPREnabled) {
// If the postpone API isn't available, we can't postpone the render and
// therefore we can't use the dynamic API.
const err = Object.defineProperty(new DynamicServerError(`Page with \`dynamic = "force-dynamic"\` won't be rendered statically.`), "__NEXT_ERROR_CODE", {
value: "E585",
enumerable: false,
configurable: true
});
workStore.dynamicUsageDescription = err.message;
workStore.dynamicUsageStack = err.stack;
throw err;
}
} else {
workStore.dynamicShouldError = false;
workStore.forceStatic = dynamic === 'force-static';
}
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.fetchCache) === 'string') {
workStore.fetchCache = layoutOrPageMod == null ? void 0 : layoutOrPageMod.fetchCache;
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate) !== 'undefined') {
validateRevalidate(layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate, workStore.route);
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate) === 'number') {
const defaultRevalidate = layoutOrPageMod.revalidate;
const workUnitStore = workUnitAsyncStorage.getStore();
if (workUnitStore) {
if (workUnitStore.type === 'prerender' || workUnitStore.type === 'prerender-legacy' || workUnitStore.type === 'prerender-ppr' || workUnitStore.type === 'cache') {
if (workUnitStore.revalidate > defaultRevalidate) {
workUnitStore.revalidate = defaultRevalidate;
}
}
}
if (!workStore.forceStatic && workStore.isStaticGeneration && defaultRevalidate === 0 && // If the postpone API isn't available, we can't postpone the render and
// therefore we can't use the dynamic API.
!experimental.isRoutePPREnabled) {
const dynamicUsageDescription = `revalidate: 0 configured ${segment}`;
workStore.dynamicUsageDescription = dynamicUsageDescription;
throw Object.defineProperty(new DynamicServerError(dynamicUsageDescription), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
}
const isStaticGeneration = workStore.isStaticGeneration;
// Assume the segment we're rendering contains only partial data if PPR is
// enabled and this is a statically generated response. This is used by the
// client Segment Cache after a prefetch to determine if it can skip the
// second request to fill in the dynamic data.
//
// It's OK for this to be `true` when the data is actually fully static, but
// it's not OK for this to be `false` when the data possibly contains holes.
// Although the value here is overly pessimistic, for prefetches, it will be
// replaced by a more specific value when the data is later processed into
// per-segment responses (see collect-segment-data.tsx)
//
// For dynamic requests, this must always be `false` because dynamic responses
// are never partial.
const isPossiblyPartialResponse = isStaticGeneration && experimental.isRoutePPREnabled === true;
// If there's a dynamic usage error attached to the store, throw it.
if (workStore.dynamicUsageErr) {
throw workStore.dynamicUsageErr;
}
const LayoutOrPage = layoutOrPageMod ? interopDefault(layoutOrPageMod) : undefined;
/**
* The React Component to render.
*/ let MaybeComponent = LayoutOrPage;
if (process.env.NODE_ENV === 'development') {
const { isValidElementType } = require('next/dist/compiled/react-is');
if (typeof MaybeComponent !== 'undefined' && !isValidElementType(MaybeComponent)) {
errorMissingDefaultExport(pagePath, modType ?? 'page');
}
if (typeof ErrorComponent !== 'undefined' && !isValidElementType(ErrorComponent)) {
errorMissingDefaultExport(pagePath, 'error');
}
if (typeof Loading !== 'undefined' && !isValidElementType(Loading)) {
errorMissingDefaultExport(pagePath, 'loading');
}
if (typeof NotFound !== 'undefined' && !isValidElementType(NotFound)) {
errorMissingDefaultExport(pagePath, 'not-found');
}
if (typeof Forbidden !== 'undefined' && !isValidElementType(Forbidden)) {
errorMissingDefaultExport(pagePath, 'forbidden');
}
if (typeof Unauthorized !== 'undefined' && !isValidElementType(Unauthorized)) {
errorMissingDefaultExport(pagePath, 'unauthorized');
}
}
// Handle dynamic segment params.
const segmentParam = getDynamicParamFromSegment(segment);
// Create object holding the parent params and current params
let currentParams = parentParams;
if (segmentParam && segmentParam.value !== null) {
currentParams = {
...parentParams,
[segmentParam.param]: segmentParam.value
};
}
// Resolve the segment param
const actualSegment = segmentParam ? segmentParam.treeSegment : segment;
const metadata = StreamingMetadata ? /*#__PURE__*/ _jsx(StreamingMetadata, {}) : undefined;
// Use the same condition to render metadataOutlet as metadata
const metadataOutlet = StreamingMetadataOutlet ? /*#__PURE__*/ _jsx(StreamingMetadataOutlet, {}) : undefined;
const notFoundElement = NotFound ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsx(NotFound, {}),
notFoundStyles
]
}) : undefined;
const forbiddenElement = Forbidden ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsx(Forbidden, {}),
forbiddenStyles
]
}) : undefined;
const unauthorizedElement = Unauthorized ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsx(Unauthorized, {}),
unauthorizedStyles
]
}) : undefined;
// TODO: Combine this `map` traversal with the loop below that turns the array
// into an object.
const parallelRouteMap = await Promise.all(Object.keys(parallelRoutes).map(async (parallelRouteKey)=>{
const isChildrenRouteKey = parallelRouteKey === 'children';
const parallelRoute = parallelRoutes[parallelRouteKey];
const notFoundComponent = isChildrenRouteKey ? notFoundElement : undefined;
const forbiddenComponent = isChildrenRouteKey ? forbiddenElement : undefined;
const unauthorizedComponent = isChildrenRouteKey ? unauthorizedElement : undefined;
// if we're prefetching and that there's a Loading component, we bail out
// otherwise we keep rendering for the prefetch.
// We also want to bail out if there's no Loading component in the tree.
let childCacheNodeSeedData = null;
if (// Before PPR, the way instant navigations work in Next.js is we
// prefetch everything up to the first route segment that defines a
// loading.tsx boundary. (We do the same if there's no loading
// boundary in the entire tree, because we don't want to prefetch too
// much) The rest of the tree is deferred until the actual navigation.
// It does not take into account whether the data is dynamic — even if
// the tree is completely static, it will still defer everything
// inside the loading boundary.
//
// This behavior predates PPR and is only relevant if the
// PPR flag is not enabled.
isPrefetch && (Loading || !hasLoadingComponentInTree(parallelRoute)) && // The approach with PPR is different — loading.tsx behaves like a
// regular Suspense boundary and has no special behavior.
//
// With PPR, we prefetch as deeply as possible, and only defer when
// dynamic data is accessed. If so, we only defer the nearest parent
// Suspense boundary of the dynamic data access, regardless of whether
// the boundary is defined by loading.tsx or a normal <Suspense>
// component in userspace.
//
// NOTE: In practice this usually means we'll end up prefetching more
// than we were before PPR, which may or may not be considered a
// performance regression by some apps. The plan is to address this
// before General Availability of PPR by introducing granular
// per-segment fetching, so we can reuse as much of the tree as
// possible during both prefetches and dynamic navigations. But during
// the beta period, we should be clear about this trade off in our
// communications.
!experimental.isRoutePPREnabled) {
// Don't prefetch this child. This will trigger a lazy fetch by the
// client router.
} else {
// Create the child component
if (process.env.NODE_ENV === 'development' && missingSlots) {
var _parsedTree_layoutOrPagePath;
// When we detect the default fallback (which triggers a 404), we collect the missing slots
// to provide more helpful debug information during development mode.
const parsedTree = parseLoaderTree(parallelRoute);
if ((_parsedTree_layoutOrPagePath = parsedTree.layoutOrPagePath) == null ? void 0 : _parsedTree_layoutOrPagePath.endsWith(PARALLEL_ROUTE_DEFAULT_PATH)) {
missingSlots.add(parallelRouteKey);
}
}
const seedData = await createComponentTreeInternal({
loaderTree: parallelRoute,
parentParams: currentParams,
rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove,
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout,
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
// `getMetadataReady` and `getViewportReady` are used to conditionally throw. In the case of parallel routes we will have more than one page
// but we only want to throw on the first one.
getMetadataReady: isChildrenRouteKey ? getMetadataReady : ()=>Promise.resolve(),
getViewportReady: isChildrenRouteKey ? getViewportReady : ()=>Promise.resolve(),
ctx,
missingSlots,
preloadCallbacks,
authInterrupts,
StreamingMetadata: isChildrenRouteKey ? StreamingMetadata : null,
// `StreamingMetadataOutlet` is used to conditionally throw. In the case of parallel routes we will have more than one page
// but we only want to throw on the first one.
StreamingMetadataOutlet: isChildrenRouteKey ? StreamingMetadataOutlet : null
});
childCacheNodeSeedData = seedData;
}
// This is turned back into an object below.
return [
parallelRouteKey,
/*#__PURE__*/ _jsx(LayoutRouter, {
parallelRouterKey: parallelRouteKey,
// TODO-APP: Add test for loading returning `undefined`. This currently can't be tested as the `webdriver()` tab will wait for the full page to load before returning.
error: ErrorComponent,
errorStyles: errorStyles,
errorScripts: errorScripts,
template: /*#__PURE__*/ _jsx(Template, {
children: /*#__PURE__*/ _jsx(RenderFromTemplateContext, {})
}),
templateStyles: templateStyles,
templateScripts: templateScripts,
notFound: notFoundComponent,
forbidden: forbiddenComponent,
unauthorized: unauthorizedComponent
}),
childCacheNodeSeedData
];
}));
// Convert the parallel route map into an object after all promises have been resolved.
let parallelRouteProps = {};
let parallelRouteCacheNodeSeedData = {};
for (const parallelRoute of parallelRouteMap){
const [parallelRouteKey, parallelRouteProp, flightData] = parallelRoute;
parallelRouteProps[parallelRouteKey] = parallelRouteProp;
parallelRouteCacheNodeSeedData[parallelRouteKey] = flightData;
}
const loadingData = Loading ? [
/*#__PURE__*/ _jsx(Loading, {}, "l"),
loadingStyles,
loadingScripts
] : null;
// When the segment does not have a layout or page we still have to add the layout router to ensure the path holds the loading component
if (!MaybeComponent) {
return [
actualSegment,
/*#__PURE__*/ _jsxs(React.Fragment, {
children: [
layerAssets,
parallelRouteProps.children
]
}, cacheNodeKey),
parallelRouteCacheNodeSeedData,
loadingData,
isPossiblyPartialResponse
];
}
const Component = MaybeComponent;
// If force-dynamic is used and the current render supports postponing, we
// replace it with a node that will postpone the render. This ensures that the
// postpone is invoked during the react render phase and not during the next
// render phase.
// @TODO this does not actually do what it seems like it would or should do. The idea is that
// if we are rendering in a force-dynamic mode and we can postpone we should only make the segments
// that ask for force-dynamic to be dynamic, allowing other segments to still prerender. However
// because this comes after the children traversal and the static generation store is mutated every segment
// along the parent path of a force-dynamic segment will hit this condition effectively making the entire
// render force-dynamic. We should refactor this function so that we can correctly track which segments
// need to be dynamic
if (workStore.isStaticGeneration && workStore.forceDynamic && experimental.isRoutePPREnabled) {
return [
actualSegment,
/*#__PURE__*/ _jsxs(React.Fragment, {
children: [
/*#__PURE__*/ _jsx(Postpone, {
reason: 'dynamic = "force-dynamic" was used',
route: workStore.route
}),
layerAssets
]
}, cacheNodeKey),
parallelRouteCacheNodeSeedData,
loadingData,
true
];
}
const isClientComponent = isClientReference(layoutOrPageMod);
if (process.env.NODE_ENV === 'development' && 'params' in parallelRouteProps) {
// @TODO consider making this an error and running the check in build as well
console.error(`"params" is a reserved prop in Layouts and Pages and cannot be used as the name of a parallel route in ${segment}`);
}
if (isPage) {
const PageComponent = Component;
// Assign searchParams to props if this is a page
let pageElement;
if (isClientComponent) {
if (isStaticGeneration) {
const promiseOfParams = createPrerenderParamsForClientSegment(currentParams, workStore);
const promiseOfSearchParams = createPrerenderSearchParamsForClientPage(workStore);
pageElement = /*#__PURE__*/ _jsx(ClientPageRoot, {
Component: PageComponent,
searchParams: query,
params: currentParams,
promises: [
promiseOfSearchParams,
promiseOfParams
]
});
} else {
pageElement = /*#__PURE__*/ _jsx(ClientPageRoot, {
Component: PageComponent,
searchParams: query,
params: currentParams
});
}
} else {
// If we are passing params to a server component Page we need to track
// their usage in case the current render mode tracks dynamic API usage.
const params = createServerParamsForServerSegment(currentParams, workStore);
// TODO(useCache): Should we use this trick also if dynamicIO is enabled,
// instead of relying on the searchParams being a hanging promise?
if (!experimental.dynamicIO && isUseCacheFunction(PageComponent)) {
const UseCachePageComponent = PageComponent;
// The "use cache" wrapper takes care of converting this into an
// erroring search params promise when passing it to the original
// function.
const searchParams = Promise.resolve({});
pageElement = /*#__PURE__*/ _jsx(UseCachePageComponent, {
params: params,
searchParams: searchParams,
$$isPageComponent: true
});
} else {
// If we are passing searchParams to a server component Page we need to
// track their usage in case the current render mode tracks dynamic API
// usage.
const searchParams = createServerSearchParamsForServerPage(query, workStore);
pageElement = /*#__PURE__*/ _jsx(PageComponent, {
params: params,
searchParams: searchParams
});
}
}
return [
actualSegment,
/*#__PURE__*/ _jsxs(React.Fragment, {
children: [
pageElement,
metadata,
layerAssets,
/*#__PURE__*/ _jsxs(OutletBoundary, {
children: [
/*#__PURE__*/ _jsx(MetadataOutlet, {
ready: getViewportReady
}),
/*#__PURE__*/ _jsx(MetadataOutlet, {
ready: getMetadataReady
}),
metadataOutlet
]
})
]
}, cacheNodeKey),
parallelRouteCacheNodeSeedData,
loadingData,
isPossiblyPartialResponse
];
} else {
const SegmentComponent = Component;
const isRootLayoutWithChildrenSlotAndAtLeastOneMoreSlot = rootLayoutAtThisLevel && 'children' in parallelRoutes && Object.keys(parallelRoutes).length > 1;
let segmentNode;
if (isClientComponent) {
let clientSegment;
if (isStaticGeneration) {
const promiseOfParams = createPrerenderParamsForClientSegment(currentParams, workStore);
clientSegment = /*#__PURE__*/ _jsx(ClientSegmentRoot, {
Component: SegmentComponent,
slots: parallelRouteProps,
params: currentParams,
promise: promiseOfParams
});
} else {
clientSegment = /*#__PURE__*/ _jsx(ClientSegmentRoot, {
Component: SegmentComponent,
slots: parallelRouteProps,
params: currentParams
});
}
if (isRootLayoutWithChildrenSlotAndAtLeastOneMoreSlot) {
let notfoundClientSegment;
let forbiddenClientSegment;
let unauthorizedClientSegment;
// TODO-APP: This is a hack to support unmatched parallel routes, which will throw `notFound()`.
// This ensures that a `HTTPAccessFallbackBoundary` is available for when that happens,
// but it's not ideal, as it needlessly invokes the `NotFound` component and renders the `RootLayout` twice.
// We should instead look into handling the fallback behavior differently in development mode so that it doesn't
// rely on the `NotFound` behavior.
notfoundClientSegment = createErrorBoundaryClientSegmentRoot({
ErrorBoundaryComponent: NotFound,
errorElement: notFoundElement,
ClientSegmentRoot,
layerAssets,
SegmentComponent,
currentParams
});
forbiddenClientSegment = createErrorBoundaryClientSegmentRoot({
ErrorBoundaryComponent: Forbidden,
errorElement: forbiddenElement,
ClientSegmentRoot,
layerAssets,
SegmentComponent,
currentParams
});
unauthorizedClientSegment = createErrorBoundaryClientSegmentRoot({
ErrorBoundaryComponent: Unauthorized,
errorElement: unauthorizedElement,
ClientSegmentRoot,
layerAssets,
SegmentComponent,
currentParams
});
if (notfoundClientSegment || forbiddenClientSegment || unauthorizedClientSegment) {
segmentNode = /*#__PURE__*/ _jsxs(HTTPAccessFallbackBoundary, {
notFound: notfoundClientSegment,
forbidden: forbiddenClientSegment,
unauthorized: unauthorizedClientSegment,
children: [
layerAssets,
clientSegment
]
}, cacheNodeKey);
} else {
segmentNode = /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
layerAssets,
clientSegment
]
}, cacheNodeKey);
}
} else {
segmentNode = /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
layerAssets,
clientSegment
]
}, cacheNodeKey);
}
} else {
const params = createServerParamsForServerSegment(currentParams, workStore);
let serverSegment = /*#__PURE__*/ _jsx(SegmentComponent, {
...parallelRouteProps,
params: params
});
if (isRootLayoutWithChildrenSlotAndAtLeastOneMoreSlot) {
// TODO-APP: This is a hack to support unmatched parallel routes, which will throw `notFound()`.
// This ensures that a `HTTPAccessFallbackBoundary` is available for when that happens,
// but it's not ideal, as it needlessly invokes the `NotFound` component and renders the `RootLayout` twice.
// We should instead look into handling the fallback behavior differently in development mode so that it doesn't
// rely on the `NotFound` behavior.
segmentNode = /*#__PURE__*/ _jsxs(HTTPAccessFallbackBoundary, {
notFound: NotFound ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
layerAssets,
/*#__PURE__*/ _jsxs(SegmentComponent, {
params: params,
children: [
notFoundStyles,
/*#__PURE__*/ _jsx(NotFound, {})
]
})
]
}) : undefined,
children: [
layerAssets,
serverSegment
]
}, cacheNodeKey);
} else {
segmentNode = /*#__PURE__*/ _jsxs(React.Fragment, {
children: [
layerAssets,
serverSegment
]
}, cacheNodeKey);
}
}
// For layouts we just render the component
return [
actualSegment,
segmentNode,
parallelRouteCacheNodeSeedData,
loadingData,
isPossiblyPartialResponse
];
}
}
async function MetadataOutlet({ ready }) {
const r = ready();
// We can avoid a extra microtask by unwrapping the instrumented promise directly if available.
if (r.status === 'rejected') {
throw r.value;
} else if (r.status !== 'fulfilled') {
await r;
}
return null;
}
MetadataOutlet.displayName = OUTLET_BOUNDARY_NAME;
function createErrorBoundaryClientSegmentRoot({ ErrorBoundaryComponent, errorElement, ClientSegmentRoot, layerAssets, SegmentComponent, currentParams }) {
if (ErrorBoundaryComponent) {
const notFoundParallelRouteProps = {
children: errorElement
};
return /*#__PURE__*/ _jsxs(_Fragment, {
children: [
layerAssets,
/*#__PURE__*/ _jsx(ClientSegmentRoot, {
Component: SegmentComponent,
slots: notFoundParallelRouteProps,
params: currentParams
})
]
});
}
return null;
}
export function getRootParams(loaderTree, getDynamicParamFromSegment) {
return getRootParamsImpl({}, loaderTree, getDynamicParamFromSegment);
}
function getRootParamsImpl(parentParams, loaderTree, getDynamicParamFromSegment) {
const { segment, modules: { layout }, parallelRoutes } = parseLoaderTree(loaderTree);
const segmentParam = getDynamicParamFromSegment(segment);
let currentParams = parentParams;
if (segmentParam && segmentParam.value !== null) {
currentParams = {
...parentParams,
[segmentParam.param]: segmentParam.value
};
}
const isRootLayout = typeof layout !== 'undefined';
if (isRootLayout) {
return currentParams;
} else if (!parallelRoutes.children) {
// This should really be an error but there are bugs in Turbopack that cause
// the _not-found LoaderTree to not have any layouts. For rootParams sake
// this is somewhat irrelevant when you are not customizing the 404 page.
// If you are customizing 404
// TODO update rootParams to make all params optional if `/app/not-found.tsx` is defined
return currentParams;
} else {
return getRootParamsImpl(currentParams, // We stop looking for root params as soon as we hit the first layout
// and it is not possible to use parallel route children above the root layout
// so every parallelRoutes object that this function can visit will necessarily
// have a single `children` prop and no others.
parallelRoutes.children, getDynamicParamFromSegment);
}
}
//# sourceMappingURL=create-component-tree.js.map