mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Fix custom document compilation (#534)
* compile _document using webpack * don't emit the bundle file of _document.js * exclude _document.js from minChunks of CommonsChunkPlugin * handle creation/removal of pages/_document.js * improve path handlings
This commit is contained in:
parent
815f17989b
commit
798fd3c1e8
|
@ -15,17 +15,25 @@ const handlers = {
|
|||
return
|
||||
}
|
||||
|
||||
if (route === '/_document') {
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
|
||||
Router.reload(route)
|
||||
},
|
||||
|
||||
change (route) {
|
||||
if (route === '/_document') {
|
||||
window.location.reload()
|
||||
return
|
||||
}
|
||||
|
||||
const { Component } = Router.components[route] || {}
|
||||
if (Component && Component.__route === '/_error-debug') {
|
||||
// reload to recover from runtime errors
|
||||
Router.reload(route)
|
||||
}
|
||||
},
|
||||
hardReload () {
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@
|
|||
"babel-preset-es2015": "6.18.0",
|
||||
"babel-preset-react": "6.16.0",
|
||||
"babel-runtime": "6.20.0",
|
||||
"chokidar": "1.6.1",
|
||||
"cross-spawn": "5.0.1",
|
||||
"del": "2.2.2",
|
||||
"friendly-errors-webpack-plugin": "1.1.2",
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import { resolve, join, dirname } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
import { readFile, writeFile } from 'mz/fs'
|
||||
import { transform } from 'babel-core'
|
||||
import chokidar from 'chokidar'
|
||||
import mkdirp from 'mkdirp-then'
|
||||
|
||||
export default babel
|
||||
|
||||
async function babel (dir, { dev = false } = {}) {
|
||||
dir = resolve(dir)
|
||||
|
||||
let src
|
||||
try {
|
||||
src = await readFile(join(dir, 'pages', '_document.js'), 'utf8')
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
src = await readFile(join(__dirname, '..', '..', '..', 'pages', '_document.js'), 'utf8')
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
let babelOptions = {
|
||||
babelrc: true,
|
||||
sourceMaps: dev ? 'inline' : false,
|
||||
presets: []
|
||||
}
|
||||
|
||||
const hasBabelRc = existsSync(join(dir, '.babelrc'))
|
||||
if (!hasBabelRc) {
|
||||
babelOptions.presets.push(require.resolve('./preset'))
|
||||
}
|
||||
|
||||
const { code } = transform(src, babelOptions)
|
||||
|
||||
const file = join(dir, '.next', 'dist', 'pages', '_document.js')
|
||||
await mkdirp(dirname(file))
|
||||
await writeFile(file, code)
|
||||
}
|
||||
|
||||
export function watch (dir) {
|
||||
return chokidar.watch('pages/_document.js', {
|
||||
cwd: dir,
|
||||
ignoreInitial: true
|
||||
})
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import webpack from './webpack'
|
||||
import babel from './babel'
|
||||
import clean from './clean'
|
||||
|
||||
export default async function build (dir) {
|
||||
|
@ -8,10 +7,7 @@ export default async function build (dir) {
|
|||
clean(dir)
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
runCompiler(compiler),
|
||||
babel(dir)
|
||||
])
|
||||
await runCompiler(compiler)
|
||||
}
|
||||
|
||||
function runCompiler (compiler) {
|
||||
|
|
|
@ -1,5 +1,15 @@
|
|||
import { resolve, relative, join, extname } from 'path'
|
||||
|
||||
const hotMiddlewareClientPath = join(__dirname, '..', '..', '..', 'client/webpack-hot-middleware-client')
|
||||
|
||||
const defaultPages = new Map()
|
||||
for (const p of ['_error.js', '_document.js']) {
|
||||
defaultPages.set(
|
||||
join('bundles', 'pages', p),
|
||||
join(__dirname, '..', '..', '..', 'pages', p)
|
||||
)
|
||||
}
|
||||
|
||||
export default class WatchPagesPlugin {
|
||||
constructor (dir) {
|
||||
this.dir = resolve(dir, 'pages')
|
||||
|
@ -7,6 +17,15 @@ export default class WatchPagesPlugin {
|
|||
}
|
||||
|
||||
apply (compiler) {
|
||||
compiler.plugin('compilation', (compilation) => {
|
||||
compilation.plugin('optimize-assets', (assets, callback) => {
|
||||
// transpile pages/_document.js and descendants,
|
||||
// but don't need the bundle file
|
||||
delete assets[join('bundles', 'pages', '_document.js')]
|
||||
callback()
|
||||
})
|
||||
})
|
||||
|
||||
compiler.plugin('emit', (compilation, callback) => {
|
||||
// watch the pages directory
|
||||
compilation.contextDependencies =
|
||||
|
@ -21,9 +40,6 @@ export default class WatchPagesPlugin {
|
|||
const getEntryName = (f) => {
|
||||
return join('bundles', relative(compiler.options.context, f))
|
||||
}
|
||||
const errorPageName = join('bundles', 'pages', '_error.js')
|
||||
const errorPagePath = join(__dirname, '..', '..', '..', 'pages', '_error.js')
|
||||
const hotMiddlewareClientPath = join(__dirname, '..', '..', '..', 'client/webpack-hot-middleware-client')
|
||||
|
||||
compiler.plugin('watch-run', (watching, callback) => {
|
||||
Object.keys(compiler.fileTimestamps)
|
||||
|
@ -31,7 +47,7 @@ export default class WatchPagesPlugin {
|
|||
.filter((f) => this.prevFileDependencies.indexOf(f) < 0)
|
||||
.forEach((f) => {
|
||||
const name = getEntryName(f)
|
||||
if (name === errorPageName) {
|
||||
if (defaultPages.has(name)) {
|
||||
compiler.removeEntry(name)
|
||||
}
|
||||
|
||||
|
@ -47,10 +63,10 @@ export default class WatchPagesPlugin {
|
|||
const name = getEntryName(f)
|
||||
compiler.removeEntry(name)
|
||||
|
||||
if (name === errorPageName) {
|
||||
if (defaultPages.has(name)) {
|
||||
compiler.addEntry([
|
||||
hotMiddlewareClientPath,
|
||||
errorPagePath
|
||||
defaultPages.get(name)
|
||||
], name)
|
||||
}
|
||||
})
|
||||
|
@ -60,9 +76,7 @@ export default class WatchPagesPlugin {
|
|||
}
|
||||
|
||||
isPageFile (f) {
|
||||
return f.indexOf(this.dir) === 0 &&
|
||||
relative(this.dir, f) !== '_document.js' &&
|
||||
extname(f) === '.js'
|
||||
return f.indexOf(this.dir) === 0 && extname(f) === '.js'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,14 +12,18 @@ import DynamicEntryPlugin from './plugins/dynamic-entry-plugin'
|
|||
import DetachPlugin from './plugins/detach-plugin'
|
||||
import getConfig from '../config'
|
||||
|
||||
const documentPage = join('pages', '_document.js')
|
||||
const defaultPages = [
|
||||
'_error.js',
|
||||
'_error-debug.js',
|
||||
'_document.js'
|
||||
]
|
||||
|
||||
export default async function createCompiler (dir, { dev = false, quiet = false } = {}) {
|
||||
dir = resolve(dir)
|
||||
const config = getConfig(dir)
|
||||
|
||||
const pages = await glob('pages/**/*.js', {
|
||||
cwd: dir,
|
||||
ignore: 'pages/_document.js'
|
||||
})
|
||||
const pages = await glob('pages/**/*.js', { cwd: dir })
|
||||
|
||||
const entry = {
|
||||
'main.js': dev ? require.resolve('../../client/next-dev') : require.resolve('../../client/next')
|
||||
|
@ -32,18 +36,19 @@ export default async function createCompiler (dir, { dev = false, quiet = false
|
|||
}
|
||||
|
||||
const nextPagesDir = join(__dirname, '..', '..', 'pages')
|
||||
const interpolateNames = new Map()
|
||||
|
||||
const errorEntry = join('bundles', 'pages', '_error.js')
|
||||
const defaultErrorPath = join(nextPagesDir, '_error.js')
|
||||
if (!entry[errorEntry]) {
|
||||
entry[errorEntry] = defaultEntries.concat([defaultErrorPath + '?entry'])
|
||||
for (const p of defaultPages) {
|
||||
const entryName = join('bundles', 'pages', p)
|
||||
const path = join(nextPagesDir, p)
|
||||
if (!entry[entryName]) {
|
||||
entry[entryName] = defaultEntries.concat([path + '?entry'])
|
||||
}
|
||||
interpolateNames.set(path, `dist/pages/${p}`)
|
||||
}
|
||||
|
||||
const errorDebugEntry = join('bundles', 'pages', '_error-debug.js')
|
||||
const errorDebugPath = join(nextPagesDir, '_error-debug.js')
|
||||
entry[errorDebugEntry] = defaultEntries.concat([errorDebugPath + '?entry'])
|
||||
|
||||
const nodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
|
||||
const minChunks = pages.filter((p) => p !== documentPage).length
|
||||
|
||||
const plugins = [
|
||||
new WriteFilePlugin({
|
||||
|
@ -55,7 +60,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false
|
|||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: 'commons',
|
||||
filename: 'commons.js',
|
||||
minChunks: Math.max(2, pages.length)
|
||||
minChunks: Math.max(2, minChunks)
|
||||
})
|
||||
]
|
||||
|
||||
|
@ -153,11 +158,6 @@ export default async function createCompiler (dir, { dev = false, quiet = false
|
|||
query: mainBabelOptions
|
||||
}])
|
||||
|
||||
const interpolateNames = new Map([
|
||||
[defaultErrorPath, 'dist/pages/_error.js'],
|
||||
[errorDebugPath, 'dist/pages/_error-debug.js']
|
||||
])
|
||||
|
||||
let webpackConfig = {
|
||||
context: dir,
|
||||
entry,
|
||||
|
|
|
@ -4,7 +4,6 @@ import webpackHotMiddleware from 'webpack-hot-middleware'
|
|||
import isWindowsBash from 'is-windows-bash'
|
||||
import webpack from './build/webpack'
|
||||
import clean from './build/clean'
|
||||
import babel, { watch } from './build/babel'
|
||||
import read from './read'
|
||||
|
||||
export default class HotReloader {
|
||||
|
@ -14,7 +13,6 @@ export default class HotReloader {
|
|||
this.middlewares = []
|
||||
this.webpackDevMiddleware = null
|
||||
this.webpackHotMiddleware = null
|
||||
this.watcher = null
|
||||
this.initialized = false
|
||||
this.stats = null
|
||||
this.compilationErrors = null
|
||||
|
@ -36,24 +34,16 @@ export default class HotReloader {
|
|||
}
|
||||
|
||||
async start () {
|
||||
this.watch()
|
||||
|
||||
const [compiler] = await Promise.all([
|
||||
webpack(this.dir, { dev: true, quiet: this.quiet }),
|
||||
clean(this.dir)
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
this.prepareMiddlewares(compiler),
|
||||
babel(this.dir, { dev: true })
|
||||
])
|
||||
|
||||
this.prepareMiddlewares(compiler)
|
||||
this.stats = await this.waitUntilValid()
|
||||
}
|
||||
|
||||
async stop () {
|
||||
if (this.watcher) this.watcher.close()
|
||||
|
||||
if (this.webpackDevMiddleware) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.webpackDevMiddleware.close((err) => {
|
||||
|
@ -188,26 +178,6 @@ export default class HotReloader {
|
|||
send (action, ...args) {
|
||||
this.webpackHotMiddleware.publish({ action, data: args })
|
||||
}
|
||||
|
||||
watch () {
|
||||
const onChange = (path) => {
|
||||
babel(this.dir, { dev: true })
|
||||
.then(() => {
|
||||
const f = join(this.dir, '.next', 'dist', relative(this.dir, path))
|
||||
delete require.cache[f]
|
||||
this.send('hardReload')
|
||||
})
|
||||
}
|
||||
|
||||
this.watcher = watch(this.dir)
|
||||
this.watcher
|
||||
.on('add', onChange)
|
||||
.on('change', onChange)
|
||||
.on('unlink', onChange)
|
||||
.on('error', (err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCache (path) {
|
||||
|
|
Loading…
Reference in a new issue