2018-10-02 22:08:57 +00:00
|
|
|
/* eslint-disable */
|
|
|
|
import React, { Component } from 'react'
|
|
|
|
import PropTypes from 'prop-types'
|
2019-01-27 15:12:17 +00:00
|
|
|
import {htmlEscapeJsonString} from '../server/htmlescape'
|
2018-10-02 22:08:57 +00:00
|
|
|
import flush from 'styled-jsx/server'
|
|
|
|
|
|
|
|
const Fragment = React.Fragment || function Fragment ({ children }) {
|
|
|
|
return <div>{children}</div>
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class Document extends Component {
|
|
|
|
static childContextTypes = {
|
2019-02-03 00:12:49 +00:00
|
|
|
_documentProps: PropTypes.any,
|
|
|
|
_devOnlyInvalidateCacheQueryString: PropTypes.string,
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static getInitialProps ({ renderPage }) {
|
2018-12-13 00:00:46 +00:00
|
|
|
const { html, head } = renderPage()
|
2018-10-02 22:08:57 +00:00
|
|
|
const styles = flush()
|
2018-12-13 00:00:46 +00:00
|
|
|
return { html, head, styles }
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getChildContext () {
|
2019-02-03 00:12:49 +00:00
|
|
|
return {
|
|
|
|
_documentProps: this.props,
|
|
|
|
// In dev we invalidate the cache by appending a timestamp to the resource URL.
|
|
|
|
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
|
|
|
|
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
|
|
|
|
_devOnlyInvalidateCacheQueryString: process.env.NODE_ENV !== 'production' ? '?ts=' + Date.now() : ''
|
|
|
|
}
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
return <html>
|
|
|
|
<Head />
|
|
|
|
<body>
|
|
|
|
<Main />
|
|
|
|
<NextScript />
|
|
|
|
</body>
|
|
|
|
</html>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Head extends Component {
|
|
|
|
static contextTypes = {
|
2019-02-03 00:12:49 +00:00
|
|
|
_documentProps: PropTypes.any,
|
|
|
|
_devOnlyInvalidateCacheQueryString: PropTypes.string,
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static propTypes = {
|
2018-11-13 20:36:09 +00:00
|
|
|
nonce: PropTypes.string,
|
|
|
|
crossOrigin: PropTypes.string
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getCssLinks () {
|
|
|
|
const { assetPrefix, files } = this.context._documentProps
|
|
|
|
if(!files || files.length === 0) {
|
|
|
|
return null
|
|
|
|
}
|
2018-11-01 13:05:39 +00:00
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return files.map((file) => {
|
|
|
|
// Only render .css files here
|
|
|
|
if(!/\.css$/.exec(file)) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
return <link
|
|
|
|
key={file}
|
|
|
|
nonce={this.props.nonce}
|
|
|
|
rel='stylesheet'
|
|
|
|
href={`${assetPrefix}/_next/${file}`}
|
2018-12-13 00:05:21 +00:00
|
|
|
crossOrigin={this.props.crossOrigin || process.crossOrigin}
|
2018-10-02 22:08:57 +00:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
getPreloadDynamicChunks () {
|
|
|
|
const { dynamicImports, assetPrefix } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return dynamicImports.map((bundle) => {
|
|
|
|
return <link
|
|
|
|
rel='preload'
|
|
|
|
key={bundle.file}
|
2019-02-03 00:12:49 +00:00
|
|
|
href={`${assetPrefix}/_next/${bundle.file}${_devOnlyInvalidateCacheQueryString}`}
|
2018-10-02 22:08:57 +00:00
|
|
|
as='script'
|
|
|
|
nonce={this.props.nonce}
|
2018-12-13 00:05:21 +00:00
|
|
|
crossOrigin={this.props.crossOrigin || process.crossOrigin}
|
2018-10-02 22:08:57 +00:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
getPreloadMainLinks () {
|
|
|
|
const { assetPrefix, files } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
if (!files || files.length === 0) {
|
2018-10-02 22:08:57 +00:00
|
|
|
return null
|
|
|
|
}
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
2018-11-01 13:05:39 +00:00
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return files.map((file) => {
|
|
|
|
// Only render .js files here
|
|
|
|
if(!/\.js$/.exec(file)) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
return <link
|
|
|
|
key={file}
|
|
|
|
nonce={this.props.nonce}
|
|
|
|
rel='preload'
|
2019-02-03 00:12:49 +00:00
|
|
|
href={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`}
|
2018-10-02 22:08:57 +00:00
|
|
|
as='script'
|
2018-12-13 00:05:21 +00:00
|
|
|
crossOrigin={this.props.crossOrigin || process.crossOrigin}
|
2018-10-02 22:08:57 +00:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const { head, styles, assetPrefix, __NEXT_DATA__ } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
2018-10-10 19:58:15 +00:00
|
|
|
const { page, buildId } = __NEXT_DATA__
|
|
|
|
const pagePathname = getPagePathname(page)
|
2018-10-02 22:08:57 +00:00
|
|
|
|
|
|
|
let children = this.props.children
|
|
|
|
// show a warning if Head contains <title> (only in development)
|
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
children = React.Children.map(children, (child) => {
|
|
|
|
if (child && child.type === 'title') {
|
|
|
|
console.warn("Warning: <title> should not be used in _document.js's <Head>. https://err.sh/next.js/no-document-title")
|
|
|
|
}
|
|
|
|
return child
|
|
|
|
})
|
2018-12-13 00:05:21 +00:00
|
|
|
if (this.props.crossOrigin) console.warn('Warning: `Head` attribute `crossOrigin` is deprecated. https://err.sh/next.js/doc-crossorigin-deprecated')
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
return <head {...this.props}>
|
2018-12-10 22:40:42 +00:00
|
|
|
{children}
|
2018-10-02 22:08:57 +00:00
|
|
|
{head}
|
2019-02-03 00:12:49 +00:00
|
|
|
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}${_devOnlyInvalidateCacheQueryString}`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
|
|
|
|
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js${_devOnlyInvalidateCacheQueryString}`} as='script' nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
|
2018-10-02 22:08:57 +00:00
|
|
|
{this.getPreloadDynamicChunks()}
|
|
|
|
{this.getPreloadMainLinks()}
|
|
|
|
{this.getCssLinks()}
|
|
|
|
{styles || null}
|
|
|
|
</head>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class Main extends Component {
|
|
|
|
static contextTypes = {
|
2019-02-03 00:12:49 +00:00
|
|
|
_documentProps: PropTypes.any,
|
|
|
|
_devOnlyInvalidateCacheQueryString: PropTypes.string,
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const { html } = this.context._documentProps
|
|
|
|
return (
|
|
|
|
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class NextScript extends Component {
|
|
|
|
static contextTypes = {
|
2019-02-03 00:12:49 +00:00
|
|
|
_documentProps: PropTypes.any,
|
|
|
|
_devOnlyInvalidateCacheQueryString: PropTypes.string,
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static propTypes = {
|
2018-11-13 20:36:09 +00:00
|
|
|
nonce: PropTypes.string,
|
|
|
|
crossOrigin: PropTypes.string
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
getDynamicChunks () {
|
|
|
|
const { dynamicImports, assetPrefix } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return dynamicImports.map((bundle) => {
|
|
|
|
return <script
|
|
|
|
async
|
|
|
|
key={bundle.file}
|
2019-02-03 00:12:49 +00:00
|
|
|
src={`${assetPrefix}/_next/${bundle.file}${_devOnlyInvalidateCacheQueryString}`}
|
2018-11-13 20:36:09 +00:00
|
|
|
nonce={this.props.nonce}
|
2018-12-13 00:05:21 +00:00
|
|
|
crossOrigin={this.props.crossOrigin || process.crossOrigin}
|
2018-10-02 22:08:57 +00:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
getScripts () {
|
|
|
|
const { assetPrefix, files } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
if (!files || files.length === 0) {
|
2018-10-02 22:08:57 +00:00
|
|
|
return null
|
|
|
|
}
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
2018-11-01 13:05:39 +00:00
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return files.map((file) => {
|
|
|
|
// Only render .js files here
|
|
|
|
if(!/\.js$/.exec(file)) {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
return <script
|
|
|
|
key={file}
|
2019-02-03 00:12:49 +00:00
|
|
|
src={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`}
|
2018-10-02 22:08:57 +00:00
|
|
|
nonce={this.props.nonce}
|
|
|
|
async
|
2018-12-13 00:05:21 +00:00
|
|
|
crossOrigin={this.props.crossOrigin || process.crossOrigin}
|
2018-10-02 22:08:57 +00:00
|
|
|
/>
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
static getInlineScriptSource (documentProps) {
|
2019-01-27 15:12:17 +00:00
|
|
|
const {__NEXT_DATA__} = documentProps
|
|
|
|
try {
|
|
|
|
const data = JSON.stringify(__NEXT_DATA__)
|
|
|
|
return htmlEscapeJsonString(data)
|
|
|
|
} catch(err) {
|
|
|
|
if(err.message.indexOf('circular structure')) {
|
|
|
|
throw new Error(`Circular structure in "getInitialProps" result of page "${__NEXT_DATA__.page}". https://err.sh/zeit/next.js/circular-structure`)
|
|
|
|
}
|
|
|
|
throw err
|
|
|
|
}
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
render () {
|
|
|
|
const { staticMarkup, assetPrefix, devFiles, __NEXT_DATA__ } = this.context._documentProps
|
2019-02-03 00:12:49 +00:00
|
|
|
const { _devOnlyInvalidateCacheQueryString } = this.context
|
2018-10-10 19:58:15 +00:00
|
|
|
const { page, buildId } = __NEXT_DATA__
|
|
|
|
const pagePathname = getPagePathname(page)
|
2018-10-02 22:08:57 +00:00
|
|
|
|
2018-12-13 00:05:21 +00:00
|
|
|
if (process.env.NODE_ENV !== 'production') {
|
|
|
|
if (this.props.crossOrigin) console.warn('Warning: `NextScript` attribute `crossOrigin` is deprecated. https://err.sh/next.js/doc-crossorigin-deprecated')
|
|
|
|
}
|
|
|
|
|
2018-10-02 22:08:57 +00:00
|
|
|
return <Fragment>
|
2019-02-03 00:12:49 +00:00
|
|
|
{devFiles ? devFiles.map((file) => <script key={file} src={`${assetPrefix}/_next/${file}${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />) : null}
|
2018-12-13 00:05:21 +00:00
|
|
|
{staticMarkup ? null : <script id="__NEXT_DATA__" type="application/json" nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} dangerouslySetInnerHTML={{
|
2018-10-02 22:08:57 +00:00
|
|
|
__html: NextScript.getInlineScriptSource(this.context._documentProps)
|
|
|
|
}} />}
|
2019-02-03 00:12:49 +00:00
|
|
|
{page !== '/_error' && <script async id={`__NEXT_PAGE__${page}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />}
|
|
|
|
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js${_devOnlyInvalidateCacheQueryString}`} nonce={this.props.nonce} crossOrigin={this.props.crossOrigin || process.crossOrigin} />
|
2018-10-02 22:08:57 +00:00
|
|
|
{staticMarkup ? null : this.getDynamicChunks()}
|
|
|
|
{staticMarkup ? null : this.getScripts()}
|
|
|
|
</Fragment>
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-10 19:58:15 +00:00
|
|
|
function getPagePathname (page) {
|
|
|
|
if (page === '/') {
|
2018-10-02 22:08:57 +00:00
|
|
|
return '/index.js'
|
|
|
|
}
|
|
|
|
|
2018-10-10 19:58:15 +00:00
|
|
|
return `${page}.js`
|
2018-10-02 22:08:57 +00:00
|
|
|
}
|