1141 lines
64 KiB
JavaScript
1141 lines
64 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "renderToHTMLOrFlight", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return renderToHTMLOrFlight;
|
|
}
|
|
});
|
|
const _react = /*#__PURE__*/ _interop_require_default(require("react"));
|
|
const _createservercomponentsrenderer = require("./create-server-components-renderer");
|
|
const _renderresult = /*#__PURE__*/ _interop_require_default(require("../render-result"));
|
|
const _nodewebstreamshelper = require("../stream-utils/node-web-streams-helper");
|
|
const _matchsegments = require("../../client/components/match-segments");
|
|
const _internalutils = require("../internal-utils");
|
|
const _approuterheaders = require("../../client/components/app-router-headers");
|
|
const _metadata = require("../../lib/metadata/metadata");
|
|
const _requestasyncstoragewrapper = require("../async-storage/request-async-storage-wrapper");
|
|
const _staticgenerationasyncstoragewrapper = require("../async-storage/static-generation-async-storage-wrapper");
|
|
const _clientreference = require("../../lib/client-reference");
|
|
const _appdirmodule = require("../lib/app-dir-module");
|
|
const _notfound = require("../../client/components/not-found");
|
|
const _redirect = require("../../client/components/redirect");
|
|
const _getredirectstatuscodefromerror = require("../../client/components/get-redirect-status-code-from-error");
|
|
const _patchfetch = require("../lib/patch-fetch");
|
|
const _constants = require("../lib/trace/constants");
|
|
const _tracer = require("../lib/trace/tracer");
|
|
const _interopdefault = require("./interop-default");
|
|
const _preloadcomponent = require("./preload-component");
|
|
const _flightrenderresult = require("./flight-render-result");
|
|
const _createerrorhandler = require("./create-error-handler");
|
|
const _getshortdynamicparamtype = require("./get-short-dynamic-param-type");
|
|
const _getsegmentparam = require("./get-segment-param");
|
|
const _getcssinlinedlinktags = require("./get-css-inlined-link-tags");
|
|
const _getpreloadablefonts = require("./get-preloadable-fonts");
|
|
const _getscriptnoncefromheader = require("./get-script-nonce-from-header");
|
|
const _rendertostring = require("./render-to-string");
|
|
const _parseandvalidateflightrouterstate = require("./parse-and-validate-flight-router-state");
|
|
const _validateurl = require("./validate-url");
|
|
const _createflightrouterstatefromloadertree = require("./create-flight-router-state-from-loader-tree");
|
|
const _actionhandler = require("./action-handler");
|
|
const _nossrerror = require("../../shared/lib/lazy-dynamic/no-ssr-error");
|
|
const _log = require("../../build/output/log");
|
|
const _requestcookies = require("../web/spec-extension/adapters/request-cookies");
|
|
const _serverinsertedhtml = require("./server-inserted-html");
|
|
const _requiredscripts = require("./required-scripts");
|
|
const _addpathprefix = require("../../shared/lib/router/utils/add-path-prefix");
|
|
function _interop_require_default(obj) {
|
|
return obj && obj.__esModule ? obj : {
|
|
default: obj
|
|
};
|
|
}
|
|
function createNotFoundLoaderTree(loaderTree) {
|
|
// Align the segment with parallel-route-default in next-app-loader
|
|
return [
|
|
"",
|
|
{},
|
|
loaderTree[2]
|
|
];
|
|
}
|
|
/* This method is important for intercepted routes to function:
|
|
* when a route is intercepted, e.g. /blog/[slug], it will be rendered
|
|
* with the layout of the previous page, e.g. /profile/[id]. The problem is
|
|
* that the loader tree needs to know the dynamic param in order to render (id and slug in the example).
|
|
* Normally they are read from the path but since we are intercepting the route, the path would not contain id,
|
|
* so we need to read it from the router state.
|
|
*/ function findDynamicParamFromRouterState(providedFlightRouterState, segment) {
|
|
if (!providedFlightRouterState) {
|
|
return null;
|
|
}
|
|
const treeSegment = providedFlightRouterState[0];
|
|
if ((0, _matchsegments.canSegmentBeOverridden)(segment, treeSegment)) {
|
|
if (!Array.isArray(treeSegment) || Array.isArray(segment)) {
|
|
return null;
|
|
}
|
|
return {
|
|
param: treeSegment[0],
|
|
value: treeSegment[1],
|
|
treeSegment: treeSegment,
|
|
type: treeSegment[2]
|
|
};
|
|
}
|
|
for (const parallelRouterState of Object.values(providedFlightRouterState[1])){
|
|
const maybeDynamicParam = findDynamicParamFromRouterState(parallelRouterState, segment);
|
|
if (maybeDynamicParam) {
|
|
return maybeDynamicParam;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function hasLoadingComponentInTree(tree) {
|
|
const [, parallelRoutes, { loading }] = tree;
|
|
if (loading) {
|
|
return true;
|
|
}
|
|
return Object.values(parallelRoutes).some((parallelRoute)=>hasLoadingComponentInTree(parallelRoute));
|
|
}
|
|
async function renderToHTMLOrFlight(req, res, pagePath, query, renderOpts) {
|
|
const isFlight = req.headers[_approuterheaders.RSC.toLowerCase()] !== undefined;
|
|
const pathname = (0, _validateurl.validateURL)(req.url);
|
|
// A unique request timestamp used by development to ensure that it's
|
|
// consistent and won't change during this request. This is important to
|
|
// avoid that resources can be deduped by React Float if the same resource is
|
|
// rendered or preloaded multiple times: `<link href="a.css?v={Date.now()}"/>`.
|
|
const DEV_REQUEST_TS = Date.now();
|
|
const { buildManifest, subresourceIntegrityManifest, serverActionsManifest, ComponentMod, dev, nextFontManifest, supportsDynamicHTML, nextConfigOutput, serverActionsBodySizeLimit, buildId, deploymentId, appDirDevErrorLogger } = renderOpts;
|
|
// We need to expose the bundled `require` API globally for
|
|
// react-server-dom-webpack. This is a hack until we find a better way.
|
|
if (ComponentMod.__next_app__) {
|
|
// @ts-ignore
|
|
globalThis.__next_require__ = ComponentMod.__next_app__.require;
|
|
// @ts-ignore
|
|
globalThis.__next_chunk_load__ = ComponentMod.__next_app__.loadChunk;
|
|
}
|
|
const extraRenderResultMeta = {};
|
|
const appUsingSizeAdjust = !!(nextFontManifest == null ? void 0 : nextFontManifest.appUsingSizeAdjust);
|
|
const clientReferenceManifest = renderOpts.clientReferenceManifest;
|
|
const capturedErrors = [];
|
|
const allCapturedErrors = [];
|
|
const isNextExport = !!renderOpts.nextExport;
|
|
const serverComponentsErrorHandler = (0, _createerrorhandler.createErrorHandler)({
|
|
_source: "serverComponentsRenderer",
|
|
dev,
|
|
isNextExport,
|
|
errorLogger: appDirDevErrorLogger,
|
|
capturedErrors
|
|
});
|
|
const flightDataRendererErrorHandler = (0, _createerrorhandler.createErrorHandler)({
|
|
_source: "flightDataRenderer",
|
|
dev,
|
|
isNextExport,
|
|
errorLogger: appDirDevErrorLogger,
|
|
capturedErrors
|
|
});
|
|
const htmlRendererErrorHandler = (0, _createerrorhandler.createErrorHandler)({
|
|
_source: "htmlRenderer",
|
|
dev,
|
|
isNextExport,
|
|
errorLogger: appDirDevErrorLogger,
|
|
capturedErrors,
|
|
allCapturedErrors
|
|
});
|
|
(0, _patchfetch.patchFetch)(ComponentMod);
|
|
/**
|
|
* Rules of Static & Dynamic HTML:
|
|
*
|
|
* 1.) We must generate static HTML unless the caller explicitly opts
|
|
* in to dynamic HTML support.
|
|
*
|
|
* 2.) If dynamic HTML support is requested, we must honor that request
|
|
* or throw an error. It is the sole responsibility of the caller to
|
|
* ensure they aren't e.g. requesting dynamic HTML for an AMP page.
|
|
*
|
|
* These rules help ensure that other existing features like request caching,
|
|
* coalescing, and ISR continue working as intended.
|
|
*/ const generateStaticHTML = supportsDynamicHTML !== true;
|
|
const staticGenerationAsyncStorage = ComponentMod.staticGenerationAsyncStorage;
|
|
const requestAsyncStorage = ComponentMod.requestAsyncStorage;
|
|
const staticGenerationBailout = ComponentMod.staticGenerationBailout;
|
|
// we wrap the render in an AsyncLocalStorage context
|
|
const wrappedRender = async ()=>{
|
|
var _getTracer_getRootSpanAttributes, _staticGenerationStore_tags;
|
|
const staticGenerationStore = staticGenerationAsyncStorage.getStore();
|
|
if (!staticGenerationStore) {
|
|
throw new Error(`Invariant: Render expects to have staticGenerationAsyncStorage, none found`);
|
|
}
|
|
staticGenerationStore.fetchMetrics = [];
|
|
extraRenderResultMeta.fetchMetrics = staticGenerationStore.fetchMetrics;
|
|
const requestStore = requestAsyncStorage.getStore();
|
|
if (!requestStore) {
|
|
throw new Error(`Invariant: Render expects to have requestAsyncStorage, none found`);
|
|
}
|
|
// don't modify original query object
|
|
query = {
|
|
...query
|
|
};
|
|
(0, _internalutils.stripInternalQueries)(query);
|
|
const isPrefetch = req.headers[_approuterheaders.NEXT_ROUTER_PREFETCH.toLowerCase()] !== undefined;
|
|
/**
|
|
* Router state provided from the client-side router. Used to handle rendering from the common layout down.
|
|
*/ let providedFlightRouterState = isFlight ? (0, _parseandvalidateflightrouterstate.parseAndValidateFlightRouterState)(req.headers[_approuterheaders.NEXT_ROUTER_STATE_TREE.toLowerCase()]) : undefined;
|
|
/**
|
|
* The tree created in next-app-loader that holds component segments and modules
|
|
*/ const loaderTree = ComponentMod.tree;
|
|
/**
|
|
* The metadata items array created in next-app-loader with all relevant information
|
|
* that we need to resolve the final metadata.
|
|
*/ let requestId;
|
|
if (process.env.NEXT_RUNTIME === "edge") {
|
|
requestId = crypto.randomUUID();
|
|
} else {
|
|
requestId = require("next/dist/compiled/nanoid").nanoid();
|
|
}
|
|
const LayoutRouter = ComponentMod.LayoutRouter;
|
|
const RenderFromTemplateContext = ComponentMod.RenderFromTemplateContext;
|
|
const createSearchParamsBailoutProxy = ComponentMod.createSearchParamsBailoutProxy;
|
|
const StaticGenerationSearchParamsBailoutProvider = ComponentMod.StaticGenerationSearchParamsBailoutProvider;
|
|
const isStaticGeneration = staticGenerationStore.isStaticGeneration;
|
|
// During static generation we need to call the static generation bailout when reading searchParams
|
|
const providedSearchParams = isStaticGeneration ? createSearchParamsBailoutProxy() : query;
|
|
const searchParamsProps = {
|
|
searchParams: providedSearchParams
|
|
};
|
|
/**
|
|
* Server Context is specifically only available in Server Components.
|
|
* It has to hold values that can't change while rendering from the common layout down.
|
|
* An example of this would be that `headers` are available but `searchParams` are not because that'd mean we have to render from the root layout down on all requests.
|
|
*/ const serverContexts = [
|
|
[
|
|
"WORKAROUND",
|
|
null
|
|
]
|
|
];
|
|
/**
|
|
* Dynamic parameters. E.g. when you visit `/dashboard/vercel` which is rendered by `/dashboard/[slug]` the value will be {"slug": "vercel"}.
|
|
*/ const pathParams = renderOpts.params;
|
|
/**
|
|
* Parse the dynamic segment and return the associated value.
|
|
*/ const getDynamicParamFromSegment = (// [slug] / [[slug]] / [...slug]
|
|
segment)=>{
|
|
const segmentParam = (0, _getsegmentparam.getSegmentParam)(segment);
|
|
if (!segmentParam) {
|
|
return null;
|
|
}
|
|
const key = segmentParam.param;
|
|
let value = pathParams[key];
|
|
// this is a special marker that will be present for interception routes
|
|
if (value === "__NEXT_EMPTY_PARAM__") {
|
|
value = undefined;
|
|
}
|
|
if (Array.isArray(value)) {
|
|
value = value.map((i)=>encodeURIComponent(i));
|
|
} else if (typeof value === "string") {
|
|
value = encodeURIComponent(value);
|
|
}
|
|
if (!value) {
|
|
// Handle case where optional catchall does not have a value, e.g. `/dashboard/[...slug]` when requesting `/dashboard`
|
|
if (segmentParam.type === "optional-catchall") {
|
|
const type = _getshortdynamicparamtype.dynamicParamTypes[segmentParam.type];
|
|
return {
|
|
param: key,
|
|
value: null,
|
|
type: type,
|
|
// This value always has to be a string.
|
|
treeSegment: [
|
|
key,
|
|
"",
|
|
type
|
|
]
|
|
};
|
|
}
|
|
return findDynamicParamFromRouterState(providedFlightRouterState, segment);
|
|
}
|
|
const type = (0, _getshortdynamicparamtype.getShortDynamicParamType)(segmentParam.type);
|
|
return {
|
|
param: key,
|
|
// The value that is passed to user code.
|
|
value: value,
|
|
// The value that is rendered in the router tree.
|
|
treeSegment: [
|
|
key,
|
|
Array.isArray(value) ? value.join("/") : value,
|
|
type
|
|
],
|
|
type: type
|
|
};
|
|
};
|
|
let defaultRevalidate = false;
|
|
const assetPrefix = renderOpts.assetPrefix || "";
|
|
const getAssetQueryString = (addTimestamp)=>{
|
|
const isDev = process.env.NODE_ENV === "development";
|
|
let qs = "";
|
|
if (isDev && addTimestamp) {
|
|
qs += `?v=${DEV_REQUEST_TS}`;
|
|
}
|
|
if (deploymentId) {
|
|
qs += `${isDev ? "&" : "?"}dpl=${deploymentId}`;
|
|
}
|
|
return qs;
|
|
};
|
|
const createComponentAndStyles = async ({ filePath, getComponent, injectedCSS })=>{
|
|
const cssHrefs = (0, _getcssinlinedlinktags.getCssInlinedLinkTags)(clientReferenceManifest, filePath, injectedCSS);
|
|
const styles = cssHrefs ? cssHrefs.map((href, index)=>{
|
|
// In dev, Safari and Firefox will cache the resource during HMR:
|
|
// - https://github.com/vercel/next.js/issues/5860
|
|
// - https://bugs.webkit.org/show_bug.cgi?id=187726
|
|
// Because of this, we add a `?v=` query to bypass the cache during
|
|
// development. We need to also make sure that the number is always
|
|
// increasing.
|
|
const fullHref = `${assetPrefix}/_next/${href}${getAssetQueryString(true)}`;
|
|
// `Precedence` is an opt-in signal for React to handle resource
|
|
// loading and deduplication, etc. It's also used as the key to sort
|
|
// resources so they will be injected in the correct order.
|
|
// During HMR, it's critical to use different `precedence` values
|
|
// for different stylesheets, so their order will be kept.
|
|
// https://github.com/facebook/react/pull/25060
|
|
const precedence = process.env.NODE_ENV === "development" ? "next_" + href : "next";
|
|
return /*#__PURE__*/ _react.default.createElement("link", {
|
|
rel: "stylesheet",
|
|
href: fullHref,
|
|
// @ts-ignore
|
|
precedence: precedence,
|
|
key: index
|
|
});
|
|
}) : null;
|
|
const Comp = (0, _interopdefault.interopDefault)(await getComponent());
|
|
return [
|
|
Comp,
|
|
styles
|
|
];
|
|
};
|
|
const getLayerAssets = ({ layoutOrPagePath, injectedCSS: injectedCSSWithCurrentLayout, injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout })=>{
|
|
const stylesheets = layoutOrPagePath ? (0, _getcssinlinedlinktags.getCssInlinedLinkTags)(clientReferenceManifest, layoutOrPagePath, injectedCSSWithCurrentLayout, true) : [];
|
|
const preloadedFontFiles = layoutOrPagePath ? (0, _getpreloadablefonts.getPreloadableFonts)(nextFontManifest, layoutOrPagePath, injectedFontPreloadTagsWithCurrentLayout) : null;
|
|
if (preloadedFontFiles) {
|
|
if (preloadedFontFiles.length) {
|
|
for(let i = 0; i < preloadedFontFiles.length; i++){
|
|
const fontFilename = preloadedFontFiles[i];
|
|
const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFilename)[1];
|
|
const type = `font/${ext}`;
|
|
const href = `${assetPrefix}/_next/${fontFilename}`;
|
|
ComponentMod.preloadFont(href, type);
|
|
}
|
|
} else {
|
|
try {
|
|
let url = new URL(assetPrefix);
|
|
ComponentMod.preconnect(url.origin, "anonymous");
|
|
} catch (error) {
|
|
// assetPrefix must not be a fully qualified domain name. We assume
|
|
// we should preconnect to same origin instead
|
|
ComponentMod.preconnect("/", "anonymous");
|
|
}
|
|
}
|
|
}
|
|
const styles = stylesheets ? stylesheets.map((href, index)=>{
|
|
// In dev, Safari and Firefox will cache the resource during HMR:
|
|
// - https://github.com/vercel/next.js/issues/5860
|
|
// - https://bugs.webkit.org/show_bug.cgi?id=187726
|
|
// Because of this, we add a `?v=` query to bypass the cache during
|
|
// development. We need to also make sure that the number is always
|
|
// increasing.
|
|
const fullHref = `${assetPrefix}/_next/${href}${getAssetQueryString(true)}`;
|
|
// `Precedence` is an opt-in signal for React to handle resource
|
|
// loading and deduplication, etc. It's also used as the key to sort
|
|
// resources so they will be injected in the correct order.
|
|
// During HMR, it's critical to use different `precedence` values
|
|
// for different stylesheets, so their order will be kept.
|
|
// https://github.com/facebook/react/pull/25060
|
|
const precedence = process.env.NODE_ENV === "development" ? "next_" + href : "next";
|
|
ComponentMod.preloadStyle(fullHref);
|
|
return /*#__PURE__*/ _react.default.createElement("link", {
|
|
rel: "stylesheet",
|
|
href: fullHref,
|
|
// @ts-ignore
|
|
precedence: precedence,
|
|
key: index
|
|
});
|
|
}) : null;
|
|
return styles;
|
|
};
|
|
const parseLoaderTree = (tree)=>{
|
|
const [segment, parallelRoutes, components] = tree;
|
|
const { layout } = components;
|
|
let { page } = components;
|
|
// a __DEFAULT__ segment means that this route didn't match any of the
|
|
// segments in the route, so we should use the default page
|
|
page = segment === "__DEFAULT__" ? components.defaultPage : page;
|
|
const layoutOrPagePath = (layout == null ? void 0 : layout[1]) || (page == null ? void 0 : page[1]);
|
|
return {
|
|
page,
|
|
segment,
|
|
components,
|
|
layoutOrPagePath,
|
|
parallelRoutes
|
|
};
|
|
};
|
|
/**
|
|
* Use the provided loader tree to create the React Component tree.
|
|
*/ const createComponentTree = async ({ createSegmentPath, loaderTree: tree, parentParams, firstItem, rootLayoutIncluded, injectedCSS, injectedFontPreloadTags, asNotFound, metadataOutlet })=>{
|
|
const { page, layoutOrPagePath, segment, components, parallelRoutes } = parseLoaderTree(tree);
|
|
const { layout, template, error, loading, "not-found": notFound } = components;
|
|
const injectedCSSWithCurrentLayout = new Set(injectedCSS);
|
|
const injectedFontPreloadTagsWithCurrentLayout = new Set(injectedFontPreloadTags);
|
|
const styles = getLayerAssets({
|
|
layoutOrPagePath,
|
|
injectedCSS: injectedCSSWithCurrentLayout,
|
|
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout
|
|
});
|
|
const [Template, templateStyles] = template ? await createComponentAndStyles({
|
|
filePath: template[1],
|
|
getComponent: template[0],
|
|
injectedCSS: injectedCSSWithCurrentLayout
|
|
}) : [
|
|
_react.default.Fragment
|
|
];
|
|
const [ErrorComponent, errorStyles] = error ? await createComponentAndStyles({
|
|
filePath: error[1],
|
|
getComponent: error[0],
|
|
injectedCSS: injectedCSSWithCurrentLayout
|
|
}) : [];
|
|
const [Loading, loadingStyles] = loading ? await createComponentAndStyles({
|
|
filePath: loading[1],
|
|
getComponent: loading[0],
|
|
injectedCSS: injectedCSSWithCurrentLayout
|
|
}) : [];
|
|
const isLayout = typeof layout !== "undefined";
|
|
const isPage = typeof page !== "undefined";
|
|
const [layoutOrPageMod] = await (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 createComponentAndStyles({
|
|
filePath: notFound[1],
|
|
getComponent: notFound[0],
|
|
injectedCSS: injectedCSSWithCurrentLayout
|
|
}) : [];
|
|
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;
|
|
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) === "number") {
|
|
defaultRevalidate = layoutOrPageMod.revalidate;
|
|
if (typeof staticGenerationStore.revalidate === "undefined" || typeof staticGenerationStore.revalidate === "number" && staticGenerationStore.revalidate > defaultRevalidate) {
|
|
staticGenerationStore.revalidate = defaultRevalidate;
|
|
}
|
|
if (staticGenerationStore.isStaticGeneration && defaultRevalidate === 0) {
|
|
const { DynamicServerError } = ComponentMod.serverHooks;
|
|
const dynamicUsageDescription = `revalidate: 0 configured ${segment}`;
|
|
staticGenerationStore.dynamicUsageDescription = dynamicUsageDescription;
|
|
throw new DynamicServerError(dynamicUsageDescription);
|
|
}
|
|
}
|
|
if (staticGenerationStore == null ? void 0 : 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;
|
|
if (hasSlotKey && rootLayoutAtThisLevel) {
|
|
const NotFoundBoundary = ComponentMod.NotFoundBoundary;
|
|
Component = (componentProps)=>{
|
|
const NotFoundComponent = NotFound;
|
|
const RootLayoutComponent = LayoutOrPage;
|
|
return /*#__PURE__*/ _react.default.createElement(NotFoundBoundary, {
|
|
notFound: /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, styles, /*#__PURE__*/ _react.default.createElement(RootLayoutComponent, null, notFoundStyles, /*#__PURE__*/ _react.default.createElement(NotFoundComponent, null)))
|
|
}, /*#__PURE__*/ _react.default.createElement(RootLayoutComponent, componentProps));
|
|
};
|
|
}
|
|
if (dev) {
|
|
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;
|
|
// This happens outside of rendering in order to eagerly kick off data fetching for layouts / the page further down
|
|
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 childSegment = parallelRoute[0];
|
|
const childSegmentParam = getDynamicParamFromSegment(childSegment);
|
|
const notFoundComponent = NotFound && isChildrenRouteKey ? /*#__PURE__*/ _react.default.createElement(NotFound, null) : undefined;
|
|
function getParallelRoutePair(currentChildProp, currentStyles) {
|
|
// This is turned back into an object below.
|
|
return [
|
|
parallelRouteKey,
|
|
/*#__PURE__*/ _react.default.createElement(LayoutRouter, {
|
|
parallelRouterKey: parallelRouteKey,
|
|
segmentPath: createSegmentPath(currentSegmentPath),
|
|
loading: Loading ? /*#__PURE__*/ _react.default.createElement(Loading, null) : undefined,
|
|
loadingStyles: loadingStyles,
|
|
// 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,
|
|
template: /*#__PURE__*/ _react.default.createElement(Template, null, /*#__PURE__*/ _react.default.createElement(RenderFromTemplateContext, null)),
|
|
templateStyles: templateStyles,
|
|
notFound: notFoundComponent,
|
|
notFoundStyles: notFoundStyles,
|
|
childProp: currentChildProp,
|
|
styles: currentStyles
|
|
})
|
|
];
|
|
}
|
|
// 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 childElement = null;
|
|
const childPropSegment = (0, _createflightrouterstatefromloadertree.addSearchParamsIfPageSegment)(childSegmentParam ? childSegmentParam.treeSegment : childSegment, query);
|
|
if (!(isPrefetch && (Loading || !hasLoadingComponentInTree(parallelRoute)))) {
|
|
// Create the child component
|
|
const { Component: ChildComponent, styles: childComponentStyles } = await createComponentTree({
|
|
createSegmentPath: (child)=>{
|
|
return createSegmentPath([
|
|
...currentSegmentPath,
|
|
...child
|
|
]);
|
|
},
|
|
loaderTree: parallelRoute,
|
|
parentParams: currentParams,
|
|
rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove,
|
|
injectedCSS: injectedCSSWithCurrentLayout,
|
|
injectedFontPreloadTags: injectedFontPreloadTagsWithCurrentLayout,
|
|
asNotFound,
|
|
metadataOutlet
|
|
});
|
|
currentStyles = childComponentStyles;
|
|
childElement = /*#__PURE__*/ _react.default.createElement(ChildComponent, null);
|
|
}
|
|
const childProp = {
|
|
current: childElement,
|
|
segment: childPropSegment
|
|
};
|
|
return getParallelRoutePair(childProp, currentStyles);
|
|
}));
|
|
// Convert the parallel route map into an object after all promises have been resolved.
|
|
const parallelRouteComponents = parallelRouteMap.reduce((list, [parallelRouteKey, Comp])=>{
|
|
list[parallelRouteKey] = Comp;
|
|
return list;
|
|
}, {});
|
|
// 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 {
|
|
Component: ()=>/*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, parallelRouteComponents.children),
|
|
styles
|
|
};
|
|
}
|
|
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__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "robots",
|
|
content: "noindex"
|
|
}), process.env.NODE_ENV === "development" && /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "next-error",
|
|
content: "not-found"
|
|
}), notFoundStyles, /*#__PURE__*/ _react.default.createElement(NotFound, null))
|
|
};
|
|
}
|
|
const props = {
|
|
...parallelRouteComponents,
|
|
...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 && isStaticGeneration) {
|
|
return {};
|
|
}
|
|
if (isPage) {
|
|
return searchParamsProps;
|
|
}
|
|
})()
|
|
};
|
|
// Eagerly execute layout/page component to trigger fetches early.
|
|
if (!isClientComponent) {
|
|
Component = await Promise.resolve().then(()=>(0, _preloadcomponent.preloadComponent)(Component, props));
|
|
}
|
|
return {
|
|
Component: ()=>{
|
|
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, isPage ? metadataOutlet : null, isPage && isClientComponent && isStaticGeneration ? /*#__PURE__*/ _react.default.createElement(StaticGenerationSearchParamsBailoutProvider, {
|
|
propsForComponent: props,
|
|
Component: Component
|
|
}) : /*#__PURE__*/ _react.default.createElement(Component, props), null);
|
|
},
|
|
styles
|
|
};
|
|
};
|
|
// Handle Flight render request. This is only used when client-side navigating. E.g. when you `router.push('/dashboard')` or `router.reload()`.
|
|
const generateFlight = async (options)=>{
|
|
/**
|
|
* 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.
|
|
*/ const walkTreeWithFlightRouterState = async ({ createSegmentPath, loaderTreeToFilter, parentParams, isFirst, flightRouterState, parentRendered, rscPayloadHead, injectedCSS, injectedFontPreloadTags, rootLayoutIncluded, asNotFound, metadataOutlet })=>{
|
|
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 = (0, _createflightrouterstatefromloadertree.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
|
|
!(0, _matchsegments.matchSegment)(actualSegment, flightRouterState[0]) || // Last item in the tree
|
|
parallelRoutesKeys.length === 0 || // Explicit refresh
|
|
flightRouterState[3] === "refetch";
|
|
if (!parentRendered && renderComponentsOnThisLevel) {
|
|
const overriddenSegment = flightRouterState && (0, _matchsegments.canSegmentBeOverridden)(actualSegment, flightRouterState[0]) ? flightRouterState[0] : null;
|
|
return [
|
|
[
|
|
overriddenSegment ?? actualSegment,
|
|
(0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(// Create router state using the slice of the loaderTree
|
|
loaderTreeToFilter, getDynamicParamFromSegment, query),
|
|
isPrefetch && !Boolean(components.loading) ? null : // @ts-expect-error TODO-APP: fix async component type
|
|
/*#__PURE__*/ _react.default.createElement(async ()=>{
|
|
const { Component } = await createComponentTree(// This ensures flightRouterPath is valid and filters down the tree
|
|
{
|
|
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.default.createElement(Component, null);
|
|
}),
|
|
isPrefetch && !Boolean(components.loading) ? null : (()=>{
|
|
const { layoutOrPagePath } = parseLoaderTree(loaderTreeToFilter);
|
|
const styles = getLayerAssets({
|
|
layoutOrPagePath,
|
|
injectedCSS: new Set(injectedCSS),
|
|
injectedFontPreloadTags: new Set(injectedFontPreloadTags)
|
|
});
|
|
return /*#__PURE__*/ _react.default.createElement(_react.default.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) {
|
|
(0, _getcssinlinedlinktags.getCssInlinedLinkTags)(clientReferenceManifest, layoutPath, injectedCSSWithCurrentLayout, true);
|
|
(0, _getpreloadablefonts.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({
|
|
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;
|
|
};
|
|
// Flight data that is going to be passed to the browser.
|
|
// Currently a single item array but in the future multiple patches might be combined in a single request.
|
|
let flightData = null;
|
|
if (!(options == null ? void 0 : options.skipFlight)) {
|
|
const [MetadataTree, MetadataOutlet] = (0, _metadata.createMetadataComponents)({
|
|
tree: loaderTree,
|
|
pathname,
|
|
searchParams: providedSearchParams,
|
|
getDynamicParamFromSegment,
|
|
appUsingSizeAdjust
|
|
});
|
|
flightData = (await walkTreeWithFlightRouterState({
|
|
createSegmentPath: (child)=>child,
|
|
loaderTreeToFilter: loaderTree,
|
|
parentParams: {},
|
|
flightRouterState: providedFlightRouterState,
|
|
isFirst: true,
|
|
// For flight, render metadata inside leaf page
|
|
rscPayloadHead: // Adding requestId as react key to make metadata remount for each render
|
|
/*#__PURE__*/ _react.default.createElement(MetadataTree, {
|
|
key: requestId
|
|
}),
|
|
injectedCSS: new Set(),
|
|
injectedFontPreloadTags: new Set(),
|
|
rootLayoutIncluded: false,
|
|
asNotFound: pagePath === "/404" || (options == null ? void 0 : options.asNotFound),
|
|
metadataOutlet: /*#__PURE__*/ _react.default.createElement(MetadataOutlet, null)
|
|
})).map((path)=>path.slice(1)) // remove the '' (root) segment
|
|
;
|
|
}
|
|
const buildIdFlightDataPair = [
|
|
buildId,
|
|
flightData
|
|
];
|
|
// For app dir, use the bundled version of Fizz renderer (renderToReadableStream)
|
|
// which contains the subset React.
|
|
const readable = ComponentMod.renderToReadableStream(options ? [
|
|
options.actionResult,
|
|
buildIdFlightDataPair
|
|
] : buildIdFlightDataPair, clientReferenceManifest.clientModules, {
|
|
context: serverContexts,
|
|
onError: flightDataRendererErrorHandler
|
|
}).pipeThrough((0, _nodewebstreamshelper.createBufferedTransformStream)());
|
|
return new _flightrenderresult.FlightRenderResult(readable);
|
|
};
|
|
if (isFlight && !staticGenerationStore.isStaticGeneration) {
|
|
return generateFlight();
|
|
}
|
|
// Below this line is handling for rendering to HTML.
|
|
// AppRouter is provided by next-app-loader
|
|
const AppRouter = ComponentMod.AppRouter;
|
|
const GlobalError = /** GlobalError can be either the default error boundary or the overwritten app/global-error.js **/ ComponentMod.GlobalError;
|
|
const serverComponentsInlinedTransformStream = new TransformStream();
|
|
// Get the nonce from the incoming request if it has one.
|
|
const csp = req.headers["content-security-policy"];
|
|
let nonce;
|
|
if (csp && typeof csp === "string") {
|
|
nonce = (0, _getscriptnoncefromheader.getScriptNonceFromHeader)(csp);
|
|
}
|
|
const serverComponentsRenderOpts = {
|
|
transformStream: serverComponentsInlinedTransformStream,
|
|
clientReferenceManifest,
|
|
serverContexts,
|
|
rscChunks: []
|
|
};
|
|
const validateRootLayout = dev ? {
|
|
validateRootLayout: {
|
|
assetPrefix: renderOpts.assetPrefix,
|
|
getTree: ()=>(0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(loaderTree, getDynamicParamFromSegment, query)
|
|
}
|
|
} : {};
|
|
/**
|
|
* A new React Component that renders the provided React Component
|
|
* using Flight which can then be rendered to HTML.
|
|
*/ const createServerComponentsRenderer = (loaderTreeToRender, preinitScripts)=>(0, _createservercomponentsrenderer.createServerComponentRenderer)(async (props)=>{
|
|
preinitScripts();
|
|
// Create full component tree from root to leaf.
|
|
const injectedCSS = new Set();
|
|
const injectedFontPreloadTags = new Set();
|
|
const initialTree = (0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(loaderTreeToRender, getDynamicParamFromSegment, query);
|
|
const [MetadataTree, MetadataOutlet] = (0, _metadata.createMetadataComponents)({
|
|
tree: loaderTreeToRender,
|
|
errorType: props.asNotFound ? "not-found" : undefined,
|
|
pathname,
|
|
searchParams: providedSearchParams,
|
|
getDynamicParamFromSegment: getDynamicParamFromSegment,
|
|
appUsingSizeAdjust: appUsingSizeAdjust
|
|
});
|
|
const { Component: ComponentTree, styles } = await createComponentTree({
|
|
createSegmentPath: (child)=>child,
|
|
loaderTree: loaderTreeToRender,
|
|
parentParams: {},
|
|
firstItem: true,
|
|
injectedCSS,
|
|
injectedFontPreloadTags,
|
|
rootLayoutIncluded: false,
|
|
asNotFound: props.asNotFound,
|
|
metadataOutlet: /*#__PURE__*/ _react.default.createElement(MetadataOutlet, null)
|
|
});
|
|
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, styles, /*#__PURE__*/ _react.default.createElement(AppRouter, {
|
|
buildId: buildId,
|
|
assetPrefix: assetPrefix,
|
|
initialCanonicalUrl: pathname,
|
|
initialTree: initialTree,
|
|
initialHead: /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, res.statusCode > 400 && /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "robots",
|
|
content: "noindex"
|
|
}), /*#__PURE__*/ _react.default.createElement(MetadataTree, {
|
|
key: requestId
|
|
})),
|
|
globalErrorComponent: GlobalError
|
|
}, /*#__PURE__*/ _react.default.createElement(ComponentTree, null)));
|
|
}, ComponentMod, serverComponentsRenderOpts, serverComponentsErrorHandler, nonce);
|
|
const { HeadManagerContext } = require("../../shared/lib/head-manager-context.shared-runtime");
|
|
// On each render, create a new `ServerInsertedHTML` context to capture
|
|
// injected nodes from user code (`useServerInsertedHTML`).
|
|
const { ServerInsertedHTMLProvider, renderServerInsertedHTML } = (0, _serverinsertedhtml.createServerInsertedHTML)();
|
|
(_getTracer_getRootSpanAttributes = (0, _tracer.getTracer)().getRootSpanAttributes()) == null ? void 0 : _getTracer_getRootSpanAttributes.set("next.route", pagePath);
|
|
const bodyResult = (0, _tracer.getTracer)().wrap(_constants.AppRenderSpan.getBodyResult, {
|
|
spanName: `render route (app) ${pagePath}`,
|
|
attributes: {
|
|
"next.route": pagePath
|
|
}
|
|
}, async ({ asNotFound, tree })=>{
|
|
const polyfills = buildManifest.polyfillFiles.filter((polyfill)=>polyfill.endsWith(".js") && !polyfill.endsWith(".module.js")).map((polyfill)=>({
|
|
src: `${assetPrefix}/_next/${polyfill}${getAssetQueryString(false)}`,
|
|
integrity: subresourceIntegrityManifest == null ? void 0 : subresourceIntegrityManifest[polyfill]
|
|
}));
|
|
const [preinitScripts, bootstrapScript] = (0, _requiredscripts.getRequiredScripts)(buildManifest, assetPrefix, subresourceIntegrityManifest, getAssetQueryString(true), nonce);
|
|
const ServerComponentsRenderer = createServerComponentsRenderer(tree, preinitScripts);
|
|
const content = /*#__PURE__*/ _react.default.createElement(HeadManagerContext.Provider, {
|
|
value: {
|
|
appDir: true,
|
|
nonce
|
|
}
|
|
}, /*#__PURE__*/ _react.default.createElement(ServerInsertedHTMLProvider, null, /*#__PURE__*/ _react.default.createElement(ServerComponentsRenderer, {
|
|
asNotFound: asNotFound
|
|
})));
|
|
let polyfillsFlushed = false;
|
|
let flushedErrorMetaTagsUntilIndex = 0;
|
|
const getServerInsertedHTML = (serverCapturedErrors)=>{
|
|
// Loop through all the errors that have been captured but not yet
|
|
// flushed.
|
|
const errorMetaTags = [];
|
|
for(; flushedErrorMetaTagsUntilIndex < serverCapturedErrors.length; flushedErrorMetaTagsUntilIndex++){
|
|
const error = serverCapturedErrors[flushedErrorMetaTagsUntilIndex];
|
|
if ((0, _notfound.isNotFoundError)(error)) {
|
|
errorMetaTags.push(/*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "robots",
|
|
content: "noindex",
|
|
key: error.digest
|
|
}), process.env.NODE_ENV === "development" ? /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "next-error",
|
|
content: "not-found",
|
|
key: "next-error"
|
|
}) : null);
|
|
} else if ((0, _redirect.isRedirectError)(error)) {
|
|
const redirectUrl = (0, _redirect.getURLFromRedirectError)(error);
|
|
const isPermanent = (0, _getredirectstatuscodefromerror.getRedirectStatusCodeFromError)(error) === 308 ? true : false;
|
|
if (redirectUrl) {
|
|
errorMetaTags.push(/*#__PURE__*/ _react.default.createElement("meta", {
|
|
httpEquiv: "refresh",
|
|
content: `${isPermanent ? 0 : 1};url=${redirectUrl}`,
|
|
key: error.digest
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
const flushed = (0, _rendertostring.renderToString)({
|
|
ReactDOMServer: require("react-dom/server.edge"),
|
|
element: /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, polyfillsFlushed ? null : polyfills == null ? void 0 : polyfills.map((polyfill)=>{
|
|
return /*#__PURE__*/ _react.default.createElement("script", {
|
|
key: polyfill.src,
|
|
src: polyfill.src,
|
|
integrity: polyfill.integrity,
|
|
noModule: true,
|
|
nonce: nonce
|
|
});
|
|
}), renderServerInsertedHTML(), errorMetaTags)
|
|
});
|
|
polyfillsFlushed = true;
|
|
return flushed;
|
|
};
|
|
try {
|
|
const renderStream = await (0, _nodewebstreamshelper.renderToInitialStream)({
|
|
ReactDOMServer: require("react-dom/server.edge"),
|
|
element: content,
|
|
streamOptions: {
|
|
onError: htmlRendererErrorHandler,
|
|
nonce,
|
|
// Include hydration scripts in the HTML
|
|
bootstrapScripts: [
|
|
bootstrapScript
|
|
]
|
|
}
|
|
});
|
|
const result = await (0, _nodewebstreamshelper.continueFromInitialStream)(renderStream, {
|
|
dataStream: serverComponentsRenderOpts.transformStream.readable,
|
|
generateStaticHTML: staticGenerationStore.isStaticGeneration || generateStaticHTML,
|
|
getServerInsertedHTML: ()=>getServerInsertedHTML(allCapturedErrors),
|
|
serverInsertedHTMLToHead: true,
|
|
...validateRootLayout
|
|
});
|
|
return result;
|
|
} catch (err) {
|
|
var _err_message;
|
|
if (err.code === "NEXT_STATIC_GEN_BAILOUT" || ((_err_message = err.message) == null ? void 0 : _err_message.includes("https://nextjs.org/docs/advanced-features/static-html-export"))) {
|
|
// Ensure that "next dev" prints the red error overlay
|
|
throw err;
|
|
}
|
|
if (err.digest === _nossrerror.NEXT_DYNAMIC_NO_SSR_CODE) {
|
|
(0, _log.warn)(`Entire page ${pagePath} deopted into client-side rendering. https://nextjs.org/docs/messages/deopted-into-client-rendering`, pagePath);
|
|
}
|
|
if ((0, _notfound.isNotFoundError)(err)) {
|
|
res.statusCode = 404;
|
|
}
|
|
let hasRedirectError = false;
|
|
if ((0, _redirect.isRedirectError)(err)) {
|
|
hasRedirectError = true;
|
|
res.statusCode = (0, _getredirectstatuscodefromerror.getRedirectStatusCodeFromError)(err);
|
|
if (err.mutableCookies) {
|
|
const headers = new Headers();
|
|
// If there were mutable cookies set, we need to set them on the
|
|
// response.
|
|
if ((0, _requestcookies.appendMutableCookies)(headers, err.mutableCookies)) {
|
|
res.setHeader("set-cookie", Array.from(headers.values()));
|
|
}
|
|
}
|
|
const redirectUrl = (0, _addpathprefix.addPathPrefix)((0, _redirect.getURLFromRedirectError)(err), renderOpts.basePath);
|
|
res.setHeader("Location", redirectUrl);
|
|
}
|
|
const is404 = res.statusCode === 404;
|
|
// Preserve the existing RSC inline chunks from the page rendering.
|
|
// To avoid the same stream being operated twice, clone the origin stream for error rendering.
|
|
const serverErrorComponentsRenderOpts = {
|
|
...serverComponentsRenderOpts,
|
|
rscChunks: [],
|
|
transformStream: (0, _nodewebstreamshelper.cloneTransformStream)(serverComponentsRenderOpts.transformStream)
|
|
};
|
|
const errorType = is404 ? "not-found" : hasRedirectError ? "redirect" : undefined;
|
|
const errorMeta = /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, res.statusCode >= 400 && /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "robots",
|
|
content: "noindex"
|
|
}), process.env.NODE_ENV === "development" && /*#__PURE__*/ _react.default.createElement("meta", {
|
|
name: "next-error",
|
|
content: "not-found"
|
|
}));
|
|
const [errorPreinitScripts, errorBootstrapScript] = (0, _requiredscripts.getRequiredScripts)(buildManifest, assetPrefix, subresourceIntegrityManifest, getAssetQueryString(false), nonce);
|
|
const ErrorPage = (0, _createservercomponentsrenderer.createServerComponentRenderer)(async ()=>{
|
|
errorPreinitScripts();
|
|
const [MetadataTree] = (0, _metadata.createMetadataComponents)({
|
|
tree,
|
|
pathname,
|
|
errorType,
|
|
searchParams: providedSearchParams,
|
|
getDynamicParamFromSegment,
|
|
appUsingSizeAdjust
|
|
});
|
|
const head = /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/ _react.default.createElement(MetadataTree, {
|
|
key: requestId
|
|
}), errorMeta);
|
|
const initialTree = (0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(tree, getDynamicParamFromSegment, query);
|
|
// For metadata notFound error there's no global not found boundary on top
|
|
// so we create a not found page with AppRouter
|
|
return /*#__PURE__*/ _react.default.createElement(AppRouter, {
|
|
buildId: buildId,
|
|
assetPrefix: assetPrefix,
|
|
initialCanonicalUrl: pathname,
|
|
initialTree: initialTree,
|
|
initialHead: head,
|
|
globalErrorComponent: GlobalError
|
|
}, /*#__PURE__*/ _react.default.createElement("html", {
|
|
id: "__next_error__"
|
|
}, /*#__PURE__*/ _react.default.createElement("head", null), /*#__PURE__*/ _react.default.createElement("body", null)));
|
|
}, ComponentMod, serverErrorComponentsRenderOpts, serverComponentsErrorHandler, nonce);
|
|
try {
|
|
const renderStream = await (0, _nodewebstreamshelper.renderToInitialStream)({
|
|
ReactDOMServer: require("react-dom/server.edge"),
|
|
element: /*#__PURE__*/ _react.default.createElement(ErrorPage, null),
|
|
streamOptions: {
|
|
nonce,
|
|
// Include hydration scripts in the HTML
|
|
bootstrapScripts: [
|
|
errorBootstrapScript
|
|
]
|
|
}
|
|
});
|
|
return await (0, _nodewebstreamshelper.continueFromInitialStream)(renderStream, {
|
|
dataStream: serverErrorComponentsRenderOpts.transformStream.readable,
|
|
generateStaticHTML: staticGenerationStore.isStaticGeneration,
|
|
getServerInsertedHTML: ()=>getServerInsertedHTML([]),
|
|
serverInsertedHTMLToHead: true,
|
|
...validateRootLayout
|
|
});
|
|
} catch (finalErr) {
|
|
if (process.env.NODE_ENV === "development" && (0, _notfound.isNotFoundError)(finalErr)) {
|
|
const bailOnNotFound = require("../../client/components/dev-root-not-found-boundary").bailOnNotFound;
|
|
bailOnNotFound();
|
|
}
|
|
throw finalErr;
|
|
}
|
|
}
|
|
});
|
|
// For action requests, we handle them differently with a special render result.
|
|
const actionRequestResult = await (0, _actionhandler.handleAction)({
|
|
req,
|
|
res,
|
|
ComponentMod,
|
|
page: renderOpts.page,
|
|
serverActionsManifest,
|
|
generateFlight,
|
|
staticGenerationStore,
|
|
requestStore,
|
|
serverActionsBodySizeLimit
|
|
});
|
|
if (actionRequestResult === "not-found") {
|
|
const notFoundLoaderTree = createNotFoundLoaderTree(loaderTree);
|
|
return new _renderresult.default(await bodyResult({
|
|
asNotFound: true,
|
|
tree: notFoundLoaderTree
|
|
}), {
|
|
...extraRenderResultMeta
|
|
});
|
|
} else if (actionRequestResult) {
|
|
actionRequestResult.extendMetadata(extraRenderResultMeta);
|
|
return actionRequestResult;
|
|
}
|
|
const renderResult = new _renderresult.default(await bodyResult({
|
|
asNotFound: pagePath === "/404",
|
|
tree: loaderTree
|
|
}), {
|
|
...extraRenderResultMeta
|
|
});
|
|
if (staticGenerationStore.pendingRevalidates) {
|
|
await Promise.all(staticGenerationStore.pendingRevalidates);
|
|
}
|
|
(0, _patchfetch.addImplicitTags)(staticGenerationStore);
|
|
extraRenderResultMeta.fetchTags = (_staticGenerationStore_tags = staticGenerationStore.tags) == null ? void 0 : _staticGenerationStore_tags.join(",");
|
|
renderResult.extendMetadata({
|
|
fetchTags: extraRenderResultMeta.fetchTags
|
|
});
|
|
if (staticGenerationStore.isStaticGeneration) {
|
|
const htmlResult = await (0, _nodewebstreamshelper.streamToBufferedResult)(renderResult);
|
|
// if we encountered any unexpected errors during build
|
|
// we fail the prerendering phase and the build
|
|
if (capturedErrors.length > 0) {
|
|
throw capturedErrors[0];
|
|
}
|
|
// TODO-APP: derive this from same pass to prevent additional
|
|
// render during static generation
|
|
const filteredFlightData = await (0, _nodewebstreamshelper.streamToBufferedResult)(await generateFlight());
|
|
if (staticGenerationStore.forceStatic === false) {
|
|
staticGenerationStore.revalidate = 0;
|
|
}
|
|
extraRenderResultMeta.pageData = filteredFlightData;
|
|
extraRenderResultMeta.revalidate = staticGenerationStore.revalidate ?? defaultRevalidate;
|
|
// provide bailout info for debugging
|
|
if (extraRenderResultMeta.revalidate === 0) {
|
|
extraRenderResultMeta.staticBailoutInfo = {
|
|
description: staticGenerationStore.dynamicUsageDescription,
|
|
stack: staticGenerationStore.dynamicUsageStack
|
|
};
|
|
}
|
|
return new _renderresult.default(htmlResult, {
|
|
...extraRenderResultMeta
|
|
});
|
|
}
|
|
return renderResult;
|
|
};
|
|
return _requestasyncstoragewrapper.RequestAsyncStorageWrapper.wrap(requestAsyncStorage, {
|
|
req,
|
|
res,
|
|
renderOpts
|
|
}, ()=>_staticgenerationasyncstoragewrapper.StaticGenerationAsyncStorageWrapper.wrap(staticGenerationAsyncStorage, {
|
|
urlPathname: pathname,
|
|
renderOpts
|
|
}, ()=>wrappedRender()));
|
|
}
|
|
|
|
//# sourceMappingURL=app-render.js.map
|