Skip to content

Commit

Permalink
Fix custom document compilation (vercel#534)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
nkzawa authored and rauchg committed Dec 27, 2016
1 parent 815f179 commit 798fd3c
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 114 deletions.
14 changes: 11 additions & 3 deletions client/webpack-hot-middleware-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
}

Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
47 changes: 0 additions & 47 deletions server/build/babel/index.js

This file was deleted.

6 changes: 1 addition & 5 deletions server/build/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import webpack from './webpack'
import babel from './babel'
import clean from './clean'

export default async function build (dir) {
Expand All @@ -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) {
Expand Down
32 changes: 23 additions & 9 deletions server/build/plugins/watch-pages-plugin.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
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')
this.prevFileDependencies = []
}

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 =
Expand All @@ -21,17 +40,14 @@ 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)
.filter(isPageFile)
.filter((f) => this.prevFileDependencies.indexOf(f) < 0)
.forEach((f) => {
const name = getEntryName(f)
if (name === errorPageName) {
if (defaultPages.has(name)) {
compiler.removeEntry(name)
}

Expand All @@ -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)
}
})
Expand All @@ -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'
}
}

36 changes: 18 additions & 18 deletions server/build/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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({
Expand All @@ -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)
})
]

Expand Down Expand Up @@ -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,
Expand Down
32 changes: 1 addition & 31 deletions server/hot-reloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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) => {
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 798fd3c

Please sign in to comment.