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

Remove webpack-dev-server (#276)

* remove webpack-dev-server

* webpack: fix publicPath
This commit is contained in:
Naoyuki Kanezawa 2016-11-24 03:32:49 +09:00 committed by Guillermo Rauch
parent 3a6dd325a1
commit a14cc66720
9 changed files with 74 additions and 236 deletions

View file

@ -1,5 +1,4 @@
import 'react-hot-loader/patch'
import './webpack-dev-client?http://localhost:3030'
import * as next from './next'
module.exports = next

View file

@ -1,167 +0,0 @@
/* global __resourceQuery, next */
// Based on 'webpack-dev-server/client'
import url from 'url'
import stripAnsi from 'strip-ansi'
import socket from './socket'
function getCurrentScriptSource () {
// `document.currentScript` is the most accurate way to find the current script,
// but is not supported in all browsers.
if (document.currentScript) {
return document.currentScript.getAttribute('src')
}
// Fall back to getting all scripts in the document.
const scriptElements = document.scripts || []
const currentScript = scriptElements[scriptElements.length - 1]
if (currentScript) {
return currentScript.getAttribute('src')
}
// Fail as there was no script to use.
throw new Error('[WDS] Failed to get current script source')
}
let urlParts
if (typeof __resourceQuery === 'string' && __resourceQuery) {
// If this bundle is inlined, use the resource query to get the correct url.
urlParts = url.parse(__resourceQuery.substr(1))
} else {
// Else, get the url from the <script> this file was called with.
let scriptHost = getCurrentScriptSource()
scriptHost = scriptHost.replace(/\/[^\/]+$/, '')
urlParts = url.parse(scriptHost || '/', false, true)
}
let hot = false
let initial = true
let currentHash = ''
let logLevel = 'info'
function log (level, msg) {
if (logLevel === 'info' && level === 'info') {
return console.log(msg)
}
if (['info', 'warning'].indexOf(logLevel) >= 0 && level === 'warning') {
return console.warn(msg)
}
if (['info', 'warning', 'error'].indexOf(logLevel) >= 0 && level === 'error') {
return console.error(msg)
}
}
const onSocketMsg = {
hot () {
hot = true
log('info', '[WDS] Hot Module Replacement enabled.')
},
invalid () {
log('info', '[WDS] App updated. Recompiling...')
},
hash (hash) {
currentHash = hash
},
'still-ok': () => {
log('info', '[WDS] Nothing changed.')
},
'log-level': (level) => {
logLevel = level
},
ok () {
if (initial) {
initial = false
return
}
reloadApp()
},
warnings (warnings) {
log('info', '[WDS] Warnings while compiling.')
for (let i = 0; i < warnings.length; i++) {
console.warn(stripAnsi(warnings[i]))
}
if (initial) {
initial = false
return
}
reloadApp()
},
errors (errors) {
log('info', '[WDS] Errors while compiling.')
for (let i = 0; i < errors.length; i++) {
console.error(stripAnsi(errors[i]))
}
if (initial) {
initial = false
return
}
reloadApp()
},
'proxy-error': (errors) => {
log('info', '[WDS] Proxy error.')
for (let i = 0; i < errors.length; i++) {
log('error', stripAnsi(errors[i]))
}
if (initial) {
initial = false
return
}
},
reload (route) {
if (route === '/_error') {
for (const r of Object.keys(next.router.components)) {
const { Component } = next.router.components[r]
if (Component.__route === '/_error-debug') {
// reload all '/_error-debug'
// which are expected to be errors of '/_error' routes
next.router.reload(r)
}
}
return
}
next.router.reload(route)
},
close () {
log('error', '[WDS] Disconnected!')
}
}
let hostname = urlParts.hostname
let protocol = urlParts.protocol
if (urlParts.hostname === '0.0.0.0') {
// why do we need this check?
// hostname n/a for file protocol (example, when using electron, ionic)
// see: https://github.com/webpack/webpack-dev-server/pull/384
if (window.location.hostname && !!~window.location.protocol.indexOf('http')) {
hostname = window.location.hostname
}
}
// `hostname` can be empty when the script path is relative. In that case, specifying
// a protocol would result in an invalid URL.
// When https is used in the app, secure websockets are always necessary
// because the browser doesn't accept non-secure websockets.
if (hostname && (window.location.protocol === 'https:' || urlParts.hostname === '0.0.0.0')) {
protocol = window.location.protocol
}
const socketUrl = url.format({
protocol,
auth: urlParts.auth,
hostname,
port: (urlParts.port === '0') ? window.location.port : urlParts.port,
pathname: urlParts.path == null || urlParts.path === '/' ? '/sockjs-node' : urlParts.path
})
socket(socketUrl, onSocketMsg)
function reloadApp () {
if (hot) {
log('info', '[WDS] App hot update...')
window.postMessage('webpackHotUpdate' + currentHash, '*')
} else {
log('info', '[WDS] App updated. Reloading...')
window.location.reload()
}
}

View file

@ -1,39 +0,0 @@
import SockJS from 'sockjs-client'
let retries = 0
let sock = null
export default function socket (url, handlers) {
sock = new SockJS(url)
sock.onopen = () => {
retries = 0
}
sock.onclose = () => {
if (retries === 0) handlers.close()
// Try to reconnect.
sock = null
// After 10 retries stop trying, to prevent logspam.
if (retries <= 10) {
// Exponentially increase timeout to reconnect.
// Respectfully copied from the package `got`.
const retryInMs = 1000 * Math.pow(2, retries) + Math.random() * 100
retries += 1
setTimeout(() => {
socket(url, handlers)
}, retryInMs)
}
}
sock.onmessage = (e) => {
// This assumes that all data sent via the websocket is JSON.
const msg = JSON.parse(e.data)
if (handlers[msg.type]) {
handlers[msg.type](msg.data)
}
}
}

View file

@ -0,0 +1,30 @@
/* global next */
import webpackHotMiddlewareClient from 'webpack-hot-middleware/client?overlay=false&reload=true'
const handlers = {
reload (route) {
if (route === '/_error') {
for (const r of Object.keys(next.router.components)) {
const { Component } = next.router.components[r]
if (Component.__route === '/_error-debug') {
// reload all '/_error-debug'
// which are expected to be errors of '/_error' routes
next.router.reload(r)
}
}
return
}
next.router.reload(route)
}
}
webpackHotMiddlewareClient.subscribe((obj) => {
const fn = handlers[obj.action]
if (fn) {
const data = obj.data || []
fn(...data)
} else {
throw new Error('Unexpected action ' + obj.action)
}
})

View file

@ -58,8 +58,8 @@
"strip-ansi": "3.0.1",
"url": "0.11.0",
"webpack": "1.13.3",
"webpack-dev-server": "1.16.2",
"sockjs-client": "1.1.1",
"webpack-dev-middleware": "1.8.4",
"webpack-hot-middleware": "2.13.2",
"write-file-webpack-plugin": "3.4.2"
},
"devDependencies": {

View file

@ -35,7 +35,7 @@ export default class WatchPagesPlugin {
if (compiler.hasEntry(name)) return
const entries = ['webpack/hot/dev-server', f]
const entries = ['next/dist/client/webpack-hot-middleware-client', f]
compiler.addEntry(entries, name)
})
@ -47,7 +47,7 @@ export default class WatchPagesPlugin {
if (name === errorPageName) {
compiler.addEntry([
'webpack/hot/dev-server',
'next/dist/client/webpack-hot-middleware-client',
join(__dirname, '..', '..', '..', 'pages', '_error.js')
], name)
}

View file

@ -14,7 +14,7 @@ export default async function createCompiler (dir, { hotReload = false, dev = fa
const pages = await glob('pages/**/*.js', { cwd: dir })
const entry = {}
const defaultEntries = hotReload ? ['webpack/hot/dev-server'] : []
const defaultEntries = hotReload ? ['next/dist/client/webpack-hot-middleware-client'] : []
for (const p of pages) {
entry[join('bundles', p)] = defaultEntries.concat(['./' + p])
}
@ -56,7 +56,9 @@ export default async function createCompiler (dir, { hotReload = false, dev = fa
if (hotReload) {
plugins.push(
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(),
new DetachPlugin(),
new DynamicEntryPlugin(),
new UnlinkFilePlugin(),
@ -144,7 +146,7 @@ export default async function createCompiler (dir, { hotReload = false, dev = fa
path: join(dir, '.next'),
filename: '[name]',
libraryTarget: 'commonjs2',
publicPath: hotReload ? 'http://localhost:3030/' : null
publicPath: hotReload ? '/_webpack/' : null
},
externals: [
'react',

View file

@ -1,5 +1,6 @@
import { join, relative, sep } from 'path'
import WebpackDevServer from 'webpack-dev-server'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import webpack from './build/webpack'
import read from './read'
@ -7,7 +8,9 @@ export default class HotReloader {
constructor (dir, dev = false) {
this.dir = dir
this.dev = dev
this.server = null
this.middlewares = []
this.webpackDevMiddleware = null
this.webpackHotMiddleware = null
this.initialized = false
this.stats = null
this.compilationErrors = null
@ -16,13 +19,23 @@ export default class HotReloader {
this.prevFailedChunkNames = null
}
async start () {
await this.prepareServer()
this.stats = await this.waitUntilValid()
await this.listen()
async run (req, res) {
for (const fn of this.middlewares) {
await new Promise((resolve, reject) => {
fn(req, res, (err) => {
if (err) reject(err)
resolve()
})
})
}
}
async prepareServer () {
async start () {
await this.prepareMiddlewares()
this.stats = await this.waitUntilValid()
}
async prepareMiddlewares () {
const compiler = await webpack(this.dir, { hotReload: true, dev: this.dev })
compiler.plugin('after-emit', (compilation, callback) => {
@ -79,11 +92,9 @@ export default class HotReloader {
this.prevFailedChunkNames = failedChunkNames
})
this.server = new WebpackDevServer(compiler, {
publicPath: '/',
hot: true,
this.webpackDevMiddleware = webpackDevMiddleware(compiler, {
publicPath: '/_webpack/',
noInfo: true,
clientLogLevel: 'warning',
stats: {
assets: false,
children: false,
@ -101,20 +112,18 @@ export default class HotReloader {
warnings: false
}
})
this.webpackHotMiddleware = webpackHotMiddleware(compiler, { log: false })
this.middlewares = [
this.webpackDevMiddleware,
this.webpackHotMiddleware
]
}
waitUntilValid () {
return new Promise((resolve) => {
this.server.middleware.waitUntilValid(resolve)
})
}
listen () {
return new Promise((resolve, reject) => {
this.server.listen(3030, (err) => {
if (err) return reject(err)
resolve()
})
this.webpackDevMiddleware.waitUntilValid(resolve)
})
}
@ -141,8 +150,8 @@ export default class HotReloader {
return this.compilationErrors
}
send (type, data) {
this.server.sockWrite(this.server.sockets, type, data)
send (action, ...args) {
this.webpackHotMiddleware.publish({ action, data: args })
}
}

View file

@ -60,6 +60,10 @@ export default class Server {
}
async run (req, res) {
if (this.hotReloader) {
await this.hotReloader.run(req, res)
}
const fn = this.router.match(req, res)
if (fn) {
await fn()