2020-12-01 19:02:07 +01:00
import React , { Component , ReactElement , ReactNode , useContext } from 'react'
2019-11-11 04:24:53 +01:00
import flush from 'styled-jsx/server'
2020-07-28 12:19:28 +02:00
import {
2021-08-13 05:36:54 +02:00
BODY_RENDER_TARGET ,
2020-07-28 12:19:28 +02:00
OPTIMIZED_FONT_PROVIDERS ,
2021-06-30 11:43:31 +02:00
} from '../shared/lib/constants'
2019-11-11 04:24:53 +01:00
import {
DocumentContext ,
DocumentInitialProps ,
DocumentProps ,
2021-08-13 05:36:54 +02:00
HtmlContext ,
HtmlProps ,
2021-06-30 11:43:31 +02:00
} from '../shared/lib/utils'
2021-06-30 13:44:40 +02:00
import { BuildManifest , getPageFiles } from '../server/get-page-files'
import { cleanAmpPath } from '../server/utils'
2019-11-11 04:24:53 +01:00
import { htmlEscapeJsonString } from '../server/htmlescape'
2021-07-07 18:35:50 +02:00
import Script , { ScriptProps } from '../client/script'
2018-10-03 00:08:57 +02:00
2019-04-26 09:37:57 +02:00
export { DocumentContext , DocumentInitialProps , DocumentProps }
2019-04-22 19:55:03 +02:00
2019-04-26 09:37:57 +02:00
export type OriginProps = {
2019-04-22 19:55:03 +02:00
nonce? : string
2019-05-30 03:19:32 +02:00
crossOrigin? : string
2019-04-22 19:55:03 +02:00
}
2020-08-14 16:20:03 +02:00
type DocumentFiles = {
sharedFiles : readonly string [ ]
pageFiles : readonly string [ ]
allFiles : readonly string [ ]
}
function getDocumentFiles (
buildManifest : BuildManifest ,
2020-12-22 04:18:01 +01:00
pathname : string ,
inAmpMode : boolean
2020-08-14 16:20:03 +02:00
) : DocumentFiles {
const sharedFiles : readonly string [ ] = getPageFiles ( buildManifest , '/_app' )
2020-12-22 04:18:01 +01:00
const pageFiles : readonly string [ ] = inAmpMode
? [ ]
: getPageFiles ( buildManifest , pathname )
2020-08-14 16:20:03 +02:00
return {
sharedFiles ,
pageFiles ,
allFiles : [ . . . new Set ( [ . . . sharedFiles , . . . pageFiles ] ) ] ,
}
}
2021-08-13 05:36:54 +02:00
function getPolyfillScripts ( context : HtmlProps , props : OriginProps ) {
2021-05-13 12:39:36 +02:00
// polyfills.js has to be rendered as nomodule without async
// It also has to be the first script to load
const {
assetPrefix ,
buildManifest ,
devOnlyCacheBusterQueryString ,
disableOptimizedLoading ,
} = context
return buildManifest . polyfillFiles
. filter (
( polyfill ) = > polyfill . endsWith ( '.js' ) && ! polyfill . endsWith ( '.module.js' )
)
. map ( ( polyfill ) = > (
< script
key = { polyfill }
defer = { ! disableOptimizedLoading }
nonce = { props . nonce }
crossOrigin = { props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN }
noModule = { true }
src = { ` ${ assetPrefix } /_next/ ${ polyfill } ${ devOnlyCacheBusterQueryString } ` }
/ >
) )
}
2021-08-13 05:36:54 +02:00
function getPreNextScripts ( context : HtmlProps , props : OriginProps ) {
2021-05-13 12:39:36 +02:00
const { scriptLoader , disableOptimizedLoading } = context
return ( scriptLoader . beforeInteractive || [ ] ) . map (
2021-07-07 18:35:50 +02:00
( file : ScriptProps , index : number ) = > {
2021-05-13 12:39:36 +02:00
const { strategy , . . . scriptProps } = file
return (
< script
{ . . . scriptProps }
2021-07-02 22:23:11 +02:00
key = { scriptProps . src || index }
2021-05-13 12:39:36 +02:00
defer = { ! disableOptimizedLoading }
nonce = { props . nonce }
crossOrigin = { props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN }
/ >
)
}
)
}
function getDynamicChunks (
2021-08-13 05:36:54 +02:00
context : HtmlProps ,
2021-05-13 12:39:36 +02:00
props : OriginProps ,
files : DocumentFiles
) {
const {
dynamicImports ,
assetPrefix ,
isDevelopment ,
devOnlyCacheBusterQueryString ,
disableOptimizedLoading ,
} = context
return dynamicImports . map ( ( file ) = > {
if ( ! file . endsWith ( '.js' ) || files . allFiles . includes ( file ) ) return null
return (
< script
async = { ! isDevelopment && disableOptimizedLoading }
defer = { ! disableOptimizedLoading }
key = { file }
src = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
) } $ { devOnlyCacheBusterQueryString } ` }
nonce = { props . nonce }
crossOrigin = { props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN }
/ >
)
} )
}
function getScripts (
2021-08-13 05:36:54 +02:00
context : HtmlProps ,
2021-05-13 12:39:36 +02:00
props : OriginProps ,
files : DocumentFiles
) {
const {
assetPrefix ,
buildManifest ,
isDevelopment ,
devOnlyCacheBusterQueryString ,
disableOptimizedLoading ,
} = context
const normalScripts = files . allFiles . filter ( ( file ) = > file . endsWith ( '.js' ) )
const lowPriorityScripts = buildManifest . lowPriorityFiles ? . filter ( ( file ) = >
file . endsWith ( '.js' )
)
return [ . . . normalScripts , . . . lowPriorityScripts ] . map ( ( file ) = > {
return (
< script
key = { file }
src = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
) } $ { devOnlyCacheBusterQueryString } ` }
nonce = { props . nonce }
async = { ! isDevelopment && disableOptimizedLoading }
defer = { ! disableOptimizedLoading }
crossOrigin = { props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN }
/ >
)
} )
}
2019-05-23 21:31:22 +02:00
/ * *
* ` Document ` component handles the initial ` document ` markup and renders only on the server side .
* Commonly used for implementing server side rendering for ` css-in-js ` libraries .
* /
2019-04-29 18:47:40 +02:00
export default class Document < P = {} > extends Component < DocumentProps & P > {
2019-05-23 21:31:22 +02:00
/ * *
2019-08-06 13:23:50 +02:00
* ` getInitialProps ` hook returns the context object with the addition of ` renderPage ` .
2019-05-23 21:31:22 +02:00
* ` renderPage ` callback executes ` React ` rendering logic synchronously to support server - rendering wrappers
* /
2019-11-01 20:13:13 +01:00
static async getInitialProps (
ctx : DocumentContext
) : Promise < DocumentInitialProps > {
const enhanceApp = ( App : any ) = > {
return ( props : any ) = > < App { ...props } / >
}
2020-01-04 17:40:18 +01:00
const { html , head } = await ctx . renderPage ( { enhanceApp } )
2020-06-29 16:26:49 +02:00
const styles = [ . . . flush ( ) ]
2020-01-04 17:40:18 +01:00
return { html , head , styles }
2018-10-03 00:08:57 +02:00
}
2019-02-20 15:46:29 +01:00
render() {
return (
< Html >
< Head / >
< body >
< Main / >
< NextScript / >
< / body >
< / Html >
)
}
}
2020-06-07 01:00:03 +02:00
export function Html (
props : React.DetailedHTMLProps <
2019-06-06 15:41:31 +02:00
React . HtmlHTMLAttributes < HTMLHtmlElement > ,
HTMLHtmlElement
>
2020-06-07 01:00:03 +02:00
) {
2021-08-13 05:36:54 +02:00
const { inAmpMode , docComponentsRendered , locale } = useContext ( HtmlContext )
2020-08-24 04:42:51 +02:00
docComponentsRendered . Html = true
2020-06-07 01:00:03 +02:00
return (
< html
{ . . . props }
2020-10-07 23:11:01 +02:00
lang = { props . lang || locale || undefined }
2020-06-07 01:00:03 +02:00
amp = { inAmpMode ? '' : undefined }
data - ampdevmode = {
inAmpMode && process . env . NODE_ENV !== 'production' ? '' : undefined
}
/ >
)
2018-10-03 00:08:57 +02:00
}
2019-06-06 15:41:31 +02:00
export class Head extends Component <
OriginProps &
React . DetailedHTMLProps <
React . HTMLAttributes < HTMLHeadElement > ,
HTMLHeadElement
>
> {
2021-08-13 05:36:54 +02:00
static contextType = HtmlContext
2018-10-03 00:08:57 +02:00
2021-08-13 05:36:54 +02:00
context ! : React . ContextType < typeof HtmlContext >
2019-04-22 19:55:03 +02:00
2020-08-14 16:20:03 +02:00
getCssLinks ( files : DocumentFiles ) : JSX . Element [ ] | null {
2020-08-24 07:20:11 +02:00
const {
assetPrefix ,
devOnlyCacheBusterQueryString ,
dynamicImports ,
} = this . context
2020-08-14 16:20:03 +02:00
const cssFiles = files . allFiles . filter ( ( f ) = > f . endsWith ( '.css' ) )
2020-11-03 06:23:31 +01:00
const sharedFiles : Set < string > = new Set ( files . sharedFiles )
2019-10-02 23:47:34 +02:00
2020-11-03 06:23:31 +01:00
// Unmanaged files are CSS files that will be handled directly by the
// webpack runtime (`mini-css-extract-plugin`).
let unmangedFiles : Set < string > = new Set ( [ ] )
2021-04-21 13:18:05 +02:00
let dynamicCssFiles = Array . from (
new Set ( dynamicImports . filter ( ( file ) = > file . endsWith ( '.css' ) ) )
)
2020-08-24 07:20:11 +02:00
if ( dynamicCssFiles . length ) {
const existing = new Set ( cssFiles )
dynamicCssFiles = dynamicCssFiles . filter (
( f ) = > ! ( existing . has ( f ) || sharedFiles . has ( f ) )
)
2020-11-03 06:23:31 +01:00
unmangedFiles = new Set ( dynamicCssFiles )
2020-08-24 07:20:11 +02:00
cssFiles . push ( . . . dynamicCssFiles )
}
2020-12-01 19:02:07 +01:00
let cssLinkElements : JSX.Element [ ] = [ ]
2020-05-18 21:24:37 +02:00
cssFiles . forEach ( ( file ) = > {
2020-08-17 23:20:05 +02:00
const isSharedFile = sharedFiles . has ( file )
2020-12-01 19:02:07 +01:00
if ( ! process . env . __NEXT_OPTIMIZE_CSS ) {
cssLinkElements . push (
< link
key = { ` ${ file } -preload ` }
nonce = { this . props . nonce }
rel = "preload"
href = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
) } $ { devOnlyCacheBusterQueryString } ` }
as = "style"
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
/ >
)
}
2020-11-03 06:23:31 +01:00
const isUnmanagedFile = unmangedFiles . has ( file )
2019-10-16 06:01:11 +02:00
cssLinkElements . push (
< link
key = { file }
nonce = { this . props . nonce }
rel = "stylesheet"
2020-02-22 10:39:36 +01:00
href = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
2020-08-03 16:22:55 +02:00
) } $ { devOnlyCacheBusterQueryString } ` }
2020-06-07 01:00:03 +02:00
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
2020-11-03 06:23:31 +01:00
data - n - g = { isUnmanagedFile ? undefined : isSharedFile ? '' : undefined }
data - n - p = { isUnmanagedFile ? undefined : isSharedFile ? undefined : '' }
2019-10-16 06:01:11 +02:00
/ >
)
} )
2020-12-01 19:02:07 +01:00
2020-12-21 20:26:00 +01:00
if (
process . env . NODE_ENV !== 'development' &&
process . env . __NEXT_OPTIMIZE_FONTS
) {
2020-12-01 19:02:07 +01:00
cssLinkElements = this . makeStylesheetInert (
cssLinkElements
) as ReactElement [ ]
}
2019-10-16 06:01:11 +02:00
return cssLinkElements . length === 0 ? null : cssLinkElements
2018-10-03 00:08:57 +02:00
}
2019-03-01 20:51:13 +01:00
getPreloadDynamicChunks() {
2020-08-03 16:22:55 +02:00
const {
dynamicImports ,
assetPrefix ,
devOnlyCacheBusterQueryString ,
} = this . context
2019-02-03 01:12:49 +01:00
2019-07-25 04:16:32 +02:00
return (
2021-04-21 13:18:05 +02:00
dynamicImports
. map ( ( file ) = > {
if ( ! file . endsWith ( '.js' ) ) {
2019-07-25 04:16:32 +02:00
return null
}
return (
< link
rel = "preload"
2021-04-21 13:18:05 +02:00
key = { file }
2019-08-29 18:56:24 +02:00
href = { ` ${ assetPrefix } /_next/ ${ encodeURI (
2021-04-21 13:18:05 +02:00
file
2020-08-03 16:22:55 +02:00
) } $ { devOnlyCacheBusterQueryString } ` }
2019-07-25 04:16:32 +02:00
as = "script"
nonce = { this . props . nonce }
2020-06-07 01:00:03 +02:00
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
2019-07-25 04:16:32 +02:00
/ >
)
} )
// Filter out nulled scripts
. filter ( Boolean )
)
2018-10-03 00:08:57 +02:00
}
2020-08-14 16:20:03 +02:00
getPreloadMainLinks ( files : DocumentFiles ) : JSX . Element [ ] | null {
2020-12-01 19:10:16 +01:00
const {
assetPrefix ,
devOnlyCacheBusterQueryString ,
scriptLoader ,
} = this . context
2020-08-14 16:20:03 +02:00
const preloadFiles = files . allFiles . filter ( ( file : string ) = > {
2020-11-18 19:30:00 +01:00
return file . endsWith ( '.js' )
2020-08-14 16:20:03 +02:00
} )
2018-10-03 00:08:57 +02:00
2020-12-01 19:10:16 +01:00
return [
2021-05-12 13:37:57 +02:00
. . . ( scriptLoader . beforeInteractive || [ ] ) . map ( ( file ) = > (
2020-12-01 19:10:16 +01:00
< link
key = { file . src }
nonce = { this . props . nonce }
rel = "preload"
href = { file . src }
as = "script"
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
/ >
) ) ,
. . . preloadFiles . map ( ( file : string ) = > (
< link
key = { file }
nonce = { this . props . nonce }
rel = "preload"
href = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
) } $ { devOnlyCacheBusterQueryString } ` }
as = "script"
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
/ >
) ) ,
]
2018-10-03 00:08:57 +02:00
}
2021-05-13 12:39:36 +02:00
getDynamicChunks ( files : DocumentFiles ) {
return getDynamicChunks ( this . context , this . props , files )
}
getPreNextScripts() {
return getPreNextScripts ( this . context , this . props )
}
getScripts ( files : DocumentFiles ) {
return getScripts ( this . context , this . props , files )
}
getPolyfillScripts() {
return getPolyfillScripts ( this . context , this . props )
}
2021-03-02 20:17:33 +01:00
handleDocumentScriptLoaderItems ( children : React.ReactNode ) : ReactNode [ ] {
const { scriptLoader } = this . context
2021-07-07 18:35:50 +02:00
const scriptLoaderItems : ScriptProps [ ] = [ ]
2021-03-02 20:17:33 +01:00
const filteredChildren : ReactNode [ ] = [ ]
React . Children . forEach ( children , ( child : any ) = > {
if ( child . type === Script ) {
2021-05-12 13:37:57 +02:00
if ( child . props . strategy === 'beforeInteractive' ) {
scriptLoader . beforeInteractive = (
scriptLoader . beforeInteractive || [ ]
2021-05-10 10:39:08 +02:00
) . concat ( [
2021-03-02 20:17:33 +01:00
{
. . . child . props ,
} ,
] )
return
2021-05-10 10:39:08 +02:00
} else if (
2021-05-12 13:37:57 +02:00
[ 'lazyOnload' , 'afterInteractive' ] . includes ( child . props . strategy )
2021-05-10 10:39:08 +02:00
) {
2021-03-02 20:17:33 +01:00
scriptLoaderItems . push ( child . props )
return
}
}
filteredChildren . push ( child )
} )
this . context . __NEXT_DATA__ . scriptLoader = scriptLoaderItems
return filteredChildren
}
2020-12-01 19:02:07 +01:00
makeStylesheetInert ( node : ReactNode ) : ReactNode [ ] {
2020-07-28 12:19:28 +02:00
return React . Children . map ( node , ( c : any ) = > {
if (
c . type === 'link' &&
c . props [ 'href' ] &&
2021-06-02 11:43:03 +02:00
OPTIMIZED_FONT_PROVIDERS . some ( ( { url } ) = >
c . props [ 'href' ] . startsWith ( url )
)
2020-07-28 12:19:28 +02:00
) {
const newProps = { . . . ( c . props || { } ) }
newProps [ 'data-href' ] = newProps [ 'href' ]
newProps [ 'href' ] = undefined
return React . cloneElement ( c , newProps )
} else if ( c . props && c . props [ 'children' ] ) {
c . props [ 'children' ] = this . makeStylesheetInert ( c . props [ 'children' ] )
}
return c
} )
}
2019-03-01 20:51:13 +01:00
render() {
const {
styles ,
2019-03-20 04:53:47 +01:00
ampPath ,
2019-06-27 16:22:24 +02:00
inAmpMode ,
hybridAmp ,
2019-05-29 02:32:18 +02:00
canonicalBase ,
2019-03-01 20:51:13 +01:00
__NEXT_DATA__ ,
2019-04-15 11:26:23 +02:00
dangerousAsPath ,
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 ,
2021-05-13 12:39:36 +02:00
disableOptimizedLoading ,
2020-08-03 16:22:55 +02:00
} = this . context
2021-05-13 12:39:36 +02:00
2020-04-17 11:22:03 +02:00
const disableRuntimeJS = unstable_runtimeJS === false
2021-05-13 12:39:36 +02:00
const disableJsPreload =
unstable_JsPreload === false || ! disableOptimizedLoading
2018-10-03 00:08:57 +02:00
2020-08-24 04:42:51 +02:00
this . context . docComponentsRendered . Head = true
2020-08-03 16:22:55 +02:00
let { head } = this . context
2020-12-30 23:12:46 +01:00
let cssPreloads : Array < JSX.Element > = [ ]
let otherHeadElements : Array < JSX.Element > = [ ]
if ( head ) {
head . forEach ( ( c ) = > {
if (
c &&
c . type === 'link' &&
c . props [ 'rel' ] === 'preload' &&
c . props [ 'as' ] === 'style'
) {
cssPreloads . push ( c )
} else {
c && otherHeadElements . push ( c )
}
} )
head = cssPreloads . concat ( otherHeadElements )
}
2021-04-05 19:47:03 +02:00
let children = React . Children . toArray ( this . props . children ) . filter ( Boolean )
2018-10-03 00:08:57 +02:00
// show a warning if Head contains <title> (only in development)
if ( process . env . NODE_ENV !== 'production' ) {
2019-04-22 19:55:03 +02:00
children = React . Children . map ( children , ( child : any ) = > {
2020-05-02 06:52:56 +02:00
const isReactHelmet = child ? . props ? . [ 'data-react-helmet' ]
2020-07-07 07:43:16 +02:00
if ( ! isReactHelmet ) {
if ( child ? . type === 'title' ) {
console . warn (
2021-03-29 10:25:00 +02:00
"Warning: <title> should not be used in _document.js's <Head>. https://nextjs.org/docs/messages/no-document-title"
2020-07-07 07:43:16 +02:00
)
} else if (
child ? . type === 'meta' &&
child ? . props ? . name === 'viewport'
) {
console . warn (
2021-03-29 10:25:00 +02:00
"Warning: viewport meta tags should not be used in _document.js's <Head>. https://nextjs.org/docs/messages/no-document-viewport-meta"
2020-07-07 07:43:16 +02:00
)
}
2018-10-03 00:08:57 +02:00
}
return child
} )
2019-03-01 20:51:13 +01:00
if ( this . props . crossOrigin )
console . warn (
2021-03-29 10:25:00 +02:00
'Warning: `Head` attribute `crossOrigin` is deprecated. https://nextjs.org/docs/messages/doc-crossorigin-deprecated'
2019-03-01 20:51:13 +01:00
)
2018-10-03 00:08:57 +02:00
}
2019-05-04 18:46:50 +02:00
2020-12-21 20:26:00 +01:00
if (
process . env . NODE_ENV !== 'development' &&
process . env . __NEXT_OPTIMIZE_FONTS &&
! inAmpMode
) {
2020-07-28 12:19:28 +02:00
children = this . makeStylesheetInert ( children )
}
2021-06-03 13:56:50 +02:00
children = this . handleDocumentScriptLoaderItems ( children )
2021-03-02 20:17:33 +01:00
2019-07-29 09:18:23 +02:00
let hasAmphtmlRel = false
let hasCanonicalRel = false
2019-03-18 12:29:31 +01:00
// show warning and remove conflicting amp head tags
2020-05-18 21:24:37 +02:00
head = React . Children . map ( head || [ ] , ( child ) = > {
2019-10-18 17:22:53 +02:00
if ( ! child ) return child
const { type , props } = child
if ( inAmpMode ) {
let badProp : string = ''
if ( type === 'meta' && props . name === 'viewport' ) {
badProp = 'name="viewport"'
} else if ( type === 'link' && props . rel === 'canonical' ) {
hasCanonicalRel = true
} else if ( type === 'script' ) {
// only block if
// 1. it has a src and isn't pointing to ampproject's CDN
// 2. it is using dangerouslySetInnerHTML without a type or
// a type of text/javascript
if (
( props . src && props . src . indexOf ( 'ampproject' ) < - 1 ) ||
( props . dangerouslySetInnerHTML &&
( ! props . type || props . type === 'text/javascript' ) )
) {
badProp = '<script'
2020-05-18 21:24:37 +02:00
Object . keys ( props ) . forEach ( ( prop ) = > {
2019-10-18 17:22:53 +02:00
badProp += ` ${ prop } =" ${ props [ prop ] } " `
} )
badProp += '/>'
2019-05-30 03:19:32 +02:00
}
2019-10-18 17:22:53 +02:00
}
2019-03-19 00:21:18 +01:00
2019-10-18 17:22:53 +02:00
if ( badProp ) {
console . warn (
2021-03-29 10:25:00 +02:00
` Found conflicting amp tag " ${ child . type } " with conflicting prop ${ badProp } in ${ __NEXT_DATA__ . page } . https://nextjs.org/docs/messages/conflicting-amp-tag `
2019-10-18 17:22:53 +02:00
)
return null
}
} else {
// non-amp mode
if ( type === 'link' && props . rel === 'amphtml' ) {
hasAmphtmlRel = true
}
}
return child
} )
2019-04-22 17:32:46 +02:00
// try to parse styles from fragment for backwards compat
2019-07-10 07:39:07 +02:00
const curStyles : React.ReactElement [ ] = Array . isArray ( styles )
? ( styles as React . ReactElement [ ] )
: [ ]
2019-05-30 03:19:32 +02:00
if (
2019-06-27 16:22:24 +02:00
inAmpMode &&
2019-05-30 03:19:32 +02:00
styles &&
// @ts-ignore Property 'props' does not exist on type ReactElement
styles . props &&
2019-04-22 19:55:03 +02:00
// @ts-ignore Property 'props' does not exist on type ReactElement
2019-05-30 03:19:32 +02:00
Array . isArray ( styles . props . children )
2019-04-22 17:32:46 +02:00
) {
2019-05-30 03:19:32 +02:00
const hasStyles = ( el : React.ReactElement ) = >
2020-05-02 06:52:56 +02:00
el ? . props ? . dangerouslySetInnerHTML ? . __html
2019-04-22 19:55:03 +02:00
// @ts-ignore Property 'props' does not exist on type ReactElement
2019-11-11 04:24:53 +01:00
styles . props . children . forEach ( ( child : React.ReactElement ) = > {
2019-04-22 17:32:46 +02:00
if ( Array . isArray ( child ) ) {
2020-05-18 21:24:37 +02:00
child . forEach ( ( el ) = > hasStyles ( el ) && curStyles . push ( el ) )
2019-04-22 17:32:46 +02:00
} else if ( hasStyles ( child ) ) {
curStyles . push ( child )
}
} )
}
2020-08-14 16:20:03 +02:00
const files : DocumentFiles = getDocumentFiles (
this . context . buildManifest ,
2020-12-22 04:18:01 +01:00
this . context . __NEXT_DATA__ . page ,
inAmpMode
2020-08-14 16:20:03 +02:00
)
2020-12-22 04:18:01 +01:00
2019-03-01 20:51:13 +01:00
return (
< head { ...this.props } >
2020-08-03 16:22:55 +02:00
{ this . context . isDevelopment && (
2020-04-25 21:00:41 +02:00
< >
< style
data - next - hide - fouc
data - ampdevmode = { inAmpMode ? 'true' : undefined }
dangerouslySetInnerHTML = { {
__html : ` body{display:none} ` ,
} }
/ >
< noscript
data - next - hide - fouc
data - ampdevmode = { inAmpMode ? 'true' : undefined }
>
2019-09-17 22:05:20 +02:00
< style
dangerouslySetInnerHTML = { {
2020-04-25 21:00:41 +02:00
__html : ` body{display:block} ` ,
2019-09-17 22:05:20 +02:00
} }
/ >
2020-04-25 21:00:41 +02:00
< / noscript >
< / >
) }
2019-03-01 20:51:13 +01:00
{ children }
2021-06-02 11:43:03 +02:00
{ process . env . __NEXT_OPTIMIZE_FONTS && (
< meta name = "next-font-preconnect" / >
) }
2019-03-01 20:51:13 +01:00
{ head }
2020-11-10 22:35:47 +01:00
< meta
name = "next-head-count"
content = { React . Children . count ( head || [ ] ) . toString ( ) }
/ >
2019-06-27 16:22:24 +02:00
{ inAmpMode && (
2019-03-01 20:51:13 +01:00
< >
< meta
name = "viewport"
content = "width=device-width,minimum-scale=1,initial-scale=1"
/ >
2019-07-29 09:18:23 +02:00
{ ! hasCanonicalRel && (
< link
rel = "canonical"
href = { canonicalBase + cleanAmpPath ( dangerousAsPath ) }
/ >
) }
2019-03-01 20:51:13 +01:00
{ /* https://www.ampproject.org/docs/fundamentals/optimize_amp#optimize-the-amp-runtime-loading */ }
< link
rel = "preload"
as = "script"
href = "https://cdn.ampproject.org/v0.js"
/ >
{ /* Add custom styles before AMP styles to prevent accidental overrides */ }
{ styles && (
< style
amp - custom = ""
dangerouslySetInnerHTML = { {
2019-04-22 17:32:46 +02:00
__html : curStyles
2020-05-18 21:24:37 +02:00
. map ( ( style ) = > style . props . dangerouslySetInnerHTML . __html )
2019-03-15 19:53:02 +01:00
. join ( '' )
. replace ( /\/\*# sourceMappingURL=.*\*\//g , '' )
2019-04-22 19:55:03 +02:00
. replace ( /\/\*@ sourceURL=.*?\*\//g , '' ) ,
2019-03-01 20:51:13 +01:00
} }
/ >
) }
< style
amp - boilerplate = ""
dangerouslySetInnerHTML = { {
__html : ` body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}} ` ,
} }
/ >
< noscript >
< style
amp - boilerplate = ""
dangerouslySetInnerHTML = { {
__html : ` body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none} ` ,
} }
/ >
< / noscript >
< script async src = "https://cdn.ampproject.org/v0.js" / >
< / >
) }
2019-06-27 16:22:24 +02:00
{ ! inAmpMode && (
2019-03-01 20:51:13 +01:00
< >
2019-07-29 09:18:23 +02:00
{ ! hasAmphtmlRel && hybridAmp && (
2019-05-30 03:19:32 +02:00
< link
rel = "amphtml"
href = { canonicalBase + getAmpPath ( ampPath , dangerousAsPath ) }
/ >
) }
2020-12-01 19:02:07 +01:00
{ ! process . env . __NEXT_OPTIMIZE_CSS && this . getCssLinks ( files ) }
{ ! process . env . __NEXT_OPTIMIZE_CSS && (
< noscript data-n-css = { this . props . nonce ? ? '' } / >
) }
2021-06-12 13:26:47 +02:00
{ process . env . __NEXT_OPTIMIZE_IMAGES && (
< meta name = "next-image-preload" / >
) }
2021-01-19 20:38:15 +01:00
{ ! disableRuntimeJS &&
! disableJsPreload &&
this . getPreloadDynamicChunks ( ) }
{ ! disableRuntimeJS &&
! disableJsPreload &&
this . getPreloadMainLinks ( files ) }
2021-05-13 12:39:36 +02:00
{ ! disableOptimizedLoading &&
! disableRuntimeJS &&
this . getPolyfillScripts ( ) }
{ ! disableOptimizedLoading &&
! disableRuntimeJS &&
this . getPreNextScripts ( ) }
{ ! disableOptimizedLoading &&
! disableRuntimeJS &&
this . getDynamicChunks ( files ) }
{ ! disableOptimizedLoading &&
! disableRuntimeJS &&
this . getScripts ( files ) }
2020-12-01 19:02:07 +01:00
{ process . env . __NEXT_OPTIMIZE_CSS && this . getCssLinks ( files ) }
{ process . env . __NEXT_OPTIMIZE_CSS && (
< noscript data-n-css = { this . props . nonce ? ? '' } / >
) }
2020-08-03 16:22:55 +02:00
{ this . context . isDevelopment && (
2020-04-25 21:00:41 +02:00
// this element is used to mount development styles so the
// ordering matches production
// (by default, style-loader injects at the bottom of <head />)
< noscript id = "__next_css__DO_NOT_USE__" / >
) }
2019-03-01 20:51:13 +01:00
{ styles || null }
< / >
) }
2019-11-01 20:13:13 +01:00
{ React . createElement ( React . Fragment , { } , . . . ( headTags || [ ] ) ) }
2019-03-01 20:51:13 +01:00
< / head >
)
2018-10-03 00:08:57 +02:00
}
}
2020-06-08 20:11:00 +02:00
export function Main() {
2021-08-13 05:36:54 +02:00
const { inAmpMode , docComponentsRendered } = useContext ( HtmlContext )
2020-08-24 04:42:51 +02:00
docComponentsRendered . Main = true
2021-08-13 05:36:54 +02:00
if ( inAmpMode ) return < > { BODY_RENDER_TARGET } < / >
return < div id = "__next" > { BODY_RENDER_TARGET } < / div >
2018-10-03 00:08:57 +02:00
}
2019-04-26 09:37:57 +02:00
export class NextScript extends Component < OriginProps > {
2021-08-13 05:36:54 +02:00
static contextType = HtmlContext
2018-10-03 00:08:57 +02:00
2021-08-13 05:36:54 +02:00
context ! : React . ContextType < typeof HtmlContext >
2019-04-22 19:55:03 +02:00
2019-07-25 04:16:32 +02:00
// Source: https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc
static safariNomoduleFix =
'!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()},!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();'
2020-08-14 16:20:03 +02:00
getDynamicChunks ( files : DocumentFiles ) {
2021-05-13 12:39:36 +02:00
return getDynamicChunks ( this . context , this . props , files )
2018-10-03 00:08:57 +02:00
}
2020-12-01 19:10:16 +01:00
getPreNextScripts() {
2021-05-13 12:39:36 +02:00
return getPreNextScripts ( this . context , this . props )
2020-12-01 19:10:16 +01:00
}
2020-08-14 16:20:03 +02:00
getScripts ( files : DocumentFiles ) {
2021-05-13 12:39:36 +02:00
return getScripts ( this . context , this . props , files )
2018-10-03 00:08:57 +02:00
}
2019-11-02 02:00:56 +01:00
getPolyfillScripts() {
2021-05-13 12:39:36 +02:00
return getPolyfillScripts ( this . context , this . props )
2019-11-02 02:00:56 +01:00
}
2021-08-13 05:36:54 +02:00
static getInlineScriptSource ( context : Readonly < HtmlProps > ) : string {
const { __NEXT_DATA__ } = context
2019-01-27 16:12:17 +01:00
try {
const data = JSON . stringify ( __NEXT_DATA__ )
return htmlEscapeJsonString ( data )
2019-03-01 20:51:13 +01:00
} catch ( err ) {
if ( err . message . indexOf ( 'circular structure' ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Circular structure in "getInitialProps" result of page " ${ __NEXT_DATA__ . page } ". https://nextjs.org/docs/messages/circular-structure `
2019-03-01 20:51:13 +01:00
)
2019-01-27 16:12:17 +01:00
}
throw err
}
2018-10-03 00:08:57 +02:00
}
2019-03-01 20:51:13 +01:00
render() {
const {
assetPrefix ,
2019-06-27 16:22:24 +02:00
inAmpMode ,
2020-06-07 01:00:03 +02:00
buildManifest ,
2020-04-17 11:22:03 +02:00
unstable_runtimeJS ,
2020-08-24 04:42:51 +02:00
docComponentsRendered ,
2020-08-03 16:22:55 +02:00
devOnlyCacheBusterQueryString ,
2021-05-13 12:39:36 +02:00
disableOptimizedLoading ,
2020-08-03 16:22:55 +02:00
} = this . context
2020-04-17 11:22:03 +02:00
const disableRuntimeJS = unstable_runtimeJS === false
2020-01-06 20:55:08 +01:00
2020-08-24 04:42:51 +02:00
docComponentsRendered . NextScript = true
2019-06-27 16:22:24 +02:00
if ( inAmpMode ) {
2019-02-25 23:00:14 +01:00
if ( process . env . NODE_ENV === 'production' ) {
return null
}
2020-06-20 21:59:47 +02:00
const ampDevFiles = [
. . . buildManifest . devFiles ,
2020-12-29 22:01:42 +01:00
. . . buildManifest . polyfillFiles ,
2020-06-20 21:59:47 +02:00
. . . buildManifest . ampDevFiles ,
2019-02-25 23:00:14 +01:00
]
return (
< >
2020-06-07 01:00:03 +02:00
{ disableRuntimeJS ? null : (
2019-02-25 23:00:14 +01:00
< script
id = "__NEXT_DATA__"
type = "application/json"
nonce = { this . props . nonce }
2020-06-07 01:00:03 +02:00
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
2019-02-25 23:00:14 +01:00
dangerouslySetInnerHTML = { {
2020-08-03 16:22:55 +02:00
__html : NextScript.getInlineScriptSource ( this . context ) ,
2019-02-25 23:00:14 +01:00
} }
2019-09-03 17:11:22 +02:00
data - ampdevmode
2019-02-25 23:00:14 +01:00
/ >
) }
2020-06-20 21:59:47 +02:00
{ ampDevFiles . map ( ( file ) = > (
< script
key = { file }
2020-08-03 16:22:55 +02:00
src = { ` ${ assetPrefix } /_next/ ${ file } ${ devOnlyCacheBusterQueryString } ` }
2020-06-20 21:59:47 +02:00
nonce = { this . props . nonce }
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
data - ampdevmode
/ >
) ) }
2019-02-25 23:00:14 +01:00
< / >
)
2019-02-14 16:22:57 +01:00
}
2018-12-13 01:05:21 +01:00
if ( process . env . NODE_ENV !== 'production' ) {
2019-03-01 20:51:13 +01:00
if ( this . props . crossOrigin )
console . warn (
2021-03-29 10:25:00 +02:00
'Warning: `NextScript` attribute `crossOrigin` is deprecated. https://nextjs.org/docs/messages/doc-crossorigin-deprecated'
2019-03-01 20:51:13 +01:00
)
2018-12-13 01:05:21 +01:00
}
2020-08-14 16:20:03 +02:00
const files : DocumentFiles = getDocumentFiles (
this . context . buildManifest ,
2020-12-22 04:18:01 +01:00
this . context . __NEXT_DATA__ . page ,
inAmpMode
2020-08-14 16:20:03 +02:00
)
2020-12-22 04:18:01 +01:00
2019-03-01 20:51:13 +01:00
return (
< >
2020-06-07 01:00:03 +02:00
{ ! disableRuntimeJS && buildManifest . devFiles
2020-06-08 20:11:00 +02:00
? buildManifest . devFiles . map ( ( file : string ) = > (
< script
key = { file }
src = { ` ${ assetPrefix } /_next/ ${ encodeURI (
file
2020-08-03 16:22:55 +02:00
) } $ { devOnlyCacheBusterQueryString } ` }
2020-06-08 20:11:00 +02:00
nonce = { this . props . nonce }
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
/ >
) )
2019-03-01 20:51:13 +01:00
: null }
2020-06-07 01:00:03 +02:00
{ disableRuntimeJS ? null : (
2019-03-01 20:51:13 +01:00
< script
id = "__NEXT_DATA__"
type = "application/json"
nonce = { this . props . nonce }
2020-06-07 01:00:03 +02:00
crossOrigin = {
this . props . crossOrigin || process . env . __NEXT_CROSS_ORIGIN
}
2019-03-01 20:51:13 +01:00
dangerouslySetInnerHTML = { {
2020-08-03 16:22:55 +02:00
__html : NextScript.getInlineScriptSource ( this . context ) ,
2019-03-01 20:51:13 +01:00
} }
/ >
) }
2021-05-13 12:39:36 +02:00
{ disableOptimizedLoading &&
! disableRuntimeJS &&
this . getPolyfillScripts ( ) }
{ disableOptimizedLoading &&
! disableRuntimeJS &&
this . getPreNextScripts ( ) }
{ disableOptimizedLoading &&
! disableRuntimeJS &&
this . getDynamicChunks ( files ) }
{ disableOptimizedLoading && ! disableRuntimeJS && this . getScripts ( files ) }
2019-03-01 20:51:13 +01:00
< / >
)
2018-10-03 00:08:57 +02:00
}
}
2020-05-10 23:52:30 +02:00
function getAmpPath ( ampPath : string , asPath : string ) : string {
2020-05-02 06:52:56 +02:00
return ampPath || ` ${ asPath } ${ asPath . includes ( '?' ) ? '&' : '?' } amp=1 `
2019-04-22 19:55:03 +02:00
}