You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
21 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
Postpone: null,
createComponentTree: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
Postpone: function() {
return Postpone;
},
createComponentTree: function() {
return createComponentTree;
}
});
const _jsxruntime = require("react/jsx-runtime");
const _react = /*#__PURE__*/ _interop_require_default(require("react"));
const _clientreference = require("../../lib/client-reference");
const _appdirmodule = require("../lib/app-dir-module");
const _interopdefault = require("./interop-default");
const _parseloadertree = require("./parse-loader-tree");
const _createcomponentstylesandscripts = require("./create-component-styles-and-scripts");
const _getlayerassets = require("./get-layer-assets");
const _hasloadingcomponentintree = require("./has-loading-component-in-tree");
const _patchfetch = require("../lib/patch-fetch");
const _parallelroutedefault = require("../../client/components/parallel-route-default");
const _tracer = require("../lib/trace/tracer");
const _constants = require("../lib/trace/constants");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const Postpone = ({ postpone })=>{
// Call the postpone API now with the reason set to "force-dynamic".
return postpone('dynamic = "force-dynamic" was used');
};
async function createComponentTree({ createSegmentPath, loaderTree: tree, parentParams, firstItem, rootLayoutIncluded, injectedCSS, injectedJS, injectedFontPreloadTags, asNotFound, metadataOutlet, ctx, missingSlots }) {
const { renderOpts: { nextConfigOutput, experimental }, staticGenerationStore, componentMod: { staticGenerationBailout, NotFoundBoundary, LayoutRouter, RenderFromTemplateContext, StaticGenerationSearchParamsBailoutProvider, serverHooks: { DynamicServerError } }, pagePath, getDynamicParamFromSegment, isPrefetch, searchParamsProps } = ctx;
const { page, layoutOrPagePath, segment, components, parallelRoutes } = (0, _parseloadertree.parseLoaderTree)(tree);
const { layout, template, error, loading, "not-found": notFound } = components;
const injectedCSSWithCurrentLayout = new Set(injectedCSS);
const injectedJSWithCurrentLayout = new Set(injectedJS);
const injectedFontPreloadTagsWithCurrentLayout = new Set(injectedFontPreloadTags);
const layerAssets = (0, _getlayerassets.getLayerAssets)({
ctx,
layoutOrPagePath,
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout,
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout
});
const [Template, templateStyles, templateScripts] = template ? await (0, _createcomponentstylesandscripts.createComponentStylesAndScripts)({
ctx,
filePath: template[1],
getComponent: template[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [
_react.default.Fragment
];
const [ErrorComponent, errorStyles, errorScripts] = error ? await (0, _createcomponentstylesandscripts.createComponentStylesAndScripts)({
ctx,
filePath: error[1],
getComponent: error[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const [Loading, loadingStyles, loadingScripts] = loading ? await (0, _createcomponentstylesandscripts.createComponentStylesAndScripts)({
ctx,
filePath: loading[1],
getComponent: loading[0],
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout
}) : [];
const isLayout = typeof layout !== "undefined";
const isPage = typeof page !== "undefined";
const [layoutOrPageMod] = await (0, _tracer.getTracer)().trace(_constants.NextNodeServerSpan.getLayoutOrPageModule, {
hideSpan: !(isLayout || isPage),
spanName: "resolve segment modules",
attributes: {
"next.segment": segment
}
}, ()=>(0, _appdirmodule.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 (0, _createcomponentstylesandscripts.createComponentStylesAndScripts)({
ctx,
filePath: notFound[1],
getComponent: notFound[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") {
staticGenerationStore.forceDynamic = true;
staticGenerationStore.dynamicShouldError = true;
staticGenerationBailout(`output: export`, {
dynamic,
link: "https://nextjs.org/docs/advanced-features/static-html-export"
});
}
}
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") {
staticGenerationStore.dynamicShouldError = true;
} else if (dynamic === "force-dynamic") {
staticGenerationStore.forceDynamic = true;
// TODO: (PPR) remove this bailout once PPR is the default
if (!staticGenerationStore.postpone) {
// If the postpone API isn't available, we can't postpone the render and
// therefore we can't use the dynamic API.
staticGenerationBailout(`force-dynamic`, {
dynamic
});
}
} else {
staticGenerationStore.dynamicShouldError = false;
if (dynamic === "force-static") {
staticGenerationStore.forceStatic = true;
} else {
staticGenerationStore.forceStatic = false;
}
}
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.fetchCache) === "string") {
staticGenerationStore.fetchCache = layoutOrPageMod == null ? void 0 : layoutOrPageMod.fetchCache;
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate) !== "undefined") {
(0, _patchfetch.validateRevalidate)(layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate, staticGenerationStore.urlPathname);
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate) === "number") {
ctx.defaultRevalidate = layoutOrPageMod.revalidate;
if (typeof staticGenerationStore.revalidate === "undefined" || typeof staticGenerationStore.revalidate === "number" && staticGenerationStore.revalidate > ctx.defaultRevalidate) {
staticGenerationStore.revalidate = ctx.defaultRevalidate;
}
if (!staticGenerationStore.forceStatic && staticGenerationStore.isStaticGeneration && ctx.defaultRevalidate === 0 && // If the postpone API isn't available, we can't postpone the render and
// therefore we can't use the dynamic API.
!staticGenerationStore.postpone) {
const dynamicUsageDescription = `revalidate: 0 configured ${segment}`;
staticGenerationStore.dynamicUsageDescription = dynamicUsageDescription;
throw new DynamicServerError(dynamicUsageDescription);
}
}
// If there's a dynamic usage error attached to the store, throw it.
if (staticGenerationStore.dynamicUsageErr) {
throw staticGenerationStore.dynamicUsageErr;
}
const LayoutOrPage = layoutOrPageMod ? (0, _interopdefault.interopDefault)(layoutOrPageMod) : undefined;
/**
* The React Component to render.
*/ let Component = LayoutOrPage;
const parallelKeys = Object.keys(parallelRoutes);
const hasSlotKey = parallelKeys.length > 1;
// TODO-APP: This is a hack to support unmatched parallel routes, which will throw `notFound()`.
// This ensures that a `NotFoundBoundary` 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.
if (hasSlotKey && rootLayoutAtThisLevel && LayoutOrPage) {
Component = (componentProps)=>{
const NotFoundComponent = NotFound;
const RootLayoutComponent = LayoutOrPage;
return /*#__PURE__*/ (0, _jsxruntime.jsx)(NotFoundBoundary, {
notFound: NotFoundComponent ? /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
children: [
layerAssets,
/*#__PURE__*/ (0, _jsxruntime.jsxs)(RootLayoutComponent, {
params: componentProps.params,
children: [
notFoundStyles,
/*#__PURE__*/ (0, _jsxruntime.jsx)(NotFoundComponent, {})
]
})
]
}) : undefined,
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(RootLayoutComponent, {
...componentProps
})
});
};
}
if (process.env.NODE_ENV === "development") {
const { isValidElementType } = require("next/dist/compiled/react-is");
if ((isPage || typeof Component !== "undefined") && !isValidElementType(Component)) {
throw new Error(`The default export is not a React Component in page: "${pagePath}"`);
}
if (typeof ErrorComponent !== "undefined" && !isValidElementType(ErrorComponent)) {
throw new Error(`The default export of error is not a React Component in page: ${segment}`);
}
if (typeof Loading !== "undefined" && !isValidElementType(Loading)) {
throw new Error(`The default export of loading is not a React Component in ${segment}`);
}
if (typeof NotFound !== "undefined" && !isValidElementType(NotFound)) {
throw new Error(`The default export of notFound is not a React Component in ${segment}`);
}
}
// Handle dynamic segment params.
const segmentParam = getDynamicParamFromSegment(segment);
/**
* Create object holding the parent params and current params
*/ const currentParams = // Handle null case where dynamic param is optional
segmentParam && segmentParam.value !== null ? {
...parentParams,
[segmentParam.param]: segmentParam.value
} : parentParams;
// Resolve the segment param
const actualSegment = segmentParam ? segmentParam.treeSegment : segment;
//
// 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 currentSegmentPath = firstItem ? [
parallelRouteKey
] : [
actualSegment,
parallelRouteKey
];
const parallelRoute = parallelRoutes[parallelRouteKey];
const notFoundComponent = NotFound && isChildrenRouteKey ? /*#__PURE__*/ (0, _jsxruntime.jsx)(NotFound, {}) : 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 currentStyles = undefined;
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 defered 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 || !(0, _hasloadingcomponentintree.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.ppr) {
// 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) {
// 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 = (0, _parseloadertree.parseLoaderTree)(parallelRoute);
if (parsedTree.layoutOrPagePath === _parallelroutedefault.PARALLEL_ROUTE_DEFAULT_PATH) {
missingSlots.add(parallelRouteKey);
}
}
const { seedData, styles: childComponentStyles } = await createComponentTree({
createSegmentPath: (child)=>{
return createSegmentPath([
...currentSegmentPath,
...child
]);
},
loaderTree: parallelRoute,
parentParams: currentParams,
rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove,
injectedCSS: injectedCSSWithCurrentLayout,
injectedJS: injectedJSWithCurrentLayout,
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
asNotFound,
metadataOutlet,
ctx,
missingSlots
});
currentStyles = childComponentStyles;
childCacheNodeSeedData = seedData;
}
// This is turned back into an object below.
return [
parallelRouteKey,
/*#__PURE__*/ (0, _jsxruntime.jsx)(LayoutRouter, {
parallelRouterKey: parallelRouteKey,
segmentPath: createSegmentPath(currentSegmentPath),
loading: Loading ? /*#__PURE__*/ (0, _jsxruntime.jsx)(Loading, {}) : undefined,
loadingStyles: loadingStyles,
loadingScripts: loadingScripts,
// 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.
hasLoading: Boolean(Loading),
error: ErrorComponent,
errorStyles: errorStyles,
errorScripts: errorScripts,
template: /*#__PURE__*/ (0, _jsxruntime.jsx)(Template, {
children: /*#__PURE__*/ (0, _jsxruntime.jsx)(RenderFromTemplateContext, {})
}),
templateStyles: templateStyles,
templateScripts: templateScripts,
notFound: notFoundComponent,
notFoundStyles: notFoundStyles,
styles: currentStyles
}),
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;
}
// 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 (!Component) {
return {
seedData: [
actualSegment,
parallelRouteCacheNodeSeedData,
// TODO: I don't think the extra fragment is necessary. React treats top
// level fragments as transparent, i.e. the runtime behavior should be
// identical even without it. But maybe there's some findDOMNode-related
// reason that I'm not aware of, so I'm leaving it as-is out of extreme
// caution, for now.
/*#__PURE__*/ (0, _jsxruntime.jsx)(_jsxruntime.Fragment, {
children: parallelRouteProps.children
})
],
styles: layerAssets
};
}
// 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.
if (staticGenerationStore.forceDynamic && staticGenerationStore.postpone) {
return {
seedData: [
actualSegment,
parallelRouteCacheNodeSeedData,
/*#__PURE__*/ (0, _jsxruntime.jsx)(Postpone, {
postpone: staticGenerationStore.postpone
})
],
styles: layerAssets
};
}
const isClientComponent = (0, _clientreference.isClientReference)(layoutOrPageMod);
// If it's a not found route, and we don't have any matched parallel
// routes, we try to render the not found component if it exists.
let notFoundComponent = {};
if (NotFound && asNotFound && // In development, it could hit the parallel-route-default not found, so we only need to check the segment.
// Or if there's no parallel routes means it reaches the end.
!parallelRouteMap.length) {
notFoundComponent = {
children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
children: [
/*#__PURE__*/ (0, _jsxruntime.jsx)("meta", {
name: "robots",
content: "noindex"
}),
process.env.NODE_ENV === "development" && /*#__PURE__*/ (0, _jsxruntime.jsx)("meta", {
name: "next-error",
content: "not-found"
}),
notFoundStyles,
/*#__PURE__*/ (0, _jsxruntime.jsx)(NotFound, {})
]
})
};
}
const props = {
...parallelRouteProps,
...notFoundComponent,
// TODO-APP: params and query have to be blocked parallel route names. Might have to add a reserved name list.
// Params are always the current params that apply to the layout
// If you have a `/dashboard/[team]/layout.js` it will provide `team` as a param but not anything further down.
params: currentParams,
// Query is only provided to page
...(()=>{
if (isClientComponent && staticGenerationStore.isStaticGeneration) {
return {};
}
if (isPage) {
return searchParamsProps;
}
})()
};
return {
seedData: [
actualSegment,
parallelRouteCacheNodeSeedData,
/*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
children: [
isPage ? metadataOutlet : null,
isPage && isClientComponent ? /*#__PURE__*/ (0, _jsxruntime.jsx)(StaticGenerationSearchParamsBailoutProvider, {
propsForComponent: props,
Component: Component,
isStaticGeneration: staticGenerationStore.isStaticGeneration
}) : /*#__PURE__*/ (0, _jsxruntime.jsx)(Component, {
...props
}),
null
]
})
],
styles: layerAssets
};
}
//# sourceMappingURL=create-component-tree.js.map