UNPKG

next

Version:

The React Framework

963 lines (962 loc) • 195 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "renderToHTMLOrFlight", { enumerable: true, get: function() { return renderToHTMLOrFlight; } }); const _jsxruntime = require("react/jsx-runtime"); const _workasyncstorageexternal = require("../app-render/work-async-storage.external"); const _react = /*#__PURE__*/ _interop_require_wildcard(require("react")); const _renderresult = /*#__PURE__*/ _interop_require_default(require("../render-result")); const _nodewebstreamshelper = require("../stream-utils/node-web-streams-helper"); const _internalutils = require("../internal-utils"); const _approuterheaders = require("../../client/components/app-router-headers"); const _metadatacontext = require("../../lib/metadata/metadata-context"); const _requeststore = require("../async-storage/request-store"); const _workstore = require("../async-storage/work-store"); const _httpaccessfallback = require("../../client/components/http-access-fallback/http-access-fallback"); const _redirect = require("../../client/components/redirect"); const _redirecterror = require("../../client/components/redirect-error"); const _implicittags = require("../lib/implicit-tags"); const _constants = require("../lib/trace/constants"); const _tracer = require("../lib/trace/tracer"); const _flightrenderresult = require("./flight-render-result"); const _createerrorhandler = require("./create-error-handler"); const _getshortdynamicparamtype = require("./get-short-dynamic-param-type"); const _getsegmentparam = require("../../shared/lib/router/utils/get-segment-param"); const _getscriptnoncefromheader = require("./get-script-nonce-from-header"); const _parseandvalidateflightrouterstate = require("./parse-and-validate-flight-router-state"); const _createflightrouterstatefromloadertree = require("./create-flight-router-state-from-loader-tree"); const _actionhandler = require("./action-handler"); const _bailouttocsr = require("../../shared/lib/lazy-dynamic/bailout-to-csr"); 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"); const _makegetserverinsertedhtml = require("./make-get-server-inserted-html"); const _walktreewithflightrouterstate = require("./walk-tree-with-flight-router-state"); const _createcomponenttree = require("./create-component-tree"); const _getassetquerystring = require("./get-asset-query-string"); const _encryptionutils = require("./encryption-utils"); const _postponedstate = require("./postponed-state"); const _hooksservercontext = require("../../client/components/hooks-server-context"); const _useflightresponse = require("./use-flight-response"); const _staticgenerationbailout = require("../../client/components/static-generation-bailout"); const _formatservererror = require("../../lib/format-server-error"); const _dynamicrendering = require("./dynamic-rendering"); const _clientcomponentrendererlogger = require("../client-component-renderer-logger"); const _actionutils = require("./action-utils"); const _helpers = require("../base-http/helpers"); const _parserelativeurl = require("../../shared/lib/router/utils/parse-relative-url"); const _approuter = /*#__PURE__*/ _interop_require_default(require("../../client/components/app-router")); const _serveractionrequestmeta = require("../lib/server-action-request-meta"); const _createinitialrouterstate = require("../../client/components/router-reducer/create-initial-router-state"); const _approuterinstance = require("../../client/components/app-router-instance"); const _utils = require("../instrumentation/utils"); const _segment = require("../../shared/lib/segment"); const _apprenderprerenderutils = require("./app-render-prerender-utils"); const _prospectiverenderutils = require("./prospective-render-utils"); const _apprenderrenderutils = require("./app-render-render-utils"); const _scheduler = require("../../lib/scheduler"); const _workunitasyncstorageexternal = require("./work-unit-async-storage.external"); const _consoleasyncstorageexternal = require("./console-async-storage.external"); const _cachesignal = require("./cache-signal"); const _utils1 = require("../lib/trace/utils"); const _invarianterror = require("../../shared/lib/invariant-error"); const _constants1 = require("../../lib/constants"); const _createcomponentstylesandscripts = require("./create-component-styles-and-scripts"); const _parseloadertree = require("../../shared/lib/router/utils/parse-loader-tree"); const _resumedatacache = require("../resume-data-cache/resume-data-cache"); const _iserror = /*#__PURE__*/ _interop_require_default(require("../../lib/is-error")); const _createserverinsertedmetadata = require("./metadata-insertion/create-server-inserted-metadata"); const _serverutils = require("../server-utils"); const _revalidationutils = require("../revalidation-utils"); const _trackmoduleloadingexternal = require("./module-loading/track-module-loading.external"); const _reactlargeshellerror = require("./react-large-shell-error"); const _segmentexplorerpath = require("./segment-explorer-path"); const _requestmeta = require("../request-meta"); const _getdynamicparam = require("../../shared/lib/router/utils/get-dynamic-param"); const _promisewithresolvers = require("../../shared/lib/promise-with-resolvers"); const _imageconfigcontextsharedruntime = require("../../shared/lib/image-config-context.shared-runtime"); const _imageconfig = require("../../shared/lib/image-config"); const _stagedrendering = require("./staged-rendering"); const _stagedvalidation = require("./staged-validation"); const _warnonce = require("../../shared/lib/utils/warn-once"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interop_require_wildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = { __proto__: null }; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for(var key in obj){ if (key !== "default" && 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 flightDataPathHeadKey = 'h'; const getFlightViewportKey = (requestId)=>requestId + 'v'; const getFlightMetadataKey = (requestId)=>requestId + 'm'; const filterStackFrame = process.env.NODE_ENV !== 'production' ? require('../lib/source-maps').filterStackFrameDEV : undefined; function parseRequestHeaders(headers, options) { // runtime prefetch requests are *not* treated as prefetch requests // (TODO: this is confusing, we should refactor this to express this better) const isPrefetchRequest = headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER] === '1'; const isRuntimePrefetchRequest = headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER] === '2'; const isHmrRefresh = headers[_approuterheaders.NEXT_HMR_REFRESH_HEADER] !== undefined; const isRSCRequest = headers[_approuterheaders.RSC_HEADER] !== undefined; const shouldProvideFlightRouterState = isRSCRequest && (!isPrefetchRequest || !options.isRoutePPREnabled); const flightRouterState = shouldProvideFlightRouterState ? (0, _parseandvalidateflightrouterstate.parseAndValidateFlightRouterState)(headers[_approuterheaders.NEXT_ROUTER_STATE_TREE_HEADER]) : undefined; // Checks if this is a prefetch of the Route Tree by the Segment Cache const isRouteTreePrefetchRequest = headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER] === '/_tree'; const csp = headers['content-security-policy'] || headers['content-security-policy-report-only']; const nonce = typeof csp === 'string' ? (0, _getscriptnoncefromheader.getScriptNonceFromHeader)(csp) : undefined; const previouslyRevalidatedTags = (0, _serverutils.getPreviouslyRevalidatedTags)(headers, options.previewModeId); let requestId; let htmlRequestId; if (process.env.NODE_ENV !== 'production') { // The request IDs are only used in development mode to send debug // information to the matching client (identified by the HTML request ID // that was sent to the client with the HTML document) for the current // request (identified by the request ID, as defined by the client). requestId = typeof headers[_approuterheaders.NEXT_REQUEST_ID_HEADER] === 'string' ? headers[_approuterheaders.NEXT_REQUEST_ID_HEADER] : undefined; htmlRequestId = typeof headers[_approuterheaders.NEXT_HTML_REQUEST_ID_HEADER] === 'string' ? headers[_approuterheaders.NEXT_HTML_REQUEST_ID_HEADER] : undefined; } return { flightRouterState, isPrefetchRequest, isRuntimePrefetchRequest, isRouteTreePrefetchRequest, isHmrRefresh, isRSCRequest, nonce, previouslyRevalidatedTags, requestId, htmlRequestId }; } function createNotFoundLoaderTree(loaderTree) { const components = loaderTree[2]; const hasGlobalNotFound = !!components['global-not-found']; const notFoundTreeComponents = hasGlobalNotFound ? { layout: components['global-not-found'], page: [ ()=>null, 'next/dist/client/components/builtin/empty-stub' ] } : { page: components['not-found'] }; return [ '', { children: [ _segment.PAGE_SEGMENT_KEY, {}, notFoundTreeComponents ] }, // When global-not-found is present, skip layout from components hasGlobalNotFound ? components : {} ]; } /** * Returns a function that parses the dynamic segment and return the associated value. */ function makeGetDynamicParamFromSegment(interpolatedParams, fallbackRouteParams) { return function getDynamicParamFromSegment(// [slug] / [[slug]] / [...slug] segment) { const segmentParam = (0, _getsegmentparam.getSegmentParam)(segment); if (!segmentParam) { return null; } const segmentKey = segmentParam.param; const dynamicParamType = _getshortdynamicparamtype.dynamicParamTypes[segmentParam.type]; return (0, _getdynamicparam.getDynamicParam)(interpolatedParams, segmentKey, dynamicParamType, fallbackRouteParams); }; } function NonIndex({ createElement, pagePath, statusCode, isPossibleServerAction }) { const is404Page = pagePath === '/404'; const isInvalidStatusCode = typeof statusCode === 'number' && statusCode > 400; // Only render noindex for page request, skip for server actions // TODO: is this correct if `isPossibleServerAction` is a false positive? if (!isPossibleServerAction && (is404Page || isInvalidStatusCode)) { return createElement('meta', { name: 'robots', content: 'noindex' }); } return null; } /** * This is used by server actions & client-side navigations to generate RSC data from a client-side request. * This function is only called on "dynamic" requests (ie, there wasn't already a static response). * It uses request headers (namely `next-router-state-tree`) to determine where to start rendering. */ async function generateDynamicRSCPayload(ctx, options) { // 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. // We initialize `flightData` to an empty string because the client router knows how to tolerate // it (treating it as an MPA navigation). The only time this function wouldn't generate flight data // is for server actions, if the server action handler instructs this function to skip it. When the server // action reducer sees a falsy value, it'll simply resolve the action with no data. let flightData = ''; const { componentMod: { routeModule: { userland: { loaderTree } }, createElement, createMetadataComponents, Fragment }, getDynamicParamFromSegment, query, requestId, flightRouterState, workStore, url } = ctx; const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; if (!(options == null ? void 0 : options.skipFlight)) { const preloadCallbacks = []; const { Viewport, Metadata, MetadataOutlet } = createMetadataComponents({ tree: loaderTree, parsedQuery: query, pathname: url.pathname, metadataContext: (0, _metadatacontext.createMetadataContext)(ctx.renderOpts), getDynamicParamFromSegment, workStore, serveStreamingMetadata }); flightData = (await (0, _walktreewithflightrouterstate.walkTreeWithFlightRouterState)({ ctx, loaderTreeToFilter: loaderTree, parentParams: {}, flightRouterState, // For flight, render metadata inside leaf page rscHead: createElement(Fragment, { key: flightDataPathHeadKey }, createElement(NonIndex, { createElement, pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), createElement(Viewport, { key: getFlightViewportKey(requestId) }), createElement(Metadata, { key: getFlightMetadataKey(requestId) })), injectedCSS: new Set(), injectedJS: new Set(), injectedFontPreloadTags: new Set(), rootLayoutIncluded: false, preloadCallbacks, MetadataOutlet })).map((path)=>path.slice(1)) // remove the '' (root) segment ; } // If we have an action result, then this is a server action response. // We can rely on this because `ActionResult` will always be a promise, even if // the result is falsey. if (options == null ? void 0 : options.actionResult) { return { a: options.actionResult, f: flightData, b: ctx.sharedContext.buildId }; } // Otherwise, it's a regular RSC response. const baseResponse = { b: ctx.sharedContext.buildId, f: flightData, S: workStore.isStaticGeneration }; // For runtime prefetches, we encode the stale time and isPartial flag in the response body // rather than relying on response headers. Both of these values will be transformed // by a transform stream before being sent to the client. if ((options == null ? void 0 : options.runtimePrefetchSentinel) !== undefined) { return { ...baseResponse, rp: [ options.runtimePrefetchSentinel ] }; } return baseResponse; } function createErrorContext(ctx, renderSource) { return { routerKind: 'App Router', routePath: ctx.pagePath, // TODO: is this correct if `isPossibleServerAction` is a false positive? routeType: ctx.isPossibleServerAction ? 'action' : 'render', renderSource, revalidateReason: (0, _utils.getRevalidateReason)(ctx.workStore) }; } /** * Produces a RenderResult containing the Flight data for the given request. See * `generateDynamicRSCPayload` for information on the contents of the render result. */ async function generateDynamicFlightRenderResult(req, ctx, requestStore, options) { const { clientReferenceManifest, componentMod: { renderToReadableStream }, htmlRequestId, renderOpts, requestId, workStore } = ctx; const { dev = false, onInstrumentationRequestError, setReactDebugChannel } = renderOpts; function onFlightDataRenderError(err) { return onInstrumentationRequestError == null ? void 0 : onInstrumentationRequestError(err, req, createErrorContext(ctx, 'react-server-components-payload')); } const onError = (0, _createerrorhandler.createFlightReactServerErrorHandler)(dev, onFlightDataRenderError); const debugChannel = setReactDebugChannel && createDebugChannel(); if (debugChannel) { setReactDebugChannel(debugChannel.clientSide, htmlRequestId, requestId); } // For app dir, use the bundled version of Flight server renderer (renderToReadableStream) // which contains the subset React. const rscPayload = await _workunitasyncstorageexternal.workUnitAsyncStorage.run(requestStore, generateDynamicRSCPayload, ctx, options); const flightReadableStream = _workunitasyncstorageexternal.workUnitAsyncStorage.run(requestStore, renderToReadableStream, rscPayload, clientReferenceManifest.clientModules, { onError, temporaryReferences: options == null ? void 0 : options.temporaryReferences, filterStackFrame, debugChannel: debugChannel == null ? void 0 : debugChannel.serverSide }); return new _flightrenderresult.FlightRenderResult(flightReadableStream, { fetchMetrics: workStore.fetchMetrics }); } async function stagedRenderToReadableStreamWithoutCachesInDev(ctx, requestStore, getPayload, clientReferenceManifest, options) { const { componentMod: { renderToReadableStream } } = ctx; // We're rendering while bypassing caches, // so we have no hope of showing a useful runtime stage. // But we still want things like `params` to show up in devtools correctly, // which relies on mechanisms we've set up for staged rendering, // so we do a 2-task version (Static -> Dynamic) instead. const stageController = new _stagedrendering.StagedRenderingController(); const environmentName = ()=>{ const currentStage = stageController.currentStage; switch(currentStage){ case _stagedrendering.RenderStage.Static: return 'Prerender'; case _stagedrendering.RenderStage.Runtime: case _stagedrendering.RenderStage.Dynamic: return 'Server'; default: currentStage; throw Object.defineProperty(new _invarianterror.InvariantError(`Invalid render stage: ${currentStage}`), "__NEXT_ERROR_CODE", { value: "E881", enumerable: false, configurable: true }); } }; requestStore.stagedRendering = stageController; requestStore.asyncApiPromises = createAsyncApiPromisesInDev(stageController, requestStore.cookies, requestStore.mutableCookies, requestStore.headers); const rscPayload = await getPayload(requestStore); return await _workunitasyncstorageexternal.workUnitAsyncStorage.run(requestStore, _apprenderrenderutils.scheduleInSequentialTasks, ()=>{ return renderToReadableStream(rscPayload, clientReferenceManifest.clientModules, { ...options, environmentName }); }, ()=>{ stageController.advanceStage(_stagedrendering.RenderStage.Dynamic); }); } /** * Fork of `generateDynamicFlightRenderResult` that renders using `renderWithRestartOnCacheMissInDev` * to ensure correct separation of environments Prerender/Server (for use in Cache Components) */ async function generateDynamicFlightRenderResultWithStagesInDev(req, ctx, initialRequestStore, createRequestStore) { const { htmlRequestId, renderOpts, requestId, workStore, componentMod: { createElement } } = ctx; const { dev = false, onInstrumentationRequestError, setReactDebugChannel, setCacheStatus, clientReferenceManifest } = renderOpts; function onFlightDataRenderError(err) { return onInstrumentationRequestError == null ? void 0 : onInstrumentationRequestError(err, req, createErrorContext(ctx, 'react-server-components-payload')); } const onError = (0, _createerrorhandler.createFlightReactServerErrorHandler)(dev, onFlightDataRenderError); const getPayload = async (requestStore)=>{ const payload = await _workunitasyncstorageexternal.workUnitAsyncStorage.run(requestStore, generateDynamicRSCPayload, ctx, undefined); if (isBypassingCachesInDev(renderOpts, requestStore)) { // Mark the RSC payload to indicate that caches were bypassed in dev. // This lets the client know not to cache anything based on this render. payload._bypassCachesInDev = createElement(WarnForBypassCachesInDev, { route: workStore.route }); } return payload; }; let debugChannel; let stream; if (// We only do this flow if we can safely recreate the store from scratch // (which is not the case for renders after an action) createRequestStore && // We only do this flow if we're not bypassing caches in dev using // "disable cache" in devtools or a hard refresh (cache-control: "no-store") !isBypassingCachesInDev(renderOpts, initialRequestStore)) { // Before we kick off the render, we set the cache status back to it's initial state // in case a previous render bypassed the cache. if (setCacheStatus) { setCacheStatus('ready', htmlRequestId); } const result = await renderWithRestartOnCacheMissInDev(ctx, initialRequestStore, createRequestStore, getPayload, onError); debugChannel = result.debugChannel; stream = result.stream; } else { // We're either bypassing caches or we can't restart the render. // Do a dynamic render, but with (basic) environment labels. assertClientReferenceManifest(clientReferenceManifest); // Set cache status to bypass when specifically bypassing caches in dev if (setCacheStatus) { setCacheStatus('bypass', htmlRequestId); } debugChannel = setReactDebugChannel && createDebugChannel(); stream = await stagedRenderToReadableStreamWithoutCachesInDev(ctx, initialRequestStore, getPayload, clientReferenceManifest, { onError: onError, filterStackFrame, debugChannel: debugChannel == null ? void 0 : debugChannel.serverSide }); } if (debugChannel && setReactDebugChannel) { setReactDebugChannel(debugChannel.clientSide, htmlRequestId, requestId); } return new _flightrenderresult.FlightRenderResult(stream, { fetchMetrics: workStore.fetchMetrics }); } async function generateRuntimePrefetchResult(req, ctx, requestStore) { const { workStore } = ctx; const renderOpts = ctx.renderOpts; function onFlightDataRenderError(err) { return renderOpts.onInstrumentationRequestError == null ? void 0 : renderOpts.onInstrumentationRequestError.call(renderOpts, err, req, // TODO(runtime-ppr): should we use a different value? createErrorContext(ctx, 'react-server-components-payload')); } const onError = (0, _createerrorhandler.createFlightReactServerErrorHandler)(false, onFlightDataRenderError); const metadata = {}; // Generate a random sentinel that will be used as a placeholder in the payload // and later replaced by the transform stream const runtimePrefetchSentinel = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); const generatePayload = ()=>generateDynamicRSCPayload(ctx, { runtimePrefetchSentinel }); const { componentMod: { routeModule: { userland: { loaderTree } } }, getDynamicParamFromSegment } = ctx; const rootParams = (0, _createcomponenttree.getRootParams)(loaderTree, getDynamicParamFromSegment); // We need to share caches between the prospective prerender and the final prerender, // but we're not going to persist this anywhere. const prerenderResumeDataCache = (0, _resumedatacache.createPrerenderResumeDataCache)(); // We're not resuming an existing render. const renderResumeDataCache = null; await prospectiveRuntimeServerPrerender(ctx, generatePayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, requestStore.headers, requestStore.cookies, requestStore.draftMode); const response = await finalRuntimeServerPrerender(ctx, generatePayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, requestStore.headers, requestStore.cookies, requestStore.draftMode, onError, runtimePrefetchSentinel); applyMetadataFromPrerenderResult(response, metadata, workStore); metadata.fetchMetrics = ctx.workStore.fetchMetrics; return new _flightrenderresult.FlightRenderResult(response.result.prelude, metadata); } async function prospectiveRuntimeServerPrerender(ctx, getPayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, headers, cookies, draftMode) { const { implicitTags, renderOpts, workStore } = ctx; const { clientReferenceManifest, ComponentMod } = renderOpts; assertClientReferenceManifest(clientReferenceManifest); // Prerender controller represents the lifetime of the prerender. // It will be aborted when a Task is complete or a synchronously aborting // API is called. Notably during cache-filling renders this does not actually // terminate the render itself which will continue until all caches are filled const initialServerPrerenderController = new AbortController(); // This controller represents the lifetime of the React render call. Notably // during the cache-filling render it is different from the prerender controller // because we don't want to end the react render until all caches are filled. const initialServerRenderController = new AbortController(); // The cacheSignal helps us track whether caches are still filling or we are ready // to cut the render off. const cacheSignal = new _cachesignal.CacheSignal(); const initialServerPrerenderStore = { type: 'prerender-runtime', phase: 'render', rootParams, implicitTags, renderSignal: initialServerRenderController.signal, controller: initialServerPrerenderController, // During the initial prerender we need to track all cache reads to ensure // we render long enough to fill every cache it is possible to visit during // the final prerender. cacheSignal, // We only need to track dynamic accesses during the final prerender. dynamicTracking: null, // Runtime prefetches are never cached server-side, only client-side, // so we set `expire` and `revalidate` to their minimum values just in case. revalidate: 1, expire: 0, stale: _constants1.INFINITE_CACHE, tags: [ ...implicitTags.tags ], renderResumeDataCache, prerenderResumeDataCache, hmrRefreshHash: undefined, // We only need task sequencing in the final prerender. runtimeStagePromise: null, // These are not present in regular prerenders, but allowed in a runtime prerender. headers, cookies, draftMode }; // We're not going to use the result of this render because the only time it could be used // is if it completes in a microtask and that's likely very rare for any non-trivial app const initialServerPayload = await _workunitasyncstorageexternal.workUnitAsyncStorage.run(initialServerPrerenderStore, getPayload); const pendingInitialServerResult = _workunitasyncstorageexternal.workUnitAsyncStorage.run(initialServerPrerenderStore, ComponentMod.prerender, initialServerPayload, clientReferenceManifest.clientModules, { filterStackFrame, onError: (err)=>{ const digest = (0, _createerrorhandler.getDigestForWellKnownError)(err); if (digest) { return digest; } if (initialServerPrerenderController.signal.aborted) { // The render aborted before this error was handled which indicates // the error is caused by unfinished components within the render return; } else if (process.env.NEXT_DEBUG_BUILD || process.env.__NEXT_VERBOSE_LOGGING) { (0, _prospectiverenderutils.printDebugThrownValueForProspectiveRender)(err, workStore.route); } }, // We don't want to stop rendering until the cacheSignal is complete so we pass // a different signal to this render call than is used by dynamic APIs to signify // transitioning out of the prerender environment signal: initialServerRenderController.signal }); // Wait for all caches to be finished filling and for async imports to resolve (0, _trackmoduleloadingexternal.trackPendingModules)(cacheSignal); await cacheSignal.cacheReady(); initialServerRenderController.abort(); initialServerPrerenderController.abort(); // We don't need to continue the prerender process if we already // detected invalid dynamic usage in the initial prerender phase. if (workStore.invalidDynamicUsageError) { throw workStore.invalidDynamicUsageError; } try { return await (0, _apprenderprerenderutils.createReactServerPrerenderResult)(pendingInitialServerResult); } catch (err) { if (initialServerRenderController.signal.aborted || initialServerPrerenderController.signal.aborted) { // These are expected errors that might error the prerender. we ignore them. } else if (process.env.NEXT_DEBUG_BUILD || process.env.__NEXT_VERBOSE_LOGGING) { // We don't normally log these errors because we are going to retry anyway but // it can be useful for debugging Next.js itself to get visibility here when needed (0, _prospectiverenderutils.printDebugThrownValueForProspectiveRender)(err, workStore.route); } return null; } } /** * Updates the runtime prefetch metadata in the RSC payload as it streams: * "rp":[<sentinel>] -> "rp":[<isPartial>,<staleTime>] * * We use a transform stream to do this to avoid needing to trigger an additional render. * A random sentinel number guarantees no collision with user data. */ function createRuntimePrefetchTransformStream(sentinel, isPartial, staleTime) { const encoder = new TextEncoder(); // Search for: [<sentinel>] // Replace with: [<isPartial>,<staleTime>] const search = encoder.encode(`[${sentinel}]`); const first = search[0]; const replace = encoder.encode(`[${isPartial},${staleTime}]`); const searchLen = search.length; let currentChunk = null; let found = false; function processChunk(controller, nextChunk) { if (found) { if (nextChunk) { controller.enqueue(nextChunk); } return; } if (currentChunk) { // We can't search past the index that can contain a full match let exclusiveUpperBound = currentChunk.length - (searchLen - 1); if (nextChunk) { // If we have any overflow bytes we can search up to the chunk's final byte exclusiveUpperBound += Math.min(nextChunk.length, searchLen - 1); } if (exclusiveUpperBound < 1) { // we can't match the current chunk. controller.enqueue(currentChunk); currentChunk = nextChunk // advance so we don't process this chunk again ; return; } let currentIndex = currentChunk.indexOf(first); // check the current candidate match if it is within the bounds of our search space for the currentChunk candidateLoop: while(-1 < currentIndex && currentIndex < exclusiveUpperBound){ // We already know index 0 matches because we used indexOf to find the candidateIndex so we start at index 1 let matchIndex = 1; while(matchIndex < searchLen){ const candidateIndex = currentIndex + matchIndex; const candidateValue = candidateIndex < currentChunk.length ? currentChunk[candidateIndex] : nextChunk[candidateIndex - currentChunk.length]; if (candidateValue !== search[matchIndex]) { // No match, reset and continue the search from the next position currentIndex = currentChunk.indexOf(first, currentIndex + 1); continue candidateLoop; } matchIndex++; } // We found a complete match. currentIndex is our starting point to replace the value. found = true; // enqueue everything up to the match controller.enqueue(currentChunk.subarray(0, currentIndex)); // enqueue the replacement value controller.enqueue(replace); // If there are bytes in the currentChunk after the match enqueue them if (currentIndex + searchLen < currentChunk.length) { controller.enqueue(currentChunk.slice(currentIndex + searchLen)); } // If we have a next chunk we enqueue it now if (nextChunk) { // if replacement spills over to the next chunk we first exclude the replaced bytes const overflowBytes = currentIndex + searchLen - currentChunk.length; const truncatedChunk = overflowBytes > 0 ? nextChunk.subarray(overflowBytes) : nextChunk; controller.enqueue(truncatedChunk); } // We are now in found mode and don't need to track currentChunk anymore currentChunk = null; return; } // No match found in this chunk, emit it and wait for the next one controller.enqueue(currentChunk); } // Advance to the next chunk currentChunk = nextChunk; } return new TransformStream({ transform (chunk, controller) { processChunk(controller, chunk); }, flush (controller) { processChunk(controller, null); } }); } async function finalRuntimeServerPrerender(ctx, getPayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, headers, cookies, draftMode, onError, runtimePrefetchSentinel) { const { implicitTags, renderOpts } = ctx; const { clientReferenceManifest, ComponentMod, experimental, isDebugDynamicAccesses } = renderOpts; assertClientReferenceManifest(clientReferenceManifest); const selectStaleTime = createSelectStaleTime(experimental); let serverIsDynamic = false; const finalServerController = new AbortController(); const serverDynamicTracking = (0, _dynamicrendering.createDynamicTrackingState)(isDebugDynamicAccesses); const { promise: runtimeStagePromise, resolve: resolveBlockedRuntimeAPIs } = (0, _promisewithresolvers.createPromiseWithResolvers)(); const finalServerPrerenderStore = { type: 'prerender-runtime', phase: 'render', rootParams, implicitTags, renderSignal: finalServerController.signal, controller: finalServerController, // All caches we could read must already be filled so no tracking is necessary cacheSignal: null, dynamicTracking: serverDynamicTracking, // Runtime prefetches are never cached server-side, only client-side, // so we set `expire` and `revalidate` to their minimum values just in case. revalidate: 1, expire: 0, stale: _constants1.INFINITE_CACHE, tags: [ ...implicitTags.tags ], prerenderResumeDataCache, renderResumeDataCache, hmrRefreshHash: undefined, // Used to separate the "Static" stage from the "Runtime" stage. runtimeStagePromise, // These are not present in regular prerenders, but allowed in a runtime prerender. headers, cookies, draftMode }; const finalRSCPayload = await _workunitasyncstorageexternal.workUnitAsyncStorage.run(finalServerPrerenderStore, getPayload); let prerenderIsPending = true; const result = await (0, _apprenderprerenderutils.prerenderAndAbortInSequentialTasksWithStages)(async ()=>{ // Static stage const prerenderResult = await _workunitasyncstorageexternal.workUnitAsyncStorage.run(finalServerPrerenderStore, ComponentMod.prerender, finalRSCPayload, clientReferenceManifest.clientModules, { filterStackFrame, onError, signal: finalServerController.signal }); prerenderIsPending = false; return prerenderResult; }, ()=>{ // Advance to the runtime stage. // // We make runtime APIs hang during the first task (above), and unblock them in the following task (here). // This makes sure that, at this point, we'll have finished all the static parts (what we'd prerender statically). // We know that they don't contain any incorrect sync IO, because that'd have caused a build error. // After we unblock Runtime APIs, if we encounter sync IO (e.g. `await cookies(); Date.now()`), // we'll abort, but we'll produce at least as much output as a static prerender would. resolveBlockedRuntimeAPIs(); }, ()=>{ // Abort. if (finalServerController.signal.aborted) { // If the server controller is already aborted we must have called something // that required aborting the prerender synchronously such as with new Date() serverIsDynamic = true; return; } if (prerenderIsPending) { // If prerenderIsPending then we have blocked for longer than a Task and we assume // there is something unfinished. serverIsDynamic = true; } finalServerController.abort(); }); // Update the RSC payload stream to replace the sentinel with actual values. // React has already serialized the payload with the sentinel, so we need to transform the stream. const collectedStale = selectStaleTime(finalServerPrerenderStore.stale); result.prelude = result.prelude.pipeThrough(createRuntimePrefetchTransformStream(runtimePrefetchSentinel, serverIsDynamic, collectedStale)); return { result, // TODO(runtime-ppr): do we need to produce a digest map here? // digestErrorsMap: ..., dynamicAccess: serverDynamicTracking, isPartial: serverIsDynamic, collectedRevalidate: finalServerPrerenderStore.revalidate, collectedExpire: finalServerPrerenderStore.expire, collectedStale, collectedTags: finalServerPrerenderStore.tags }; } /** * Crawlers will inadvertently think the canonicalUrl in the RSC payload should be crawled * when our intention is to just seed the router state with the current URL. * This function splits up the pathname so that we can later join it on * when we're ready to consume the path. */ function prepareInitialCanonicalUrl(url) { return (url.pathname + url.search).split('/'); } function getRenderedSearch(query) { // Inlined implementation of querystring.encode, which is not available in // the Edge runtime. const pairs = []; for(const key in query){ const value = query[key]; if (value == null) continue; if (Array.isArray(value)) { for (const v of value){ pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(v))}`); } } else { pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); } } // The result should match the format of a web URL's `search` property, since // this is the format that's stored in the App Router state. // TODO: We're a bit inconsistent about this. The x-nextjs-rewritten-query // header omits the leading question mark. Should refactor to always do // that instead. if (pairs.length === 0) { // If the search string is empty, return an empty string. return ''; } // Prepend '?' to the search params string. return '?' + pairs.join('&'); } // This is the data necessary to render <AppRouter /> when no SSR errors are encountered async function getRSCPayload(tree, ctx, is404) { const injectedCSS = new Set(); const injectedJS = new Set(); const injectedFontPreloadTags = new Set(); let missingSlots; // We only track missing parallel slots in development if (process.env.NODE_ENV === 'development') { missingSlots = new Set(); } const { getDynamicParamFromSegment, query, appUsingSizeAdjustment, componentMod: { createMetadataComponents, createElement, Fragment }, url, workStore } = ctx; const initialTree = (0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(tree, getDynamicParamFromSegment, query); const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; const hasGlobalNotFound = !!tree[2]['global-not-found']; const { Viewport, Metadata, MetadataOutlet } = createMetadataComponents({ tree, // When it's using global-not-found, metadata errorType is undefined, which will retrieve the // metadata from the page. // When it's using not-found, metadata errorType is 'not-found', which will retrieve the // metadata from the not-found.js boundary. // TODO: remove this condition and keep it undefined when global-not-found is stabilized. errorType: is404 && !hasGlobalNotFound ? 'not-found' : undefined, parsedQuery: query, pathname: url.pathname, metadataContext: (0, _metadatacontext.createMetadataContext)(ctx.renderOpts), getDynamicParamFromSegment, workStore, serveStreamingMetadata }); const preloadCallbacks = []; const seedData = await (0, _createcomponenttree.createComponentTree)({ ctx, loaderTree: tree, parentParams: {}, injectedCSS, injectedJS, injectedFontPreloadTags, rootLayoutIncluded: false, missingSlots, preloadCallbacks, authInterrupts: ctx.renderOpts.experimental.authInterrupts, MetadataOutlet }); // When the `vary` response header is present with `Next-URL`, that means there's a chance // it could respond differently if there's an interception route. We provide this information // to `AppRouter` so that it can properly seed the prefetch cache with a prefix, if needed. const varyHeader = ctx.res.getHeader('vary'); const couldBeIntercepted = typeof varyHeader === 'string' && varyHeader.includes(_approuterheaders.NEXT_URL); const initialHead = createElement(Fragment, { key: flightDataPathHeadKey }, createElement(NonIndex, { createElement, pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), createElement(Viewport, null), createElement(Metadata, null), appUsingSizeAdjustment ? createElement('meta', { name: 'next-size-adjust', content: '' }) : null); const { GlobalError, styles: globalErrorStyles } = await getGlobalErrorStyles(tree, ctx); // Assume the head we're rendering contains only partial data if PPR is // enabled and this is a statically generated response. This is used by the // client Segment Cache after a prefetch to determine if it can skip the // second request to fill in the dynamic data. // // See similar comment in create-component-tree.tsx for more context. const isPossiblyPartialHead = workStore.isStaticGeneration && ctx.renderOpts.experimental.isRoutePPREnabled === true; return { // See the comment above the `Preloads` component (below) for why this is part of the payload P: createElement(Preloads, { preloadCallbacks: preloadCallbacks }), b: ctx.sharedContext.buildId, c: prepareInitialCanonicalUrl(url), q: getRenderedSearch(query), i: !!couldBeIntercepted, f: [ [ initialTree, seedData, initialHead, isPossiblyPartialHead ] ], m: missingSlots, G: [ GlobalError, globalErrorStyles ], S: workStore.isStaticGeneration }; } /** * Preload calls (such as `ReactDOM.preloadStyle` and `ReactDOM.preloadFont`) need to be called during rendering * in order to create the appropriate preload tags in the DOM, otherwise they're a no-op. Since we invoke * renderToReadableStream with a function that returns component props rather than a component itself, we use * this component to "render " the preload calls. */ function Preloads({ preloadCallbacks }) { preloadCallbacks.forEach((preloadFn)=>preloadFn()); return null; } // This is the data necessary to render <AppRouter /> when an error state is triggered async function getErrorRSCPayload(tree, ctx, ssrError, errorType) { const { getDynamicParamFromSegment, query, componentMod: { createMetadataComponents, createElement, Fragment }, url, workStore } = ctx; const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; const { Viewport, Metadata } = createMetadataComponents({ tree, parsedQuery: query, pathname: url.pathname, metadataContext: (0, _metadatacontext.createMetadataContext)(ctx.renderOpts), errorType, getDynamicParamFromSegment, workStore, serveStreamingMetadata: serveStreamingMetadata }); const initialHead = createElement(Fragment, { key: flightDataPathHeadKey }, createElement(NonIndex, { createElement, pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), createElement(Viewport, null), process.env.NODE_ENV === 'development' && createElement('meta', { name: 'next-error', content: 'not-found' }), createElement(Metadata, null)); const initialTree = (0, _createflightrouterstatefromloadertree.createFlightRouterStateFromLoaderTree)(tree, getDynamicParamFromSegment, query); let err = undefined; if (ssrError) { err = (0, _iserror.default)(ssrError) ? ssrError : Object.defineProperty(new Error(ssrError + ''), "__NEXT_ERROR_CODE", { value: "E394", enumerable: false, configurable: true }); } // For metadata notFound error there's no global not found boundary on top // so we create a not found page with AppRouter const seedData = [ createElement('html', { id: '__next_error__' }, createElement('head', null), createElement('body', null, process.env.NODE_ENV !== 'production' && err ? createElement('template', { 'data-next-error-message': err.message, 'data-next-error-digest': 'digest' in err ? err.digest : '', 'data-next-error-stack': err.stack }) : null)), {}, null, false, false ]; const { GlobalError, styles: globalErrorStyles } = await getGlobalErrorStyles(tree, ctx); const isPossiblyPartialHead = workStore.isStaticGeneration && ctx.renderOpts.experimental.isRoutePPREnabled === true; return { b: ctx.sharedContext.buildId, c: prepareInitialCanonicalUrl(url), q: getRenderedSearch(query), m: undefined, i: false, f: [ [ initialTree, seedData, initialHead, isPossiblyPartialHead ] ], G: [ GlobalError, globalErrorStyles ], S: workStore.isStaticGeneration }; } function assertClientReferenceManifest(clientReferenceManifest) { if (!clientReferenceManifest) { throw Object.defineProperty(new _invarianterror.InvariantError('Expected clientReferenceManifest to be defined.'), "__NEXT_ERROR_CODE", { value: "E692", enumerable: false, configurable: true }); } } // This component must run in an SSR context. It will render the RSC root component function App({ reactServerStream, reactDebugStream, preinitScripts, clientReferenceManifest, ServerInsertedHTMLProvider, nonce, images }) { preinitScripts(); const response = _react.use((0, _useflightresponse.useFlightStream)(reactServerStream, reactDebugStream, clientReferenceManifest, nonce)); const initialState = (0, _createinitialrouterstate.createInitialRouterState)({ // This is not used during hydration, so we don't have to pass a // real timestamp.