1
0
Fork 0
mirror of https://github.com/terribleplan/next.js.git synced 2024-01-19 02:48:18 +00:00
next.js/server/document.js

218 lines
5.7 KiB
JavaScript
Raw Normal View History

import React, { Component } from 'react'
import PropTypes from 'prop-types'
2016-12-16 18:42:40 +00:00
import htmlescape from 'htmlescape'
import flush from 'styled-jsx/server'
2016-12-16 18:42:40 +00:00
const Fragment = React.Fragment || function Fragment ({ children }) {
Universal Webpack (#3578) * Speed up next build * Document webpack config * Speed up next build * Remove comment * Add comment * Clean up rules * Add comments * Run in parallel * Push plugins seperately * Create a new chunk for react * Don’t uglify react since it’s already uglified. Move react to commons in development * Use the minified version directly * Re-add globpattern * Move loaders into a separate variable * Add comment linking to Dan’s explanation * Remove dot * Add universal webpack * Initial dev support * Fix linting * Add changes from Arunoda's work * Made next dev works. But super slow and no HMR support. * Fix client side hot reload * Server side hmr * Only in dev * Add on-demand-entries client + hot-middleware * Add .babelrc support * Speed up on demand entries by running in parallel * Serve static generated files * Add missing config in dev * Add sass support * Add support for .map * Add cssloader config and fix .jsx support * Rename * use same defaults as css-loader. Fix linting * Add NoEmitErrorsPlugin * Add clientBootstrap * Use webpackhotmiddleware on the multi compiler * alpha.3 * Use babel 16.2.x * Fix reloading after error * Remove comment * Release 5.0.0-univeral-alpha.1 * Remove check for React 16 * Release 5.0.0-universal-alpha.2 * React hot loader v4 * Use our static file rendering machanism to serve pages. This should work well since the file path for a page is predictable. * Release 5.0.0-universal-alpha.3 * Remove optional loaders * Release 5.0.0-universal-alpha.4 * Remove clientBootstrap * Remove renderScript * Make sure pages bundles are served correctly * Remove unused import * Revert to using the same code as canary * Fix hot loader * Release 5.0.0-universal-alpha.5 * Check if externals dir exist before applying config * Add typescript support * Add support for transpiling certain packages in node_modules Thanks to @giuseppeg’s work in https://github.com/zeit/next.js/pull/3319 * Add BABEL_DISABLE_CACHE support * Make sourcemaps in production opt-in * Revert "Add support for transpiling certain packages in node_modules" This reverts commit d4b1d9babfb4b9ed4f4b12d56d52dee233e862da. In favor of a better api around this. * Support typescript through next.config.js * Remove comments * Bring back commons.js calculation * Remove unused dependencies * Move base.config.js to webpack.js * Make sure to only invalidate webpackDevMiddleware one after other. * Allow babel-loder caching by default. * Add comment about preact support * Bring back buildir replace * Remove obsolete plugin * Remove build replace, speed up build * Resolve page entries like pages/day/index.js to pages/day.js * Add componentDidCatch back * Compile to bundles * Use config.distDir everywhere * Make sure the file is an array * Remove console.log * Apply optimization to uglifyjs * Add comment pointing to source * Create entries the same way in dev and production * Remove unused and broken pagesGlobPattern * day/index.js is automatically turned into day.js at build time * Remove poweredByHeader option * Load pages with the correct path. * Release 5.0.0-universal-alpha.6 * Make sure react-dom/server can be overwritten by module-alias * Only add react-hot-loader babel plugin in dev * Release 5.0.0-universal-alpha.7 * Revert tests * Release 5.0.0-universal-alpha.10 * Make sure next/head is working properly. * Add wepack alias for 'next' back. * Make sure overriding className in next/head works * Alias react too * Add missing r * Fragment fallback has to wrap the children * Use min.js * Remove css.js * Remove wallaby.js * Release 5.0.0-universal-alpha.11 * Resolve relative to workdir instead of next * Make sure we touch the right file * Resolve next modules * Remove dotjsx removal plugins since we use webpack on the server * Revert "Resolve relative to workdir instead of next" This reverts commit a13f3e4ab565df9e2c9a3dfc8eb4009c0c2e02ed. * Externalize any locally loaded module lives outside of app dir. * Remove server aliases * Check node_modules reliably * Add symlink to next for tests * Make sure dynamic imports work locally. This is why we need it: https://github.com/webpack/webpack/blob/b545b519b2024e3f8be3041385bd326bf5d24449/lib/MainTemplate.js#L68 We need to have the finally clause in the above in __webpack_require__. webpack output option strictModuleExceptionHandling does that. * dynmaic -> dynamic * Remove webpack-node-externals * Make sure dynamic imports support SSR. * Remove css support in favor of next-css * Make sure we load path from `/` since it’s included in the path matching * Catch when ensurepage couldn’t be fulfilled for `.js.map` * Register require cache flusher for both client and server * Add comment explaining this is to facilitate hot reloading * Only load module when needed * Remove unused modules * Release 5.0.0-universal-alpha.12 * Only log the `found babel` message once * Make sure ondemand entries working correctly. Now we are just using a single instance of OnDemandEntryHandler. * Better sourcemaps * Release 5.0.0-universal-alpha.13 * Lock uglify version to 1.1.6 * Release 5.0.0-universal-alpha.14 * Fix a typo. * Introduce multi-zones support for mircofrontends * Add section on css
2018-01-30 15:40:52 +00:00
return <div>{children}</div>
}
2016-12-16 18:42:40 +00:00
export default class Document extends Component {
static getInitialProps ({ renderPage }) {
const { html, head, errorHtml, chunks } = renderPage()
const styles = flush()
return { html, head, errorHtml, chunks, styles }
2016-12-16 18:42:40 +00:00
}
static childContextTypes = {
_documentProps: PropTypes.any
}
getChildContext () {
return { _documentProps: this.props }
}
render () {
return <html>
<Head />
<body>
<Main />
<NextScript />
</body>
</html>
}
}
export class Head extends Component {
static contextTypes = {
_documentProps: PropTypes.any
}
2017-04-12 10:41:54 +00:00
getChunkPreloadLink (filename) {
const { __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix, buildId } = __NEXT_DATA__
const hash = buildId
2017-04-12 10:41:54 +00:00
return (
<link
key={filename}
rel='preload'
href={`${assetPrefix}/_next/${hash}/${filename}`}
2017-04-12 10:41:54 +00:00
as='script'
/>
)
}
getPreloadMainLinks () {
const { dev } = this.context._documentProps
if (dev) {
return [
this.getChunkPreloadLink('manifest.js'),
this.getChunkPreloadLink('main.js')
]
}
// In the production mode, we have a single asset with all the JS content.
return [
this.getChunkPreloadLink('main.js')
2017-04-12 10:41:54 +00:00
]
}
getPreloadDynamicChunks () {
2017-04-18 16:12:21 +00:00
const { chunks, __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix } = __NEXT_DATA__
return chunks.filenames.map((chunk) => (
<link
key={chunk}
rel='preload'
href={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
as='script'
/>
))
}
2016-12-16 18:42:40 +00:00
render () {
2017-04-12 10:41:54 +00:00
const { head, styles, __NEXT_DATA__ } = this.context._documentProps
const { page, pathname, buildId, assetPrefix } = __NEXT_DATA__
const pagePathname = getPagePathname(pathname)
2017-04-12 10:41:54 +00:00
return <head {...this.props}>
{(head || []).map((h, i) => React.cloneElement(h, { key: h.key || i }))}
{page !== '/_error' && <link rel='preload' href={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} as='script' />}
<link rel='preload' href={`${assetPrefix}/_next/${buildId}/page/_error.js`} as='script' />
{this.getPreloadDynamicChunks()}
2017-04-12 10:41:54 +00:00
{this.getPreloadMainLinks()}
{styles || null}
2016-12-16 18:42:40 +00:00
{this.props.children}
</head>
}
}
export class Main extends Component {
static contextTypes = {
_documentProps: PropTypes.any
}
render () {
const { html, errorHtml } = this.context._documentProps
return (
<Fragment>
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
<div id='__next-error' dangerouslySetInnerHTML={{ __html: errorHtml }} />
</Fragment>
)
2016-12-16 18:42:40 +00:00
}
}
export class NextScript extends Component {
static propTypes = {
nonce: PropTypes.string
}
2016-12-16 18:42:40 +00:00
static contextTypes = {
_documentProps: PropTypes.any
}
getChunkScript (filename, additionalProps = {}) {
const { __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix, buildId } = __NEXT_DATA__
const hash = buildId
return (
<script
key={filename}
src={`${assetPrefix}/_next/${hash}/${filename}`}
{...additionalProps}
/>
)
}
getScripts () {
const { dev } = this.context._documentProps
if (dev) {
return [
this.getChunkScript('manifest.js'),
this.getChunkScript('main.js')
]
}
// In the production mode, we have a single asset with all the JS content.
// So, we can load the script with async
return [this.getChunkScript('main.js', { async: true })]
}
getDynamicChunks () {
2017-04-18 16:12:21 +00:00
const { chunks, __NEXT_DATA__ } = this.context._documentProps
let { assetPrefix } = __NEXT_DATA__
return (
<Fragment>
{chunks.filenames.map((chunk) => (
<script
async
key={chunk}
src={`${assetPrefix}/_next/webpack/chunks/${chunk}`}
/>
))}
</Fragment>
)
}
2016-12-16 18:42:40 +00:00
render () {
const { staticMarkup, __NEXT_DATA__, chunks } = this.context._documentProps
const { page, pathname, buildId, assetPrefix } = __NEXT_DATA__
const pagePathname = getPagePathname(pathname)
__NEXT_DATA__.chunks = chunks.names
return <Fragment>
{staticMarkup ? null : <script nonce={this.props.nonce} dangerouslySetInnerHTML={{
__html: `
__NEXT_DATA__ = ${htmlescape(__NEXT_DATA__)}
module={}
__NEXT_LOADED_PAGES__ = []
__NEXT_LOADED_CHUNKS__ = []
__NEXT_REGISTER_PAGE = function (route, fn) {
__NEXT_LOADED_PAGES__.push({ route: route, fn: fn })
}
__NEXT_REGISTER_CHUNK = function (chunkName, fn) {
__NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn })
}
${page === '_error' && `
__NEXT_REGISTER_PAGE(${htmlescape(pathname)}, function() {
var error = new Error('Page does not exist: ${htmlescape(pathname)}')
error.statusCode = 404
return { error: error }
})
`}
`
}} />}
2018-02-21 13:15:51 +00:00
{page !== '/_error' && <script async id={`__NEXT_PAGE__${pathname}`} src={`${assetPrefix}/_next/${buildId}/page${pagePathname}`} />}
<script async id={`__NEXT_PAGE__/_error`} src={`${assetPrefix}/_next/${buildId}/page/_error.js`} />
{staticMarkup ? null : this.getDynamicChunks()}
{staticMarkup ? null : this.getScripts()}
</Fragment>
2016-12-16 18:42:40 +00:00
}
}
function getPagePathname (pathname) {
if (pathname === '/') {
return '/index.js'
}
return `${pathname}.js`
}