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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (route === '/_document') {
|
||||||
|
window.location.reload()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
Router.reload(route)
|
Router.reload(route)
|
||||||
},
|
},
|
||||||
|
|
||||||
change (route) {
|
change (route) {
|
||||||
|
if (route === '/_document') {
|
||||||
|
window.location.reload()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const { Component } = Router.components[route] || {}
|
const { Component } = Router.components[route] || {}
|
||||||
if (Component && Component.__route === '/_error-debug') {
|
if (Component && Component.__route === '/_error-debug') {
|
||||||
// reload to recover from runtime errors
|
// reload to recover from runtime errors
|
||||||
Router.reload(route)
|
Router.reload(route)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
hardReload () {
|
|
||||||
window.location.reload()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
"babel-preset-es2015": "6.18.0",
|
"babel-preset-es2015": "6.18.0",
|
||||||
"babel-preset-react": "6.16.0",
|
"babel-preset-react": "6.16.0",
|
||||||
"babel-runtime": "6.20.0",
|
"babel-runtime": "6.20.0",
|
||||||
"chokidar": "1.6.1",
|
|
||||||
"cross-spawn": "5.0.1",
|
"cross-spawn": "5.0.1",
|
||||||
"del": "2.2.2",
|
"del": "2.2.2",
|
||||||
"friendly-errors-webpack-plugin": "1.1.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 webpack from './webpack'
|
||||||
import babel from './babel'
|
|
||||||
import clean from './clean'
|
import clean from './clean'
|
||||||
|
|
||||||
export default async function build (dir) {
|
export default async function build (dir) {
|
||||||
|
@ -8,10 +7,7 @@ export default async function build (dir) {
|
||||||
clean(dir)
|
clean(dir)
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
await runCompiler(compiler)
|
||||||
runCompiler(compiler),
|
|
||||||
babel(dir)
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runCompiler (compiler) {
|
function runCompiler (compiler) {
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
import { resolve, relative, join, extname } from 'path'
|
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 {
|
export default class WatchPagesPlugin {
|
||||||
constructor (dir) {
|
constructor (dir) {
|
||||||
this.dir = resolve(dir, 'pages')
|
this.dir = resolve(dir, 'pages')
|
||||||
|
@ -7,6 +17,15 @@ export default class WatchPagesPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
apply (compiler) {
|
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) => {
|
compiler.plugin('emit', (compilation, callback) => {
|
||||||
// watch the pages directory
|
// watch the pages directory
|
||||||
compilation.contextDependencies =
|
compilation.contextDependencies =
|
||||||
|
@ -21,9 +40,6 @@ export default class WatchPagesPlugin {
|
||||||
const getEntryName = (f) => {
|
const getEntryName = (f) => {
|
||||||
return join('bundles', relative(compiler.options.context, 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) => {
|
compiler.plugin('watch-run', (watching, callback) => {
|
||||||
Object.keys(compiler.fileTimestamps)
|
Object.keys(compiler.fileTimestamps)
|
||||||
|
@ -31,7 +47,7 @@ export default class WatchPagesPlugin {
|
||||||
.filter((f) => this.prevFileDependencies.indexOf(f) < 0)
|
.filter((f) => this.prevFileDependencies.indexOf(f) < 0)
|
||||||
.forEach((f) => {
|
.forEach((f) => {
|
||||||
const name = getEntryName(f)
|
const name = getEntryName(f)
|
||||||
if (name === errorPageName) {
|
if (defaultPages.has(name)) {
|
||||||
compiler.removeEntry(name)
|
compiler.removeEntry(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,10 +63,10 @@ export default class WatchPagesPlugin {
|
||||||
const name = getEntryName(f)
|
const name = getEntryName(f)
|
||||||
compiler.removeEntry(name)
|
compiler.removeEntry(name)
|
||||||
|
|
||||||
if (name === errorPageName) {
|
if (defaultPages.has(name)) {
|
||||||
compiler.addEntry([
|
compiler.addEntry([
|
||||||
hotMiddlewareClientPath,
|
hotMiddlewareClientPath,
|
||||||
errorPagePath
|
defaultPages.get(name)
|
||||||
], name)
|
], name)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -60,9 +76,7 @@ export default class WatchPagesPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
isPageFile (f) {
|
isPageFile (f) {
|
||||||
return f.indexOf(this.dir) === 0 &&
|
return f.indexOf(this.dir) === 0 && extname(f) === '.js'
|
||||||
relative(this.dir, f) !== '_document.js' &&
|
|
||||||
extname(f) === '.js'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,18 @@ import DynamicEntryPlugin from './plugins/dynamic-entry-plugin'
|
||||||
import DetachPlugin from './plugins/detach-plugin'
|
import DetachPlugin from './plugins/detach-plugin'
|
||||||
import getConfig from '../config'
|
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 } = {}) {
|
export default async function createCompiler (dir, { dev = false, quiet = false } = {}) {
|
||||||
dir = resolve(dir)
|
dir = resolve(dir)
|
||||||
const config = getConfig(dir)
|
const config = getConfig(dir)
|
||||||
|
|
||||||
const pages = await glob('pages/**/*.js', {
|
const pages = await glob('pages/**/*.js', { cwd: dir })
|
||||||
cwd: dir,
|
|
||||||
ignore: 'pages/_document.js'
|
|
||||||
})
|
|
||||||
|
|
||||||
const entry = {
|
const entry = {
|
||||||
'main.js': dev ? require.resolve('../../client/next-dev') : require.resolve('../../client/next')
|
'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 nextPagesDir = join(__dirname, '..', '..', 'pages')
|
||||||
|
const interpolateNames = new Map()
|
||||||
|
|
||||||
const errorEntry = join('bundles', 'pages', '_error.js')
|
for (const p of defaultPages) {
|
||||||
const defaultErrorPath = join(nextPagesDir, '_error.js')
|
const entryName = join('bundles', 'pages', p)
|
||||||
if (!entry[errorEntry]) {
|
const path = join(nextPagesDir, p)
|
||||||
entry[errorEntry] = defaultEntries.concat([defaultErrorPath + '?entry'])
|
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 nodeModulesDir = join(__dirname, '..', '..', '..', 'node_modules')
|
||||||
|
const minChunks = pages.filter((p) => p !== documentPage).length
|
||||||
|
|
||||||
const plugins = [
|
const plugins = [
|
||||||
new WriteFilePlugin({
|
new WriteFilePlugin({
|
||||||
|
@ -55,7 +60,7 @@ export default async function createCompiler (dir, { dev = false, quiet = false
|
||||||
new webpack.optimize.CommonsChunkPlugin({
|
new webpack.optimize.CommonsChunkPlugin({
|
||||||
name: 'commons',
|
name: 'commons',
|
||||||
filename: 'commons.js',
|
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
|
query: mainBabelOptions
|
||||||
}])
|
}])
|
||||||
|
|
||||||
const interpolateNames = new Map([
|
|
||||||
[defaultErrorPath, 'dist/pages/_error.js'],
|
|
||||||
[errorDebugPath, 'dist/pages/_error-debug.js']
|
|
||||||
])
|
|
||||||
|
|
||||||
let webpackConfig = {
|
let webpackConfig = {
|
||||||
context: dir,
|
context: dir,
|
||||||
entry,
|
entry,
|
||||||
|
|
|
@ -4,7 +4,6 @@ import webpackHotMiddleware from 'webpack-hot-middleware'
|
||||||
import isWindowsBash from 'is-windows-bash'
|
import isWindowsBash from 'is-windows-bash'
|
||||||
import webpack from './build/webpack'
|
import webpack from './build/webpack'
|
||||||
import clean from './build/clean'
|
import clean from './build/clean'
|
||||||
import babel, { watch } from './build/babel'
|
|
||||||
import read from './read'
|
import read from './read'
|
||||||
|
|
||||||
export default class HotReloader {
|
export default class HotReloader {
|
||||||
|
@ -14,7 +13,6 @@ export default class HotReloader {
|
||||||
this.middlewares = []
|
this.middlewares = []
|
||||||
this.webpackDevMiddleware = null
|
this.webpackDevMiddleware = null
|
||||||
this.webpackHotMiddleware = null
|
this.webpackHotMiddleware = null
|
||||||
this.watcher = null
|
|
||||||
this.initialized = false
|
this.initialized = false
|
||||||
this.stats = null
|
this.stats = null
|
||||||
this.compilationErrors = null
|
this.compilationErrors = null
|
||||||
|
@ -36,24 +34,16 @@ export default class HotReloader {
|
||||||
}
|
}
|
||||||
|
|
||||||
async start () {
|
async start () {
|
||||||
this.watch()
|
|
||||||
|
|
||||||
const [compiler] = await Promise.all([
|
const [compiler] = await Promise.all([
|
||||||
webpack(this.dir, { dev: true, quiet: this.quiet }),
|
webpack(this.dir, { dev: true, quiet: this.quiet }),
|
||||||
clean(this.dir)
|
clean(this.dir)
|
||||||
])
|
])
|
||||||
|
|
||||||
await Promise.all([
|
this.prepareMiddlewares(compiler)
|
||||||
this.prepareMiddlewares(compiler),
|
|
||||||
babel(this.dir, { dev: true })
|
|
||||||
])
|
|
||||||
|
|
||||||
this.stats = await this.waitUntilValid()
|
this.stats = await this.waitUntilValid()
|
||||||
}
|
}
|
||||||
|
|
||||||
async stop () {
|
async stop () {
|
||||||
if (this.watcher) this.watcher.close()
|
|
||||||
|
|
||||||
if (this.webpackDevMiddleware) {
|
if (this.webpackDevMiddleware) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.webpackDevMiddleware.close((err) => {
|
this.webpackDevMiddleware.close((err) => {
|
||||||
|
@ -188,26 +178,6 @@ export default class HotReloader {
|
||||||
send (action, ...args) {
|
send (action, ...args) {
|
||||||
this.webpackHotMiddleware.publish({ action, data: 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) {
|
function deleteCache (path) {
|
||||||
|
|
Loading…
Reference in a new issue