UNPKG

next

Version:

The React Framework

982 lines • 158 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { workAsyncStorage } from '../app-render/work-async-storage.external'; import React from 'react'; import RenderResult from '../render-result'; import { chainStreams, renderToInitialFizzStream, createDocumentClosingStream, continueFizzStream, continueDynamicPrerender, continueStaticPrerender, continueDynamicHTMLResume, streamToBuffer, streamToString } from '../stream-utils/node-web-streams-helper'; import { stripInternalQueries } from '../internal-utils'; import { NEXT_HMR_REFRESH_HEADER, NEXT_ROUTER_PREFETCH_HEADER, NEXT_ROUTER_STATE_TREE_HEADER, NEXT_ROUTER_STALE_TIME_HEADER, NEXT_URL, RSC_HEADER, NEXT_ROUTER_SEGMENT_PREFETCH_HEADER, NEXT_HMR_REFRESH_HASH_COOKIE, NEXT_DID_POSTPONE_HEADER } from '../../client/components/app-router-headers'; import { createMetadataContext } from '../../lib/metadata/metadata-context'; import { createRequestStoreForRender } from '../async-storage/request-store'; import { createWorkStore } from '../async-storage/work-store'; import { getAccessFallbackErrorTypeByStatus, getAccessFallbackHTTPStatus, isHTTPAccessFallbackError } from '../../client/components/http-access-fallback/http-access-fallback'; import { getURLFromRedirectError, getRedirectStatusCodeFromError } from '../../client/components/redirect'; import { isRedirectError } from '../../client/components/redirect-error'; import { getImplicitTags } from '../lib/implicit-tags'; import { AppRenderSpan, NextNodeServerSpan } from '../lib/trace/constants'; import { getTracer } from '../lib/trace/tracer'; import { FlightRenderResult } from './flight-render-result'; import { createFlightReactServerErrorHandler, createHTMLReactServerErrorHandler, createHTMLErrorHandler, isUserLandError, getDigestForWellKnownError } from './create-error-handler'; import { dynamicParamTypes } from './get-short-dynamic-param-type'; import { getSegmentParam } from './get-segment-param'; import { getScriptNonceFromHeader } from './get-script-nonce-from-header'; import { parseAndValidateFlightRouterState } from './parse-and-validate-flight-router-state'; import { createFlightRouterStateFromLoaderTree } from './create-flight-router-state-from-loader-tree'; import { handleAction } from './action-handler'; import { isBailoutToCSRError } from '../../shared/lib/lazy-dynamic/bailout-to-csr'; import { warn, error } from '../../build/output/log'; import { appendMutableCookies } from '../web/spec-extension/adapters/request-cookies'; import { createServerInsertedHTML } from './server-inserted-html'; import { getRequiredScripts } from './required-scripts'; import { addPathPrefix } from '../../shared/lib/router/utils/add-path-prefix'; import { makeGetServerInsertedHTML } from './make-get-server-inserted-html'; import { walkTreeWithFlightRouterState } from './walk-tree-with-flight-router-state'; import { createComponentTree, getRootParams } from './create-component-tree'; import { getAssetQueryString } from './get-asset-query-string'; import { getServerModuleMap, setReferenceManifestsSingleton } from './encryption-utils'; import { DynamicState, DynamicHTMLPreludeState, parsePostponedState } from './postponed-state'; import { getDynamicDataPostponedState, getDynamicHTMLPostponedState, getPostponedFromState } from './postponed-state'; import { isDynamicServerError } from '../../client/components/hooks-server-context'; import { useFlightStream, createInlinedDataReadableStream } from './use-flight-response'; import { StaticGenBailoutError, isStaticGenBailoutError } from '../../client/components/static-generation-bailout'; import { getStackWithoutErrorMessage } from '../../lib/format-server-error'; import { accessedDynamicData, createRenderInBrowserAbortSignal, formatDynamicAPIAccesses, isPrerenderInterruptedError, createDynamicTrackingState, createDynamicValidationState, trackAllowedDynamicAccess, throwIfDisallowedDynamic, PreludeState, consumeDynamicAccess, logDisallowedDynamicError, warnOnSyncDynamicError } from './dynamic-rendering'; import { getClientComponentLoaderMetrics, wrapClientComponentLoader } from '../client-component-renderer-logger'; import { createServerModuleMap } from './action-utils'; import { isNodeNextRequest } from '../base-http/helpers'; import { parseRelativeUrl } from '../../shared/lib/router/utils/parse-relative-url'; import AppRouter from '../../client/components/app-router'; import { getIsPossibleServerAction } from '../lib/server-action-request-meta'; import { createInitialRouterState } from '../../client/components/router-reducer/create-initial-router-state'; import { createMutableActionQueue } from '../../client/components/app-router-instance'; import { getRevalidateReason } from '../instrumentation/utils'; import { PAGE_SEGMENT_KEY } from '../../shared/lib/segment'; import { prerenderAndAbortInSequentialTasksWithStages, processPrelude } from './app-render-prerender-utils'; import { ReactServerResult, createReactServerPrerenderResult, createReactServerPrerenderResultFromRender, prerenderAndAbortInSequentialTasks } from './app-render-prerender-utils'; import { printDebugThrownValueForProspectiveRender } from './prospective-render-utils'; import { scheduleInSequentialTasks } from './app-render-render-utils'; import { waitAtLeastOneReactRenderTask } from '../../lib/scheduler'; import { workUnitAsyncStorage } from './work-unit-async-storage.external'; import { CacheSignal } from './cache-signal'; import { getTracedMetadata } from '../lib/trace/utils'; import { InvariantError } from '../../shared/lib/invariant-error'; import { HTML_CONTENT_TYPE_HEADER, INFINITE_CACHE } from '../../lib/constants'; import { createComponentStylesAndScripts } from './create-component-styles-and-scripts'; import { parseLoaderTree } from './parse-loader-tree'; import { createPrerenderResumeDataCache, createRenderResumeDataCache } from '../resume-data-cache/resume-data-cache'; import isError from '../../lib/is-error'; import { createServerInsertedMetadata } from './metadata-insertion/create-server-inserted-metadata'; import { getPreviouslyRevalidatedTags } from '../server-utils'; import { executeRevalidates } from '../revalidation-utils'; import { trackPendingChunkLoad, trackPendingImport, trackPendingModules } from './module-loading/track-module-loading.external'; import { isReactLargeShellError } from './react-large-shell-error'; import { normalizeConventionFilePath } from './segment-explorer-path'; import { getRequestMeta } from '../request-meta'; import { getDynamicParam } from '../../shared/lib/router/utils/get-dynamic-param'; import { createPromiseWithResolvers } from '../../shared/lib/promise-with-resolvers'; 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) { const isDevWarmupRequest = options.isDevWarmup === true; // dev warmup requests are treated as prefetch RSC requests // runtime prefetch requests are *not* treated as prefetch requests // (TODO: this is confusing, we should refactor this to express this better) const isPrefetchRequest = isDevWarmupRequest || headers[NEXT_ROUTER_PREFETCH_HEADER] === '1'; const isRuntimePrefetchRequest = headers[NEXT_ROUTER_PREFETCH_HEADER] === '2'; const isHmrRefresh = headers[NEXT_HMR_REFRESH_HEADER] !== undefined; // dev warmup requests are treated as prefetch RSC requests const isRSCRequest = isDevWarmupRequest || headers[RSC_HEADER] !== undefined; const shouldProvideFlightRouterState = isRSCRequest && (!isPrefetchRequest || !options.isRoutePPREnabled); const flightRouterState = shouldProvideFlightRouterState ? parseAndValidateFlightRouterState(headers[NEXT_ROUTER_STATE_TREE_HEADER]) : undefined; // Checks if this is a prefetch of the Route Tree by the Segment Cache const isRouteTreePrefetchRequest = headers[NEXT_ROUTER_SEGMENT_PREFETCH_HEADER] === '/_tree'; const csp = headers['content-security-policy'] || headers['content-security-policy-report-only']; const nonce = typeof csp === 'string' ? getScriptNonceFromHeader(csp) : undefined; const previouslyRevalidatedTags = getPreviouslyRevalidatedTags(headers, options.previewModeId); return { flightRouterState, isPrefetchRequest, isRuntimePrefetchRequest, isRouteTreePrefetchRequest, isHmrRefresh, isRSCRequest, isDevWarmupRequest, nonce, previouslyRevalidatedTags }; } function createNotFoundLoaderTree(loaderTree) { const components = loaderTree[2]; const hasGlobalNotFound = !!components['global-not-found']; return [ '', { children: [ PAGE_SEGMENT_KEY, {}, { page: components['global-not-found'] ?? components['not-found'] } ] }, // 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(params, pagePath, fallbackRouteParams) { return function getDynamicParamFromSegment(// [slug] / [[slug]] / [...slug] segment) { const segmentParam = getSegmentParam(segment); if (!segmentParam) { return null; } const segmentKey = segmentParam.param; const dynamicParamType = dynamicParamTypes[segmentParam.type]; return getDynamicParam(params, segmentKey, dynamicParamType, pagePath, fallbackRouteParams); }; } function NonIndex({ 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 /*#__PURE__*/ _jsx("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: { tree: loaderTree, createMetadataComponents, MetadataBoundary, ViewportBoundary }, getDynamicParamFromSegment, appUsingSizeAdjustment, query, requestId, flightRouterState, workStore, url } = ctx; const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; if (!(options == null ? void 0 : options.skipFlight)) { const preloadCallbacks = []; const { ViewportTree, MetadataTree, getViewportReady, getMetadataReady, StreamingMetadataOutlet } = createMetadataComponents({ tree: loaderTree, parsedQuery: query, pathname: url.pathname, metadataContext: createMetadataContext(ctx.renderOpts), getDynamicParamFromSegment, appUsingSizeAdjustment, workStore, MetadataBoundary, ViewportBoundary, serveStreamingMetadata }); flightData = (await walkTreeWithFlightRouterState({ ctx, loaderTreeToFilter: loaderTree, parentParams: {}, flightRouterState, // For flight, render metadata inside leaf page rscHead: /*#__PURE__*/ _jsxs(React.Fragment, { children: [ /*#__PURE__*/ _jsx(NonIndex, { pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), /*#__PURE__*/ _jsx(ViewportTree, {}, getFlightViewportKey(requestId)), /*#__PURE__*/ _jsx(MetadataTree, {}, getFlightMetadataKey(requestId)) ] }, flightDataPathHeadKey), injectedCSS: new Set(), injectedJS: new Set(), injectedFontPreloadTags: new Set(), rootLayoutIncluded: false, getViewportReady, getMetadataReady, preloadCallbacks, StreamingMetadataOutlet })).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. return { b: ctx.sharedContext.buildId, f: flightData, S: workStore.isStaticGeneration }; } 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: 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 renderOpts = ctx.renderOpts; function onFlightDataRenderError(err) { return renderOpts.onInstrumentationRequestError == null ? void 0 : renderOpts.onInstrumentationRequestError.call(renderOpts, err, req, createErrorContext(ctx, 'react-server-components-payload')); } const onError = createFlightReactServerErrorHandler(!!renderOpts.dev, onFlightDataRenderError); const RSCPayload = await workUnitAsyncStorage.run(requestStore, generateDynamicRSCPayload, ctx, options); // For app dir, use the bundled version of Flight server renderer (renderToReadableStream) // which contains the subset React. const flightReadableStream = workUnitAsyncStorage.run(requestStore, ctx.componentMod.renderToReadableStream, RSCPayload, ctx.clientReferenceManifest.clientModules, { onError, temporaryReferences: options == null ? void 0 : options.temporaryReferences, filterStackFrame }); return new FlightRenderResult(flightReadableStream, { fetchMetrics: ctx.workStore.fetchMetrics }); } async function generateRuntimePrefetchResult(req, res, 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 = createFlightReactServerErrorHandler(false, onFlightDataRenderError); const metadata = {}; const generatePayload = ()=>generateDynamicRSCPayload(ctx, undefined); const { componentMod: { tree }, getDynamicParamFromSegment } = ctx; const rootParams = getRootParams(tree, 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 = createPrerenderResumeDataCache(); // We're not resuming an existing render. const renderResumeDataCache = null; await prospectiveRuntimeServerPrerender(ctx, generatePayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, requestStore.cookies, requestStore.draftMode); const response = await finalRuntimeServerPrerender(ctx, generatePayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, requestStore.cookies, requestStore.draftMode, onError); applyMetadataFromPrerenderResult(response, metadata, workStore); metadata.fetchMetrics = ctx.workStore.fetchMetrics; if (response.isPartial) { res.setHeader(NEXT_DID_POSTPONE_HEADER, '1'); } return new FlightRenderResult(response.result.prelude, metadata); } async function prospectiveRuntimeServerPrerender(ctx, getPayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, 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(); 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: INFINITE_CACHE, tags: [ ...implicitTags.tags ], renderResumeDataCache, prerenderResumeDataCache, hmrRefreshHash: undefined, captureOwnerStack: 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. 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 workUnitAsyncStorage.run(initialServerPrerenderStore, getPayload); const pendingInitialServerResult = workUnitAsyncStorage.run(initialServerPrerenderStore, ComponentMod.prerender, initialServerPayload, clientReferenceManifest.clientModules, { filterStackFrame, onError: (err)=>{ const digest = 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) { printDebugThrownValueForProspectiveRender(err, workStore.route); } }, // we don't care to track postpones during the prospective render because we need // to always do a final render anyway onPostpone: undefined, // 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 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 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 printDebugThrownValueForProspectiveRender(err, workStore.route); } return null; } } async function finalRuntimeServerPrerender(ctx, getPayload, prerenderResumeDataCache, renderResumeDataCache, rootParams, cookies, draftMode, onError) { 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 = createDynamicTrackingState(isDebugDynamicAccesses); const { promise: runtimeStagePromise, resolve: resolveBlockedRuntimeAPIs } = 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: INFINITE_CACHE, tags: [ ...implicitTags.tags ], prerenderResumeDataCache, renderResumeDataCache, hmrRefreshHash: undefined, captureOwnerStack: 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. cookies, draftMode }; const finalRSCPayload = await workUnitAsyncStorage.run(finalServerPrerenderStore, getPayload); let prerenderIsPending = true; const result = await prerenderAndAbortInSequentialTasksWithStages(async ()=>{ // Static stage const prerenderResult = await 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(); }); warnOnSyncDynamicError(serverDynamicTracking); 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: selectStaleTime(finalServerPrerenderStore.stale), collectedTags: finalServerPrerenderStore.tags }; } /** * Performs a "warmup" render of the RSC payload for a given route. This function is called by the server * prior to an actual render request in Dev mode only. It's purpose is to fill caches so the actual render * can accurately log activity in the right render context (Prerender vs Render). * * At the moment this implementation is mostly a fork of generateDynamicFlightRenderResult */ async function warmupDevRender(req, ctx) { const { clientReferenceManifest, componentMod: ComponentMod, getDynamicParamFromSegment, implicitTags, renderOpts, workStore } = ctx; const { allowEmptyStaticShell = false, dev, onInstrumentationRequestError } = renderOpts; if (!dev) { throw Object.defineProperty(new InvariantError('generateDynamicFlightRenderResult should never be called in `next start` mode.'), "__NEXT_ERROR_CODE", { value: "E523", enumerable: false, configurable: true }); } const rootParams = getRootParams(ComponentMod.tree, getDynamicParamFromSegment); function onFlightDataRenderError(err) { return onInstrumentationRequestError == null ? void 0 : onInstrumentationRequestError(err, req, createErrorContext(ctx, 'react-server-components-payload')); } const onError = createFlightReactServerErrorHandler(true, onFlightDataRenderError); // We're doing a dev warmup, so we should create a new resume data cache so // we can fill it. const prerenderResumeDataCache = createPrerenderResumeDataCache(); const renderController = new AbortController(); const prerenderController = new AbortController(); const reactController = new AbortController(); const cacheSignal = new CacheSignal(); const prerenderStore = { type: 'prerender', phase: 'render', rootParams, implicitTags, renderSignal: renderController.signal, controller: prerenderController, cacheSignal, dynamicTracking: null, allowEmptyStaticShell, revalidate: INFINITE_CACHE, expire: INFINITE_CACHE, stale: INFINITE_CACHE, tags: [], prerenderResumeDataCache, renderResumeDataCache: null, hmrRefreshHash: req.cookies[NEXT_HMR_REFRESH_HASH_COOKIE], captureOwnerStack: ComponentMod.captureOwnerStack, // warmup is a dev only feature and no fallback params are used in the // primary render which is static. We only use a prerender store here to // allow the warmup to halt on Request data APIs and fetches. fallbackRouteParams: null }; const rscPayload = await workUnitAsyncStorage.run(prerenderStore, generateDynamicRSCPayload, ctx); // For app dir, use the bundled version of Flight server renderer (renderToReadableStream) // which contains the subset React. workUnitAsyncStorage.run(prerenderStore, ComponentMod.renderToReadableStream, rscPayload, clientReferenceManifest.clientModules, { filterStackFrame, onError, signal: renderController.signal }); // Wait for all caches to be finished filling and for async imports to resolve trackPendingModules(cacheSignal); await cacheSignal.cacheReady(); // We unset the cache so any late over-run renders aren't able to write into this cache prerenderStore.prerenderResumeDataCache = null; // Abort the render reactController.abort(); renderController.abort(); // We don't really want to return a result here but the stack of functions // that calls into renderToHTML... expects a result. We should refactor this to // lift the warmup pathway outside of renderToHTML... but for now this suffices return new FlightRenderResult('', { fetchMetrics: workStore.fetchMetrics, renderResumeDataCache: createRenderResumeDataCache(prerenderResumeDataCache) }); } /** * 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('/'); } // 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, MetadataBoundary, ViewportBoundary }, url, workStore } = ctx; const initialTree = createFlightRouterStateFromLoaderTree(tree, getDynamicParamFromSegment, query); const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; const hasGlobalNotFound = !!tree[2]['global-not-found']; const { ViewportTree, MetadataTree, getViewportReady, getMetadataReady, StreamingMetadataOutlet } = 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: createMetadataContext(ctx.renderOpts), getDynamicParamFromSegment, appUsingSizeAdjustment, workStore, MetadataBoundary, ViewportBoundary, serveStreamingMetadata }); const preloadCallbacks = []; const seedData = await createComponentTree({ ctx, loaderTree: tree, parentParams: {}, injectedCSS, injectedJS, injectedFontPreloadTags, rootLayoutIncluded: false, getViewportReady, getMetadataReady, missingSlots, preloadCallbacks, authInterrupts: ctx.renderOpts.experimental.authInterrupts, StreamingMetadataOutlet }); // 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(NEXT_URL); const initialHead = /*#__PURE__*/ _jsxs(React.Fragment, { children: [ /*#__PURE__*/ _jsx(NonIndex, { pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), /*#__PURE__*/ _jsx(ViewportTree, {}), /*#__PURE__*/ _jsx(MetadataTree, {}) ] }, flightDataPathHeadKey); 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: /*#__PURE__*/ _jsx(Preloads, { preloadCallbacks: preloadCallbacks }), b: ctx.sharedContext.buildId, p: ctx.assetPrefix, c: prepareInitialCanonicalUrl(url), i: !!couldBeIntercepted, f: [ [ initialTree, seedData, initialHead, isPossiblyPartialHead ] ], m: missingSlots, G: [ GlobalError, globalErrorStyles ], s: typeof ctx.renderOpts.postponed === 'string', 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, appUsingSizeAdjustment, componentMod: { createMetadataComponents, MetadataBoundary, ViewportBoundary }, url, workStore } = ctx; const serveStreamingMetadata = !!ctx.renderOpts.serveStreamingMetadata; const { MetadataTree, ViewportTree } = createMetadataComponents({ tree, parsedQuery: query, pathname: url.pathname, metadataContext: createMetadataContext(ctx.renderOpts), errorType, getDynamicParamFromSegment, appUsingSizeAdjustment, workStore, MetadataBoundary, ViewportBoundary, serveStreamingMetadata: serveStreamingMetadata }); const initialHead = /*#__PURE__*/ _jsxs(React.Fragment, { children: [ /*#__PURE__*/ _jsx(NonIndex, { pagePath: ctx.pagePath, statusCode: ctx.res.statusCode, isPossibleServerAction: ctx.isPossibleServerAction }), /*#__PURE__*/ _jsx(ViewportTree, {}), process.env.NODE_ENV === 'development' && /*#__PURE__*/ _jsx("meta", { name: "next-error", content: "not-found" }), /*#__PURE__*/ _jsx(MetadataTree, {}) ] }, flightDataPathHeadKey); const initialTree = createFlightRouterStateFromLoaderTree(tree, getDynamicParamFromSegment, query); let err = undefined; if (ssrError) { err = isError(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 = [ initialTree[0], /*#__PURE__*/ _jsxs("html", { id: "__next_error__", children: [ /*#__PURE__*/ _jsx("head", {}), /*#__PURE__*/ _jsx("body", { children: process.env.NODE_ENV !== 'production' && err ? /*#__PURE__*/ _jsx("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 ]; const { GlobalError, styles: globalErrorStyles } = await getGlobalErrorStyles(tree, ctx); const isPossiblyPartialHead = workStore.isStaticGeneration && ctx.renderOpts.experimental.isRoutePPREnabled === true; return { b: ctx.sharedContext.buildId, p: ctx.assetPrefix, c: prepareInitialCanonicalUrl(url), m: undefined, i: false, f: [ [ initialTree, seedData, initialHead, isPossiblyPartialHead ] ], G: [ GlobalError, globalErrorStyles ], s: typeof ctx.renderOpts.postponed === 'string', S: workStore.isStaticGeneration }; } function assertClientReferenceManifest(clientReferenceManifest) { if (!clientReferenceManifest) { throw Object.defineProperty(new 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, preinitScripts, clientReferenceManifest, ServerInsertedHTMLProvider, nonce }) { preinitScripts(); const response = React.use(useFlightStream(reactServerStream, clientReferenceManifest, nonce)); const initialState = createInitialRouterState({ // This is not used during hydration, so we don't have to pass a // real timestamp. navigatedAt: -1, initialFlightData: response.f, initialCanonicalUrlParts: response.c, initialParallelRoutes: new Map(), // location is not initialized in the SSR render // it's set to window.location during hydration location: null, couldBeIntercepted: response.i, postponed: response.s, prerendered: response.S }); const actionQueue = createMutableActionQueue(initialState, null); const { HeadManagerContext } = require('../../shared/lib/head-manager-context.shared-runtime'); return /*#__PURE__*/ _jsx(HeadManagerContext.Provider, { value: { appDir: true, nonce }, children: /*#__PURE__*/ _jsx(ServerInsertedHTMLProvider, { children: /*#__PURE__*/ _jsx(AppRouter, { actionQueue: actionQueue, globalErrorState: response.G, assetPrefix: response.p }) }) }); } // @TODO our error stream should be probably just use the same root component. But it was previously // different I don't want to figure out if that is meaningful at this time so just keeping the behavior // consistent for now. function ErrorApp({ reactServerStream, preinitScripts, clientReferenceManifest, ServerInsertedHTMLProvider, nonce }) { preinitScripts(); const response = React.use(useFlightStream(reactServerStream, clientReferenceManifest, nonce)); const initialState = createInitialRouterState({ // This is not used during hydration, so we don't have to pass a // real timestamp. navigatedAt: -1, initialFlightData: response.f, initialCanonicalUrlParts: response.c, initialParallelRoutes: new Map(), // location is not initialized in the SSR render // it's set to window.location during hydration location: null, couldBeIntercepted: response.i, postponed: response.s, prerendered: response.S }); const actionQueue = createMutableActionQueue(initialState, null); return /*#__PURE__*/ _jsx(ServerInsertedHTMLProvider, { children: /*#__PURE__*/ _jsx(AppRouter, { actionQueue: actionQueue, globalErrorState: response.G, assetPrefix: response.p }) }); } async function renderToHTMLOrFlightImpl(req, res, url, pagePath, query, renderOpts, workStore, parsedRequestHeaders, postponedState, serverComponentsHmrCache, sharedContext, fallbackRouteParams) { const isNotFoundPath = pagePath === '/404'; if (isNotFoundPath) { res.statusCode = 404; } // A unique request timestamp used by development to ensure that it's // consistent and won't change during this request. This is important to // avoid that resources can be deduped by React Float if the same resource is // rendered or preloaded multiple times: `<link href="a.css?v={Date.now()}"/>`. const requestTimestamp = Date.now(); const { clientReferenceManifest, serverActionsManifest, ComponentMod, nextFontManifest, serverActions, assetPrefix = '', enableTainting } = renderOpts; // We need to expose the bundled `require` API globally for // react-server-dom-webpack. This is a hack until we find a better way. if (ComponentMod.__next_app__) { const instrumented = wrapClientComponentLoader(ComponentMod); // When we are prerendering if there is a cacheSignal for tracking // cache reads we track calls to `loadChunk` and `require`. This allows us // to treat chunk/module loading with similar semantics as cache reads to avoid // module loading from causing a prerender to abort too early. const shouldTrackModuleLoading = ()=>{ if (!renderOpts.experimental.cacheComponents) { return false; } if (renderOpts.dev) { return true; } const workUnitStore = workUnitAsyncStorage.getStore(); if (!workUnitStore) { return false; } switch(workUnitStore.type){ case 'prerender': case 'prerender-client': case 'prerender-runtime': case 'cache': case 'private-cache': return true; case 'prerender-ppr': case 'prerender-legacy': case 'request': case 'unstable-cache': return false; default: workUnitStore; } }; const __next_require__ = (...args)=>{ const exportsOrPromise = instrumented.require(...args); if (shouldTrackModuleLoading()) { // requiring an async module returns a promise. trackPendingImport(exportsOrPromise); } return exportsOrPromise; }; // @ts-expect-error globalThis.__next_require__ = __next_require__; const __next_chunk_load__ = (...args)=>{ const loadingChunk = instrumented.loadChunk(...args); if (shouldTrackModuleLoading()) { trackPendingChunkLoad(loadingChunk); } return loadingChunk; }; // @ts-expect-error globalThis.__next_chunk_load__ = __next_chunk_load__; } if (process.env.NODE_ENV === 'development') { // reset isr status at start of request const { pathname } = new URL(req.url || '/', 'http://n'); renderOpts.setIsrStatus == null ? void 0 : renderOpts.setIsrStatus.call(renderOpts, pathname, null); } if (// The type check here ensures that `req` is correctly typed, and the // environment variable check provides dead code elimination. process.env.NEXT_RUNTIME !== 'edge' && isNodeNextRequest(req)) { res.onClose(()=>{ // We stop tracking fetch metrics when the response closes, since we // report them at that time. workStore.shouldTrackFetchMetrics = false; }); req.originalRequest.on('end', ()=>{ if ('performance' in globalThis) { const metrics = getClientComponentLoaderMetrics({ reset: true }); if (metrics) { getTracer().startSpan(NextNodeServerSpan.clientComponentLoading, { startTime: metrics.clientComponentLoadStart, attributes: { 'next.clientComponentLoadCount': metrics.clientComponentLoadCount, 'next.span_type': NextNodeServerSpan.clientComponentLoading } }).end(metrics.clientComponentLoadStart + metrics.clientComponentLoadTimes); } } }); } const metadata = { statusCode: isNotFoundPath ? 404 : undefined }; const appUsingSizeAdjustment = !!(nextFontManifest == null ? void 0 : nextFontManifest.appUsingSizeAdjust); assertClientReferenceManifest(clientReferenceManifest); const serverModuleMap = createServerModuleMap({ serverActionsManifest }); setReferenceManifestsSingleton({ page: workStore.page, clientReferenceManifest, serverActionsManifest, serverModuleMap }); ComponentMod.patchFetch(); // Pull out the hooks/references from the component. const { tree: loaderTree, taintObjectReference } = ComponentMod; if (enableTainting) { taintObjectReference('Do not pass process.env to Client Components since it will leak sensitive data', process.env); } workStore.fetchMetrics = []; metadata.fetchMetrics = workStore.fetchMetrics; // don't modify original query object query = { ...query }; stripInternalQueries(query); const { flightRouterState, isPrefetchRequest, isRuntimePrefetchRequest, isRSCRequest, isDevWarmupRequest, isHmrRefresh, nonce } = parsedRequestHeaders; const { isStaticGeneration } = workStore; /** * The metadata items array created in next-app-loader with all relevant information * that we need to resolve the final metadata. */ let requestId; if (isStaticGeneration) { requestId = Buffer.from(await crypto.subtle.digest('SHA-1', Buffer.from(req.url))).toString('hex'); } else if (process.env.NEXT_RUNTIME === 'edge') { requestId = crypto.randomUUID(); } else { requestId = require('next/dist/compiled/nanoid').nanoid(); } /** * Dynamic parameters. E.g. when you visit `/dashboard/vercel` which is rendered by `/dashboard/[slug]` the value will be {"slug": "vercel"}. */ const params = renderOpts.params ?? {}; const getDynamicParamFromSegment = makeGetDynamicParamFromSegment(params, pagePath, fallbackRouteParams); const isPossibleActionRequest = getIsPossibleServerAction(req); const implicitTags = await getImplicitTags(workStore.page, url, fallbackRouteParams); const ctx = { componentMod: ComponentMod, url, renderOpts, workStore, parsedRequestHeaders, getDynamicParamFromSegment, query, isPrefetch: isPrefetchRequest, isPossibleServerAction: isPossibleActionRequest, requestTimestamp, appUsingSizeAdjustment, flightRouterState, requestId, pagePath, clientReferenceManifest, assetPrefix, isNotFoundPath, nonce, res, sharedContext, implicitTags }; getTracer().setRootSpanAttribute('next.route', pagePath); if (isStaticGeneration) { // We're either building or revalidating. In either case we need to // prerender our page rather than render it. const prerenderToS