1
0
Fork 0
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:
Naoyuki Kanezawa 2016-12-28 08:28:19 +09:00 committed by Guillermo Rauch
parent 815f17989b
commit 798fd3c1e8
7 changed files with 54 additions and 114 deletions

View file

@ -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()
}
}

View file

@ -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",

View file

@ -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
})
}

View file

@ -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) {

View file

@ -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'
}
}

View file

@ -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,

View file

@ -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) {