From d882b1ac779f18944ac1b83579b7ae7cb21e75f7 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Thu, 15 Jun 2017 19:23:00 +0430 Subject: [PATCH] improvements --- lib/builder.js | 53 +++++++++++++++++------------------- lib/nuxt.js | 3 +- lib/renderer.js | 39 ++++++++++++++++++-------- lib/webpack/client.config.js | 11 +++++++- 4 files changed, 64 insertions(+), 42 deletions(-) diff --git a/lib/builder.js b/lib/builder.js index 160cb58518..e2c17ab7e9 100644 --- a/lib/builder.js +++ b/lib/builder.js @@ -5,10 +5,9 @@ import hash from 'hash-sum' import pify from 'pify' import webpack from 'webpack' import serialize from 'serialize-javascript' -import webpackDevMiddleware from 'webpack-dev-middleware' -import webpackHotMiddleware from 'webpack-hot-middleware' import { join, resolve, basename, dirname } from 'path' import Tapable from 'tappable' +import chalk from 'chalk' import { r, wp, createRoutes } from './utils' import clientWebpackConfig from './webpack/client.config.js' import serverWebpackConfig from './webpack/server.config.js' @@ -282,7 +281,7 @@ export default class Builder extends Tapable { // Start build return new Promise((resolve, reject) => { - this.compiler.run((err, multiStats) => { + const handler = (err, multiStats) => { if (err) { return reject(err) } @@ -292,8 +291,15 @@ export default class Builder extends Tapable { return reject(new Error('Webpack build exited with errors')) } } + // Use watch handler instead of compiler.apply('done') to prevent duplicate emits + this.applyPlugins('reload', multiStats) resolve() - }) + } + if (this.options.dev) { + this.compiler.watch(this.options.watchers.webpack, handler) + } else { + this.compiler.run(handler) + } }) } @@ -306,42 +312,33 @@ export default class Builder extends Tapable { compiler.outputFileSystem = mfs }) - let clientConfig = this.compiler.$client.options + // Watch + this.plugin('reload', () => { + // Show open URL + let _host = host === '0.0.0.0' ? 'localhost' : host + // eslint-disable-next-line no-console + console.log(chalk.bold(chalk.bgCyan.black(' OPEN ') + chalk.cyan(` http://${_host}:${port}\n`))) - // Setup on the fly compilation + hot-reload - clientConfig.entry.app = _.flatten(['webpack-hot-middleware/client?reload=true', clientConfig.entry.app]) - clientConfig.plugins.push( - new webpack.HotModuleReplacementPlugin(), - new webpack.NoEmitOnErrorsPlugin() - ) + // Reload renderer if available + if (this.nuxt.renderer) { + this.nuxt.renderer.loadResources(mfs) + } + }) - // Create webpack dev middleware - this.webpackDevMiddleware = pify(webpackDevMiddleware(this.compiler.$client, { - publicPath: clientConfig.output.publicPath, + // Create webpack Dev/Hot middleware + this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(this.compiler.$client, { + publicPath: this.options.build.publicPath, stats: this.webpackStats, quiet: true, noInfo: true, watchOptions: this.options.watchers.webpack })) - this.webpackHotMiddleware = pify(webpackHotMiddleware(this.compiler.$client, { + this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(this.compiler.$client, { log: false, heartbeat: 2500 })) - // Run after compilation is done - this.compiler.plugin('done', async stats => { - // Reload renderer if available - if (this.nuxt.renderer) { - await this.nuxt.renderer.loadResources(mfs) - } - // Show open URL - if (!stats.hasErrors() && !stats.hasWarnings()) { - let _host = host === '0.0.0.0' ? 'localhost' : host - console.log(`> Open http://${_host}:${port}\n`) // eslint-disable-line no-console - } - }) - this.watchFiles() } diff --git a/lib/nuxt.js b/lib/nuxt.js index 38cf9769b7..e6aafdcde9 100644 --- a/lib/nuxt.js +++ b/lib/nuxt.js @@ -1,4 +1,5 @@ import Tapable from 'tappable' +import Builder from './builder' import * as Utils from './utils' import Renderer from './renderer' import ModuleContainer from './module-container' @@ -56,7 +57,7 @@ export default class Nuxt extends Tapable { if (this._builder) { return this._builder } - const Builder = require('./builder').default + // const Builder = require('./builder').default this._builder = new Builder(this) return this._builder } diff --git a/lib/renderer.js b/lib/renderer.js index f51fb396df..0aa1729356 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -69,8 +69,10 @@ export default class Renderer extends Tapable { this.gzipMiddleware = pify(compression(this.options.render.gzip)) } - // Try to load resources from fs - return this.loadResources() + // Load resources from fs + if (!this.options.dev) { + return this.loadResources() + } } async loadResources (_fs = fs, distPath) { @@ -91,21 +93,32 @@ export default class Renderer extends Tapable { } } + let updated = [] + Object.keys(resourceMap).forEach(resourceKey => { let { path, transform } = resourceMap[resourceKey] - let data - if (_fs.existsSync(path)) { - data = _fs.readFileSync(path, 'utf8') - if (typeof transform === 'function') { - data = transform(data) - } + let rawKey = '$$' + resourceKey + let rawData, data + if (!_fs.existsSync(path)) { + return // Resource not exists } - if (data) { - this.resources[resourceKey] = data + rawData = _fs.readFileSync(path, 'utf8') + if (!rawData || rawData === this.resources[rawKey]) { + return // No changes } + this.resources[rawKey] = rawData + data = transform(rawData) + if (!data) { + return // Invalid data ? + } + this.resources[resourceKey] = data + updated.push(resourceKey) }) - this.createRenderer() + if (updated.length > 0) { + // debug('Updated', updated.join(', ')) + this.createRenderer() + } } createRenderer () { @@ -232,7 +245,9 @@ export default class Renderer extends Tapable { async renderRoute (url, context = {}) { /* istanbul ignore if */ if (!this.bundleRenderer || !this.resources.appTemplate) { - return Promise.reject(new Error('bundleRenderer is not available')) + return new Promise(resolve => { + setTimeout(() => resolve(this.renderRoute(url, context)), 1000) + }) } // Log rendered url diff --git a/lib/webpack/client.config.js b/lib/webpack/client.config.js index 05b0ed2dbc..f68ddf1e18 100644 --- a/lib/webpack/client.config.js +++ b/lib/webpack/client.config.js @@ -1,4 +1,4 @@ -import { each, defaults } from 'lodash' +import { each, defaults, flatten } from 'lodash' import webpack from 'webpack' import VueSSRClientPlugin from 'vue-server-renderer/client-plugin' import HTMLPlugin from 'html-webpack-plugin' @@ -88,6 +88,15 @@ export default function webpackClientConfig () { if (this.options.dev) { config.plugins.push(new FriendlyErrorsWebpackPlugin()) } + // Dev client build + if(this.options.dev){ + // Add HMR support + config.entry.app = flatten(['webpack-hot-middleware/client?name=$client&reload=true', config.entry.app]) + config.plugins.push( + new webpack.HotModuleReplacementPlugin(), + new webpack.NoEmitOnErrorsPlugin() + ) + } // Production client build if (!this.options.dev) { config.plugins.push(