"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.renderToHTMLOrFlight = renderToHTMLOrFlight;
var _react = _interopRequireWildcard(require("react"));
var _querystring = require("querystring");
var _reactServerDomWebpack = require("next/dist/compiled/react-server-dom-webpack");
var _writerBrowserServer = require("next/dist/compiled/react-server-dom-webpack/writer.browser.server");
var _renderResult = _interopRequireDefault(require("./render-result"));
var _nodeWebStreamsHelper = require("./node-web-streams-helper");
var _htmlescape = require("./htmlescape");
var _utils = require("./utils");
var _matchSegments = require("../client/components/match-segments");
var _flushEffects = require("../shared/lib/flush-effects");
var _internalUtils = require("./internal-utils");
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;
}
// this needs to be required lazily so that `next-server` can set
// the env before we require
const ReactDOMServer = _utils.shouldUseReactRoot ? require("react-dom/server.browser") : require("react-dom/server");
/**
* Flight Response is always set to application/octet-stream to ensure it does not
*/ 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
function onError(err) {
const { DynamicServerError } = require("../client/components/hooks-server-context");
if (!(err instanceof DynamicServerError)) {
console.error(err);
}
}
let isFetchPatched = false;
// we patch fetch to collect cache information used for
// determining if a page is static or not
function patchFetch() {
if (isFetchPatched) return;
isFetchPatched = true;
const { DynamicServerError } = require("../client/components/hooks-server-context");
const { useTrackStaticGeneration } = require("../client/components/hooks-server");
const origFetch = global.fetch;
global.fetch = async (init, opts)=>{
let staticGenerationContext = {};
try {
// eslint-disable-next-line react-hooks/rules-of-hooks
staticGenerationContext = useTrackStaticGeneration() || {};
} catch (_) {}
const { isStaticGeneration , fetchRevalidate , pathname } = staticGenerationContext;
if (isStaticGeneration) {
if (opts && typeof opts === "object") {
if (opts.cache === "no-store") {
staticGenerationContext.revalidate = 0;
// TODO: ensure this error isn't logged to the user
// seems it's slipping through currently
throw new DynamicServerError(`no-store fetch ${init}${pathname ? ` ${pathname}` : ""}`);
}
if (typeof opts.revalidate === "number" && (typeof fetchRevalidate === "undefined" || opts.revalidate < fetchRevalidate)) {
staticGenerationContext.fetchRevalidate = opts.revalidate;
}
}
}
return origFetch(init, opts);
};
}
/**
* 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 [renderStream, forwardStream] = (0, _nodeWebStreamsHelper).readableStreamTee(req);
const res = (0, _reactServerDomWebpack).createFromReadableStream(renderStream, {
moduleMap: 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_s=self.__next_s||[]).push(${(0, _htmlescape).htmlEscapeJsonString(JSON.stringify([
1,
responsePartial
]))})`;
writer.write((0, _nodeWebStreamsHelper).encodeText(scripts));
process();
}
});
}
process();
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 }, 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__ || ComponentMod.__next_rsc__) {
var ref;
// @ts-ignore
globalThis.__next_require__ = ComponentMod.__next_app_webpack_require__ || ((ref = ComponentMod.__next_rsc__) == null ? void 0 : ref.__webpack_require__);
// @ts-ignore
globalThis.__next_chunk_load__ = ()=>Promise.resolve();
}
let RSCStream;
const createRSCStream = ()=>{
if (!RSCStream) {
RSCStream = (0, _writerBrowserServer).renderToReadableStream(/*#__PURE__*/ _react.default.createElement(ComponentToRender, null), serverComponentManifest, {
context: serverContexts,
onError
});
}
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).experimental_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) {
var ref;
const layoutOrPageCss = serverCSSManifest[filePath] || ((ref = serverComponentManifest.__client_css_manifest__) == null ? void 0 : ref[filePath]);
if (!layoutOrPageCss) {
return [];
}
const chunks = new Set();
for (const css of layoutOrPageCss){
const mod = serverComponentManifest[css];
if (mod) {
for (const chunk of mod.default.chunks){
chunks.add(chunk);
}
}
}
return [
...chunks
];
}
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;
}
async function renderToHTMLOrFlight(req, res, pathname, query, renderOpts, isPagesDir, isStaticGeneration = false) {
patchFetch();
const { CONTEXT_NAMES } = require("../client/components/hooks-server-context");
// @ts-expect-error createServerContext exists in react@experimental + react-dom@experimental
if (typeof _react.default.createServerContext === "undefined") {
throw new Error('"app" directory requires React.createServerContext which is not available in the version of React you are using. Please update to react@experimental and react-dom@experimental.');
}
// don't modify original query object
query = Object.assign({}, query);
const { buildManifest , subresourceIntegrityManifest , serverComponentManifest , serverCSSManifest ={} , supportsDynamicHTML , ComponentMod , } = renderOpts;
const isFlight = query.__flight__ !== undefined;
const isPrefetch = query.__flight_prefetch__ !== undefined;
// Handle client-side navigation to pages directory
if (isFlight && isPagesDir) {
(0, _internalUtils).stripInternalQueries(query);
const search = (0, _querystring).stringify(query);
// Empty so that the client-side router will do a full page navigation.
const flightData = pathname + (search ? `?${search}` : "");
return new FlightRenderResult((0, _writerBrowserServer).renderToReadableStream(flightData, serverComponentManifest, {
onError
}).pipeThrough((0, _nodeWebStreamsHelper).createBufferedTransformStream()));
}
// 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.
*/ const providedFlightRouterState = isFlight ? query.__flight_router_state_tree__ ? JSON.parse(query.__flight_router_state_tree__) : {} : undefined;
(0, _internalUtils).stripInternalQueries(query);
const LayoutRouter = ComponentMod.LayoutRouter;
const RenderFromTemplateContext = ComponentMod.RenderFromTemplateContext;
const HotReloader = ComponentMod.HotReloader;
const headers = req.headers;
// TODO-APP: fix type of req
// @ts-expect-error
const cookies = req.cookies;
/**
* The tree created in next-app-loader that holds component segments and modules
*/ const loaderTree = ComponentMod.tree;
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);
/**
* 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 staticGenerationContext = {
isStaticGeneration,
pathname
};
const serverContexts = [
[
"WORKAROUND",
null
],
[
CONTEXT_NAMES.HeadersContext,
headers
],
[
CONTEXT_NAMES.CookiesContext,
cookies
],
[
CONTEXT_NAMES.PreviewDataContext,
previewData
],
[
CONTEXT_NAMES.StaticGenerationContext,
staticGenerationContext
],
];
/**
* 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;
const value = pathParams[key];
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
};
};
const createFlightRouterStateFromLoaderTree = ([segment, parallelRoutes])=>{
const dynamicParam = getDynamicParamFromSegment(segment);
const segmentTree = [
dynamicParam ? dynamicParam.treeSegment : segment,
{},
];
if (parallelRoutes) {
segmentTree[1] = Object.keys(parallelRoutes).reduce((existingValue, currentValue)=>{
existingValue[currentValue] = createFlightRouterStateFromLoaderTree(parallelRoutes[currentValue]);
return existingValue;
}, {});
}
return segmentTree;
};
let defaultRevalidate = false;
/**
* Use the provided loader tree to create the React Component tree.
*/ const createComponentTree = async ({ createSegmentPath , loaderTree: [segment, parallelRoutes, { layoutOrPagePath , layout , template , error , loading , page }, ] , parentParams , firstItem , rootLayoutIncluded })=>{
// TODO-APP: enable stylesheet per layout/page
const stylesheets = layoutOrPagePath ? getCssInlinedLinkTags(serverComponentManifest, serverCSSManifest, layoutOrPagePath) : [];
const Template = template ? await interopDefault(template()) : _react.default.Fragment;
const ErrorComponent = error ? await interopDefault(error()) : undefined;
const Loading = loading ? await interopDefault(loading()) : undefined;
const isLayout = typeof layout !== "undefined";
const isPage = typeof page !== "undefined";
const layoutOrPageMod = isLayout ? await layout() : isPage ? await page() : undefined;
if (layoutOrPageMod == null ? void 0 : layoutOrPageMod.config) {
defaultRevalidate = layoutOrPageMod.config.revalidate;
}
/**
* 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;
// TODO-APP: move these errors to the loader instead?
// we will also need a migration doc here to link to
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.getServerSideProps) === "function") {
throw new Error(`getServerSideProps is not supported in app/, detected in ${segment}`);
}
if (typeof (layoutOrPageMod == null ? void 0 : layoutOrPageMod.getStaticProps) === "function") {
throw new Error(`getStaticProps is not supported in app/, detected in ${segment}`);
}
/**
* The React Component to render.
*/ const Component = layoutOrPageMod ? interopDefault(layoutOrPageMod) : undefined;
// 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,
error: ErrorComponent,
template: /*#__PURE__*/ _react.default.createElement(Template, null, /*#__PURE__*/ _react.default.createElement(RenderFromTemplateContext, null)),
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,
loading: Loading ? /*#__PURE__*/ _react.default.createElement(Loading, null) : undefined,
template: /*#__PURE__*/ _react.default.createElement(Template, null, /*#__PURE__*/ _react.default.createElement(RenderFromTemplateContext, null)),
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)
};
}
return {
Component: ()=>{
let props = {};
return /*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, stylesheets ? stylesheets.map((href)=>/*#__PURE__*/ _react.default.createElement("link", {
rel: "stylesheet",
href: `/_next/${href}?ts=${Date.now()}`,
// `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: href
})) : null, /*#__PURE__*/ _react.default.createElement(Component, Object.assign({}, 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
}, isPage ? {
searchParams: query
} : {})));
}
};
};
/**
* 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;
// Handle Flight render request. This is only used when client-side navigating. E.g. when you `router.push('/dashboard')` or `router.reload()`.
if (isFlight) {
// 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 (loaderTreeToFilter, parentParams, flightRouterState, parentRendered)=>{
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 : /*#__PURE__*/ _react.default.createElement((await createComponentTree(// This ensures flightRouterPath is valid and filters down the tree
{
createSegmentPath: (child)=>child,
loaderTree: loaderTreeToFilter,
parentParams: currentParams,
firstItem: true
})).Component),
];
}
// Walk through all parallel routes.
for (const parallelRouteKey of parallelRoutesKeys){
const parallelRoute = parallelRoutes[parallelRouteKey];
const path = await walkTreeWithFlightRouterState(parallelRoute, currentParams, flightRouterState && flightRouterState[1][parallelRouteKey], parentRendered || renderComponentsOnThisLevel);
if (typeof path[path.length - 1] !== "string") {
return [
actualSegment,
parallelRouteKey,
...path
];
}
}
return [
actualSegment
];
};
// 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 = [
// TODO-APP: change walk to output without ''
(await walkTreeWithFlightRouterState(loaderTree, {}, providedFlightRouterState)).slice(1),
];
const readable = (0, _writerBrowserServer).renderToReadableStream(flightData, serverComponentManifest, {
context: serverContexts,
onError
}).pipeThrough((0, _nodeWebStreamsHelper).createBufferedTransformStream());
if (generateStaticHTML) {
let staticHtml = Buffer.from((await readable.getReader().read()).value || "").toString();
return new FlightRenderResult(staticHtml);
}
return new FlightRenderResult(readable);
}
// Below this line is handling for rendering to HTML.
// Create full component tree from root to leaf.
const { Component: ComponentTree } = await createComponentTree({
createSegmentPath: (child)=>child,
loaderTree: loaderTree,
parentParams: {},
firstItem: true
});
// AppRouter is provided by next-app-loader
const AppRouter = ComponentMod.AppRouter;
let serverComponentsInlinedTransformStream = new TransformStream();
// TODO-APP: validate req.url as it gets passed to render.
const initialCanonicalUrl = req.url;
// Get the nonce from the incomming 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: []
};
/**
* A new React Component that renders the provided React Component
* using Flight which can then be rendered to HTML.
*/ const ServerComponentsRenderer = createServerComponentRenderer(()=>{
const initialTree = createFlightRouterStateFromLoaderTree(loaderTree);
return /*#__PURE__*/ _react.default.createElement(AppRouter, {
hotReloader: HotReloader && /*#__PURE__*/ _react.default.createElement(HotReloader, {
assetPrefix: renderOpts.assetPrefix || ""
}),
initialCanonicalUrl: initialCanonicalUrl,
initialTree: initialTree
}, /*#__PURE__*/ _react.default.createElement(ComponentTree, null));
}, ComponentMod, serverComponentsRenderOpts, nonce);
const flushEffectsCallbacks = new Set();
function FlushEffects({ children }) {
// Reset flushEffectsHandler on each render
flushEffectsCallbacks.clear();
const addFlushEffects = _react.default.useCallback((handler)=>{
flushEffectsCallbacks.add(handler);
}, []);
return /*#__PURE__*/ _react.default.createElement(_flushEffects.FlushEffectsContext.Provider, {
value: addFlushEffects
}, children);
}
const bodyResult = async ()=>{
const content = /*#__PURE__*/ _react.default.createElement(FlushEffects, null, /*#__PURE__*/ _react.default.createElement(ServerComponentsRenderer, null));
const flushEffectHandler = ()=>{
const flushed = ReactDOMServer.renderToString(/*#__PURE__*/ _react.default.createElement(_react.default.Fragment, null, Array.from(flushEffectsCallbacks).map((callback)=>callback())));
return flushed;
};
try {
const renderStream = await (0, _nodeWebStreamsHelper).renderToInitialStream({
ReactDOMServer,
element: content,
streamOptions: {
nonce,
// Include hydration scripts in the HTML
bootstrapScripts: subresourceIntegrityManifest ? buildManifest.rootMainFiles.map((src)=>({
src: `${renderOpts.assetPrefix || ""}/_next/` + src,
integrity: subresourceIntegrityManifest[src]
})) : buildManifest.rootMainFiles.map((src)=>`${renderOpts.assetPrefix || ""}/_next/` + src)
}
});
return await (0, _nodeWebStreamsHelper).continueFromInitialStream(renderStream, {
dataStream: serverComponentsInlinedTransformStream == null ? void 0 : serverComponentsInlinedTransformStream.readable,
generateStaticHTML: generateStaticHTML,
flushEffectHandler,
flushEffectsToHead: true
});
} catch (err) {
// TODO-APP: show error overlay in development. `element` should probably be wrapped in AppRouter for this case.
const renderStream = await (0, _nodeWebStreamsHelper).renderToInitialStream({
ReactDOMServer,
element: /*#__PURE__*/ _react.default.createElement("html", {
id: "__next_error__"
}, /*#__PURE__*/ _react.default.createElement("head", null), /*#__PURE__*/ _react.default.createElement("body", null)),
streamOptions: {
nonce,
// Include hydration scripts in the HTML
bootstrapScripts: subresourceIntegrityManifest ? buildManifest.rootMainFiles.map((src)=>({
src: `${renderOpts.assetPrefix || ""}/_next/` + src,
integrity: subresourceIntegrityManifest[src]
})) : buildManifest.rootMainFiles.map((src)=>`${renderOpts.assetPrefix || ""}/_next/` + src)
}
});
return await (0, _nodeWebStreamsHelper).continueFromInitialStream(renderStream, {
dataStream: serverComponentsInlinedTransformStream == null ? void 0 : serverComponentsInlinedTransformStream.readable,
generateStaticHTML: generateStaticHTML,
flushEffectHandler,
flushEffectsToHead: true
});
}
};
const readable = await bodyResult();
if (generateStaticHTML) {
let staticHtml = Buffer.from((await readable.getReader().read()).value || "").toString();
renderOpts.pageData = Buffer.concat(serverComponentsRenderOpts.rscChunks).toString();
renderOpts.revalidate = typeof staticGenerationContext.revalidate === "undefined" ? defaultRevalidate : staticGenerationContext.revalidate;
return new _renderResult.default(staticHtml);
}
return new _renderResult.default(readable);
}
//# sourceMappingURL=app-render.js.map