2019-02-14 16:22:57 +01:00
import { IncomingMessage , ServerResponse } from 'http'
2018-12-13 01:00:46 +01:00
import { ParsedUrlQuery } from 'querystring'
import React from 'react'
2020-02-12 02:16:42 +01:00
import { renderToStaticMarkup , renderToString } from 'react-dom/server'
2020-09-12 20:57:07 +02:00
import { warn } from '../../build/output/log'
2020-06-17 11:25:27 +02:00
import { UnwrapPromise } from '../../lib/coalesced-function'
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 ,
2020-02-12 02:16:42 +01:00
} from '../../lib/constants'
2020-03-09 18:30:44 +01:00
import { isSerializableProps } from '../../lib/is-serializable-props'
2021-04-20 20:13:48 +02:00
import { GetServerSideProps , GetStaticProps , PreviewData } from '../../types'
2020-02-12 02:16:42 +01:00
import { isInAmpMode } from '../lib/amp'
import { AmpStateContext } from '../lib/amp-context'
2020-03-06 05:15:10 +01:00
import {
AMP_RENDER_TARGET ,
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 ,
2020-03-06 05:15:10 +01:00
} from '../lib/constants'
2020-06-12 00:09:06 +02:00
import { defaultHead } from '../lib/head'
2020-06-17 11:25:27 +02:00
import { HeadManagerContext } from '../lib/head-manager-context'
2018-12-13 01:00:46 +01:00
import Loadable from '../lib/loadable'
2019-04-22 19:55:03 +02:00
import { LoadableContext } from '../lib/loadable-context'
2020-02-12 02:16:42 +01:00
import mitt , { MittEmitter } from '../lib/mitt'
2020-08-14 06:30:25 +02:00
import postProcess from '../lib/post-process'
2019-04-02 16:09:34 +02:00
import { RouterContext } from '../lib/router-context'
2020-02-12 02:16:42 +01:00
import { NextRouter } from '../lib/router/router'
2019-09-24 10:50:04 +02:00
import { isDynamicRoute } from '../lib/router/utils/is-dynamic'
2020-01-27 23:50:59 +01:00
import {
2020-02-12 02:16:42 +01:00
AppType ,
ComponentsEnhancer ,
DocumentInitialProps ,
2020-09-12 20:57:07 +02:00
DocumentProps ,
2020-02-12 02:16:42 +01:00
DocumentType ,
getDisplayName ,
isResSent ,
loadGetInitialProps ,
NextComponentType ,
RenderPage ,
} from '../lib/utils'
2021-01-26 15:26:59 +01:00
import {
tryGetPreviewData ,
NextApiRequestCookies ,
__ApiPreviewProps ,
} from './api-utils'
2020-08-14 06:30:25 +02:00
import { denormalizePagePath } from './denormalize-page-path'
import { FontManifest , getFontDefinitionFromManifest } from './font-utils'
2020-01-25 05:25:11 +01:00
import { LoadComponentsReturnType , ManifestItem } from './load-components'
2020-08-14 06:30:25 +02:00
import { normalizePagePath } from './normalize-page-path'
2020-02-12 02:16:42 +01:00
import optimizeAmp from './optimize-amp'
2020-11-04 23:18:44 +01:00
import {
allowedStatusCodes ,
getRedirectStatus ,
2020-11-11 08:13:18 +01:00
Redirect ,
2020-11-04 23:18:44 +01:00
} from '../../lib/load-custom-routes'
2020-12-30 07:44:07 +01:00
import { DomainLocales } from './config'
2019-06-29 02:48:28 +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 )
}
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
2020-12-31 09:07:51 +01:00
domainLocales? : DomainLocales
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
// TODO: Remove in the next major version, as this would mean the user is adding event listeners in server-side `render` method
static events : MittEmitter = mitt ( )
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 ,
domainLocales? : DomainLocales ,
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
}
}
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
2020-12-21 20:26:00 +01:00
optimizeFonts : boolean
2020-07-28 12:19:28 +02:00
fontManifest? : FontManifest
2020-08-05 19:49:44 +02:00
optimizeImages : boolean
2020-12-01 19:02:07 +01:00
optimizeCss : 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
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
2020-12-30 07:44:07 +01:00
domainLocales? : DomainLocales
2021-05-13 12:39:36 +02:00
disableOptimizedLoading? : boolean
2018-12-13 01:00:46 +01:00
}
2020-03-02 11:58:47 +01:00
export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
2019-02-14 16:22:57 +01:00
function renderDocument (
2019-04-22 19:55:03 +02:00
Document : DocumentType ,
2019-02-14 16:22:57 +01:00
{
2020-06-07 01:00:03 +02:00
buildManifest ,
2020-08-24 04:42:51 +02:00
docComponentsRendered ,
2019-02-14 16:22:57 +01:00
props ,
docProps ,
pathname ,
query ,
buildId ,
2019-05-29 02:32:18 +02:00
canonicalBase ,
2019-02-14 16:22:57 +01:00
assetPrefix ,
runtimeConfig ,
nextExport ,
2019-09-15 20:35:14 +02:00
autoExport ,
2020-02-07 14:09:06 +01:00
isFallback ,
2019-02-14 16:22:57 +01:00
dynamicImportsIds ,
2019-04-15 11:26:23 +02:00
dangerousAsPath ,
2019-02-14 16:22:57 +01:00
err ,
dev ,
2019-03-20 04:53:47 +01:00
ampPath ,
2019-06-27 16:22:24 +02:00
ampState ,
inAmpMode ,
hybridAmp ,
2019-02-14 16:22:57 +01:00
dynamicImports ,
2019-11-01 20:13:13 +01:00
headTags ,
2020-03-03 19:39:08 +01:00
gsp ,
gssp ,
2020-03-06 17:14:39 +01:00
customServer ,
2020-04-13 11:59:49 +02:00
gip ,
appGip ,
2020-04-17 11:22:03 +02:00
unstable_runtimeJS ,
2021-01-19 20:38:15 +01:00
unstable_JsPreload ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2020-12-01 19:10:16 +01:00
scriptLoader ,
2020-10-07 23:11:01 +02:00
locale ,
locales ,
2020-10-08 13:12:17 +02:00
defaultLocale ,
2020-12-30 07:44:07 +01:00
domainLocales ,
2021-02-18 19:34:33 +01:00
isPreview ,
2021-05-13 12:39:36 +02:00
disableOptimizedLoading ,
2019-02-14 16:22:57 +01:00
} : RenderOpts & {
props : any
2020-08-24 04:42:51 +02:00
docComponentsRendered : DocumentProps [ 'docComponentsRendered' ]
2019-04-26 09:37:57 +02:00
docProps : DocumentInitialProps
2019-02-14 16:22:57 +01:00
pathname : string
query : ParsedUrlQuery
2019-04-15 11:26:23 +02:00
dangerousAsPath : string
2019-06-27 16:22:24 +02:00
ampState : any
2019-05-30 03:19:32 +02:00
ampPath : string
2019-06-27 16:22:24 +02:00
inAmpMode : boolean
hybridAmp : boolean
2021-04-21 13:18:05 +02:00
dynamicImportsIds : ( string | number ) [ ]
dynamicImports : string [ ]
2019-11-01 20:13:13 +01:00
headTags : any
2020-02-07 14:09:06 +01:00
isFallback? : boolean
2020-03-03 19:39:08 +01:00
gsp? : boolean
gssp? : boolean
2020-03-06 17:14:39 +01:00
customServer? : boolean
2020-04-13 11:59:49 +02:00
gip? : boolean
appGip? : boolean
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString : string
2020-12-01 19:10:16 +01:00
scriptLoader : any
2021-02-18 19:34:33 +01:00
isPreview? : boolean
2021-04-02 03:21:15 +02:00
autoExport? : boolean
2019-05-30 03:19:32 +02:00
}
2019-02-14 16:22:57 +01:00
) : string {
return (
'<!DOCTYPE html>' +
renderToStaticMarkup (
2019-06-27 16:22:24 +02:00
< AmpStateContext.Provider value = { ampState } >
2019-10-14 18:45:56 +02:00
{ Document . renderDocument ( Document , {
__NEXT_DATA__ : {
2019-04-13 02:04:52 +02:00
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 , // If this is a page exported by `next export`
2019-09-15 20:35:14 +02:00
autoExport , // If this is an auto exported page
2020-02-07 14:09:06 +01:00
isFallback ,
2019-05-30 03:19:32 +02:00
dynamicIds :
dynamicImportsIds . length === 0 ? undefined : dynamicImportsIds ,
2019-04-13 02:04:52 +02:00
err : err ? serializeError ( dev , err ) : undefined , // Error if one happened, otherwise don't sent in the resulting HTML
2020-03-03 19:39:08 +01:00
gsp , // whether the page is getStaticProps
gssp , // whether the page is getServerSideProps
2020-03-06 17:14:39 +01:00
customServer , // whether the user is using a custom server
2020-04-13 11:59:49 +02:00
gip , // whether the page has getInitialProps
appGip , // whether the _app has getInitialProps
2020-10-07 23:11:01 +02:00
locale ,
locales ,
2020-10-08 13:12:17 +02:00
defaultLocale ,
2020-12-30 07:44:07 +01:00
domainLocales ,
2021-02-18 19:34:33 +01:00
isPreview ,
2019-10-14 18:45:56 +02:00
} ,
2020-06-07 01:00:03 +02:00
buildManifest ,
2020-08-24 04:42:51 +02:00
docComponentsRendered ,
2019-10-14 18:45:56 +02:00
dangerousAsPath ,
canonicalBase ,
ampPath ,
inAmpMode ,
isDevelopment : ! ! dev ,
hybridAmp ,
dynamicImports ,
assetPrefix ,
2019-11-01 20:13:13 +01:00
headTags ,
2020-04-17 11:22:03 +02:00
unstable_runtimeJS ,
2021-01-19 20:38:15 +01:00
unstable_JsPreload ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2020-12-01 19:10:16 +01:00
scriptLoader ,
2020-10-07 23:11:01 +02:00
locale ,
2021-05-13 12:39:36 +02:00
disableOptimizedLoading ,
2019-10-14 18:45:56 +02:00
. . . docProps ,
} ) }
2019-06-27 16:22:24 +02:00
< / AmpStateContext.Provider >
2019-02-14 16:22:57 +01:00
)
2018-12-13 01:00:46 +01:00
)
}
2020-01-27 23:50:59 +01:00
const invalidKeysMsg = ( methodName : string , invalidKeys : string [ ] ) = > {
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 ( ', ' ) } . ` +
2021-03-29 10:25:00 +02:00
` \ nRead more: https://nextjs.org/docs/messages/invalid-getstaticprops-value `
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
)
}
}
2019-02-14 16:22:57 +01:00
export async function renderToHTML (
req : IncomingMessage ,
res : ServerResponse ,
pathname : string ,
query : ParsedUrlQuery ,
2019-05-30 03:19:32 +02:00
renderOpts : RenderOpts
2019-02-14 16:22:57 +01:00
) : Promise < string | 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 = '' ,
2018-12-18 17:12:49 +01:00
App ,
Document ,
2019-07-16 02:06:16 +02:00
pageConfig = { } ,
2018-12-18 17:12:49 +01:00
Component ,
buildManifest ,
2020-07-28 12:19:28 +02:00
fontManifest ,
2018-12-18 17:12:49 +01:00
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 ,
2018-12-13 01:00:46 +01:00
} = renderOpts
2018-12-18 17:12:49 +01:00
2020-07-28 12:19:28 +02:00
const getFontDefinition = ( url : string ) : string = > {
if ( fontManifest ) {
return getFontDefinitionFromManifest ( url , fontManifest )
}
return ''
}
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-02-07 14:09:06 +01:00
const isFallback = ! ! query . __nextFallback
delete query . __nextFallback
2020-10-09 11:13:05 +02:00
delete query . __nextLocale
2020-11-11 03:09:45 +01:00
delete query . __nextDefaultLocale
2020-02-07 14:09:06 +01:00
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
2019-09-24 10:50:04 +02:00
const hasPageGetInitialProps = ! ! ( Component as any ) . getInitialProps
2019-09-15 20:35:14 +02:00
2020-02-21 06:57:10 +01:00
const pageIsDynamic = isDynamicRoute ( pathname )
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 &&
2020-02-27 18:57:39 +01:00
! getServerSideProps
2019-09-24 10:50:04 +02:00
2020-04-04 01:08:17 +02:00
for ( const methodName of [
'getStaticProps' ,
'getServerSideProps' ,
'getStaticPaths' ,
] ) {
if ( ( Component as any ) [ methodName ] ) {
throw new Error (
` page ${ pathname } ${ methodName } ${ GSSP_COMPONENT_MEMBER_ERROR } `
)
}
}
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 ) {
const { isValidElementType } = require ( 'react-is' )
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
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
if ( ( isSSG || getServerSideProps ) && ! isFallback ) {
// 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
2020-12-31 17:54:32 +01:00
const routerIsReady = ! ! ( getServerSideProps || hasPageGetInitialProps )
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-02-11 11:18:24 +01:00
( req as any ) . __nextIsLocaleDomain
2020-04-14 09:50:39 +02:00
)
2019-05-31 02:34:05 +02:00
const ctx = {
err ,
2019-09-24 10:50:04 +02:00
req : isAutoExport ? undefined : req ,
res : isAutoExport ? undefined : res ,
2019-05-31 02:34:05 +02:00
pathname ,
query ,
asPath ,
2021-05-10 14:11:19 +02:00
locale : renderOpts.locale ,
locales : renderOpts.locales ,
defaultLocale : renderOpts.defaultLocale ,
2019-08-13 11:33:48 +02:00
AppTree : ( props : any ) = > {
return (
< AppContainer >
< App { ...props } Component = { Component } router = { router } / >
< / AppContainer >
)
} ,
2019-05-31 02:34:05 +02:00
}
2019-08-13 11:33:48 +02:00
let props : any
2019-03-17 17:43:03 +01:00
2019-07-03 04:16:12 +02:00
const ampState = {
ampFirst : pageConfig.amp === true ,
hasQuery : Boolean ( query . amp ) ,
hybrid : pageConfig.amp === 'hybrid' ,
}
2020-06-12 00:09:06 +02:00
const inAmpMode = isInAmpMode ( ampState )
2019-07-03 04:16:12 +02:00
const reactLoadableModules : string [ ] = [ ]
2020-06-12 00:09:06 +02:00
let head : JSX.Element [ ] = defaultHead ( inAmpMode )
2020-12-01 19:10:16 +01:00
let scriptLoader : any = { }
2019-07-03 04:16:12 +02:00
const AppContainer = ( { children } : any ) = > (
2019-07-30 20:00:19 +02:00
< RouterContext.Provider value = { router } >
2020-01-04 17:40:18 +01:00
< AmpStateContext.Provider value = { ampState } >
2020-06-12 00:09:06 +02:00
< HeadManagerContext.Provider
value = { {
updateHead : ( state ) = > {
head = state
} ,
2020-12-01 19:10:16 +01:00
updateScripts : ( scripts ) = > {
scriptLoader = scripts
} ,
scripts : { } ,
2020-06-12 00:09:06 +02:00
mountedInstances : new Set ( ) ,
} }
2020-01-04 17:40:18 +01:00
>
2020-06-12 00:09:06 +02:00
< LoadableContext.Provider
value = { ( moduleName ) = > reactLoadableModules . push ( moduleName ) }
>
{ children }
< / LoadableContext.Provider >
< / HeadManagerContext.Provider >
2020-01-04 17:40:18 +01:00
< / AmpStateContext.Provider >
2019-07-30 20:00:19 +02:00
< / RouterContext.Provider >
2019-07-03 04:16:12 +02:00
)
2019-03-17 17:43:03 +01:00
try {
2019-09-24 10:50:04 +02:00
props = await loadGetInitialProps ( App , {
AppTree : ctx.AppTree ,
Component ,
router ,
ctx ,
} )
2020-03-06 05:15:10 +01:00
2021-02-18 19:34:33 +01:00
if ( ( isSSG || getServerSideProps ) && isPreview ) {
props . __N_PREVIEW = true
2020-03-06 05:15:10 +01:00
}
2021-02-18 19:34:33 +01:00
if ( isSSG ) {
props [ STATIC_PROPS_ID ] = true
2020-03-04 13:58:12 +01:00
}
if ( isSSG && ! isFallback ) {
2020-03-24 09:33:39 +01:00
let data : UnwrapPromise < ReturnType < GetStaticProps > >
try {
data = await getStaticProps ! ( {
. . . ( pageIsDynamic ? { params : query as ParsedUrlQuery } : undefined ) ,
2021-02-18 19:34:33 +01:00
. . . ( isPreview
2020-03-24 09:33:39 +01:00
? { preview : true , previewData : previewData }
: undefined ) ,
2020-10-07 23:11:01 +02:00
locales : renderOpts.locales ,
locale : renderOpts.locale ,
2020-10-27 10:43:15 +01:00
defaultLocale : renderOpts.defaultLocale ,
2020-03-24 09:33:39 +01:00
} )
2020-06-01 23:00:22 +02:00
} catch ( staticPropsError ) {
2020-03-24 09:33:39 +01:00
// remove not found error code to prevent triggering legacy
// 404 rendering
2020-06-01 23:00:22 +02:00
if ( staticPropsError . code === 'ENOENT' ) {
delete staticPropsError . code
2020-03-24 09:33:39 +01:00
}
2020-06-01 23:00:22 +02:00
throw staticPropsError
2020-03-24 09:33:39 +01:00
}
2019-09-25 17:29:22 +02:00
2020-06-17 11:25:27 +02:00
if ( data == null ) {
throw new Error ( GSP_NO_RETURNED_VALUE )
}
2019-09-25 17:29:22 +02:00
const invalidKeys = Object . keys ( data ) . filter (
2020-09-08 09:23:21 +02:00
( key ) = >
2020-10-15 23:55:38 +02:00
key !== 'revalidate' &&
key !== 'props' &&
2020-10-27 09:37:27 +01:00
key !== 'redirect' &&
2020-10-27 09:54:06 +01:00
key !== 'notFound'
2019-09-25 17:29:22 +02:00
)
2020-07-20 20:23:51 +02:00
if ( invalidKeys . includes ( 'unstable_revalidate' ) ) {
2020-04-02 20:29:41 +02:00
throw new Error ( UNSTABLE_REVALIDATE_RENAME_ERROR )
}
2019-09-25 17:29:22 +02:00
if ( invalidKeys . length ) {
2020-01-27 23:50:59 +01:00
throw new Error ( invalidKeysMsg ( 'getStaticProps' , invalidKeys ) )
2019-09-25 17:29:22 +02:00
}
2020-11-04 23:18:44 +01:00
if ( process . env . NODE_ENV !== 'production' ) {
2020-11-11 23:02:11 +01:00
if (
typeof ( data as any ) . notFound !== 'undefined' &&
typeof ( data as any ) . redirect !== 'undefined'
) {
2020-11-04 23:18:44 +01:00
throw new Error (
` \` redirect \` and \` notFound \` can not both be returned from ${
isSSG ? 'getStaticProps' : 'getServerSideProps'
2021-03-29 10:25:00 +02:00
} at the same time . Page : $ { pathname } \ nSee more info here : https : //nextjs.org/docs/messages/gssp-mixed-not-found-redirect`
2020-11-04 23:18:44 +01:00
)
}
}
2020-10-27 09:54:06 +01:00
if ( 'notFound' in data && data . notFound ) {
2020-10-24 21:22:48 +02:00
if ( pathname === '/404' ) {
throw new Error (
2020-10-27 09:54:06 +01:00
` The /404 page can not return notFound in "getStaticProps", please remove it to continue! `
2020-10-24 21:22:48 +02:00
)
}
2020-10-27 06:42:12 +01:00
; ( renderOpts as any ) . isNotFound = true
2020-10-15 23:55:38 +02:00
}
2020-09-08 09:23:21 +02:00
if (
2020-10-27 09:37:27 +01:00
'redirect' in data &&
data . redirect &&
typeof data . redirect === 'object'
2020-09-08 09:23:21 +02:00
) {
2020-11-11 08:13:18 +01:00
checkRedirectValues ( data . redirect as Redirect , req , 'getStaticProps' )
2020-09-08 09:23:21 +02:00
if ( isBuildTimeSSG ) {
throw new Error (
` \` redirect \` can not be returned from getStaticProps during prerendering ( ${ req . url } ) \ n ` +
2021-03-29 10:25:00 +02:00
` See more info here: https://nextjs.org/docs/messages/gsp-redirect-during-prerender `
2020-09-08 09:23:21 +02:00
)
}
2020-11-04 23:18:44 +01:00
; ( data as any ) . props = {
__N_REDIRECT : data.redirect.destination ,
__N_REDIRECT_STATUS : getRedirectStatus ( data . redirect ) ,
2020-09-08 09:23:21 +02:00
}
2020-11-11 08:13:18 +01:00
if ( typeof data . redirect . basePath !== 'undefined' ) {
; ( data as any ) . props . __N_REDIRECT_BASE_PATH = data . redirect . basePath
}
2020-11-04 23:18:44 +01:00
; ( renderOpts as any ) . isRedirect = true
2020-09-08 09:23:21 +02:00
}
2020-03-09 18:30:44 +01:00
if (
( dev || isBuildTimeSSG ) &&
2020-11-14 08:12:47 +01:00
! ( renderOpts as any ) . isNotFound &&
2020-10-27 07:37:44 +01:00
! isSerializableProps ( pathname , 'getStaticProps' , ( data as any ) . props )
2020-03-09 18:30:44 +01:00
) {
// this fn should throw an error instead of ever returning `false`
throw new Error (
'invariant: getStaticProps did not return valid props. Please report this.'
)
}
2020-12-22 21:15:26 +01:00
if ( 'revalidate' in data ) {
if ( typeof data . revalidate === 'number' ) {
if ( ! Number . isInteger ( data . revalidate ) ) {
throw new Error (
` 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 \` ! `
)
}
} 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'
) {
// By default, we never revalidate.
data . revalidate = false
} else {
2019-09-27 18:46:19 +02:00
throw new Error (
2020-12-22 21:15:26 +01: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 } `
2019-09-25 17:29:22 +02:00
)
}
2019-12-11 22:32:16 +01:00
} else {
// By default, we never revalidate.
2020-10-27 07:37:44 +01:00
; ( data as any ) . revalidate = false
2019-09-25 17:29:22 +02:00
}
2020-11-14 08:12:47 +01:00
// this must come after revalidate is attached
if ( ( renderOpts as any ) . isNotFound ) {
return null
}
2020-10-27 07:37:44 +01:00
props . pageProps = Object . assign (
{ } ,
props . pageProps ,
'props' in data ? data.props : undefined
)
2021-02-18 19:34:33 +01:00
2019-09-24 10:50:04 +02:00
// pass up revalidate and props for export
2020-03-02 11:58:47 +01:00
// TODO: change this to a different passing mechanism
2020-10-27 07:37:44 +01:00
; ( renderOpts as any ) . revalidate =
'revalidate' in data ? data.revalidate : undefined
2020-01-15 02:22:15 +01:00
; ( renderOpts as any ) . pageData = props
2019-09-24 10:50:04 +02:00
}
2020-03-04 13:58:12 +01:00
2020-03-06 05:15:10 +01:00
if ( getServerSideProps ) {
props [ SERVER_PROPS_ID ] = true
}
2020-03-04 13:58:12 +01:00
if ( getServerSideProps && ! isFallback ) {
2020-03-24 09:33:39 +01:00
let data : UnwrapPromise < ReturnType < GetServerSideProps > >
try {
data = await getServerSideProps ( {
2021-01-26 15:26:59 +01:00
req : req as IncomingMessage & {
cookies : NextApiRequestCookies
} ,
2020-03-24 09:33:39 +01:00
res ,
query ,
2020-09-15 21:19:07 +02:00
resolvedUrl : renderOpts.resolvedUrl as string ,
2020-03-24 09:33:39 +01:00
. . . ( pageIsDynamic ? { params : params as ParsedUrlQuery } : undefined ) ,
. . . ( previewData !== false
? { preview : true , previewData : previewData }
: undefined ) ,
2020-10-07 23:11:01 +02:00
locales : renderOpts.locales ,
locale : renderOpts.locale ,
2020-10-27 10:43:15 +01:00
defaultLocale : renderOpts.defaultLocale ,
2020-03-24 09:33:39 +01:00
} )
2020-06-01 23:00:22 +02:00
} catch ( serverSidePropsError ) {
2020-03-24 09:33:39 +01:00
// remove not found error code to prevent triggering legacy
// 404 rendering
2020-06-01 23:00:22 +02:00
if ( serverSidePropsError . code === 'ENOENT' ) {
delete serverSidePropsError . code
2020-03-24 09:33:39 +01:00
}
2020-06-01 23:00:22 +02:00
throw serverSidePropsError
2020-03-24 09:33:39 +01:00
}
2020-03-04 13:58:12 +01:00
2020-06-17 11:25:27 +02:00
if ( data == null ) {
throw new Error ( GSSP_NO_RETURNED_VALUE )
}
2020-09-08 09:23:21 +02:00
const invalidKeys = Object . keys ( data ) . filter (
2020-10-27 09:54:06 +01:00
( key ) = > key !== 'props' && key !== 'redirect' && key !== 'notFound'
2020-09-08 09:23:21 +02:00
)
2020-03-04 13:58:12 +01:00
2020-10-27 09:54:06 +01: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-10-27 09:37:27 +01: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
if ( invalidKeys . length ) {
throw new Error ( invalidKeysMsg ( 'getServerSideProps' , invalidKeys ) )
}
2021-01-04 23:43:53 +01:00
if ( 'notFound' in data && data . notFound ) {
2020-10-27 06:42:12 +01:00
if ( pathname === '/404' ) {
throw new Error (
2020-10-27 09:54:06 +01:00
` The /404 page can not return notFound in "getStaticProps", please remove it to continue! `
2020-10-27 06:42:12 +01:00
)
}
; ( renderOpts as any ) . isNotFound = true
return null
}
2020-10-27 09:37:27 +01:00
if ( 'redirect' in data && typeof data . redirect === 'object' ) {
2020-11-11 08:13:18 +01:00
checkRedirectValues (
data . redirect as Redirect ,
req ,
'getServerSideProps'
)
2020-11-04 23:18:44 +01:00
; ( data as any ) . props = {
__N_REDIRECT : data.redirect.destination ,
__N_REDIRECT_STATUS : getRedirectStatus ( data . redirect ) ,
2020-09-08 09:23:21 +02:00
}
2020-11-11 08:13:18 +01:00
if ( typeof data . redirect . basePath !== 'undefined' ) {
; ( data as any ) . props . __N_REDIRECT_BASE_PATH = data . redirect . basePath
}
2020-11-04 23:18:44 +01:00
; ( renderOpts as any ) . isRedirect = true
2020-09-08 09:23:21 +02:00
}
2020-03-09 18:30:44 +01:00
if (
( dev || isBuildTimeSSG ) &&
2020-10-27 06:42:12 +01:00
! isSerializableProps (
pathname ,
'getServerSideProps' ,
( data as any ) . props
)
2020-03-09 18:30:44 +01:00
) {
// 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-10-27 06:42:12 +01:00
props . pageProps = Object . assign ( { } , props . pageProps , ( data as any ) . props )
2020-03-04 13:58:12 +01:00
; ( renderOpts as any ) . pageData = props
}
2020-06-01 23:00:22 +02:00
} catch ( dataFetchError ) {
if ( isDataReq || ! dev || ! dataFetchError ) throw dataFetchError
ctx . err = dataFetchError
renderOpts . err = dataFetchError
console . error ( dataFetchError )
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 ) {
return props
}
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
}
}
2020-06-12 00:09:06 +02:00
const renderPage : RenderPage = (
options : ComponentsEnhancer = { }
) : { html : string ; head : any } = > {
2019-04-03 00:32:07 +02:00
if ( ctx . err && ErrorDebug ) {
2020-06-12 00:09:06 +02:00
return { html : renderToString ( < ErrorDebug error = { ctx . err } / > ) , head }
2019-04-03 00:32:07 +02:00
}
if ( dev && ( props . router || props . Component ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` 'router' and 'Component' can not be returned in getInitialProps from _app.js https://nextjs.org/docs/messages/cant-override-next-props `
2019-04-03 00:32:07 +02:00
)
}
2020-01-04 17:40:18 +01:00
const {
App : EnhancedApp ,
Component : EnhancedComponent ,
} = enhanceComponents ( options , App , Component )
2020-06-12 00:09:06 +02:00
const html = renderToString (
2020-01-04 17:40:18 +01:00
< AppContainer >
< EnhancedApp Component = { EnhancedComponent } router = { router } { ...props } / >
2020-06-12 00:09:06 +02:00
< / AppContainer >
2020-01-04 17:40:18 +01:00
)
2020-06-12 00:09:06 +02:00
return { html , head }
2019-04-02 16:09:34 +02:00
}
2019-11-01 20:13:13 +01:00
const documentCtx = { . . . ctx , renderPage }
2020-01-27 23:50:59 +01:00
const docProps : DocumentInitialProps = await loadGetInitialProps (
Document ,
documentCtx
)
2019-01-02 20:21:57 +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
2019-04-22 19:55:03 +02:00
if ( ! docProps || typeof docProps . html !== 'string' ) {
2019-05-30 03:19:32 +02:00
const message = ` " ${ getDisplayName (
Document
) } . getInitialProps ( ) " should resolve to an object with a " html " prop set with a valid html string `
2019-04-22 19:55:03 +02:00
throw new Error ( message )
}
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-04-02 03:21:15 +02:00
const nextExport =
! isSSG && ( renderOpts . nextExport || ( dev && ( isAutoExport || isFallback ) ) )
2020-08-24 04:42:51 +02:00
2019-04-02 20:01:34 +02:00
let html = renderDocument ( Document , {
2018-12-13 01:00:46 +01:00
. . . renderOpts ,
2020-10-16 01:00:08 +02:00
canonicalBase :
! renderOpts . ampPath && ( req as any ) . __nextStrippedLocale
? ` ${ renderOpts . canonicalBase || '' } / ${ renderOpts . locale } `
: renderOpts . canonicalBase ,
2020-08-24 04:42:51 +02:00
docComponentsRendered ,
2020-08-13 14:39:36 +02:00
buildManifest : filteredBuildManifest ,
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 ,
2019-04-15 11:26:23 +02:00
dangerousAsPath : router.asPath ,
2019-06-27 16:22:24 +02:00
ampState ,
2018-12-13 01:00:46 +01:00
props ,
2019-11-01 20:13:13 +01:00
headTags : await headTags ( documentCtx ) ,
2020-02-07 14:09:06 +01:00
isFallback ,
2018-12-13 01:00:46 +01:00
docProps ,
pathname ,
2019-03-20 04:53:47 +01:00
ampPath ,
2018-12-13 01:00:46 +01:00
query ,
2019-06-27 16:22:24 +02:00
inAmpMode ,
hybridAmp ,
2021-04-21 13:18:05 +02:00
dynamicImportsIds : Array.from ( dynamicImportsIds ) ,
dynamicImports : Array.from ( dynamicImports ) ,
2020-03-03 19:39:08 +01:00
gsp : ! ! getStaticProps ? true : undefined ,
gssp : ! ! getServerSideProps ? true : undefined ,
2020-04-13 11:59:49 +02:00
gip : hasPageGetInitialProps ? true : undefined ,
appGip : ! defaultAppGetInitialProps ? true : undefined ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2020-12-01 19:10:16 +01:00
scriptLoader ,
2021-02-18 19:34:33 +01:00
isPreview : isPreview === true ? true : undefined ,
2021-04-02 03:21:15 +02:00
autoExport : isAutoExport === true ? true : undefined ,
nextExport : nextExport === true ? true : undefined ,
2018-12-13 01:00:46 +01:00
} )
2019-04-02 20:01:34 +02:00
2020-08-24 04:42:51 +02:00
if ( process . env . NODE_ENV !== 'production' ) {
const nonRenderedComponents = [ ]
const expectedDocComponents = [ 'Main' , 'Head' , 'NextScript' , 'Html' ]
for ( const comp of expectedDocComponents ) {
if ( ! ( docComponentsRendered as any ) [ comp ] ) {
nonRenderedComponents . push ( comp )
}
}
const plural = nonRenderedComponents . length !== 1 ? 's' : ''
if ( nonRenderedComponents . length ) {
2020-09-12 20:57:07 +02:00
const missingComponentList = nonRenderedComponents
. map ( ( e ) = > ` < ${ e } /> ` )
. join ( ', ' )
warn (
` Your custom Document (pages/_document) did not render all the required subcomponent ${ plural } . \ n ` +
` Missing component ${ plural } : ${ missingComponentList } \ n ` +
2021-03-29 10:25:00 +02:00
'Read how to fix here: https://nextjs.org/docs/messages/missing-document-component'
2020-08-24 04:42:51 +02:00
)
}
}
2019-06-27 16:22:24 +02:00
if ( inAmpMode && html ) {
2019-11-25 21:05:52 +01:00
// inject HTML to AMP_RENDER_TARGET to allow rendering
// directly to body in AMP mode
const ampRenderIndex = html . indexOf ( AMP_RENDER_TARGET )
html =
html . substring ( 0 , ampRenderIndex ) +
` <!-- __NEXT_DATA__ --> ${ docProps . html } ` +
html . substring ( ampRenderIndex + AMP_RENDER_TARGET . length )
2020-03-24 09:31:04 +01:00
html = await optimizeAmp ( html , renderOpts . ampOptimizerConfig )
2019-04-11 20:59:26 +02:00
2020-03-24 09:31:04 +01:00
if ( ! renderOpts . ampSkipValidation && renderOpts . ampValidator ) {
2019-04-11 20:59:26 +02:00
await renderOpts . ampValidator ( html , pathname )
}
2019-04-02 20:01:34 +02:00
}
2019-04-16 15:57:17 +02:00
2020-12-01 19:02:07 +01:00
// Avoid postProcess if both flags are false
2020-12-21 20:26:00 +01:00
if ( process . env . __NEXT_OPTIMIZE_FONTS || process . env . __NEXT_OPTIMIZE_IMAGES ) {
html = await postProcess (
html ,
{ getFontDefinition } ,
{
optimizeFonts : renderOpts.optimizeFonts ,
optimizeImages : renderOpts.optimizeImages ,
}
)
}
2020-12-01 19:02:07 +01:00
if ( renderOpts . optimizeCss ) {
// eslint-disable-next-line import/no-extraneous-dependencies
const Critters = require ( 'critters' )
const cssOptimizer = new Critters ( {
ssrMode : true ,
reduceInlineStyles : false ,
path : renderOpts.distDir ,
publicPath : '/_next/' ,
preload : 'media' ,
fonts : false ,
. . . renderOpts . optimizeCss ,
} )
html = await cssOptimizer . process ( html )
}
2020-07-28 12:19:28 +02:00
2019-06-27 16:22:24 +02:00
if ( inAmpMode || hybridAmp ) {
2019-04-16 15:57:17 +02:00
// fix & being escaped for amphtml rel link
html = html . replace ( /&amp=1/g , '&=1' )
}
2019-09-24 10:50:04 +02:00
2019-04-02 20:01:34 +02:00
return html
2018-12-13 01:00:46 +01:00
}
2019-02-08 11:57:29 +01:00
function errorToJSON ( err : Error ) : Error {
2018-12-13 01:00:46 +01:00
const { name , message , stack } = err
return { name , message , stack }
}
2019-02-14 16:22:57 +01:00
function serializeError (
dev : boolean | undefined ,
2019-05-30 03:19:32 +02:00
err : Error
2019-02-14 16:22:57 +01:00
) : Error & { statusCode? : number } {
2018-12-13 01:00:46 +01:00
if ( dev ) {
return errorToJSON ( err )
}
2019-02-14 16:22:57 +01:00
return {
name : 'Internal Server Error.' ,
message : '500 - Internal Server Error.' ,
statusCode : 500 ,
}
2018-12-13 01:00:46 +01:00
}