2022-02-19 22:13:48 +01:00
import type { IncomingMessage , ServerResponse } from 'http'
2022-04-21 11:07:03 +02:00
import type { ParsedUrlQuery } from 'querystring'
2022-02-19 22:13:48 +01:00
import type { NextRouter } from '../shared/lib/router/router'
import type { HtmlProps } from '../shared/lib/html-context'
import type { DomainLocale } from './config'
import type {
AppType ,
DocumentInitialProps ,
2022-05-02 22:52:46 +02:00
DocumentType ,
2022-02-19 22:13:48 +01:00
DocumentProps ,
DocumentContext ,
NextComponentType ,
RenderPage ,
RenderPageResult ,
} from '../shared/lib/utils'
2022-02-22 15:27:18 +01:00
import type { ImageConfigComplete } from '../shared/lib/image-config'
2022-02-19 22:13:48 +01:00
import type { Redirect } from '../lib/load-custom-routes'
import type { NextApiRequestCookies , __ApiPreviewProps } from './api-utils'
2022-09-16 23:13:21 +02:00
import type { FontManifest , FontConfig } from './font-utils'
2022-02-19 22:13:48 +01:00
import type { LoadComponentsReturnType , ManifestItem } from './load-components'
2022-08-07 21:16:10 +02:00
import type {
GetServerSideProps ,
GetStaticProps ,
PreviewData ,
ServerRuntime ,
} from 'next/types'
2022-02-19 22:13:48 +01:00
import type { UnwrapPromise } from '../lib/coalesced-function'
2022-05-11 15:25:23 +02:00
import type { ReactReadableStream } from './node-web-streams-helper'
2022-09-22 07:12:59 +02:00
import type { FontLoaderManifest } from '../build/webpack/plugins/font-loader-manifest-plugin'
2022-02-19 22:13:48 +01:00
2018-12-13 01:00:46 +01:00
import React from 'react'
2022-08-12 19:08:38 +02:00
import { StyleRegistry , createStyleRegistry } from 'styled-jsx'
2019-05-30 03:19:32 +02:00
import {
2020-06-17 11:25:27 +02:00
GSP_NO_RETURNED_VALUE ,
GSSP_COMPONENT_MEMBER_ERROR ,
GSSP_NO_RETURNED_VALUE ,
2021-02-22 17:29:50 +01:00
STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR ,
2020-02-12 02:16:42 +01:00
SERVER_PROPS_GET_INIT_PROPS_CONFLICT ,
SERVER_PROPS_SSG_CONFLICT ,
SSG_GET_INITIAL_PROPS_CONFLICT ,
2020-04-02 20:29:41 +02:00
UNSTABLE_REVALIDATE_RENAME_ERROR ,
2021-06-30 13:44:40 +02:00
} from '../lib/constants'
2020-03-06 05:15:10 +01:00
import {
2022-08-12 15:01:19 +02:00
COMPILER_NAMES ,
2022-05-02 22:52:46 +02:00
NEXT_BUILTIN_DOCUMENT ,
2020-03-06 05:15:10 +01:00
SERVER_PROPS_ID ,
2020-06-17 11:25:27 +02:00
STATIC_PROPS_ID ,
2021-02-22 17:29:50 +01:00
STATIC_STATUS_PAGES ,
2021-06-30 13:44:40 +02:00
} from '../shared/lib/constants'
2022-02-19 22:13:48 +01:00
import { isSerializableProps } from '../lib/is-serializable-props'
2022-06-02 02:21:09 +02:00
import { isInAmpMode } from '../shared/lib/amp-mode'
2022-02-19 22:13:48 +01:00
import { AmpStateContext } from '../shared/lib/amp-context'
2021-06-30 13:44:40 +02:00
import { defaultHead } from '../shared/lib/head'
import { HeadManagerContext } from '../shared/lib/head-manager-context'
import Loadable from '../shared/lib/loadable'
import { LoadableContext } from '../shared/lib/loadable-context'
import { RouterContext } from '../shared/lib/router-context'
import { isDynamicRoute } from '../shared/lib/router/utils/is-dynamic'
2020-01-27 23:50:59 +01:00
import {
2020-02-12 02:16:42 +01:00
ComponentsEnhancer ,
getDisplayName ,
isResSent ,
loadGetInitialProps ,
2021-06-30 13:44:40 +02:00
} from '../shared/lib/utils'
2022-02-17 19:21:40 +01:00
import { HtmlContext } from '../shared/lib/html-context'
2022-04-30 13:19:27 +02:00
import { normalizePagePath } from '../shared/lib/page-path/normalize-page-path'
import { denormalizePagePath } from '../shared/lib/page-path/denormalize-page-path'
2021-11-09 02:28:39 +01:00
import { getRequestMeta , NextParsedUrlQuery } from './request-meta'
2022-08-09 21:34:25 +02:00
import { allowedStatusCodes , getRedirectStatus } from '../lib/redirect-status'
2022-02-05 02:13:02 +01:00
import RenderResult from './render-result'
2021-09-16 18:06:57 +02:00
import isError from '../lib/is-error'
2022-03-18 00:21:16 +01:00
import {
readableStreamTee ,
streamFromArray ,
streamToString ,
chainStreams ,
createBufferedTransformStream ,
2022-03-30 15:15:13 +02:00
renderToInitialStream ,
continueFromInitialStream ,
2022-03-18 00:21:16 +01:00
} from './node-web-streams-helper'
2022-02-09 01:55:53 +01:00
import { ImageConfigContext } from '../shared/lib/image-config-context'
2022-04-20 14:03:48 +02:00
import stripAnsi from 'next/dist/compiled/strip-ansi'
2022-09-15 16:53:51 +02:00
import { shouldUseReactRoot } from './utils'
import { stripInternalQueries } from './internal-utils'
2019-06-29 02:48:28 +02:00
2022-02-11 20:56:25 +01:00
let tryGetPreviewData : typeof import ( './api-utils/node' ) . tryGetPreviewData
2021-10-26 18:50:56 +02:00
let warn : typeof import ( '../build/output/log' ) . warn
2022-07-28 00:11:02 +02:00
let postProcessHTML : typeof import ( './post-process' ) . postProcessHTML
2021-10-26 18:50:56 +02:00
2021-11-17 15:57:48 +01:00
const DOCTYPE = '<!DOCTYPE html>'
2022-06-23 18:33:16 +02:00
const ReactDOMServer = shouldUseReactRoot
? require ( 'react-dom/server.browser' )
: require ( 'react-dom/server' )
2021-11-17 15:57:48 +01:00
2022-04-26 19:54:28 +02:00
if ( process . env . NEXT_RUNTIME !== 'edge' ) {
2022-02-05 02:13:02 +01:00
require ( './node-polyfill-web-streams' )
2022-02-11 20:56:25 +01:00
tryGetPreviewData = require ( './api-utils/node' ) . tryGetPreviewData
2021-10-26 18:50:56 +02:00
warn = require ( '../build/output/log' ) . warn
2022-07-28 00:11:02 +02:00
postProcessHTML = require ( './post-process' ) . postProcessHTML
2021-10-26 18:50:56 +02:00
} else {
warn = console . warn . bind ( console )
2022-07-28 00:11:02 +02:00
postProcessHTML = async ( _pathname : string , html : string ) = > html
2021-10-26 18:50:56 +02:00
}
2019-03-07 17:13:38 +01:00
function noRouter() {
2019-05-30 03:19:32 +02:00
const message =
2021-03-29 10:25:00 +02:00
'No router instance found. you should only use "next/router" inside the client side of your app. https://nextjs.org/docs/messages/no-router-instance'
2019-03-07 17:13:38 +01:00
throw new Error ( message )
}
2022-09-29 10:56:28 +02:00
async function renderToString ( element : React.ReactElement ) {
if ( ! shouldUseReactRoot ) return ReactDOMServer . renderToString ( element )
const renderStream = await ReactDOMServer . renderToReadableStream ( element )
await renderStream . allReady
return streamToString ( renderStream )
}
async function renderToStaticMarkup ( element : React.ReactElement ) {
if ( ! shouldUseReactRoot ) return ReactDOMServer . renderToStaticMarkup ( element )
return renderToString ( element )
}
2019-07-11 19:35:39 +02:00
class ServerRouter implements NextRouter {
2019-03-13 15:56:20 +01:00
route : string
pathname : string
2019-05-20 17:21:15 +02:00
query : ParsedUrlQuery
2019-03-13 15:56:20 +01:00
asPath : string
2020-04-14 09:50:39 +02:00
basePath : string
2019-07-11 19:35:39 +02:00
events : any
2020-02-15 19:01:10 +01:00
isFallback : boolean
2020-10-07 23:11:01 +02:00
locale? : string
2020-12-31 17:54:32 +01:00
isReady : boolean
2020-10-07 23:11:01 +02:00
locales? : string [ ]
2020-10-08 13:12:17 +02:00
defaultLocale? : string
2021-07-21 18:12:33 +02:00
domainLocales? : DomainLocale [ ]
2021-02-18 19:34:33 +01:00
isPreview : boolean
2021-02-11 11:18:24 +01:00
isLocaleDomain : boolean
2019-03-13 15:56:20 +01:00
2020-02-15 19:01:10 +01:00
constructor (
pathname : string ,
query : ParsedUrlQuery ,
as : string ,
2020-04-14 09:50:39 +02:00
{ isFallback } : { isFallback : boolean } ,
2020-12-31 17:54:32 +01:00
isReady : boolean ,
2020-10-07 23:11:01 +02:00
basePath : string ,
2021-02-18 19:34:33 +01:00
locale? : string ,
locales? : string [ ] ,
defaultLocale? : string ,
2021-07-21 18:12:33 +02:00
domainLocales? : DomainLocale [ ] ,
2021-02-18 19:34:33 +01:00
isPreview? : boolean ,
isLocaleDomain? : boolean
2020-02-15 19:01:10 +01:00
) {
2019-03-23 23:00:46 +01:00
this . route = pathname . replace ( /\/$/ , '' ) || '/'
2019-03-13 15:56:20 +01:00
this . pathname = pathname
this . query = query
this . asPath = as
2020-02-15 19:01:10 +01:00
this . isFallback = isFallback
2020-04-14 09:50:39 +02:00
this . basePath = basePath
2020-10-07 23:11:01 +02:00
this . locale = locale
this . locales = locales
2020-10-08 13:12:17 +02:00
this . defaultLocale = defaultLocale
2020-12-31 17:54:32 +01:00
this . isReady = isReady
2020-12-31 09:07:51 +01:00
this . domainLocales = domainLocales
2021-02-18 19:34:33 +01:00
this . isPreview = ! ! isPreview
this . isLocaleDomain = ! ! isLocaleDomain
2019-03-13 15:56:20 +01:00
}
2020-12-31 17:54:32 +01:00
2019-07-11 19:35:39 +02:00
push ( ) : any {
2019-03-07 17:13:38 +01:00
noRouter ( )
}
2019-07-11 19:35:39 +02:00
replace ( ) : any {
2019-03-07 17:13:38 +01:00
noRouter ( )
}
reload() {
noRouter ( )
}
back() {
noRouter ( )
}
2019-07-11 19:35:39 +02:00
prefetch ( ) : any {
2019-03-07 17:13:38 +01:00
noRouter ( )
}
beforePopState() {
noRouter ( )
}
}
2019-02-14 16:22:57 +01:00
function enhanceComponents (
options : ComponentsEnhancer ,
2019-04-22 19:55:03 +02:00
App : AppType ,
2019-05-30 03:19:32 +02:00
Component : NextComponentType
2019-02-14 16:22:57 +01:00
) : {
2019-05-30 03:19:32 +02:00
App : AppType
Component : NextComponentType
2018-12-13 01:00:46 +01:00
} {
// For backwards compatibility
2019-02-08 11:57:29 +01:00
if ( typeof options === 'function' ) {
2018-12-13 01:00:46 +01:00
return {
2019-02-08 11:57:29 +01:00
App ,
Component : options ( Component ) ,
2018-12-13 01:00:46 +01:00
}
}
return {
App : options.enhanceApp ? options . enhanceApp ( App ) : App ,
2019-02-14 16:22:57 +01:00
Component : options.enhanceComponent
? options . enhanceComponent ( Component )
: Component ,
2018-12-13 01:00:46 +01:00
}
}
2022-04-22 23:03:57 +02:00
function renderPageTree (
2022-05-20 20:07:20 +02:00
App : AppType ,
Component : NextComponentType ,
props : any
2022-04-22 23:03:57 +02:00
) {
2022-04-16 03:55:16 +02:00
return < App Component = { Component } { ...props } / >
2022-01-14 14:01:00 +01:00
}
2020-03-02 11:58:47 +01:00
export type RenderOptsPartial = {
2019-02-14 16:22:57 +01:00
buildId : string
2019-05-29 02:32:18 +02:00
canonicalBase : string
2019-02-14 16:22:57 +01:00
runtimeConfig ? : { [ key : string ] : any }
assetPrefix? : string
err? : Error | null
nextExport? : boolean
dev? : boolean
2019-06-27 16:22:24 +02:00
ampPath? : string
2019-05-30 03:19:32 +02:00
ErrorDebug? : React.ComponentType < { error : Error } >
ampValidator ? : ( html : string , pathname : string ) = > Promise < void >
2020-03-24 09:31:04 +01:00
ampSkipValidation? : boolean
ampOptimizerConfig ? : { [ key : string ] : any }
2020-01-27 23:50:59 +01:00
isDataReq? : boolean
params? : ParsedUrlQuery
2020-02-12 02:16:42 +01:00
previewProps : __ApiPreviewProps
2020-04-14 09:50:39 +02:00
basePath : string
2020-04-17 11:22:03 +02:00
unstable_runtimeJS? : false
2021-01-19 20:38:15 +01:00
unstable_JsPreload? : false
2022-09-16 23:13:21 +02:00
optimizeFonts : FontConfig
2020-07-28 12:19:28 +02:00
fontManifest? : FontManifest
2020-12-01 19:02:07 +01:00
optimizeCss : any
2022-03-11 23:26:46 +01:00
nextScriptWorkers : any
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString? : string
2020-09-14 23:01:04 +02:00
resolvedUrl? : string
2020-09-15 21:19:07 +02:00
resolvedAsPath? : string
2021-11-15 16:29:34 +01:00
serverComponentManifest? : any
2022-08-12 15:01:19 +02:00
serverCSSManifest? : any
2022-09-22 07:12:59 +02:00
fontLoaderManifest? : FontLoaderManifest
2020-12-01 19:02:07 +01:00
distDir? : string
2020-10-07 23:11:01 +02:00
locale? : string
locales? : string [ ]
2020-10-08 13:12:17 +02:00
defaultLocale? : string
2021-07-21 18:12:33 +02:00
domainLocales? : DomainLocale [ ]
2021-05-13 12:39:36 +02:00
disableOptimizedLoading? : boolean
2021-09-06 12:23:07 +02:00
supportsDynamicHTML? : boolean
2022-06-27 03:02:24 +02:00
runtime? : ServerRuntime
2022-01-14 14:01:00 +01:00
serverComponents? : boolean
2021-08-26 17:05:01 +02:00
customServer? : boolean
2021-11-30 18:14:28 +01:00
crossOrigin? : string
2022-02-09 01:55:53 +01:00
images : ImageConfigComplete
2022-05-29 03:39:48 +02:00
largePageDataBytes? : number
2018-12-13 01:00:46 +01:00
}
2020-03-02 11:58:47 +01:00
export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
2022-04-06 17:25:27 +02:00
const invalidKeysMsg = (
methodName : 'getServerSideProps' | 'getStaticProps' ,
invalidKeys : string [ ]
) = > {
const docsPathname = ` invalid- ${ methodName . toLocaleLowerCase ( ) } -value `
2020-01-27 23:50:59 +01:00
return (
` Additional keys were returned from \` ${ methodName } \` . Properties intended for your component must be nested under the \` props \` key, e.g.: ` +
` \ n \ n \ treturn { props: { title: 'My Title', content: '...' } } ` +
2020-02-21 06:45:50 +01:00
` \ n \ nKeys that need to be moved: ${ invalidKeys . join ( ', ' ) } . ` +
2022-04-06 17:25:27 +02:00
` \ nRead more: https://nextjs.org/docs/messages/ ${ docsPathname } `
2020-01-27 23:50:59 +01:00
)
}
2020-11-02 22:47:59 +01:00
function checkRedirectValues (
redirect : Redirect ,
req : IncomingMessage ,
method : 'getStaticProps' | 'getServerSideProps'
) {
2020-11-11 08:13:18 +01:00
const { destination , permanent , statusCode , basePath } = redirect
2020-11-02 22:47:59 +01:00
let errors : string [ ] = [ ]
const hasStatusCode = typeof statusCode !== 'undefined'
const hasPermanent = typeof permanent !== 'undefined'
if ( hasPermanent && hasStatusCode ) {
errors . push ( ` \` permanent \` and \` statusCode \` can not both be provided ` )
} else if ( hasPermanent && typeof permanent !== 'boolean' ) {
errors . push ( ` \` permanent \` must be \` true \` or \` false \` ` )
} else if ( hasStatusCode && ! allowedStatusCodes . has ( statusCode ! ) ) {
errors . push (
` \` statusCode \` must undefined or one of ${ [ . . . allowedStatusCodes ] . join (
', '
) } `
)
}
const destinationType = typeof destination
2020-09-08 09:23:21 +02:00
2020-11-02 22:47:59 +01:00
if ( destinationType !== 'string' ) {
errors . push (
` \` destination \` should be string but received ${ destinationType } `
)
}
2020-11-11 08:13:18 +01:00
const basePathType = typeof basePath
if ( basePathType !== 'undefined' && basePathType !== 'boolean' ) {
errors . push (
` \` basePath \` should be undefined or a false, received ${ basePathType } `
)
}
2020-11-02 22:47:59 +01:00
if ( errors . length > 0 ) {
2020-09-08 09:23:21 +02:00
throw new Error (
2020-11-02 22:47:59 +01:00
` Invalid redirect object returned from ${ method } for ${ req . url } \ n ` +
errors . join ( ' and ' ) +
'\n' +
2021-03-29 10:25:00 +02:00
` See more info here: https://nextjs.org/docs/messages/invalid-redirect-gssp `
2020-09-08 09:23:21 +02:00
)
}
}
2022-08-15 16:29:51 +02:00
function errorToJSON ( err : Error ) {
let source : typeof COMPILER_NAMES . server | typeof COMPILER_NAMES . edgeServer =
'server'
if ( process . env . NEXT_RUNTIME !== 'edge' ) {
source =
require ( 'next/dist/compiled/@next/react-dev-overlay/dist/middleware' ) . getErrorSource (
err
) || 'server'
}
return {
name : err.name ,
source ,
message : stripAnsi ( err . message ) ,
stack : err.stack ,
}
}
function serializeError (
dev : boolean | undefined ,
err : Error
) : Error & {
statusCode? : number
source? : typeof COMPILER_NAMES . server | typeof COMPILER_NAMES . edgeServer
} {
if ( dev ) {
return errorToJSON ( err )
}
return {
name : 'Internal Server Error.' ,
message : '500 - Internal Server Error.' ,
statusCode : 500 ,
}
}
2019-02-14 16:22:57 +01:00
export async function renderToHTML (
req : IncomingMessage ,
res : ServerResponse ,
pathname : string ,
2021-11-09 02:28:39 +01:00
query : NextParsedUrlQuery ,
2019-05-30 03:19:32 +02:00
renderOpts : RenderOpts
Add `RenderResult` (#27319)
Adds `RenderResult`, replacing the `string` that `renderToHTML` used to return, with an `Observable`-like API that callers can use to subscribe and get a callback when chunks are available to flush, etc.
This is the last architectural change needed for streaming. There are, however, other things currently standing in the way of streaming. For example, it is common to mutate `res` in `getServerSideProps` to do routing work, or write headers, before fetching page data. This pattern effectively nullifies any advantages of streaming. I may do a follow-up PR that adds an experimental alternative for applications not using React 18, but the main purpose for this support is for Suspense and Server Components.
For that reason, there's no actual streaming here yet: instead we just flush a single chunk. A follow-up PR will add support for streaming suspense boundaries in React 18.
2021-07-27 21:18:21 +02:00
) : Promise < RenderResult | null > {
2020-08-03 16:22:55 +02:00
// In dev we invalidate the cache by appending a timestamp to the resource URL.
// This is a workaround to fix https://github.com/vercel/next.js/issues/5860
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
renderOpts . devOnlyCacheBusterQueryString = renderOpts . dev
? renderOpts . devOnlyCacheBusterQueryString || ` ?ts= ${ Date . now ( ) } `
: ''
2020-11-14 08:12:47 +01:00
// don't modify original query object
query = Object . assign ( { } , query )
2018-12-13 01:00:46 +01:00
const {
err ,
dev = false ,
2019-03-20 04:53:47 +01:00
ampPath = '' ,
2019-07-16 02:06:16 +02:00
pageConfig = { } ,
2018-12-18 17:12:49 +01:00
buildManifest ,
reactLoadableManifest ,
2019-02-08 11:57:29 +01:00
ErrorDebug ,
2020-02-27 18:57:39 +01:00
getStaticProps ,
getStaticPaths ,
getServerSideProps ,
2020-01-27 23:50:59 +01:00
isDataReq ,
params ,
2020-02-12 02:16:42 +01:00
previewProps ,
2020-04-14 09:50:39 +02:00
basePath ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2021-09-06 12:23:07 +02:00
supportsDynamicHTML ,
2022-02-09 01:55:53 +01:00
images ,
2022-03-26 00:05:35 +01:00
runtime : globalRuntime ,
2022-05-20 20:07:20 +02:00
App ,
2018-12-13 01:00:46 +01:00
} = renderOpts
2018-12-18 17:12:49 +01:00
2022-03-11 21:38:09 +01:00
let Document = renderOpts . Document
2022-02-08 14:16:46 +01:00
2022-04-22 23:03:57 +02:00
// Component will be wrapped by ServerComponentWrapper for RSC
2022-02-08 14:16:46 +01:00
let Component : React.ComponentType < { } > | ( ( props : any ) = > JSX . Element ) =
renderOpts . Component
2022-04-22 23:03:57 +02:00
const OriginComponent = Component
2022-04-05 21:46:17 +02:00
2022-02-18 01:18:28 +01:00
let serverComponentsInlinedTransformStream : TransformStream <
2022-02-28 22:57:08 +01:00
Uint8Array ,
Uint8Array
2022-02-18 01:18:28 +01:00
> | null = null
2022-05-18 13:18:28 +02:00
2022-06-10 22:08:24 +02:00
const isFallback = ! ! query . __nextFallback
const notFoundSrcPage = query . __nextNotFoundSrcPage
2022-07-28 00:11:02 +02:00
// next internal queries should be stripped out
2022-06-10 22:08:24 +02:00
stripInternalQueries ( query )
2019-11-01 20:13:13 +01:00
const callMiddleware = async ( method : string , args : any [ ] , props = false ) = > {
let results : any = props ? { } : [ ]
if ( ( Document as any ) [ ` ${ method } Middleware ` ] ) {
2019-11-15 08:33:52 +01:00
let middlewareFunc = await ( Document as any ) [ ` ${ method } Middleware ` ]
middlewareFunc = middlewareFunc . default || middlewareFunc
const curResults = await middlewareFunc ( . . . args )
2019-11-01 20:13:13 +01:00
if ( props ) {
for ( const result of curResults ) {
results = {
. . . results ,
. . . result ,
}
}
} else {
results = curResults
}
}
return results
}
const headTags = ( . . . args : any ) = > callMiddleware ( 'headTags' , args )
2020-03-04 13:58:12 +01:00
const isSSG = ! ! getStaticProps
2020-03-09 18:30:44 +01:00
const isBuildTimeSSG = isSSG && renderOpts . nextExport
2019-09-15 20:35:14 +02:00
const defaultAppGetInitialProps =
App . getInitialProps === ( App as any ) . origGetInitialProps
2022-02-18 16:25:10 +01:00
const hasPageGetInitialProps = ! ! ( Component as any ) ? . getInitialProps
2022-04-21 23:15:53 +02:00
const hasPageScripts = ( Component as any ) ? . unstable_scriptLoader
2019-09-15 20:35:14 +02:00
2020-02-21 06:57:10 +01:00
const pageIsDynamic = isDynamicRoute ( pathname )
2022-06-13 04:34:23 +02:00
const defaultErrorGetInitialProps =
pathname === '/_error' &&
( Component as any ) . getInitialProps ===
( Component as any ) . origGetInitialProps
if (
renderOpts . nextExport &&
hasPageGetInitialProps &&
! defaultErrorGetInitialProps
) {
warn (
` Detected getInitialProps on page ' ${ pathname } ' ` +
2022-10-04 22:34:35 +02:00
` while running "next export". It's recommended to use getStaticProps ` +
` which has a more correct behavior for static exporting. ` +
2022-06-13 04:34:23 +02:00
` \ nRead more: https://nextjs.org/docs/messages/get-initial-props-export `
)
}
2019-09-24 10:50:04 +02:00
const isAutoExport =
2020-01-27 23:50:59 +01:00
! hasPageGetInitialProps &&
defaultAppGetInitialProps &&
2020-03-04 13:58:12 +01:00
! isSSG &&
2022-07-28 00:11:02 +02:00
! getServerSideProps
2019-09-24 10:50:04 +02:00
2020-03-04 13:58:12 +01:00
if ( hasPageGetInitialProps && isSSG ) {
2020-01-15 02:22:15 +01:00
throw new Error ( SSG_GET_INITIAL_PROPS_CONFLICT + ` ${ pathname } ` )
2019-09-24 10:50:04 +02:00
}
2018-12-13 01:00:46 +01:00
2020-02-27 18:57:39 +01:00
if ( hasPageGetInitialProps && getServerSideProps ) {
2020-01-27 23:50:59 +01:00
throw new Error ( SERVER_PROPS_GET_INIT_PROPS_CONFLICT + ` ${ pathname } ` )
}
2020-03-04 13:58:12 +01:00
if ( getServerSideProps && isSSG ) {
2020-01-27 23:50:59 +01:00
throw new Error ( SERVER_PROPS_SSG_CONFLICT + ` ${ pathname } ` )
}
2021-04-18 11:31:40 +02:00
if ( getStaticPaths && ! pageIsDynamic ) {
throw new Error (
` getStaticPaths is only allowed for dynamic SSG pages and was found on ' ${ pathname } '. ` +
` \ nRead more: https://nextjs.org/docs/messages/non-dynamic-getstaticpaths-usage `
)
}
2020-03-04 13:58:12 +01:00
if ( ! ! getStaticPaths && ! isSSG ) {
2019-10-28 18:24:29 +01:00
throw new Error (
2020-02-27 18:57:39 +01:00
` getStaticPaths was added without a getStaticProps in ${ pathname } . Without getStaticProps, getStaticPaths does nothing `
2019-10-28 18:24:29 +01:00
)
}
2020-03-04 13:58:12 +01:00
if ( isSSG && pageIsDynamic && ! getStaticPaths ) {
2020-02-21 06:57:10 +01:00
throw new Error (
2020-02-27 18:57:39 +01:00
` getStaticPaths is required for dynamic SSG pages and is missing for ' ${ pathname } '. ` +
2021-03-29 10:25:00 +02:00
` \ nRead more: https://nextjs.org/docs/messages/invalid-getstaticpaths-value `
2020-02-21 06:57:10 +01:00
)
}
2021-04-05 20:51:21 +02:00
let asPath : string = renderOpts . resolvedAsPath || ( req . url as string )
2018-12-17 16:09:23 +01:00
if ( dev ) {
2022-01-17 16:17:22 +01:00
const { isValidElementType } = require ( 'next/dist/compiled/react-is' )
2018-12-17 16:09:23 +01:00
if ( ! isValidElementType ( Component ) ) {
2019-02-14 16:22:57 +01:00
throw new Error (
2019-05-30 03:19:32 +02:00
` The default export is not a React Component in page: " ${ pathname } " `
2019-02-14 16:22:57 +01:00
)
2018-12-17 16:09:23 +01:00
}
2018-12-17 17:42:40 +01:00
if ( ! isValidElementType ( App ) ) {
2019-02-14 16:22:57 +01:00
throw new Error (
2019-05-30 03:19:32 +02:00
` The default export is not a React Component in page: "/_app" `
2019-02-14 16:22:57 +01:00
)
2018-12-17 17:42:40 +01:00
}
if ( ! isValidElementType ( Document ) ) {
2019-02-14 16:22:57 +01:00
throw new Error (
2019-05-30 03:19:32 +02:00
` The default export is not a React Component in page: "/_document" `
2019-02-14 16:22:57 +01:00
)
2018-12-17 17:42:40 +01:00
}
2019-05-22 18:36:53 +02:00
2020-08-23 14:35:30 +02:00
if ( isAutoExport || isFallback ) {
2019-06-28 22:01:11 +02:00
// remove query values except ones that will be set during export
query = {
2020-07-10 05:16:02 +02:00
. . . ( query . amp
? {
amp : query.amp ,
}
: { } ) ,
2019-05-22 18:36:53 +02:00
}
2021-04-05 20:51:21 +02:00
asPath = ` ${ pathname } ${
2020-11-20 19:45:47 +01:00
// ensure trailing slash is present for non-dynamic auto-export pages
req . url ! . endsWith ( '/' ) && pathname !== '/' && ! pageIsDynamic ? '/' : ''
} `
2019-08-16 18:53:47 +02:00
req . url = pathname
2019-05-22 18:36:53 +02:00
}
2020-02-01 15:47:42 +01:00
2020-03-12 10:48:14 +01:00
if ( pathname === '/404' && ( hasPageGetInitialProps || getServerSideProps ) ) {
2021-02-22 17:29:50 +01:00
throw new Error (
` \` pages/404 \` ${ STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR } `
)
}
if (
STATIC_STATUS_PAGES . includes ( pathname ) &&
( hasPageGetInitialProps || getServerSideProps )
) {
throw new Error (
` \` pages ${ pathname } \` ${ STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR } `
)
2020-02-01 15:47:42 +01:00
}
2018-12-17 17:42:40 +01:00
}
2018-12-13 01:00:46 +01:00
2022-05-22 23:50:21 +02:00
for ( const methodName of [
'getStaticProps' ,
'getServerSideProps' ,
'getStaticPaths' ,
] ) {
if ( ( Component as any ) ? . [ methodName ] ) {
throw new Error (
` page ${ pathname } ${ methodName } ${ GSSP_COMPONENT_MEMBER_ERROR } `
)
}
}
2019-09-24 10:50:04 +02:00
await Loadable . preloadAll ( ) // Make sure all dynamic imports are loaded
2021-02-18 19:34:33 +01:00
let isPreview
2021-04-20 20:13:48 +02:00
let previewData : PreviewData
2021-02-18 19:34:33 +01:00
2022-04-26 19:54:28 +02:00
if (
( isSSG || getServerSideProps ) &&
! isFallback &&
process . env . NEXT_RUNTIME !== 'edge'
) {
2021-02-18 19:34:33 +01:00
// 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.
previewData = tryGetPreviewData ( req , res , previewProps )
isPreview = previewData !== false
}
2020-02-17 22:16:19 +01:00
// url will always be set
2021-07-26 22:25:55 +02:00
const routerIsReady = ! ! (
getServerSideProps ||
hasPageGetInitialProps ||
( ! defaultAppGetInitialProps && ! isSSG )
)
2020-04-14 09:50:39 +02:00
const router = new ServerRouter (
pathname ,
query ,
asPath ,
{
isFallback : isFallback ,
} ,
2020-12-31 17:54:32 +01:00
routerIsReady ,
2020-10-07 23:11:01 +02:00
basePath ,
renderOpts . locale ,
2020-10-08 13:12:17 +02:00
renderOpts . locales ,
2020-12-31 09:07:51 +01:00
renderOpts . defaultLocale ,
2021-02-11 11:18:24 +01:00
renderOpts . domainLocales ,
2021-02-18 19:34:33 +01:00
isPreview ,
2021-11-09 02:28:39 +01:00
getRequestMeta ( req , '__nextIsLocaleDomain' )
2020-04-14 09:50:39 +02:00
)
2019-03-17 17:43:03 +01:00
2022-08-11 23:32:52 +02:00
let scriptLoader : any = { }
const jsxStyleRegistry = createStyleRegistry ( )
2019-07-03 04:16:12 +02:00
const ampState = {
ampFirst : pageConfig.amp === true ,
hasQuery : Boolean ( query . amp ) ,
hybrid : pageConfig.amp === 'hybrid' ,
}
2021-10-26 18:50:56 +02:00
// Disable AMP under the web environment
2022-04-26 19:54:28 +02:00
const inAmpMode = process . env . NEXT_RUNTIME !== 'edge' && isInAmpMode ( ampState )
2020-06-12 00:09:06 +02:00
let head : JSX.Element [ ] = defaultHead ( inAmpMode )
2022-08-11 23:32:52 +02:00
const reactLoadableModules : string [ ] = [ ]
2020-06-12 00:09:06 +02:00
2022-04-21 23:15:53 +02:00
let initialScripts : any = { }
if ( hasPageScripts ) {
initialScripts . beforeInteractive = [ ]
. concat ( hasPageScripts ( ) )
. filter ( ( script : any ) = > script . props . strategy === 'beforeInteractive' )
. map ( ( script : any ) = > script . props )
}
2021-08-26 17:05:01 +02:00
const AppContainer = ( { children } : { children : JSX.Element } ) = > (
2022-05-14 23:20:24 +02:00
< RouterContext.Provider value = { router } >
< AmpStateContext.Provider value = { ampState } >
< HeadManagerContext.Provider
value = { {
updateHead : ( state ) = > {
head = state
} ,
updateScripts : ( scripts ) = > {
scriptLoader = scripts
} ,
scripts : initialScripts ,
mountedInstances : new Set ( ) ,
} }
>
< LoadableContext.Provider
value = { ( moduleName ) = > reactLoadableModules . push ( moduleName ) }
2020-06-12 00:09:06 +02:00
>
2022-05-14 23:20:24 +02:00
< StyleRegistry registry = { jsxStyleRegistry } >
< ImageConfigContext.Provider value = { images } >
{ children }
< / ImageConfigContext.Provider >
< / StyleRegistry >
< / LoadableContext.Provider >
< / HeadManagerContext.Provider >
< / AmpStateContext.Provider >
< / RouterContext.Provider >
2019-07-03 04:16:12 +02:00
)
2021-11-09 00:32:06 +01:00
// The `useId` API uses the path indexes to generate an ID for each node.
// To guarantee the match of hydration, we need to ensure that the structure
// of wrapper nodes is isomorphic in server and client.
// TODO: With `enhanceApp` and `enhanceComponents` options, this approach may
// not be useful.
// https://github.com/facebook/react/pull/22644
const Noop = ( ) = > null
2022-01-14 14:01:00 +01:00
const AppContainerWithIsomorphicFiberStructure : React.FC < {
2021-11-09 00:32:06 +01:00
children : JSX.Element
2022-01-14 14:01:00 +01:00
} > = ( { children } ) = > {
2021-11-09 00:32:06 +01:00
return (
< >
{ /* <Head/> */ }
< Noop / >
< AppContainer >
< >
{ /* <ReactDevOverlay/> */ }
{ dev ? (
< >
{ children }
< Noop / >
< / >
) : (
children
) }
{ /* <RouteAnnouncer/> */ }
< Noop / >
< / >
< / AppContainer >
< / >
)
}
2022-08-11 23:32:52 +02:00
const ctx = {
err ,
req : isAutoExport ? undefined : req ,
res : isAutoExport ? undefined : res ,
pathname ,
query ,
asPath ,
locale : renderOpts.locale ,
locales : renderOpts.locales ,
defaultLocale : renderOpts.defaultLocale ,
AppTree : ( props : any ) = > {
return (
< AppContainerWithIsomorphicFiberStructure >
{ renderPageTree ( App , OriginComponent , { . . . props , router } ) }
< / AppContainerWithIsomorphicFiberStructure >
)
} ,
defaultGetInitialProps : async (
docCtx : DocumentContext ,
options : { nonce? : string } = { }
) : Promise < DocumentInitialProps > = > {
const enhanceApp = ( AppComp : any ) = > {
return ( props : any ) = > < AppComp { ...props } / >
}
const { html , head : renderPageHead } = await docCtx . renderPage ( {
enhanceApp ,
} )
const styles = jsxStyleRegistry . styles ( { nonce : options.nonce } )
jsxStyleRegistry . flush ( )
return { html , head : renderPageHead , styles }
} ,
}
let props : any
const nextExport =
! isSSG && ( renderOpts . nextExport || ( dev && ( isAutoExport || isFallback ) ) )
2022-10-03 15:43:35 +02:00
const styledJsxInsertedHTML = ( ) = > {
2022-08-11 23:32:52 +02:00
const styles = jsxStyleRegistry . styles ( )
jsxStyleRegistry . flush ( )
return < > { styles } < / >
}
2021-09-11 02:17:56 +02:00
props = await loadGetInitialProps ( App , {
AppTree : ctx.AppTree ,
Component ,
router ,
ctx ,
} )
if ( ( isSSG || getServerSideProps ) && isPreview ) {
props . __N_PREVIEW = true
}
2020-03-06 05:15:10 +01:00
2021-09-11 02:17:56 +02:00
if ( isSSG ) {
props [ STATIC_PROPS_ID ] = true
}
if ( isSSG && ! isFallback ) {
let data : UnwrapPromise < ReturnType < GetStaticProps > >
try {
data = await getStaticProps ! ( {
. . . ( pageIsDynamic ? { params : query as ParsedUrlQuery } : undefined ) ,
. . . ( isPreview
? { preview : true , previewData : previewData }
: undefined ) ,
locales : renderOpts.locales ,
locale : renderOpts.locale ,
defaultLocale : renderOpts.defaultLocale ,
} )
2021-09-16 18:06:57 +02:00
} catch ( staticPropsError : any ) {
2021-09-11 02:17:56 +02:00
// remove not found error code to prevent triggering legacy
// 404 rendering
2021-09-16 18:06:57 +02:00
if ( staticPropsError && staticPropsError . code === 'ENOENT' ) {
2021-09-11 02:17:56 +02:00
delete staticPropsError . code
}
throw staticPropsError
2020-03-06 05:15:10 +01:00
}
2021-09-11 02:17:56 +02:00
if ( data == null ) {
throw new Error ( GSP_NO_RETURNED_VALUE )
2020-03-04 13:58:12 +01:00
}
2021-09-11 02:17:56 +02:00
const invalidKeys = Object . keys ( data ) . filter (
( key ) = >
key !== 'revalidate' &&
key !== 'props' &&
key !== 'redirect' &&
key !== 'notFound'
)
2019-09-25 17:29:22 +02:00
2021-09-11 02:17:56 +02:00
if ( invalidKeys . includes ( 'unstable_revalidate' ) ) {
throw new Error ( UNSTABLE_REVALIDATE_RENAME_ERROR )
}
2020-06-17 11:25:27 +02:00
2021-09-11 02:17:56 +02:00
if ( invalidKeys . length ) {
throw new Error ( invalidKeysMsg ( 'getStaticProps' , invalidKeys ) )
}
2019-09-25 17:29:22 +02:00
2021-09-11 02:17:56 +02:00
if ( process . env . NODE_ENV !== 'production' ) {
if (
typeof ( data as any ) . notFound !== 'undefined' &&
typeof ( data as any ) . redirect !== 'undefined'
) {
throw new Error (
` \` redirect \` and \` notFound \` can not both be returned from ${
isSSG ? 'getStaticProps' : 'getServerSideProps'
} at the same time . Page : $ { pathname } \ nSee more info here : https : //nextjs.org/docs/messages/gssp-mixed-not-found-redirect`
)
2020-04-02 20:29:41 +02:00
}
2021-09-11 02:17:56 +02:00
}
2020-04-02 20:29:41 +02:00
2021-09-11 02:17:56 +02:00
if ( 'notFound' in data && data . notFound ) {
if ( pathname === '/404' ) {
throw new Error (
` The /404 page can not return notFound in "getStaticProps", please remove it to continue! `
)
2019-09-25 17:29:22 +02:00
}
2021-09-11 02:17:56 +02:00
; ( renderOpts as any ) . isNotFound = true
}
2020-11-04 23:18:44 +01:00
2021-09-11 02:17:56 +02:00
if (
'redirect' in data &&
data . redirect &&
typeof data . redirect === 'object'
) {
checkRedirectValues ( data . redirect as Redirect , req , 'getStaticProps' )
2020-10-24 21:22:48 +02:00
2021-09-11 02:17:56 +02:00
if ( isBuildTimeSSG ) {
throw new Error (
` \` redirect \` can not be returned from getStaticProps during prerendering ( ${ req . url } ) \ n ` +
` See more info here: https://nextjs.org/docs/messages/gsp-redirect-during-prerender `
)
2020-10-15 23:55:38 +02:00
}
2021-09-11 02:17:56 +02:00
; ( data as any ) . props = {
__N_REDIRECT : data.redirect.destination ,
__N_REDIRECT_STATUS : getRedirectStatus ( data . redirect ) ,
}
if ( typeof data . redirect . basePath !== 'undefined' ) {
; ( data as any ) . props . __N_REDIRECT_BASE_PATH = data . redirect . basePath
}
; ( renderOpts as any ) . isRedirect = true
}
2020-09-08 09:23:21 +02:00
2021-09-11 02:17:56 +02:00
if (
( dev || isBuildTimeSSG ) &&
! ( renderOpts as any ) . isNotFound &&
! isSerializableProps ( pathname , 'getStaticProps' , ( data as any ) . props )
) {
// this fn should throw an error instead of ever returning `false`
throw new Error (
'invariant: getStaticProps did not return valid props. Please report this.'
)
}
if ( 'revalidate' in data ) {
if ( typeof data . revalidate === 'number' ) {
if ( ! Number . isInteger ( data . revalidate ) ) {
2020-09-08 09:23:21 +02:00
throw new Error (
2021-09-11 02:17:56 +02:00
` A page's revalidate option must be seconds expressed as a natural number for ${ req . url } . Mixed numbers, such as ' ${ data . revalidate } ', cannot be used. ` +
` \ nTry changing the value to ' ${ Math . ceil (
data . revalidate
) } ' or using \`Math.ceil()\` if you' re computing the value . `
)
} else if ( data . revalidate <= 0 ) {
throw new Error (
` A page's revalidate option can not be less than or equal to zero for ${ req . url } . A revalidate option of zero means to revalidate after _every_ request, and implies stale data cannot be tolerated. ` +
` \ n \ nTo never revalidate, you can set revalidate to \` false \` (only ran once at build-time). ` +
` \ nTo revalidate as soon as possible, you can set the value to \` 1 \` . `
)
} else if ( data . revalidate > 31536000 ) {
// if it's greater than a year for some reason error
console . warn (
` Warning: A page's revalidate option was set to more than a year for ${ req . url } . This may have been done in error. ` +
` \ nTo only run getStaticProps at build-time and not revalidate at runtime, you can set \` revalidate \` to \` false \` ! `
2020-09-08 09:23:21 +02:00
)
}
2021-09-11 02:17:56 +02:00
} else if ( data . revalidate === true ) {
// When enabled, revalidate after 1 second. This value is optimal for
// the most up-to-date page possible, but without a 1-to-1
// request-refresh ratio.
data . revalidate = 1
} else if (
data . revalidate === false ||
typeof data . revalidate === 'undefined'
2020-03-09 18:30:44 +01:00
) {
2021-09-11 02:17:56 +02:00
// By default, we never revalidate.
data . revalidate = false
} else {
2020-03-09 18:30:44 +01:00
throw new Error (
2021-09-11 02:17:56 +02:00
` A page's revalidate option must be seconds expressed as a natural number. Mixed numbers and strings cannot be used. Received ' ${ JSON . stringify (
data . revalidate
) } ' for $ { req . url } `
2020-03-09 18:30:44 +01:00
)
}
2021-09-11 02:17:56 +02:00
} else {
// By default, we never revalidate.
; ( data as any ) . revalidate = false
}
2020-03-09 18:30:44 +01:00
2021-09-11 02:17:56 +02:00
props . pageProps = Object . assign (
{ } ,
props . pageProps ,
'props' in data ? data.props : undefined
)
2019-09-25 17:29:22 +02:00
2021-09-11 02:17:56 +02:00
// pass up revalidate and props for export
// TODO: change this to a different passing mechanism
; ( renderOpts as any ) . revalidate =
'revalidate' in data ? data.revalidate : undefined
; ( renderOpts as any ) . pageData = props
2021-02-18 19:34:33 +01:00
2021-09-11 02:17:56 +02:00
// this must come after revalidate is added to renderOpts
if ( ( renderOpts as any ) . isNotFound ) {
return null
}
}
2021-08-14 15:11:40 +02:00
2021-09-11 02:17:56 +02:00
if ( getServerSideProps ) {
props [ SERVER_PROPS_ID ] = true
}
if ( getServerSideProps && ! isFallback ) {
let data : UnwrapPromise < ReturnType < GetServerSideProps > >
2021-09-14 00:10:46 +02:00
let canAccessRes = true
let resOrProxy = res
2021-10-26 03:18:03 +02:00
let deferredContent = false
2021-09-14 00:10:46 +02:00
if ( process . env . NODE_ENV !== 'production' ) {
resOrProxy = new Proxy < ServerResponse > ( res , {
get : function ( obj , prop , receiver ) {
if ( ! canAccessRes ) {
2021-10-26 03:18:03 +02:00
const message =
2021-09-14 00:10:46 +02:00
` You should not access 'res' after getServerSideProps resolves. ` +
2021-10-26 03:18:03 +02:00
` \ nRead more: https://nextjs.org/docs/messages/gssp-no-mutating-res `
if ( deferredContent ) {
throw new Error ( message )
} else {
warn ( message )
}
2021-09-14 00:10:46 +02:00
}
2022-01-16 17:14:21 +01:00
const value = Reflect . get ( obj , prop , receiver )
// since ServerResponse uses internal fields which
// proxy can't map correctly we need to ensure functions
// are bound correctly while being proxied
if ( typeof value === 'function' ) {
return value . bind ( obj )
}
return value
2021-09-14 00:10:46 +02:00
} ,
} )
}
2021-09-11 02:17:56 +02:00
try {
data = await getServerSideProps ( {
req : req as IncomingMessage & {
cookies : NextApiRequestCookies
} ,
2021-09-14 00:10:46 +02:00
res : resOrProxy ,
2021-09-11 02:17:56 +02:00
query ,
resolvedUrl : renderOpts.resolvedUrl as string ,
. . . ( pageIsDynamic ? { params : params as ParsedUrlQuery } : undefined ) ,
. . . ( previewData !== false
? { preview : true , previewData : previewData }
: undefined ) ,
locales : renderOpts.locales ,
locale : renderOpts.locale ,
defaultLocale : renderOpts.defaultLocale ,
} )
2021-09-14 00:10:46 +02:00
canAccessRes = false
2021-09-16 18:06:57 +02:00
} catch ( serverSidePropsError : any ) {
2021-09-11 02:17:56 +02:00
// remove not found error code to prevent triggering legacy
// 404 rendering
2021-09-16 18:06:57 +02:00
if (
isError ( serverSidePropsError ) &&
serverSidePropsError . code === 'ENOENT'
) {
2021-09-11 02:17:56 +02:00
delete serverSidePropsError . code
2021-08-14 15:11:40 +02:00
}
2021-09-11 02:17:56 +02:00
throw serverSidePropsError
2019-09-24 10:50:04 +02:00
}
2020-03-04 13:58:12 +01:00
2021-09-11 02:17:56 +02:00
if ( data == null ) {
throw new Error ( GSSP_NO_RETURNED_VALUE )
2020-03-06 05:15:10 +01:00
}
2021-10-26 03:18:03 +02:00
if ( ( data as any ) . props instanceof Promise ) {
deferredContent = true
}
2021-09-11 02:17:56 +02:00
const invalidKeys = Object . keys ( data ) . filter (
( key ) = > key !== 'props' && key !== 'redirect' && key !== 'notFound'
)
2020-06-17 11:25:27 +02:00
2021-09-11 02:17:56 +02:00
if ( ( data as any ) . unstable_notFound ) {
throw new Error (
` unstable_notFound has been renamed to notFound, please update the field to continue. Page: ${ pathname } `
2020-09-08 09:23:21 +02:00
)
2021-09-11 02:17:56 +02:00
}
if ( ( data as any ) . unstable_redirect ) {
throw new Error (
` unstable_redirect has been renamed to redirect, please update the field to continue. Page: ${ pathname } `
)
}
2020-03-04 13:58:12 +01:00
2021-09-11 02:17:56 +02:00
if ( invalidKeys . length ) {
throw new Error ( invalidKeysMsg ( 'getServerSideProps' , invalidKeys ) )
}
if ( 'notFound' in data && data . notFound ) {
if ( pathname === '/404' ) {
2020-10-27 09:37:27 +01:00
throw new Error (
2021-09-11 02:17:56 +02:00
` The /404 page can not return notFound in "getStaticProps", please remove it to continue! `
2020-10-27 09:37:27 +01:00
)
}
2021-09-11 02:17:56 +02:00
; ( renderOpts as any ) . isNotFound = true
return null
}
2020-10-27 06:42:12 +01:00
2021-09-11 02:17:56 +02:00
if ( 'redirect' in data && typeof data . redirect === 'object' ) {
checkRedirectValues ( data . redirect as Redirect , req , 'getServerSideProps' )
; ( data as any ) . props = {
__N_REDIRECT : data.redirect.destination ,
__N_REDIRECT_STATUS : getRedirectStatus ( data . redirect ) ,
2020-10-27 06:42:12 +01:00
}
2021-09-11 02:17:56 +02:00
if ( typeof data . redirect . basePath !== 'undefined' ) {
; ( data as any ) . props . __N_REDIRECT_BASE_PATH = data . redirect . basePath
getServerSideProps should support props value as Promise (#28607)
Previous to this change, getServerSideProps could only return plain objects
for props, e.g.:
```javascript
export async function getServerSideProps() {
return {
props: {
text: 'some value',
}
}
}
```
With this commit, the props object can also be a Promise, e.g.
```javascript
export async function getServerSideProps() {
return {
props: (async function () {
return {
text: 'promise value',
}
})(),
}
}
```
For now, the framework simply waits for the results of the props Promise to resolve,
but this small change sets the groundwork for later allowing props to be streamed (cc @devknoll).
## Feature
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. -- *This is part of @devknoll's ongoing work to support streaming.*
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not. *not sure if this applies here*
- [ ] Errors have helpful link attached, see `contributing.md`
2021-08-30 21:52:15 +02:00
}
2021-09-11 02:17:56 +02:00
; ( renderOpts as any ) . isRedirect = true
}
getServerSideProps should support props value as Promise (#28607)
Previous to this change, getServerSideProps could only return plain objects
for props, e.g.:
```javascript
export async function getServerSideProps() {
return {
props: {
text: 'some value',
}
}
}
```
With this commit, the props object can also be a Promise, e.g.
```javascript
export async function getServerSideProps() {
return {
props: (async function () {
return {
text: 'promise value',
}
})(),
}
}
```
For now, the framework simply waits for the results of the props Promise to resolve,
but this small change sets the groundwork for later allowing props to be streamed (cc @devknoll).
## Feature
- [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. -- *This is part of @devknoll's ongoing work to support streaming.*
- [ ] Related issues linked using `fixes #number`
- [x] Integration tests added
- [x] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not. *not sure if this applies here*
- [ ] Errors have helpful link attached, see `contributing.md`
2021-08-30 21:52:15 +02:00
2021-10-26 03:18:03 +02:00
if ( deferredContent ) {
2021-09-11 02:17:56 +02:00
; ( data as any ) . props = await ( data as any ) . props
}
2020-03-09 18:30:44 +01:00
2021-09-11 02:17:56 +02:00
if (
( dev || isBuildTimeSSG ) &&
! isSerializableProps ( pathname , 'getServerSideProps' , ( data as any ) . props )
) {
// this fn should throw an error instead of ever returning `false`
throw new Error (
'invariant: getServerSideProps did not return valid props. Please report this.'
)
2020-03-04 13:58:12 +01:00
}
2021-09-11 02:17:56 +02:00
props . pageProps = Object . assign ( { } , props . pageProps , ( data as any ) . props )
; ( renderOpts as any ) . pageData = props
2019-03-17 17:43:03 +01:00
}
2018-12-13 01:00:46 +01:00
2020-02-26 19:26:55 +01:00
if (
2020-03-04 13:58:12 +01:00
! isSSG && // we only show this warning for legacy pages
2020-02-27 18:57:39 +01:00
! getServerSideProps &&
2020-02-26 19:26:55 +01:00
process . env . NODE_ENV !== 'production' &&
Object . keys ( props ? . pageProps || { } ) . includes ( 'url' )
) {
console . warn (
` The prop \` url \` is a reserved prop in Next.js for legacy reasons and will be overridden on page ${ pathname } \ n ` +
2021-03-29 10:25:00 +02:00
` See more info here: https://nextjs.org/docs/messages/reserved-page-prop `
2020-02-26 19:26:55 +01:00
)
}
2020-11-03 17:40:50 +01:00
// Avoid rendering page un-necessarily for getServerSideProps data request
// and getServerSideProps/getStaticProps redirects
2020-11-04 23:18:44 +01:00
if ( ( isDataReq && ! isSSG ) || ( renderOpts as any ) . isRedirect ) {
2022-07-28 00:11:02 +02:00
return RenderResult . fromStatic ( JSON . stringify ( props ) )
2020-11-04 23:18:44 +01:00
}
2020-01-27 23:50:59 +01:00
2020-02-27 18:04:30 +01:00
// We don't call getStaticProps or getServerSideProps while generating
2020-02-07 14:09:06 +01:00
// the fallback so make sure to set pageProps to an empty object
if ( isFallback ) {
props . pageProps = { }
}
2018-12-17 16:09:23 +01:00
// the response might be finished on the getInitialProps call
2020-03-04 13:58:12 +01:00
if ( isResSent ( res ) && ! isSSG ) return null
2018-12-13 01:00:46 +01:00
2020-08-13 14:39:36 +02:00
// we preload the buildManifest for auto-export dynamic pages
// to speed up hydrating query values
let filteredBuildManifest = buildManifest
if ( isAutoExport && pageIsDynamic ) {
2020-08-14 06:30:25 +02:00
const page = denormalizePagePath ( normalizePagePath ( pathname ) )
// This code would be much cleaner using `immer` and directly pushing into
// the result from `getPageFiles`, we could maybe consider that in the
// future.
if ( page in filteredBuildManifest . pages ) {
filteredBuildManifest = {
. . . filteredBuildManifest ,
pages : {
. . . filteredBuildManifest . pages ,
[ page ] : [
. . . filteredBuildManifest . pages [ page ] ,
. . . filteredBuildManifest . lowPriorityFiles . filter ( ( f ) = >
f . includes ( '_buildManifest' )
) ,
] ,
} ,
lowPriorityFiles : filteredBuildManifest.lowPriorityFiles.filter (
( f ) = > ! f . includes ( '_buildManifest' )
) ,
}
2020-08-13 14:39:36 +02:00
}
}
2021-11-19 09:16:05 +01:00
const Body = ( { children } : { children : JSX.Element } ) = > {
return inAmpMode ? children : < div id = "__next" > { children } < / div >
}
2021-09-06 12:23:07 +02:00
/ * *
* 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
2021-08-26 17:05:01 +02:00
const renderDocument = async ( ) = > {
2022-03-11 21:38:09 +01:00
// For `Document`, there are two cases that we don't support:
// 1. Using `Document.getInitialProps` in the Edge runtime.
// 2. Using the class component `Document` with concurrent features.
2022-05-02 22:52:46 +02:00
const BuiltinFunctionalDocument : DocumentType | undefined = (
Document as any
) [ NEXT_BUILTIN_DOCUMENT ]
2022-03-11 21:38:09 +01:00
2022-04-26 19:54:28 +02:00
if ( process . env . NEXT_RUNTIME === 'edge' && Document . getInitialProps ) {
2022-03-11 21:38:09 +01:00
// In the Edge runtime, `Document.getInitialProps` isn't supported.
// We throw an error here if it's customized.
2022-05-02 22:52:46 +02:00
if ( ! BuiltinFunctionalDocument ) {
2022-03-11 21:38:09 +01:00
throw new Error (
'`getInitialProps` in Document component is not supported with the Edge Runtime.'
)
}
}
2022-07-28 00:11:02 +02:00
if ( process . env . NEXT_RUNTIME === 'edge' && Document . getInitialProps ) {
2022-05-02 22:52:46 +02:00
if ( BuiltinFunctionalDocument ) {
Document = BuiltinFunctionalDocument
2022-03-30 18:10:48 +02:00
} else {
throw new Error (
'`getInitialProps` in Document component is not supported with React Server Components.'
)
}
2022-02-19 22:13:48 +01:00
}
2022-05-11 15:25:23 +02:00
async function loadDocumentInitialProps (
2022-04-22 23:03:57 +02:00
renderShell ? : (
_App : AppType ,
_Component : NextComponentType
2022-05-11 15:25:23 +02:00
) = > Promise < ReactReadableStream >
2022-04-21 18:01:47 +02:00
) {
2022-09-29 10:56:28 +02:00
const renderPage : RenderPage = async (
2021-08-26 17:05:01 +02:00
options : ComponentsEnhancer = { }
2022-09-29 10:56:28 +02:00
) : Promise < RenderPageResult > = > {
2021-08-26 17:05:01 +02:00
if ( ctx . err && ErrorDebug ) {
2022-04-21 18:01:47 +02:00
// Always start rendering the shell even if there's an error.
if ( renderShell ) {
2022-04-22 23:03:57 +02:00
renderShell ( App , Component )
2022-04-21 18:01:47 +02:00
}
2022-09-29 10:56:28 +02:00
const html = await renderToString (
2021-11-19 09:16:05 +01:00
< Body >
< ErrorDebug error = { ctx . err } / >
< / Body >
2021-08-26 17:05:01 +02:00
)
return { html , head }
}
2019-04-03 00:32:07 +02:00
2021-08-26 17:05:01 +02:00
if ( dev && ( props . router || props . Component ) ) {
throw new Error (
` 'router' and 'Component' can not be returned in getInitialProps from _app.js https://nextjs.org/docs/messages/cant-override-next-props `
)
}
2020-01-04 17:40:18 +01:00
2021-08-26 17:05:01 +02:00
const { App : EnhancedApp , Component : EnhancedComponent } =
enhanceComponents ( options , App , Component )
2022-04-21 18:01:47 +02:00
if ( renderShell ) {
2022-05-11 15:25:23 +02:00
return renderShell ( EnhancedApp , EnhancedComponent ) . then (
async ( stream ) = > {
const forwardStream = readableStreamTee ( stream ) [ 1 ]
const html = await streamToString ( forwardStream )
return { html , head }
}
)
2022-04-21 18:01:47 +02:00
}
2022-09-29 10:56:28 +02:00
const html = await renderToString (
2021-11-19 09:16:05 +01:00
< Body >
2021-12-06 22:22:05 +01:00
< AppContainerWithIsomorphicFiberStructure >
2022-05-20 20:07:20 +02:00
{ renderPageTree ( EnhancedApp , EnhancedComponent , {
. . . props ,
router ,
} ) }
2021-12-06 22:22:05 +01:00
< / AppContainerWithIsomorphicFiberStructure >
2021-11-19 09:16:05 +01:00
< / Body >
2021-08-26 17:05:01 +02:00
)
return { html , head }
}
const documentCtx = { . . . ctx , renderPage }
const docProps : DocumentInitialProps = await loadGetInitialProps (
Document ,
documentCtx
)
// the response might be finished on the getInitialProps call
if ( isResSent ( res ) && ! isSSG ) return null
if ( ! docProps || typeof docProps . html !== 'string' ) {
const message = ` " ${ getDisplayName (
Document
) } . getInitialProps ( ) " should resolve to an object with a " html " prop set with a valid html string `
throw new Error ( message )
}
2020-01-04 17:40:18 +01:00
2022-03-30 18:10:48 +02:00
return { docProps , documentCtx }
}
2022-03-30 15:15:13 +02:00
2022-04-22 23:03:57 +02:00
const renderContent = ( _App : AppType , _Component : NextComponentType ) = > {
const EnhancedApp = _App || App
const EnhancedComponent = _Component || Component
2022-03-30 18:10:48 +02:00
return ctx . err && ErrorDebug ? (
< Body >
< ErrorDebug error = { ctx . err } / >
< / Body >
) : (
< Body >
< AppContainerWithIsomorphicFiberStructure >
2022-05-20 20:07:20 +02:00
{ renderPageTree ( EnhancedApp , EnhancedComponent , {
. . . props ,
router ,
} ) }
2022-03-30 18:10:48 +02:00
< / AppContainerWithIsomorphicFiberStructure >
< / Body >
)
}
2022-02-18 01:18:28 +01:00
2022-04-28 21:17:23 +02:00
if ( ! process . env . __NEXT_REACT_ROOT ) {
2022-05-02 22:52:46 +02:00
// Enabling react legacy rendering mode: __NEXT_REACT_ROOT = false
2022-03-30 18:10:48 +02:00
if ( Document . getInitialProps ) {
2022-05-11 15:25:23 +02:00
const documentInitialProps = await loadDocumentInitialProps ( )
if ( documentInitialProps === null ) return null
const { docProps , documentCtx } = documentInitialProps
2022-03-30 18:10:48 +02:00
return {
bodyResult : ( suffix : string ) = >
streamFromArray ( [ docProps . html , suffix ] ) ,
documentElement : ( htmlProps : HtmlProps ) = > (
< Document { ...htmlProps } { ...docProps } / >
) ,
head : docProps.head ,
headTags : await headTags ( documentCtx ) ,
styles : docProps.styles ,
2021-11-19 20:41:19 +01:00
}
} else {
2022-04-22 23:03:57 +02:00
const content = renderContent ( App , Component )
2021-11-19 20:41:19 +01:00
// for non-concurrent rendering we need to ensure App is rendered
// before _document so that updateHead is called/collected before
// rendering _document's head
2022-09-29 10:56:28 +02:00
const result = await renderToString ( content )
2022-03-30 18:10:48 +02:00
const bodyResult = ( suffix : string ) = > streamFromArray ( [ result , suffix ] )
const styles = jsxStyleRegistry . styles ( )
jsxStyleRegistry . flush ( )
return {
bodyResult ,
documentElement : ( ) = > ( Document as any ) ( ) ,
head ,
headTags : [ ] ,
styles ,
}
}
} else {
2022-05-02 22:52:46 +02:00
// Enabling react concurrent rendering mode: __NEXT_REACT_ROOT = true
2022-04-22 23:03:57 +02:00
const renderShell = async (
EnhancedApp : AppType ,
EnhancedComponent : NextComponentType
) = > {
const content = renderContent ( EnhancedApp , EnhancedComponent )
2022-05-11 15:25:23 +02:00
return await renderToInitialStream ( {
2022-04-21 18:01:47 +02:00
ReactDOMServer ,
element : content ,
} )
}
2022-03-30 18:10:48 +02:00
2022-05-12 19:41:37 +02:00
const createBodyResult = (
initialStream : ReactReadableStream ,
suffix? : string
) = > {
// this must be called inside bodyResult so appWrappers is
// up to date when `wrapApp` is called
2022-10-03 15:43:35 +02:00
const getServerInsertedHTML = async ( ) : Promise < string > = > {
return renderToString ( styledJsxInsertedHTML ( ) )
2022-05-12 19:41:37 +02:00
}
2022-03-30 18:10:48 +02:00
2022-05-12 19:41:37 +02:00
return continueFromInitialStream ( initialStream , {
suffix ,
dataStream : serverComponentsInlinedTransformStream?.readable ,
generateStaticHTML ,
2022-10-03 15:43:35 +02:00
getServerInsertedHTML ,
serverInsertedHTMLToHead : false ,
2022-05-12 19:41:37 +02:00
} )
}
2022-03-31 02:35:00 +02:00
const hasDocumentGetInitialProps = ! (
2022-07-28 00:11:02 +02:00
process . env . NEXT_RUNTIME === 'edge' || ! Document . getInitialProps
2022-03-31 02:35:00 +02:00
)
2022-02-18 01:18:28 +01:00
2022-05-11 15:25:23 +02:00
let bodyResult : ( s : string ) = > Promise < ReadableStream < Uint8Array > >
2022-04-21 18:01:47 +02:00
// If it has getInitialProps, we will render the shell in `renderPage`.
// Otherwise we do it right now.
let documentInitialPropsRes :
| { }
2022-05-11 15:25:23 +02:00
| Awaited < ReturnType < typeof loadDocumentInitialProps > >
2022-04-21 18:01:47 +02:00
if ( hasDocumentGetInitialProps ) {
2022-05-11 15:25:23 +02:00
documentInitialPropsRes = await loadDocumentInitialProps ( renderShell )
2022-04-21 18:01:47 +02:00
if ( documentInitialPropsRes === null ) return null
2022-05-11 15:25:23 +02:00
const { docProps } = documentInitialPropsRes as any
2022-05-12 19:41:37 +02:00
// includes suffix in initial html stream
bodyResult = ( suffix : string ) = >
createBodyResult ( streamFromArray ( [ docProps . html , suffix ] ) )
2022-04-21 18:01:47 +02:00
} else {
2022-05-11 15:25:23 +02:00
const stream = await renderShell ( App , Component )
2022-05-12 19:41:37 +02:00
bodyResult = ( suffix : string ) = > createBodyResult ( stream , suffix )
2022-04-21 18:01:47 +02:00
documentInitialPropsRes = { }
}
2022-03-30 18:10:48 +02:00
2022-03-31 02:35:00 +02:00
const { docProps } = ( documentInitialPropsRes as any ) || { }
2022-08-11 23:32:52 +02:00
const documentElement = ( htmlProps : any ) = > {
2022-07-28 00:11:02 +02:00
if ( process . env . NEXT_RUNTIME === 'edge' ) {
2022-03-30 18:10:48 +02:00
return ( Document as any ) ( )
2022-07-28 00:11:02 +02:00
} else {
return < Document { ...htmlProps } { ...docProps } / >
2022-03-30 18:10:48 +02:00
}
}
2022-03-31 02:35:00 +02:00
2022-05-02 22:52:46 +02:00
let styles
2022-03-31 02:35:00 +02:00
if ( hasDocumentGetInitialProps ) {
styles = docProps . styles
2022-06-04 12:30:42 +02:00
head = docProps . head
2022-03-31 02:35:00 +02:00
} else {
styles = jsxStyleRegistry . styles ( )
jsxStyleRegistry . flush ( )
}
2022-03-30 18:10:48 +02:00
2021-08-26 17:05:01 +02:00
return {
bodyResult ,
2022-03-30 18:10:48 +02:00
documentElement ,
2021-08-26 17:05:01 +02:00
head ,
headTags : [ ] ,
2022-02-18 01:18:28 +01:00
styles ,
2021-08-26 17:05:01 +02:00
}
}
2019-04-02 16:09:34 +02:00
}
2018-12-13 01:00:46 +01:00
2021-08-26 17:05:01 +02:00
const documentResult = await renderDocument ( )
if ( ! documentResult ) {
return null
2019-04-22 19:55:03 +02:00
}
2021-04-21 13:18:05 +02:00
const dynamicImportsIds = new Set < string | number > ( )
const dynamicImports = new Set < string > ( )
2019-06-29 02:48:28 +02:00
for ( const mod of reactLoadableModules ) {
2021-04-21 13:18:05 +02:00
const manifestItem : ManifestItem = reactLoadableManifest [ mod ]
2019-06-29 02:48:28 +02:00
if ( manifestItem ) {
2021-04-21 13:18:05 +02:00
dynamicImportsIds . add ( manifestItem . id )
manifestItem . files . forEach ( ( item ) = > {
dynamicImports . add ( item )
2019-06-29 02:48:28 +02:00
} )
}
}
2019-06-27 16:22:24 +02:00
const hybridAmp = ampState . hybrid
2020-08-24 04:42:51 +02:00
const docComponentsRendered : DocumentProps [ 'docComponentsRendered' ] = { }
2021-11-07 18:36:39 +01:00
2021-08-26 17:05:01 +02:00
const {
assetPrefix ,
buildId ,
customServer ,
defaultLocale ,
disableOptimizedLoading ,
domainLocales ,
locale ,
locales ,
runtimeConfig ,
} = renderOpts
2021-12-06 22:22:05 +01:00
const htmlProps : HtmlProps = {
2021-08-26 17:05:01 +02:00
__NEXT_DATA__ : {
props , // The result of getInitialProps
page : pathname , // The rendered page
query , // querystring parsed / passed by the user
buildId , // buildId is used to facilitate caching of page bundles, we send it to the client so that pageloader knows where to load bundles
assetPrefix : assetPrefix === '' ? undefined : assetPrefix , // send assetPrefix to the client side when configured, otherwise don't sent in the resulting HTML
runtimeConfig , // runtimeConfig if provided, otherwise don't sent in the resulting HTML
nextExport : nextExport === true ? true : undefined , // If this is a page exported by `next export`
autoExport : isAutoExport === true ? true : undefined , // If this is an auto exported page
isFallback ,
dynamicIds :
dynamicImportsIds . size === 0
? undefined
: Array . from ( dynamicImportsIds ) ,
err : renderOpts.err ? serializeError ( dev , renderOpts . err ) : undefined , // Error if one happened, otherwise don't sent in the resulting HTML
gsp : ! ! getStaticProps ? true : undefined , // whether the page is getStaticProps
gssp : ! ! getServerSideProps ? true : undefined , // whether the page is getServerSideProps
customServer , // whether the user is using a custom server
gip : hasPageGetInitialProps ? true : undefined , // whether the page has getInitialProps
appGip : ! defaultAppGetInitialProps ? true : undefined , // whether the _app has getInitialProps
locale ,
locales ,
defaultLocale ,
domainLocales ,
isPreview : isPreview === true ? true : undefined ,
2022-02-16 19:53:48 +01:00
notFoundSrcPage : notFoundSrcPage && dev ? notFoundSrcPage : undefined ,
2021-08-26 17:05:01 +02:00
} ,
buildManifest : filteredBuildManifest ,
docComponentsRendered ,
dangerousAsPath : router.asPath ,
2020-10-16 01:00:08 +02:00
canonicalBase :
2021-11-09 02:28:39 +01:00
! renderOpts . ampPath && getRequestMeta ( req , '__nextStrippedLocale' )
2020-10-16 01:00:08 +02:00
? ` ${ renderOpts . canonicalBase || '' } / ${ renderOpts . locale } `
: renderOpts . canonicalBase ,
2021-08-26 17:05:01 +02:00
ampPath ,
inAmpMode ,
isDevelopment : ! ! dev ,
hybridAmp ,
dynamicImports : Array.from ( dynamicImports ) ,
assetPrefix ,
2020-04-17 11:22:03 +02:00
// Only enabled in production as development mode has features relying on HMR (style injection for example)
unstable_runtimeJS :
process . env . NODE_ENV === 'production'
? pageConfig . unstable_runtimeJS
: undefined ,
2021-01-19 20:38:15 +01:00
unstable_JsPreload : pageConfig.unstable_JsPreload ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2020-12-01 19:10:16 +01:00
scriptLoader ,
2021-08-26 17:05:01 +02:00
locale ,
disableOptimizedLoading ,
head : documentResult.head ,
2021-09-09 10:13:50 +02:00
headTags : documentResult.headTags ,
2021-08-26 17:05:01 +02:00
styles : documentResult.styles ,
2021-11-30 18:14:28 +01:00
crossOrigin : renderOpts.crossOrigin ,
optimizeCss : renderOpts.optimizeCss ,
optimizeFonts : renderOpts.optimizeFonts ,
2022-03-11 23:26:46 +01:00
nextScriptWorkers : renderOpts.nextScriptWorkers ,
2022-03-26 00:05:35 +01:00
runtime : globalRuntime ,
2022-05-29 03:39:48 +02:00
largePageDataBytes : renderOpts.largePageDataBytes ,
2022-09-22 07:12:59 +02:00
fontLoaderManifest : renderOpts.fontLoaderManifest ,
2021-08-26 17:05:01 +02:00
}
2021-11-05 22:51:10 +01:00
const document = (
2021-08-26 17:05:01 +02:00
< AmpStateContext.Provider value = { ampState } >
< HtmlContext.Provider value = { htmlProps } >
{ documentResult . documentElement ( htmlProps ) }
< / HtmlContext.Provider >
< / AmpStateContext.Provider >
)
2019-04-02 20:01:34 +02:00
2022-09-29 10:56:28 +02:00
const documentHTML = await renderToStaticMarkup ( document )
2021-11-05 22:51:10 +01:00
2021-12-13 23:47:34 +01:00
if ( process . env . NODE_ENV !== 'production' ) {
const nonRenderedComponents = [ ]
const expectedDocComponents = [ 'Main' , 'Head' , 'NextScript' , 'Html' ]
2020-08-24 04:42:51 +02:00
2021-12-13 23:47:34 +01:00
for ( const comp of expectedDocComponents ) {
if ( ! ( docComponentsRendered as any ) [ comp ] ) {
nonRenderedComponents . push ( comp )
}
2020-08-24 04:42:51 +02:00
}
2021-12-13 23:47:34 +01:00
if ( nonRenderedComponents . length ) {
const missingComponentList = nonRenderedComponents
. map ( ( e ) = > ` < ${ e } /> ` )
. join ( ', ' )
const plural = nonRenderedComponents . length !== 1 ? 's' : ''
console . warn (
` Your custom Document (pages/_document) did not render all the required subcomponent ${ plural } . \ n ` +
` Missing component ${ plural } : ${ missingComponentList } \ n ` +
'Read how to fix here: https://nextjs.org/docs/messages/missing-document-component'
)
}
2020-08-24 04:42:51 +02:00
}
2021-11-07 18:36:39 +01:00
const [ renderTargetPrefix , renderTargetSuffix ] = documentHTML . split (
2022-01-17 17:22:44 +01:00
'<next-js-internal-body-render-target></next-js-internal-body-render-target>'
2021-11-07 18:36:39 +01:00
)
2021-12-13 11:48:18 +01:00
2021-09-11 02:17:56 +02:00
const prefix : Array < string > = [ ]
2021-11-17 15:57:48 +01:00
if ( ! documentHTML . startsWith ( DOCTYPE ) ) {
prefix . push ( DOCTYPE )
}
2021-11-07 18:36:39 +01:00
prefix . push ( renderTargetPrefix )
2021-08-13 05:36:54 +02:00
if ( inAmpMode ) {
2021-09-11 02:17:56 +02:00
prefix . push ( '<!-- __NEXT_DATA__ -->' )
2019-04-02 20:01:34 +02:00
}
2021-09-11 02:17:56 +02:00
2022-05-12 19:41:37 +02:00
const streams = [
2022-02-05 02:13:02 +01:00
streamFromArray ( prefix ) ,
2021-12-13 11:48:18 +01:00
await documentResult . bodyResult ( renderTargetSuffix ) ,
2021-09-11 02:17:56 +02:00
]
2019-04-16 15:57:17 +02:00
2022-05-12 19:41:37 +02:00
const postOptimize = ( html : string ) = >
postProcessHTML ( pathname , html , renderOpts , { inAmpMode , hybridAmp } )
if ( generateStaticHTML ) {
const html = await streamToString ( chainStreams ( streams ) )
const optimizedHtml = await postOptimize ( html )
return new RenderResult ( optimizedHtml )
2020-12-21 20:26:00 +01:00
}
2020-12-01 19:02:07 +01:00
2022-04-13 19:14:53 +02:00
return new RenderResult (
2022-05-12 19:41:37 +02:00
chainStreams ( streams ) . pipeThrough (
createBufferedTransformStream ( postOptimize )
)
2022-04-13 19:14:53 +02:00
)
2021-08-13 18:40:12 +02:00
}