"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.renderToHTMLOrFlight = renderToHTMLOrFlight; var _react = _interopRequireWildcard(require("next/dist/compiled/react")); var _error = require("../client/components/error"); var _serverBrowser = _interopRequireDefault(require("next/dist/compiled/react-dom/server.browser")); var _renderResult = _interopRequireDefault(require("./render-result")); var _nodeWebStreamsHelper = require("./node-web-streams-helper"); var _htmlescape = require("./htmlescape"); var _matchSegments = require("../client/components/match-segments"); var _serverInsertedHtml = require("../shared/lib/server-inserted-html"); var _internalUtils = require("./internal-utils"); var _redirect = require("../client/components/redirect"); var _cookies = require("./web/spec-extension/cookies"); var _hooksServerContext = require("../client/components/hooks-server-context"); var _notFound = require("../client/components/not-found"); var _noSsrError = require("../shared/lib/no-ssr-error"); var _headManagerContext = require("../shared/lib/head-manager-context"); var _stream = require("stream"); var _stringHash = _interopRequireDefault(require("next/dist/compiled/string-hash")); var _appRouterHeaders = require("../client/components/app-router-headers"); var _head = require("../client/components/head"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const isEdgeRuntime = process.env.NEXT_RUNTIME === "edge"; function preloadComponent(Component, props) { const prev = console.error; // Hide invalid hook call warning when calling component console.error = function(msg) { if (msg.startsWith("Warning: Invalid hook call.")) { // ignore } else { // @ts-expect-error argument is defined prev.apply(console, arguments); } }; try { let result = Component(props); if (result && typeof result.then === "function") { // Catch promise rejections to prevent unhandledRejection errors result.then(()=>{}, ()=>{}); } return function() { // We know what this component will render already. return result; }; } catch (x) { // something suspended or errored, try again later } finally{ console.error = prev; } return Component; } const CACHE_ONE_YEAR = 31536000; const INTERNAL_HEADERS_INSTANCE = Symbol("internal for headers readonly"); function readonlyHeadersError() { return new Error("ReadonlyHeaders cannot be modified"); } class ReadonlyHeaders { constructor(headers){ // Since `new Headers` uses `this.append()` to fill the headers object ReadonlyHeaders can't extend from Headers directly as it would throw. const headersInstance = new Headers(headers); this[INTERNAL_HEADERS_INSTANCE] = headersInstance; this.entries = headersInstance.entries.bind(headersInstance); this.forEach = headersInstance.forEach.bind(headersInstance); this.get = headersInstance.get.bind(headersInstance); this.has = headersInstance.has.bind(headersInstance); this.keys = headersInstance.keys.bind(headersInstance); this.values = headersInstance.values.bind(headersInstance); } [Symbol.iterator]() { return this[INTERNAL_HEADERS_INSTANCE][Symbol.iterator](); } append() { throw readonlyHeadersError(); } delete() { throw readonlyHeadersError(); } set() { throw readonlyHeadersError(); } } exports.ReadonlyHeaders = ReadonlyHeaders; const INTERNAL_COOKIES_INSTANCE = Symbol("internal for cookies readonly"); class ReadonlyRequestCookiesError extends Error { message = "ReadonlyRequestCookies cannot be modified. Read more: https://nextjs.org/api-reference/cookies"; } class ReadonlyRequestCookies { constructor(request){ // Since `new Headers` uses `this.append()` to fill the headers object ReadonlyHeaders can't extend from Headers directly as it would throw. // Request overridden to not have to provide a fully request object. const cookiesInstance = new _cookies.RequestCookies(request.headers); this[INTERNAL_COOKIES_INSTANCE] = cookiesInstance; this.get = cookiesInstance.get.bind(cookiesInstance); this.getAll = cookiesInstance.getAll.bind(cookiesInstance); this.has = cookiesInstance.has.bind(cookiesInstance); } [Symbol.iterator]() { return this[INTERNAL_COOKIES_INSTANCE][Symbol.iterator](); } clear() { throw new ReadonlyRequestCookiesError(); } delete() { throw new ReadonlyRequestCookiesError(); } set() { throw new ReadonlyRequestCookiesError(); } } exports.ReadonlyRequestCookies = ReadonlyRequestCookies; /** * Flight Response is always set to application/octet-stream to ensure it does not get interpreted as HTML. */ class FlightRenderResult extends _renderResult.default { constructor(response){ super(response, { contentType: "application/octet-stream" }); } } /** * Interop between "export default" and "module.exports". */ function interopDefault(mod) { return mod.default || mod; } // tolerate dynamic server errors during prerendering so console // isn't spammed with unactionable errors /** * Create error handler for renderers. */ function createErrorHandler(/** * Used for debugging */ _source, capturedErrors, allCapturedErrors) { return (err)=>{ var ref; if (allCapturedErrors) allCapturedErrors.push(err); if (err && (err.digest === _hooksServerContext.DYNAMIC_ERROR_CODE || err.digest === _notFound.NOT_FOUND_ERROR_CODE || err.digest === _noSsrError.NEXT_DYNAMIC_NO_SSR_CODE || ((ref = err.digest) == null ? void 0 : ref.startsWith(_redirect.REDIRECT_ERROR_CODE)))) { return err.digest; } // Used for debugging error source // console.error(_source, err) console.error(err); capturedErrors.push(err); // TODO-APP: look at using webcrypto instead. Requires a promise to be awaited. return (0, _stringHash).default(err.message + err.stack + (err.digest || "")).toString(); }; } let isFetchPatched = false; // we patch fetch to collect cache information used for // determining if a page is static or not function patchFetch(ComponentMod) { if (isFetchPatched) return; isFetchPatched = true; const { DynamicServerError } = ComponentMod.serverHooks; const staticGenerationAsyncStorage = ComponentMod.staticGenerationAsyncStorage; const originFetch = globalThis.fetch; globalThis.fetch = async (input, init)=>{ var ref, ref1; const staticGenerationStore = ("getStore" in staticGenerationAsyncStorage ? staticGenerationAsyncStorage.getStore() : staticGenerationAsyncStorage) || {}; const { isStaticGeneration , fetchRevalidate , pathname , incrementalCache , isRevalidate , } = staticGenerationStore || {}; let revalidate; if (typeof (init == null ? void 0 : (ref = init.next) == null ? void 0 : ref.revalidate) === "number") { revalidate = init.next.revalidate; } if ((init == null ? void 0 : (ref1 = init.next) == null ? void 0 : ref1.revalidate) === false) { revalidate = CACHE_ONE_YEAR; } if (!staticGenerationStore.fetchRevalidate || typeof revalidate === "number" && revalidate < staticGenerationStore.fetchRevalidate) { staticGenerationStore.fetchRevalidate = revalidate; } let cacheKey; const doOriginalFetch = async ()=>{ return originFetch(input, init).then(async (res)=>{ if (incrementalCache && cacheKey && typeof revalidate === "number" && revalidate > 0) { const clonedRes = res.clone(); let base64Body = ""; if (process.env.NEXT_RUNTIME === "edge") { let string = ""; new Uint8Array(await clonedRes.arrayBuffer()).forEach((byte)=>{ string += String.fromCharCode(byte); }); base64Body = btoa(string); } else { base64Body = Buffer.from(await clonedRes.arrayBuffer()).toString("base64"); } await incrementalCache.set(cacheKey, { kind: "FETCH", isStale: false, age: 0, data: { headers: Object.fromEntries(clonedRes.headers.entries()), body: base64Body }, revalidate }, revalidate, true); } return res; }); }; if (incrementalCache && typeof revalidate === "number" && revalidate > 0) { cacheKey = await (incrementalCache == null ? void 0 : incrementalCache.fetchCacheKey(input.toString(), init)); const entry = await incrementalCache.get(cacheKey, true); if ((entry == null ? void 0 : entry.value) && entry.value.kind === "FETCH") { // when stale and is revalidating we wait for fresh data // so the revalidated entry has the updated data if (!isRevalidate || !entry.isStale) { if (entry.isStale) { if (!staticGenerationStore.pendingRevalidates) { staticGenerationStore.pendingRevalidates = []; } staticGenerationStore.pendingRevalidates.push(doOriginalFetch().catch(console.error)); } const resData = entry.value.data; let decodedBody = ""; // TODO: handle non-text response bodies if (process.env.NEXT_RUNTIME === "edge") { decodedBody = atob(resData.body); } else { decodedBody = Buffer.from(resData.body, "base64").toString(); } return new Response(decodedBody, { headers: resData.headers, status: resData.status }); } } } if (staticGenerationStore && isStaticGeneration) { if (init && typeof init === "object") { const cache = init.cache; // Delete `cache` property as Cloudflare Workers will throw an error if (isEdgeRuntime) { delete init.cache; } if (cache === "no-store") { staticGenerationStore.fetchRevalidate = 0; // TODO: ensure this error isn't logged to the user // seems it's slipping through currently throw new DynamicServerError(`no-store fetch ${input}${pathname ? ` ${pathname}` : ""}`); } const hasNextConfig = "next" in init; const next = init.next || {}; if (typeof next.revalidate === "number" && (typeof fetchRevalidate === "undefined" || next.revalidate < fetchRevalidate)) { const forceDynamic = staticGenerationStore.forceDynamic; if (!forceDynamic || next.revalidate !== 0) { staticGenerationStore.fetchRevalidate = next.revalidate; } if (!forceDynamic && next.revalidate === 0) { throw new DynamicServerError(`revalidate: ${next.revalidate} fetch ${input}${pathname ? ` ${pathname}` : ""}`); } } if (hasNextConfig) delete init.next; } } return doOriginalFetch(); }; } /** * Render Flight stream. * This is only used for renderToHTML, the Flight response does not need additional wrappers. */ function useFlightResponse(writable, req, serverComponentManifest, rscChunks, flightResponseRef, nonce) { if (flightResponseRef.current !== null) { return flightResponseRef.current; } const { createFromReadableStream , } = require("next/dist/compiled/react-server-dom-webpack/client"); const [renderStream, forwardStream] = (0, _nodeWebStreamsHelper).readableStreamTee(req); const res = createFromReadableStream(renderStream, { moduleMap: isEdgeRuntime ? serverComponentManifest.__edge_ssr_module_mapping__ : serverComponentManifest.__ssr_module_mapping__ }); flightResponseRef.current = res; let bootstrapped = false; // We only attach CSS chunks to the inlined data. const forwardReader = forwardStream.getReader(); const writer = writable.getWriter(); const startScriptTag = nonce ? ``)); } if (done) { flightResponseRef.current = null; writer.close(); } else { const responsePartial = (0, _nodeWebStreamsHelper).decodeText(value); const scripts = `${startScriptTag}self.__next_f.push(${(0, _htmlescape).htmlEscapeJsonString(JSON.stringify([ 1, responsePartial ]))})`; writer.write((0, _nodeWebStreamsHelper).encodeText(scripts)); read(); } }); } read(); return res; } /** * Create a component that renders the Flight stream. * This is only used for renderToHTML, the Flight response does not need additional wrappers. */ function createServerComponentRenderer(ComponentToRender, ComponentMod, { transformStream , serverComponentManifest , serverContexts , rscChunks }, serverComponentsErrorHandler, nonce) { // We need to expose the `__webpack_require__` API globally for // react-server-dom-webpack. This is a hack until we find a better way. if (ComponentMod.__next_app_webpack_require__) { // @ts-ignore globalThis.__next_require__ = ComponentMod.__next_app_webpack_require__; // @ts-ignore globalThis.__next_chunk_load__ = ()=>Promise.resolve(); } let RSCStream; const createRSCStream = ()=>{ if (!RSCStream) { RSCStream = ComponentMod.renderToReadableStream(/*#__PURE__*/ _react.default.createElement(ComponentToRender, null), serverComponentManifest, { context: serverContexts, onError: serverComponentsErrorHandler }); } return RSCStream; }; const flightResponseRef = { current: null }; const writable = transformStream.writable; return function ServerComponentWrapper() { const reqStream = createRSCStream(); const response = useFlightResponse(writable, reqStream, serverComponentManifest, rscChunks, flightResponseRef, nonce); return (0, _react).use(response); }; } /** * Shorten the dynamic param in order to make it smaller when transmitted to the browser. */ function getShortDynamicParamType(type) { switch(type){ case "catchall": return "c"; case "optional-catchall": return "oc"; case "dynamic": return "d"; default: throw new Error("Unknown dynamic param type"); } } /** * Parse dynamic route segment to type of parameter */ function getSegmentParam(segment) { if (segment.startsWith("[[...") && segment.endsWith("]]")) { return { type: "optional-catchall", param: segment.slice(5, -2) }; } if (segment.startsWith("[...") && segment.endsWith("]")) { return { type: "catchall", param: segment.slice(4, -1) }; } if (segment.startsWith("[") && segment.endsWith("]")) { return { type: "dynamic", param: segment.slice(1, -1) }; } return null; } /** * Get inline tags based on server CSS manifest. Only used when rendering to HTML. */ function getCssInlinedLinkTags(serverComponentManifest, serverCSSManifest, filePath, serverCSSForEntries) { var ref; const layoutOrPageCssModules = serverCSSManifest[filePath]; const filePathWithoutExt = filePath.replace(/\.[^.]+$/, ""); const cssFilesForEntry = new Set(((ref = serverComponentManifest.__entry_css_files__) == null ? void 0 : ref[filePathWithoutExt]) || []); if (!layoutOrPageCssModules || !cssFilesForEntry.size) { return []; } const chunks = new Set(); for (const mod of layoutOrPageCssModules){ // We only include the CSS if it's a global CSS, or it is used by this // entrypoint. if (serverCSSForEntries.includes(mod) || !/\.module\.css/.test(mod)) { const modData = serverComponentManifest[mod]; if (modData) { for (const chunk of modData.default.chunks){ if (cssFilesForEntry.has(chunk)) { chunks.add(chunk); } } } } } return [ ...chunks ]; } function getServerCSSForEntries(serverCSSManifest, entries) { const css = []; for (const entry of entries){ const entryName = entry.replace(/\.[^.]+$/, ""); if (serverCSSManifest.__entry_css_mods__ && serverCSSManifest.__entry_css_mods__[entryName]) { css.push(...serverCSSManifest.__entry_css_mods__[entryName]); } } return css; } /** * Get inline tags based on server CSS manifest and font loader manifest. Only used when rendering to HTML. */ function getPreloadedFontFilesInlineLinkTags(serverComponentManifest, serverCSSManifest, fontLoaderManifest, serverCSSForEntries, filePath) { var ref; if (!fontLoaderManifest || !filePath) { return []; } const layoutOrPageCss = serverCSSManifest[filePath] || ((ref = serverComponentManifest.__client_css_manifest__) == null ? void 0 : ref[filePath]); if (!layoutOrPageCss) { return []; } const fontFiles = new Set(); for (const css of layoutOrPageCss){ // We only include the CSS if it is used by this entrypoint. if (serverCSSForEntries.includes(css)) { const preloadedFontFiles = fontLoaderManifest.app[css]; if (preloadedFontFiles) { for (const fontFile of preloadedFontFiles){ fontFiles.add(fontFile); } } } } return [ ...fontFiles ]; } function getScriptNonceFromHeader(cspHeaderValue) { var ref; const directives = cspHeaderValue// Directives are split by ';'. .split(";").map((directive)=>directive.trim()); // First try to find the directive for the 'script-src', otherwise try to // fallback to the 'default-src'. const directive1 = directives.find((dir)=>dir.startsWith("script-src")) || directives.find((dir)=>dir.startsWith("default-src")); // If no directive could be found, then we're done. if (!directive1) { return; } // Extract the nonce from the directive const nonce = (ref = directive1.split(" ")// Remove the 'strict-src'/'default-src' string, this can't be the nonce. .slice(1).map((source)=>source.trim())// Find the first source with the 'nonce-' prefix. .find((source)=>source.startsWith("'nonce-") && source.length > 8 && source.endsWith("'"))) == null ? void 0 : ref.slice(7, -1); // If we could't find the nonce, then we're done. if (!nonce) { return; } // Don't accept the nonce value if it contains HTML escape characters. // Technically, the spec requires a base64'd value, but this is just an // extra layer. if (_htmlescape.ESCAPE_REGEX.test(nonce)) { throw new Error("Nonce value from Content-Security-Policy contained HTML escape characters.\nLearn more: https://nextjs.org/docs/messages/nonce-contained-invalid-characters"); } return nonce; } function headersWithoutFlight(headers) { const newHeaders = { ...headers }; for (const param of _appRouterHeaders.FLIGHT_PARAMETERS){ delete newHeaders[param.toString().toLowerCase()]; } return newHeaders; } async function renderToString(element) { const renderStream = await _serverBrowser.default.renderToReadableStream(element); await renderStream.allReady; return (0, _nodeWebStreamsHelper).streamToString(renderStream); } async function renderToHTMLOrFlight(req, res, pathname, query, renderOpts) { /** * 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 isStaticGeneration = renderOpts.supportsDynamicHTML !== true && !renderOpts.isBot; const isFlight = req.headers[_appRouterHeaders.RSC.toLowerCase()] !== undefined; const capturedErrors = []; const allCapturedErrors = []; const serverComponentsErrorHandler = createErrorHandler("serverComponentsRenderer", capturedErrors); const flightDataRendererErrorHandler = createErrorHandler("flightDataRenderer", capturedErrors); const htmlRendererErrorHandler = createErrorHandler("htmlRenderer", capturedErrors, allCapturedErrors); const { buildManifest , subresourceIntegrityManifest , serverComponentManifest , serverCSSManifest ={} , ComponentMod , dev , fontLoaderManifest , supportsDynamicHTML , } = renderOpts; patchFetch(ComponentMod); const generateStaticHTML = supportsDynamicHTML !== true; const staticGenerationAsyncStorage = ComponentMod.staticGenerationAsyncStorage; const requestAsyncStorage = ComponentMod.requestAsyncStorage; if (staticGenerationAsyncStorage && !("getStore" in staticGenerationAsyncStorage) && staticGenerationAsyncStorage.inUse) { throw new Error(`Invariant: A separate worker must be used for each render when AsyncLocalStorage is not available`); } // we wrap the render in an AsyncLocalStorage context const wrappedRender = async ()=>{ const staticGenerationStore = "getStore" in staticGenerationAsyncStorage ? staticGenerationAsyncStorage.getStore() : staticGenerationAsyncStorage; // don't modify original query object query = Object.assign({}, query); const isPrefetch = req.headers[_appRouterHeaders.NEXT_ROUTER_PREFETCH.toLowerCase()] !== undefined; // TODO-APP: verify the tree is valid // TODO-APP: verify query param is single value (not an array) // TODO-APP: verify tree can't grow out of control /** * Router state provided from the client-side router. Used to handle rendering from the common layout down. */ let providedFlightRouterState = isFlight ? req.headers[_appRouterHeaders.NEXT_ROUTER_STATE_TREE.toLowerCase()] ? JSON.parse(req.headers[_appRouterHeaders.NEXT_ROUTER_STATE_TREE.toLowerCase()]) : undefined : undefined; /** * The tree created in next-app-loader that holds component segments and modules */ const loaderTree = ComponentMod.tree; (0, _internalUtils).stripInternalQueries(query); const LayoutRouter = ComponentMod.LayoutRouter; const RenderFromTemplateContext = ComponentMod.RenderFromTemplateContext; /** * 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 = getSegmentParam(segment); if (!segmentParam) { return null; } const key = segmentParam.param; let value = pathParams[key]; 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(segmentParam.type); return { param: key, value: null, type: type, // This value always has to be a string. treeSegment: [ key, "", type ] }; } return null; } const type = 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 }; }; async function resolveHead([segment, parallelRoutes, { head }], parentParams, isRootHead) { // 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; for(const key in parallelRoutes){ const childTree = parallelRoutes[key]; const returnedHead = await resolveHead(childTree, currentParams, false); if (returnedHead) { return returnedHead; } } if (head) { const Head = await interopDefault(await head[0]()); return /*#__PURE__*/ _react.default.createElement(Head, { params: currentParams }); } else if (isRootHead) { return /*#__PURE__*/ _react.default.createElement(_head.DefaultHead, null); } return null; } const createFlightRouterStateFromLoaderTree = ([segment, parallelRoutes, { layout }], rootLayoutIncluded = false)=>{ const dynamicParam = getDynamicParamFromSegment(segment); const segmentTree = [ dynamicParam ? dynamicParam.treeSegment : segment, {}, ]; if (!rootLayoutIncluded && typeof layout !== "undefined") { rootLayoutIncluded = true; segmentTree[4] = true; } segmentTree[1] = Object.keys(parallelRoutes).reduce((existingValue, currentValue)=>{ existingValue[currentValue] = createFlightRouterStateFromLoaderTree(parallelRoutes[currentValue], rootLayoutIncluded); return existingValue; }, {}); return segmentTree; }; let defaultRevalidate = false; // Collect all server CSS imports used by this specific entry (or entries, for parallel routes). // Not that we can't rely on the CSS manifest because it tracks CSS imports per module, // which can be used by multiple entries and cannot be tree-shaked in the module graph. // More info: https://github.com/vercel/next.js/issues/41018 const serverCSSForEntries = getServerCSSForEntries(serverCSSManifest, ComponentMod.pages); const assetPrefix = renderOpts.assetPrefix || ""; const createComponentAndStyles = async ({ filePath , getComponent , shouldPreload })=>{ const cssHrefs = getCssInlinedLinkTags(serverComponentManifest, serverCSSManifest, filePath, serverCSSForEntries); const styles = cssHrefs ? cssHrefs.map((href, index)=>/*#__PURE__*/ _react.default.createElement("link", { rel: "stylesheet", // In dev, Safari will wrongly cache the resource if you preload it: // - https://github.com/vercel/next.js/issues/5860 // - https://bugs.webkit.org/show_bug.cgi?id=187726 // We used to add a `?ts=` query for resources in `pages` to bypass it, // but in this case it is fine as we don't need to preload the styles. href: `${assetPrefix}/_next/${href}`, // @ts-ignore precedence: shouldPreload ? "high" : undefined, key: index })) : null; const Comp = await interopDefault(await getComponent()); return [ Comp, styles ]; }; /** * Use the provided loader tree to create the React Component tree. */ const createComponentTree = async ({ createSegmentPath , loaderTree: [segment, parallelRoutes, { layout , template , error , loading , page , "not-found": notFound }, ] , parentParams , firstItem , rootLayoutIncluded })=>{ const layoutOrPagePath = (layout == null ? void 0 : layout[1]) || (page == null ? void 0 : page[1]); const stylesheets = layoutOrPagePath ? getCssInlinedLinkTags(serverComponentManifest, serverCSSManifest, layoutOrPagePath, serverCSSForEntries) : []; const preloadedFontFiles = layoutOrPagePath ? getPreloadedFontFilesInlineLinkTags(serverComponentManifest, serverCSSManifest, fontLoaderManifest, serverCSSForEntries, layoutOrPagePath) : []; const [Template, templateStyles] = template ? await createComponentAndStyles({ filePath: template[1], getComponent: template[0], shouldPreload: true }) : [ _react.default.Fragment ]; const [ErrorComponent, errorStyles] = error ? await createComponentAndStyles({ filePath: error[1], getComponent: error[0] }) : []; const [Loading, loadingStyles] = loading ? await createComponentAndStyles({ filePath: loading[1], getComponent: loading[0] }) : []; const isLayout = typeof layout !== "undefined"; const isPage = typeof page !== "undefined"; const layoutOrPageMod = isLayout ? await layout[0]() : isPage ? await page[0]() : 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; const [NotFound, notFoundStyles] = notFound ? await createComponentAndStyles({ filePath: notFound[1], getComponent: notFound[0] }) : rootLayoutAtThisLevel ? [ _error.NotFound ] : []; if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.dynamic) === "string") { // the nested most config wins so we only force-static // if it's configured above any parent that configured // otherwise if (layoutOrPageMod.dynamic === "force-static") { staticGenerationStore.forceStatic = true; } else if (layoutOrPageMod.dynamic !== "error") { staticGenerationStore.forceStatic = false; } } if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.revalidate) === "number") { defaultRevalidate = layoutOrPageMod.revalidate; if (isStaticGeneration && defaultRevalidate === 0) { const { DynamicServerError } = ComponentMod.serverHooks; throw new DynamicServerError(`revalidate: 0 configured ${segment}`); } } /** * The React Component to render. */ let Component = layoutOrPageMod ? interopDefault(layoutOrPageMod) : undefined; 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: "${pathname}"`); } 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 currentSegmentPath = firstItem ? [ parallelRouteKey ] : [ actualSegment, parallelRouteKey ]; const childSegment = parallelRoutes[parallelRouteKey][0]; const childSegmentParam = getDynamicParamFromSegment(childSegment); if (isPrefetch && Loading) { const childProp = { // Null indicates the tree is not fully rendered current: null, segment: childSegmentParam ? childSegmentParam.treeSegment : childSegment }; // 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, hasLoading: Boolean(Loading), error: ErrorComponent, errorStyles: errorStyles, template: /*#__PURE__*/ _react.default.createElement(Template, null, /*#__PURE__*/ _react.default.createElement(RenderFromTemplateContext, null)), templateStyles: templateStyles, notFound: NotFound ? /*#__PURE__*/ _react.default.createElement(NotFound, null) : undefined, notFoundStyles: notFoundStyles, childProp: childProp, rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove }), ]; } // Create the child component const { Component: ChildComponent } = await createComponentTree({ createSegmentPath: (child)=>{ return createSegmentPath([ ...currentSegmentPath, ...child ]); }, loaderTree: parallelRoutes[parallelRouteKey], parentParams: currentParams, rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove }); const childProp = { current: /*#__PURE__*/ _react.default.createElement(ChildComponent, null), segment: childSegmentParam ? childSegmentParam.treeSegment : childSegment }; const segmentPath = createSegmentPath(currentSegmentPath); // This is turned back into an object below. return [ parallelRouteKey, /*#__PURE__*/ _react.default.createElement(LayoutRouter, { parallelRouterKey: parallelRouteKey, segmentPath: segmentPath, error: ErrorComponent, errorStyles: errorStyles, 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), template: /*#__PURE__*/ _react.default.createElement(Template, null, /*#__PURE__*/ _react.default.createElement(RenderFromTemplateContext, null)), templateStyles: templateStyles, notFound: NotFound ? /*#__PURE__*/ _react.default.createElement(NotFound, null) : undefined, notFoundStyles: notFoundStyles, childProp: childProp, rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove }), ]; })); // 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) }; } const props = { ...parallelRouteComponents, // 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 ...isPage ? { searchParams: query } : {} }; // Eagerly execute layout/page component to trigger fetches early. Component = await Promise.resolve().then(()=>{ return preloadComponent(Component, props); }); return { Component: ()=>{ return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, preloadedFontFiles.map((fontFile)=>{ const ext = /\.(woff|woff2|eot|ttf|otf)$/.exec(fontFile)[1]; return /*#__PURE__*/ _react.default.createElement("link", { key: fontFile, rel: "preload", href: `${assetPrefix}/_next/${fontFile}`, as: "font", type: `font/${ext}`, crossOrigin: "anonymous" }); }), stylesheets ? stylesheets.map((href, index)=>/*#__PURE__*/ _react.default.createElement("link", { rel: "stylesheet", // In dev, Safari will wrongly cache the resource if you preload it: // - https://github.com/vercel/next.js/issues/5860 // - https://bugs.webkit.org/show_bug.cgi?id=187726 // We used to add a `?ts=` query for resources in `pages` to bypass it, // but in this case it is fine as we don't need to preload the styles. href: `${assetPrefix}/_next/${href}`, // `Precedence` is an opt-in signal for React to handle // resource loading and deduplication, etc: // https://github.com/facebook/react/pull/25060 // @ts-ignore precedence: "high", key: index })) : null, /*#__PURE__*/ _react.default.createElement(Component, Object.assign({}, props))); } }; }; const streamToBufferedResult = async (renderResult)=>{ const renderChunks = []; const writable = new _stream.Writable({ write (chunk, _encoding, callback) { renderChunks.push(chunk); callback(); } }); await renderResult.pipe(writable); return Buffer.concat(renderChunks).toString(); }; // 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 ()=>{ // TODO-APP: throw on invalid flightRouterState /** * 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 })=>{ const [segment, parallelRoutes] = loaderTreeToFilter; const parallelRoutesKeys = Object.keys(parallelRoutes); // 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 = segmentParam ? segmentParam.treeSegment : segment; /** * 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) { return [ actualSegment, // Create router state using the slice of the loaderTree createFlightRouterStateFromLoaderTree(loaderTreeToFilter), // Check if one level down from the common layout has a loading component. If it doesn't only provide the router state as part of the Flight data. isPrefetch && !Boolean(loaderTreeToFilter[2].loading) ? null : // @ts-expect-error TODO-APP: fix async component type _react.default.createElement(async ()=>{ const { Component } = await createComponentTree(// This ensures flightRouterPath is valid and filters down the tree { createSegmentPath: (child)=>{ return createSegmentPath(child); }, loaderTree: loaderTreeToFilter, parentParams: currentParams, firstItem: isFirst }); return /*#__PURE__*/ _react.default.createElement(Component, null); }), isPrefetch && !Boolean(loaderTreeToFilter[2].loading) ? null : /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, rscPayloadHead), ]; } // Walk through all parallel routes. 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 }); if (typeof path[path.length - 1] !== "string") { return [ actualSegment, parallelRouteKey, ...path ]; } } return [ actualSegment ]; }; const rscPayloadHead1 = await resolveHead(loaderTree, {}, true); // 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. const flightData = [ (await walkTreeWithFlightRouterState({ createSegmentPath: (child)=>child, loaderTreeToFilter: loaderTree, parentParams: {}, flightRouterState: providedFlightRouterState, isFirst: true, rscPayloadHead: rscPayloadHead1 })).slice(1), ]; // For app dir, use the bundled version of Fizz renderer (renderToReadableStream) // which contains the subset React. const readable = ComponentMod.renderToReadableStream(flightData, serverComponentManifest, { context: serverContexts, onError: flightDataRendererErrorHandler }).pipeThrough((0, _nodeWebStreamsHelper).createBufferedTransformStream()); return new FlightRenderResult(readable); }; if (isFlight && !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 = interopDefault(/** GlobalError can be either the default error boundary or the overwritten app/global-error.js **/ ComponentMod.GlobalError); let serverComponentsInlinedTransformStream = new TransformStream(); // TODO-APP: validate req.url as it gets passed to render. const initialCanonicalUrl = req.url; // 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 = getScriptNonceFromHeader(csp); } const serverComponentsRenderOpts = { transformStream: serverComponentsInlinedTransformStream, serverComponentManifest, serverContexts, rscChunks: [] }; const validateRootLayout = dev ? { validateRootLayout: { assetPrefix: renderOpts.assetPrefix, getTree: ()=>createFlightRouterStateFromLoaderTree(loaderTree) } } : {}; const initialHead = await resolveHead(loaderTree, {}, true); /** * A new React Component that renders the provided React Component * using Flight which can then be rendered to HTML. */ const ServerComponentsRenderer = createServerComponentRenderer(async ()=>{ // Create full component tree from root to leaf. const { Component: ComponentTree } = await createComponentTree({ createSegmentPath: (child)=>child, loaderTree: loaderTree, parentParams: {}, firstItem: true }); const initialTree = createFlightRouterStateFromLoaderTree(loaderTree); return /*#__PURE__*/ _react.default.createElement(AppRouter, { assetPrefix: assetPrefix, initialCanonicalUrl: initialCanonicalUrl, initialTree: initialTree, initialHead: initialHead, globalErrorComponent: GlobalError }, /*#__PURE__*/ _react.default.createElement(ComponentTree, null)); }, ComponentMod, serverComponentsRenderOpts, serverComponentsErrorHandler, nonce); const serverInsertedHTMLCallbacks = new Set(); function InsertedHTML({ children }) { // Reset addInsertedHtmlCallback on each render serverInsertedHTMLCallbacks.clear(); const addInsertedHtml = _react.default.useCallback((handler)=>{ serverInsertedHTMLCallbacks.add(handler); }, []); return /*#__PURE__*/ _react.default.createElement(_headManagerContext.HeadManagerContext.Provider, { value: { appDir: true, nonce } }, /*#__PURE__*/ _react.default.createElement(_serverInsertedHtml.ServerInsertedHTMLContext.Provider, { value: addInsertedHtml }, children)); } const bodyResult = async ()=>{ const polyfills = buildManifest.polyfillFiles.filter((polyfill)=>polyfill.endsWith(".js") && !polyfill.endsWith(".module.js")).map((polyfill)=>{ return { src: `${assetPrefix}/_next/${polyfill}`, integrity: subresourceIntegrityManifest == null ? void 0 : subresourceIntegrityManifest[polyfill] }; }); const content = /*#__PURE__*/ _react.default.createElement(InsertedHTML, null, /*#__PURE__*/ _react.default.createElement(ServerComponentsRenderer, null)); let polyfillsFlushed = false; const getServerInsertedHTML = ()=>{ const flushed = renderToString(/*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, Array.from(serverInsertedHTMLCallbacks).map((callback)=>callback()), 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 }); }))); polyfillsFlushed = true; return flushed; }; try { const renderStream = await (0, _nodeWebStreamsHelper).renderToInitialStream({ ReactDOMServer: _serverBrowser.default, element: content, streamOptions: { onError: htmlRendererErrorHandler, nonce, // Include hydration scripts in the HTML bootstrapScripts: [ ...subresourceIntegrityManifest ? buildManifest.rootMainFiles.map((src)=>({ src: `${assetPrefix}/_next/` + src, integrity: subresourceIntegrityManifest[src] })) : buildManifest.rootMainFiles.map((src)=>`${assetPrefix}/_next/` + src), ] } }); const result = await (0, _nodeWebStreamsHelper).continueFromInitialStream(renderStream, { dataStream: serverComponentsInlinedTransformStream == null ? void 0 : serverComponentsInlinedTransformStream.readable, generateStaticHTML: isStaticGeneration || generateStaticHTML, getServerInsertedHTML, serverInsertedHTMLToHead: true, ...validateRootLayout }); return result; } catch (err) { const shouldNotIndex = err.digest === _notFound.NOT_FOUND_ERROR_CODE; if (err.digest === _notFound.NOT_FOUND_ERROR_CODE) { res.statusCode = 404; } const renderStream = await (0, _nodeWebStreamsHelper).renderToInitialStream({ ReactDOMServer: _serverBrowser.default, element: /*#__PURE__*/ _react.default.createElement("html", { id: "__next_error__" }, /*#__PURE__*/ _react.default.createElement("head", null, shouldNotIndex ? /*#__PURE__*/ _react.default.createElement("meta", { name: "robots", content: "noindex" }) : null), /*#__PURE__*/ _react.default.createElement("body", null)), streamOptions: { nonce, // Include hydration scripts in the HTML bootstrapScripts: subresourceIntegrityManifest ? buildManifest.rootMainFiles.map((src)=>({ src: `${assetPrefix}/_next/` + src, integrity: subresourceIntegrityManifest[src] })) : buildManifest.rootMainFiles.map((src)=>`${assetPrefix}/_next/` + src) } }); return await (0, _nodeWebStreamsHelper).continueFromInitialStream(renderStream, { dataStream: serverComponentsInlinedTransformStream == null ? void 0 : serverComponentsInlinedTransformStream.readable, generateStaticHTML: isStaticGeneration, getServerInsertedHTML, serverInsertedHTMLToHead: true, ...validateRootLayout }); } }; const renderResult1 = new _renderResult.default(await bodyResult()); if (staticGenerationStore.pendingRevalidates) { await Promise.all(staticGenerationStore.pendingRevalidates); } if (isStaticGeneration) { const htmlResult = await streamToBufferedResult(renderResult1); // if we encountered any unexpected errors during build // we fail the prerendering phase and the build if (capturedErrors.length > 0) { throw capturedErrors[0]; } // const before = Buffer.concat( // serverComponentsRenderOpts.rscChunks // ).toString() // TODO-APP: derive this from same pass to prevent additional // render during static generation const filteredFlightData = await streamToBufferedResult(await generateFlight()); if ((staticGenerationStore == null ? void 0 : staticGenerationStore.forceStatic) === false) { staticGenerationStore.fetchRevalidate = 0; } renderOpts.pageData = filteredFlightData; renderOpts.revalidate = typeof (staticGenerationStore == null ? void 0 : staticGenerationStore.fetchRevalidate) === "undefined" ? defaultRevalidate : staticGenerationStore == null ? void 0 : staticGenerationStore.fetchRevalidate; return new _renderResult.default(htmlResult); } return renderResult1; }; const initialStaticGenerationStore = { isStaticGeneration, inUse: true, pathname, incrementalCache: renderOpts.incrementalCache, isRevalidate: renderOpts.isRevalidate }; const tryGetPreviewData = process.env.NEXT_RUNTIME === "edge" ? ()=>false : require("./api-utils/node").tryGetPreviewData; // Reads of this are cached on the `req` object, so this should resolve // instantly. There's no need to pass this data down from a previous // invoke, where we'd have to consider server & serverless. const previewData = tryGetPreviewData(req, res, renderOpts.previewProps); let cachedHeadersInstance; let cachedCookiesInstance; const requestStore = { get headers () { if (!cachedHeadersInstance) { cachedHeadersInstance = new ReadonlyHeaders(headersWithoutFlight(req.headers)); } return cachedHeadersInstance; }, get cookies () { if (!cachedCookiesInstance) { cachedCookiesInstance = new ReadonlyRequestCookies({ headers: { get: (key)=>{ if (key !== "cookie") { throw new Error("Only cookie header is supported"); } return req.headers.cookie; } } }); } return cachedCookiesInstance; }, previewData }; function handleRequestStoreRun(fn) { if ("getStore" in requestAsyncStorage) { return new Promise((resolve, reject)=>{ requestAsyncStorage.run(requestStore, ()=>{ return Promise.resolve(fn()).then(resolve).catch(reject); }); }); } else { Object.assign(requestAsyncStorage, requestStore); return Promise.resolve(fn()); } } function handleStaticGenerationStoreRun(fn) { if ("getStore" in staticGenerationAsyncStorage) { return new Promise((resolve, reject)=>{ staticGenerationAsyncStorage.run(initialStaticGenerationStore, ()=>{ return Promise.resolve(fn()).then(resolve).catch(reject); }); }); } else { Object.assign(staticGenerationAsyncStorage, initialStaticGenerationStore); return Promise.resolve(fn()).finally(()=>{ staticGenerationAsyncStorage.inUse = false; }); } } return handleRequestStoreRun(()=>handleStaticGenerationStoreRun(()=>wrappedRender())); } //# sourceMappingURL=app-render.js.map