mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Add missing dependencies to server (#5369)
- compile default pages correctly into `.next` - add missing runtime dependencies
This commit is contained in:
parent
de6d394d73
commit
785377d3c3
|
@ -24,10 +24,12 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime-corejs2": "^7.1.2",
|
||||
"ansi-html": "0.0.7",
|
||||
"etag": "1.8.1",
|
||||
"find-up": "3.0.0",
|
||||
"fresh": "0.5.2",
|
||||
"hoist-non-react-statics": "^3.0.1",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-errors": "1.6.2",
|
||||
"path-to-regexp": "2.1.0",
|
||||
|
|
|
@ -1 +1 @@
|
|||
module.exports = require('./dist/lib/app')
|
||||
module.exports = require('./dist/pages/_app')
|
||||
|
|
|
@ -32,14 +32,20 @@ function externalsConfig (dir, isServer) {
|
|||
return externals
|
||||
}
|
||||
|
||||
const notExternalModules = ['next/app', 'next/document', 'next/error']
|
||||
|
||||
externals.push((context, request, callback) => {
|
||||
resolve(request, { basedir: dir, preserveSymlinks: true }, (err, res) => {
|
||||
if (notExternalModules.indexOf(request) !== -1) {
|
||||
return callback()
|
||||
}
|
||||
|
||||
resolve(request, { basedir: context, preserveSymlinks: true }, (err, res) => {
|
||||
if (err) {
|
||||
return callback()
|
||||
}
|
||||
|
||||
// Default pages have to be transpiled
|
||||
if (res.match(/node_modules[/\\]next[/\\]dist[/\\]pages/)) {
|
||||
if (res.match(/next[/\\]dist[/\\]pages/)) {
|
||||
return callback()
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
module.exports = require('./dist/server/document')
|
||||
module.exports = require('./dist/pages/_document')
|
||||
|
|
|
@ -1 +1 @@
|
|||
module.exports = require('./dist/lib/error')
|
||||
module.exports = require('./dist/pages/_error')
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { execOnce, loadGetInitialProps } from 'next-server/dist/lib/utils'
|
||||
import { makePublicRouterInstance } from 'next-server/router'
|
||||
|
||||
export default class App extends Component {
|
||||
static childContextTypes = {
|
||||
headManager: PropTypes.object,
|
||||
router: PropTypes.object
|
||||
}
|
||||
|
||||
static async getInitialProps ({ Component, router, ctx }) {
|
||||
const pageProps = await loadGetInitialProps(Component, ctx)
|
||||
return {pageProps}
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
const { headManager } = this.props
|
||||
return {
|
||||
headManager,
|
||||
router: makePublicRouterInstance(this.props.router)
|
||||
}
|
||||
}
|
||||
|
||||
// Kept here for backwards compatibility.
|
||||
// When someone ended App they could call `super.componentDidCatch`. This is now deprecated.
|
||||
componentDidCatch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
render () {
|
||||
const {router, Component, pageProps} = this.props
|
||||
const url = createUrl(router)
|
||||
return <Container>
|
||||
<Component {...pageProps} url={url} />
|
||||
</Container>
|
||||
}
|
||||
}
|
||||
|
||||
export class Container extends Component {
|
||||
componentDidMount () {
|
||||
this.scrollToHash()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
this.scrollToHash()
|
||||
}
|
||||
|
||||
scrollToHash () {
|
||||
let { hash } = window.location
|
||||
hash = hash ? hash.substring(1) : false
|
||||
if (!hash) return
|
||||
|
||||
const el = document.getElementById(hash)
|
||||
if (!el) return
|
||||
|
||||
// If we call scrollIntoView() in here without a setTimeout
|
||||
// it won't scroll properly.
|
||||
setTimeout(() => el.scrollIntoView(), 0)
|
||||
}
|
||||
|
||||
render () {
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
const warnUrl = execOnce(() => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.error(`Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`)
|
||||
}
|
||||
})
|
||||
|
||||
export function createUrl (router) {
|
||||
// This is to make sure we don't references the router object at call time
|
||||
const {pathname, asPath, query} = router
|
||||
return {
|
||||
get query () {
|
||||
warnUrl()
|
||||
return query
|
||||
},
|
||||
get pathname () {
|
||||
warnUrl()
|
||||
return pathname
|
||||
},
|
||||
get asPath () {
|
||||
warnUrl()
|
||||
return asPath
|
||||
},
|
||||
back: () => {
|
||||
warnUrl()
|
||||
router.back()
|
||||
},
|
||||
push: (url, as) => {
|
||||
warnUrl()
|
||||
return router.push(url, as)
|
||||
},
|
||||
pushTo: (href, as) => {
|
||||
warnUrl()
|
||||
const pushRoute = as ? href : null
|
||||
const pushUrl = as || href
|
||||
|
||||
return router.push(pushRoute, pushUrl)
|
||||
},
|
||||
replace: (url, as) => {
|
||||
warnUrl()
|
||||
return router.replace(url, as)
|
||||
},
|
||||
replaceTo: (href, as) => {
|
||||
warnUrl()
|
||||
const replaceRoute = as ? href : null
|
||||
const replaceUrl = as || href
|
||||
|
||||
return router.replace(replaceRoute, replaceUrl)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import HTTPStatus from 'http-status'
|
||||
import Head from 'next-server/head'
|
||||
|
||||
export default class Error extends React.Component {
|
||||
static getInitialProps ({ res, err }) {
|
||||
const statusCode = res ? res.statusCode : (err ? err.statusCode : null)
|
||||
return { statusCode }
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusCode } = this.props
|
||||
const title = statusCode === 404
|
||||
? 'This page could not be found'
|
||||
: HTTPStatus[statusCode] || 'An unexpected error has occurred'
|
||||
|
||||
return <div style={styles.error}>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<title>{statusCode}: {title}</title>
|
||||
</Head>
|
||||
<div>
|
||||
<style dangerouslySetInnerHTML={{ __html: 'body { margin: 0 }' }} />
|
||||
{statusCode ? <h1 style={styles.h1}>{statusCode}</h1> : null}
|
||||
<div style={styles.desc}>
|
||||
<h2 style={styles.h2}>{title}.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Error.propTypes = {
|
||||
statusCode: PropTypes.number
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
error: {
|
||||
color: '#000',
|
||||
background: '#fff',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
desc: {
|
||||
display: 'inline-block',
|
||||
textAlign: 'left',
|
||||
lineHeight: '49px',
|
||||
height: '49px',
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
|
||||
h1: {
|
||||
display: 'inline-block',
|
||||
borderRight: '1px solid rgba(0, 0, 0,.3)',
|
||||
margin: 0,
|
||||
marginRight: '20px',
|
||||
padding: '10px 23px 10px 0',
|
||||
fontSize: '24px',
|
||||
fontWeight: 500,
|
||||
verticalAlign: 'top'
|
||||
},
|
||||
|
||||
h2: {
|
||||
fontSize: '14px',
|
||||
fontWeight: 'normal',
|
||||
lineHeight: 'inherit',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}
|
||||
}
|
|
@ -1 +1,116 @@
|
|||
module.exports = require('next/app')
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { execOnce, loadGetInitialProps } from 'next-server/dist/lib/utils'
|
||||
import { makePublicRouterInstance } from 'next-server/router'
|
||||
|
||||
export default class App extends Component {
|
||||
static childContextTypes = {
|
||||
headManager: PropTypes.object,
|
||||
router: PropTypes.object
|
||||
}
|
||||
|
||||
static async getInitialProps ({ Component, router, ctx }) {
|
||||
const pageProps = await loadGetInitialProps(Component, ctx)
|
||||
return {pageProps}
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
const { headManager } = this.props
|
||||
return {
|
||||
headManager,
|
||||
router: makePublicRouterInstance(this.props.router)
|
||||
}
|
||||
}
|
||||
|
||||
// Kept here for backwards compatibility.
|
||||
// When someone ended App they could call `super.componentDidCatch`. This is now deprecated.
|
||||
componentDidCatch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
render () {
|
||||
const {router, Component, pageProps} = this.props
|
||||
const url = createUrl(router)
|
||||
return <Container>
|
||||
<Component {...pageProps} url={url} />
|
||||
</Container>
|
||||
}
|
||||
}
|
||||
|
||||
export class Container extends Component {
|
||||
componentDidMount () {
|
||||
this.scrollToHash()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
this.scrollToHash()
|
||||
}
|
||||
|
||||
scrollToHash () {
|
||||
let { hash } = window.location
|
||||
hash = hash ? hash.substring(1) : false
|
||||
if (!hash) return
|
||||
|
||||
const el = document.getElementById(hash)
|
||||
if (!el) return
|
||||
|
||||
// If we call scrollIntoView() in here without a setTimeout
|
||||
// it won't scroll properly.
|
||||
setTimeout(() => el.scrollIntoView(), 0)
|
||||
}
|
||||
|
||||
render () {
|
||||
return this.props.children
|
||||
}
|
||||
}
|
||||
|
||||
const warnUrl = execOnce(() => {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
console.error(`Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`)
|
||||
}
|
||||
})
|
||||
|
||||
export function createUrl (router) {
|
||||
// This is to make sure we don't references the router object at call time
|
||||
const {pathname, asPath, query} = router
|
||||
return {
|
||||
get query () {
|
||||
warnUrl()
|
||||
return query
|
||||
},
|
||||
get pathname () {
|
||||
warnUrl()
|
||||
return pathname
|
||||
},
|
||||
get asPath () {
|
||||
warnUrl()
|
||||
return asPath
|
||||
},
|
||||
back: () => {
|
||||
warnUrl()
|
||||
router.back()
|
||||
},
|
||||
push: (url, as) => {
|
||||
warnUrl()
|
||||
return router.push(url, as)
|
||||
},
|
||||
pushTo: (href, as) => {
|
||||
warnUrl()
|
||||
const pushRoute = as ? href : null
|
||||
const pushUrl = as || href
|
||||
|
||||
return router.push(pushRoute, pushUrl)
|
||||
},
|
||||
replace: (url, as) => {
|
||||
warnUrl()
|
||||
return router.replace(url, as)
|
||||
},
|
||||
replaceTo: (href, as) => {
|
||||
warnUrl()
|
||||
const replaceRoute = as ? href : null
|
||||
const replaceUrl = as || href
|
||||
|
||||
return router.replace(replaceRoute, replaceUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1,218 @@
|
|||
module.exports = require('next/document')
|
||||
/* eslint-disable */
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import htmlescape from 'htmlescape'
|
||||
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 = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static getInitialProps ({ renderPage }) {
|
||||
const { html, head, buildManifest } = renderPage()
|
||||
const styles = flush()
|
||||
return { html, head, styles, buildManifest }
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
return { _documentProps: this.props }
|
||||
}
|
||||
|
||||
render () {
|
||||
return <html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
}
|
||||
|
||||
export class Head extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
nonce: PropTypes.string
|
||||
}
|
||||
|
||||
getCssLinks () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
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}`}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getPreloadDynamicChunks () {
|
||||
const { dynamicImports, assetPrefix } = this.context._documentProps
|
||||
return dynamicImports.map((bundle) => {
|
||||
return <link
|
||||
rel='preload'
|
||||
key={bundle.file}
|
||||
href={`${assetPrefix}/_next/${bundle.file}`}
|
||||
as='script'
|
||||
nonce={this.props.nonce}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getPreloadMainLinks () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
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'
|
||||
href={`${assetPrefix}/_next/${file}`}
|
||||
as='script'
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { head, styles, assetPrefix, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { page, pathname, buildId } = __NEXT_DATA__
|
||||
const pagePathname = getPagePathname(pathname)
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
return <head {...this.props}>
|
||||
{head}
|
||||
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} as='script' nonce={this.props.nonce} />}
|
||||
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} as='script' nonce={this.props.nonce} />
|
||||
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} as='script' nonce={this.props.nonce} />
|
||||
{this.getPreloadDynamicChunks()}
|
||||
{this.getPreloadMainLinks()}
|
||||
{this.getCssLinks()}
|
||||
{styles || null}
|
||||
{children}
|
||||
</head>
|
||||
}
|
||||
}
|
||||
|
||||
export class Main extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
render () {
|
||||
const { html } = this.context._documentProps
|
||||
return (
|
||||
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class NextScript extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
nonce: PropTypes.string
|
||||
}
|
||||
|
||||
getDynamicChunks () {
|
||||
const { dynamicImports, assetPrefix } = this.context._documentProps
|
||||
return dynamicImports.map((bundle) => {
|
||||
return <script
|
||||
async
|
||||
key={bundle.file}
|
||||
src={`${assetPrefix}/_next/${bundle.file}`}
|
||||
nonce={this.props.nonce}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getScripts () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return files.map((file) => {
|
||||
// Only render .js files here
|
||||
if(!/\.js$/.exec(file)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <script
|
||||
key={file}
|
||||
src={`${assetPrefix}/_next/${file}`}
|
||||
nonce={this.props.nonce}
|
||||
async
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
static getInlineScriptSource (documentProps) {
|
||||
const { __NEXT_DATA__ } = documentProps
|
||||
const { page, pathname } = __NEXT_DATA__
|
||||
return `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}${page === '/_error' ? `;__NEXT_REGISTER_PAGE(${htmlescape(pathname)},function(){var e = new Error('Page does not exist: ${htmlescape(pathname)}');e.statusCode=404;return {error:e}})`:''}`
|
||||
}
|
||||
|
||||
render () {
|
||||
const { staticMarkup, assetPrefix, devFiles, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { page, pathname, buildId } = __NEXT_DATA__
|
||||
const pagePathname = getPagePathname(pathname)
|
||||
|
||||
return <Fragment>
|
||||
{devFiles ? devFiles.map((file) => <script key={file} src={`${assetPrefix}/_next/${file}`} nonce={this.props.nonce} />) : null}
|
||||
{staticMarkup ? null : <script nonce={this.props.nonce} dangerouslySetInnerHTML={{
|
||||
__html: NextScript.getInlineScriptSource(this.context._documentProps)
|
||||
}} />}
|
||||
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} nonce={this.props.nonce} />}
|
||||
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} nonce={this.props.nonce} />
|
||||
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} nonce={this.props.nonce} />
|
||||
{staticMarkup ? null : this.getDynamicChunks()}
|
||||
{staticMarkup ? null : this.getScripts()}
|
||||
</Fragment>
|
||||
}
|
||||
}
|
||||
|
||||
function getPagePathname (pathname) {
|
||||
if (pathname === '/') {
|
||||
return '/index.js'
|
||||
}
|
||||
|
||||
return `${pathname}.js`
|
||||
}
|
||||
|
|
|
@ -1 +1,79 @@
|
|||
module.exports = require('next/error')
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import HTTPStatus from 'http-status'
|
||||
import Head from 'next-server/head'
|
||||
|
||||
export default class Error extends React.Component {
|
||||
static getInitialProps ({ res, err }) {
|
||||
const statusCode = res ? res.statusCode : (err ? err.statusCode : null)
|
||||
return { statusCode }
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusCode } = this.props
|
||||
const title = statusCode === 404
|
||||
? 'This page could not be found'
|
||||
: HTTPStatus[statusCode] || 'An unexpected error has occurred'
|
||||
|
||||
return <div style={styles.error}>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<title>{statusCode}: {title}</title>
|
||||
</Head>
|
||||
<div>
|
||||
<style dangerouslySetInnerHTML={{ __html: 'body { margin: 0 }' }} />
|
||||
{statusCode ? <h1 style={styles.h1}>{statusCode}</h1> : null}
|
||||
<div style={styles.desc}>
|
||||
<h2 style={styles.h2}>{title}.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
Error.propTypes = {
|
||||
statusCode: PropTypes.number
|
||||
}
|
||||
}
|
||||
|
||||
const styles = {
|
||||
error: {
|
||||
color: '#000',
|
||||
background: '#fff',
|
||||
fontFamily: '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||
height: '100vh',
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
desc: {
|
||||
display: 'inline-block',
|
||||
textAlign: 'left',
|
||||
lineHeight: '49px',
|
||||
height: '49px',
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
|
||||
h1: {
|
||||
display: 'inline-block',
|
||||
borderRight: '1px solid rgba(0, 0, 0,.3)',
|
||||
margin: 0,
|
||||
marginRight: '20px',
|
||||
padding: '10px 23px 10px 0',
|
||||
fontSize: '24px',
|
||||
fontWeight: 500,
|
||||
verticalAlign: 'top'
|
||||
},
|
||||
|
||||
h2: {
|
||||
fontSize: '14px',
|
||||
fontWeight: 'normal',
|
||||
lineHeight: 'inherit',
|
||||
margin: 0,
|
||||
padding: 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
/* eslint-disable */
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import htmlescape from 'htmlescape'
|
||||
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 = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static getInitialProps ({ renderPage }) {
|
||||
const { html, head, buildManifest } = renderPage()
|
||||
const styles = flush()
|
||||
return { html, head, styles, buildManifest }
|
||||
}
|
||||
|
||||
getChildContext () {
|
||||
return { _documentProps: this.props }
|
||||
}
|
||||
|
||||
render () {
|
||||
return <html>
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
}
|
||||
|
||||
export class Head extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
nonce: PropTypes.string
|
||||
}
|
||||
|
||||
getCssLinks () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
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}`}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getPreloadDynamicChunks () {
|
||||
const { dynamicImports, assetPrefix } = this.context._documentProps
|
||||
return dynamicImports.map((bundle) => {
|
||||
return <link
|
||||
rel='preload'
|
||||
key={bundle.file}
|
||||
href={`${assetPrefix}/_next/${bundle.file}`}
|
||||
as='script'
|
||||
nonce={this.props.nonce}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getPreloadMainLinks () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
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'
|
||||
href={`${assetPrefix}/_next/${file}`}
|
||||
as='script'
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
render () {
|
||||
const { head, styles, assetPrefix, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { page, pathname, buildId } = __NEXT_DATA__
|
||||
const pagePathname = getPagePathname(pathname)
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
||||
return <head {...this.props}>
|
||||
{head}
|
||||
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} as='script' nonce={this.props.nonce} />}
|
||||
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} as='script' nonce={this.props.nonce} />
|
||||
<link rel='preload' href={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} as='script' nonce={this.props.nonce} />
|
||||
{this.getPreloadDynamicChunks()}
|
||||
{this.getPreloadMainLinks()}
|
||||
{this.getCssLinks()}
|
||||
{styles || null}
|
||||
{children}
|
||||
</head>
|
||||
}
|
||||
}
|
||||
|
||||
export class Main extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
render () {
|
||||
const { html } = this.context._documentProps
|
||||
return (
|
||||
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export class NextScript extends Component {
|
||||
static contextTypes = {
|
||||
_documentProps: PropTypes.any
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
nonce: PropTypes.string
|
||||
}
|
||||
|
||||
getDynamicChunks () {
|
||||
const { dynamicImports, assetPrefix } = this.context._documentProps
|
||||
return dynamicImports.map((bundle) => {
|
||||
return <script
|
||||
async
|
||||
key={bundle.file}
|
||||
src={`${assetPrefix}/_next/${bundle.file}`}
|
||||
nonce={this.props.nonce}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
getScripts () {
|
||||
const { assetPrefix, files } = this.context._documentProps
|
||||
if(!files || files.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return files.map((file) => {
|
||||
// Only render .js files here
|
||||
if(!/\.js$/.exec(file)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <script
|
||||
key={file}
|
||||
src={`${assetPrefix}/_next/${file}`}
|
||||
nonce={this.props.nonce}
|
||||
async
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
static getInlineScriptSource (documentProps) {
|
||||
const { __NEXT_DATA__ } = documentProps
|
||||
const { page, pathname } = __NEXT_DATA__
|
||||
return `__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)};__NEXT_LOADED_PAGES__=[];__NEXT_REGISTER_PAGE=function(r,f){__NEXT_LOADED_PAGES__.push([r, f])}${page === '/_error' ? `;__NEXT_REGISTER_PAGE(${htmlescape(pathname)},function(){var e = new Error('Page does not exist: ${htmlescape(pathname)}');e.statusCode=404;return {error:e}})`:''}`
|
||||
}
|
||||
|
||||
render () {
|
||||
const { staticMarkup, assetPrefix, devFiles, __NEXT_DATA__ } = this.context._documentProps
|
||||
const { page, pathname, buildId } = __NEXT_DATA__
|
||||
const pagePathname = getPagePathname(pathname)
|
||||
|
||||
return <Fragment>
|
||||
{devFiles ? devFiles.map((file) => <script key={file} src={`${assetPrefix}/_next/${file}`} nonce={this.props.nonce} />) : null}
|
||||
{staticMarkup ? null : <script nonce={this.props.nonce} dangerouslySetInnerHTML={{
|
||||
__html: NextScript.getInlineScriptSource(this.context._documentProps)
|
||||
}} />}
|
||||
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/static/${buildId}/pages${pagePathname}`} nonce={this.props.nonce} />}
|
||||
<script async id={`__NEXT_PAGE__/_app`} src={`${assetPrefix}/_next/static/${buildId}/pages/_app.js`} nonce={this.props.nonce} />
|
||||
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/static/${buildId}/pages/_error.js`} nonce={this.props.nonce} />
|
||||
{staticMarkup ? null : this.getDynamicChunks()}
|
||||
{staticMarkup ? null : this.getScripts()}
|
||||
</Fragment>
|
||||
}
|
||||
}
|
||||
|
||||
function getPagePathname (pathname) {
|
||||
if (pathname === '/') {
|
||||
return '/index.js'
|
||||
}
|
||||
|
||||
return `${pathname}.js`
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
const notifier = require('node-notifier')
|
||||
|
||||
export async function compile (task) {
|
||||
await task.parallel(['bin', 'server', 'nextbuild', 'nextbuildstatic', 'lib', 'client'])
|
||||
await task.parallel(['bin', 'server', 'nextbuild', 'nextbuildstatic', 'pages', 'lib', 'client'])
|
||||
}
|
||||
|
||||
export async function bin (task, opts) {
|
||||
|
@ -35,18 +35,18 @@ export async function nextbuildstatic (task, opts) {
|
|||
notify('Compiled export files')
|
||||
}
|
||||
|
||||
export async function copy (task) {
|
||||
await task.source('pages/**/*.js').target('dist/pages')
|
||||
export async function pages (task, opts) {
|
||||
await task.source(opts.src || 'pages/**/*.js').babel().target('dist/pages')
|
||||
}
|
||||
|
||||
export async function build (task) {
|
||||
await task.serial(['copy', 'compile'])
|
||||
await task.serial(['compile'])
|
||||
}
|
||||
|
||||
export default async function (task) {
|
||||
await task.start('build')
|
||||
await task.watch('bin/*', 'bin')
|
||||
await task.watch('pages/**/*.js', 'copy')
|
||||
await task.watch('pages/**/*.js', 'pages')
|
||||
await task.watch('server/**/*.js', 'server')
|
||||
await task.watch('build/**/*.js', 'nextbuild')
|
||||
await task.watch('export/**/*.js', 'nextexport')
|
||||
|
|
Loading…
Reference in a new issue