many improvements

This commit is contained in:
Pooya Parsa 2017-06-16 02:49:53 +04:30
parent d68b4f0c00
commit 5722a92c4c
10 changed files with 268 additions and 182 deletions

View File

@ -87,12 +87,10 @@ function listenOnConfigChanges(nuxt, server) {
options.rootDir = rootDir options.rootDir = rootDir
nuxt.close() nuxt.close()
.then(() => { .then(() => {
nuxt.renderer = null
debug('Rebuilding the app...') debug('Rebuilding the app...')
return new Nuxt(options).build() var nuxt = new Nuxt(options)
})
.then((nuxt) => {
server.nuxt = nuxt server.nuxt = nuxt
return nuxt.ready()
}) })
.catch((error) => { .catch((error) => {
console.error('Error while rebuild the app:', error) // eslint-disable-line no-console console.error('Error while rebuild the app:', error) // eslint-disable-line no-console

View File

@ -7,11 +7,11 @@ import webpack from 'webpack'
import serialize from 'serialize-javascript' import serialize from 'serialize-javascript'
import { join, resolve, basename, dirname } from 'path' import { join, resolve, basename, dirname } from 'path'
import Tapable from 'tappable' import Tapable from 'tappable'
import chalk from 'chalk' import MFS from 'memory-fs'
import { sequence, parallel } from "./utils"
import { r, wp, createRoutes } from './utils' import { r, wp, createRoutes } from './utils'
import clientWebpackConfig from './webpack/client.config.js' import clientWebpackConfig from './webpack/client.config.js'
import serverWebpackConfig from './webpack/server.config.js' import serverWebpackConfig from './webpack/server.config.js'
import MFS from 'memory-fs'
const debug = require('debug')('nuxt:build') const debug = require('debug')('nuxt:build')
debug.color = 2 // Force green color debug.color = 2 // Force green color
@ -23,9 +23,6 @@ const writeFile = pify(fs.writeFile)
const mkdirp = pify(fs.mkdirp) const mkdirp = pify(fs.mkdirp)
const glob = pify(require('glob')) const glob = pify(require('glob'))
const host = process.env.HOST || process.env.npm_package_config_nuxt_host || 'localhost'
const port = process.env.PORT || process.env.npm_package_config_nuxt_port || '3000'
export default class Builder extends Tapable { export default class Builder extends Tapable {
constructor (nuxt) { constructor (nuxt) {
super() super()
@ -50,7 +47,7 @@ export default class Builder extends Tapable {
_.defaultsDeep(this.options.build, extraDefaults) _.defaultsDeep(this.options.build, extraDefaults)
// Mute stats on dev // Mute stats on dev
this.webpackStats = this.options.dev ? '' : { this.webpackStats = this.options.dev ? false : {
chunks: false, chunks: false,
children: false, children: false,
modules: false, modules: false,
@ -220,7 +217,7 @@ export default class Builder extends Tapable {
})) }))
// Interpret and move template files to .nuxt/ // Interpret and move template files to .nuxt/
return Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => { await Promise.all(templatesFiles.map(async ({ src, dst, options, custom }) => {
// Add template to watchers // Add template to watchers
this.options.build.watch.push(src) this.options.build.watch.push(src)
// Render template to dst // Render template to dst
@ -245,7 +242,7 @@ export default class Builder extends Tapable {
// Write file // Write file
await writeFile(path, content, 'utf8') await writeFile(path, content, 'utf8')
// Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288) // Fix webpack loop (https://github.com/webpack/watchpack/issues/25#issuecomment-287789288)
const dateFS = Date.now() / 1000 - 30 const dateFS = Date.now() / 1000 - 1000
return utimes(path, dateFS, dateFS) return utimes(path, dateFS, dateFS)
})) }))
} }
@ -256,16 +253,23 @@ export default class Builder extends Tapable {
// Client // Client
let clientConfig = clientWebpackConfig.call(this) let clientConfig = clientWebpackConfig.call(this)
clientConfig.name = '$client'
compilersOptions.push(clientConfig) compilersOptions.push(clientConfig)
// Server // Server
let serverConfig = serverWebpackConfig.call(this) let serverConfig = serverWebpackConfig.call(this)
serverConfig.name = '$server'
compilersOptions.push(serverConfig) compilersOptions.push(serverConfig)
// Leverage webpack multi-compiler for faster builds // Simulate webpack multi compiler interface
this.compiler = webpack(compilersOptions) // Separate compilers are simpler, safer and faster
this.compiler = { cache: {}, compilers: [] }
compilersOptions.forEach(compilersOption => {
this.compiler.compilers.push(webpack(compilersOption))
})
this.compiler.plugin = (...args) => {
this.compiler.compilers.forEach(compiler => {
compiler.plugin(...args)
})
}
// Access to compilers with name // Access to compilers with name
this.compiler.compilers.forEach(compiler => { this.compiler.compilers.forEach(compiler => {
@ -274,71 +278,84 @@ export default class Builder extends Tapable {
} }
}) })
// Add middleware for dev // Add dev Stuff
if (this.options.dev) { if (this.options.dev) {
this.webpackDev() this.webpackDev()
} }
// Start build // Start Builds
return new Promise((resolve, reject) => { return parallel(this.compiler.compilers, compiler => new Promise((resolve, reject) => {
const handler = (err, multiStats) => { let _resolved = false
const handler = (err, stats) => {
if (_resolved) return
_resolved = true
if (err) { if (err) {
return reject(err) return reject(err)
} }
for (let _stats of multiStats.stats) { if (!this.options.dev) {
console.log(_stats.toString(this.webpackStats)) // eslint-disable-line no-console // Show build stats for production
if (_stats.hasErrors()) { console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console
if (stats.hasErrors()) {
return reject(new Error('Webpack build exited with errors')) 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() resolve()
} }
if (this.options.dev) { if (this.options.dev) {
this.compiler.watch(this.options.watchers.webpack, handler) if (compiler.options.name === 'client') {
// Client watch is started by dev-middleware
resolve()
} else {
// Build and watch for changes
compiler.watch(this.options.watchers.webpack, handler)
}
} else { } else {
this.compiler.run(handler) // Production build
compiler.run(handler)
} }
}) }))
} }
webpackDev () { webpackDev () {
debug('Adding webpack middleware...') // Use shared MFS + Cache for faster builds
// Use MFS for faster builds
let mfs = new MFS() let mfs = new MFS()
this.compiler.compilers.forEach(compiler => { this.compiler.compilers.forEach(compiler => {
compiler.outputFileSystem = mfs compiler.outputFileSystem = mfs
compiler.cache = this.compiler.cache
}) })
// Watch // Run after each compile
this.plugin('reload', () => { this.compiler.plugin('done', stats => {
// Show open URL console.log(stats.toString(this.webpackStats)) // eslint-disable-line no-console
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`)))
// Reload renderer if available // Reload renderer if available
if (this.nuxt.renderer) { if (this.nuxt.renderer) {
this.nuxt.renderer.loadResources(mfs) this.nuxt.renderer.loadResources(mfs)
} }
}) })
// Create webpack Dev/Hot middleware // Add dev Middleware
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(this.compiler.$client, { debug('Adding webpack middleware...')
// Create webpack dev middleware
this.webpackDevMiddleware = pify(require('webpack-dev-middleware')(this.compiler.client, {
publicPath: this.options.build.publicPath, publicPath: this.options.build.publicPath,
stats: this.webpackStats, stats: this.webpackStats,
quiet: true,
noInfo: true, noInfo: true,
quiet: true,
watchOptions: this.options.watchers.webpack watchOptions: this.options.watchers.webpack
})) }))
this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(this.compiler.$client, { this.webpackHotMiddleware = pify(require('webpack-hot-middleware')(this.compiler.client, {
log: false, log: false,
heartbeat: 2500 heartbeat: 2500
})) }))
// Stop webpack middleware on nuxt.close()
this.nuxt.plugin('close', () => new Promise(resolve => {
this.webpackDevMiddleware.close(() => resolve())
}))
// Start watching files
this.watchFiles() this.watchFiles()
} }
@ -360,13 +377,21 @@ export default class Builder extends Tapable {
}) })
/* istanbul ignore next */ /* istanbul ignore next */
const refreshFiles = _.debounce(this.generateRoutesAndFiles, 200) const refreshFiles = _.debounce(this.generateRoutesAndFiles, 200)
// Watch for internals
this.filesWatcher = chokidar.watch(patterns, options) // Watch for src Files
let filesWatcher = chokidar.watch(patterns, options)
.on('add', refreshFiles) .on('add', refreshFiles)
.on('unlink', refreshFiles) .on('unlink', refreshFiles)
// Watch for custom provided files // Watch for custom provided files
this.customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options) let customFilesWatcher = chokidar.watch(_.uniq(this.options.build.watch), options)
.on('change', refreshFiles) .on('change', refreshFiles)
// Stop watching on nuxt.close()
this.nuxt.plugin('close', () => {
filesWatcher.close()
customFilesWatcher.close()
})
} }
} }

View File

@ -1,11 +1,14 @@
import Tapable from 'tappable' import Tapable from 'tappable'
import Builder from './builder' import chalk from 'chalk'
import * as Utils from './utils' import * as Utils from './utils'
import Renderer from './renderer' import Renderer from './renderer'
import ModuleContainer from './module-container' import ModuleContainer from './module-container'
import Server from './server' import Server from './server'
import defaults from './defaults' import defaults from './defaults'
const defaultHost = process.env.HOST || process.env.npm_package_config_nuxt_host || 'localhost'
const defaultPort = process.env.PORT || process.env.npm_package_config_nuxt_port || '3000'
export default class Nuxt extends Tapable { export default class Nuxt extends Tapable {
constructor (_options = {}) { constructor (_options = {}) {
super() super()
@ -57,7 +60,7 @@ export default class Nuxt extends Tapable {
if (this._builder) { if (this._builder) {
return this._builder return this._builder
} }
// const Builder = require('./builder').default const Builder = require('./builder').default
this._builder = new Builder(this) this._builder = new Builder(this)
return this._builder return this._builder
} }
@ -90,36 +93,28 @@ export default class Nuxt extends Tapable {
process.exit(1) process.exit(1)
} }
serverReady ({ host = defaultHost, port = defaultPort } = {}) {
let _host = host === '0.0.0.0' ? 'localhost' : host
// eslint-disable-next-line no-console
console.log('\n' + chalk.bold(chalk.bgBlue.black(' OPEN ') + chalk.blue(` http://${_host}:${port}\n`)))
return this.applyPluginsAsync('serverReady').catch(this.errorHandler)
}
async close (callback) { async close (callback) {
let promises = [] // Call for close
/* istanbul ignore if */ await this.applyPluginsAsync('close')
if (this.webpackDevMiddleware) {
const p = new Promise((resolve, reject) => {
this.webpackDevMiddleware.close(() => resolve())
})
promises.push(p)
}
/* istanbul ignore if */
if (this.webpackServerWatcher) {
const p = new Promise((resolve, reject) => {
this.webpackServerWatcher.close(() => resolve())
})
promises.push(p)
}
/* istanbul ignore if */
if (this.filesWatcher) {
this.filesWatcher.close()
}
/* istanbul ignore if */
if (this.customFilesWatcher) {
this.customFilesWatcher.close()
}
promises.push(this.applyPluginsAsync('close')) // Remove all references
delete this._generator
delete this._builder
return Promise.all(promises).then(() => { this.initialized = false
if (typeof callback === 'function') callback()
}) if (typeof callback === 'function') {
callback()
}
} }
} }

View File

@ -10,6 +10,7 @@ import _ from 'lodash'
import { resolve, join } from 'path' import { resolve, join } from 'path'
import fs from 'fs-extra' import fs from 'fs-extra'
import { createBundleRenderer } from 'vue-server-renderer' import { createBundleRenderer } from 'vue-server-renderer'
import chalk from 'chalk'
import { getContext, setAnsiColors, encodeHtml } from './utils' import { getContext, setAnsiColors, encodeHtml } from './utils'
const debug = require('debug')('nuxt:render') const debug = require('debug')('nuxt:render')
@ -39,6 +40,7 @@ export default class Renderer extends Tapable {
errorTemplate: parseTemplate(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8')) errorTemplate: parseTemplate(fs.readFileSync(resolve(__dirname, 'views', 'error.html'), 'utf8'))
} }
// Initialize
if (nuxt.initialized) { if (nuxt.initialized) {
// If nuxt already initialized // If nuxt already initialized
this._ready = this.ready().catch(this.nuxt.errorHandler) this._ready = this.ready().catch(this.nuxt.errorHandler)
@ -71,16 +73,18 @@ export default class Renderer extends Tapable {
// Load resources from fs // Load resources from fs
if (!this.options.dev) { if (!this.options.dev) {
return this.loadResources() await this.loadResources()
} }
return this
} }
async loadResources (_fs = fs, distPath) { async loadResources (_fs = fs, isServer) {
distPath = distPath || resolve(this.options.buildDir, 'dist') let distPath = resolve(this.options.buildDir, 'dist')
const resourceMap = { const resourceMap = {
clientManifest: { clientManifest: {
path: join(distPath, 'client-manifest.json'), path: join(distPath, 'vue-ssr-client-manifest.json'),
transform: JSON.parse transform: JSON.parse
}, },
serverBundle: { serverBundle: {
@ -116,7 +120,7 @@ export default class Renderer extends Tapable {
}) })
if (updated.length > 0) { if (updated.length > 0) {
// debug('Updated', updated.join(', ')) // debug('Updated', updated.join(', '), isServer)
this.createRenderer() this.createRenderer()
} }
} }
@ -136,6 +140,8 @@ export default class Renderer extends Tapable {
// Promisify renderToString // Promisify renderToString
this.bundleRenderer.renderToString = pify(this.bundleRenderer.renderToString) this.bundleRenderer.renderToString = pify(this.bundleRenderer.renderToString)
this.nuxt.serverReady()
} }
async render (req, res) { async render (req, res) {

View File

@ -8,17 +8,33 @@ class Server {
this.options = nuxt.options this.options = nuxt.options
// Initialize // Initialize
if (nuxt.initialized) {
// If nuxt already initialized
this._ready = this.ready().catch(this.nuxt.errorHandler)
} else {
// Wait for hook
this.nuxt.plugin('afterInit', () => {
this._ready = this.ready()
return this._ready
})
}
}
async ready() {
if (this._ready) {
return this._ready
}
this.app = connect() this.app = connect()
this.server = http.createServer(this.app) this.server = http.createServer(this.app)
this.nuxt.ready()
.then(() => { // Add Middleware
// Add Middleware this.options.serverMiddleware.forEach(m => {
this.options.serverMiddleware.forEach(m => { this.useMiddleware(m)
this.useMiddleware(m) })
}) // Add default render middleware
// Add default render middleware this.useMiddleware(this.render.bind(this))
this.useMiddleware(this.render.bind(this))
})
return this return this
} }
@ -49,12 +65,13 @@ class Server {
host = host || 'localhost' host = host || 'localhost'
port = port || 3000 port = port || 3000
this.nuxt.ready() this.nuxt.ready()
.then(() => { .then(() => {
this.server.listen(port, host, () => { this.server.listen(port, host, () => {
let _host = host === '0.0.0.0' ? 'localhost' : host // Renderer calls showURL when server is really ready
console.log('Ready on http://%s:%s', _host, port) // eslint-disable-line no-console // this.nuxt.showURL(host, port)
})
}) })
}) .catch(this.nuxt.errorHandler)
return this return this
} }

View File

@ -63,6 +63,10 @@ export function sequence (tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve()) return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve())
} }
export function parallel (tasks, fn) {
return Promise.all(tasks.map(task => fn(task)))
}
export function chainFn (base, fn) { export function chainFn (base, fn) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!(fn instanceof Function)) { if (!(fn instanceof Function)) {

View File

@ -1,6 +1,8 @@
import ExtractTextPlugin from 'extract-text-webpack-plugin' import ExtractTextPlugin from 'extract-text-webpack-plugin'
import { defaults } from 'lodash' import { defaults } from 'lodash'
import { join } from 'path' import { join, resolve, } from 'path'
import webpack from 'webpack'
import { cloneDeep } from 'lodash'
import { isUrl, urlJoin } from '../utils' import { isUrl, urlJoin } from '../utils'
import vueLoaderConfig from './vue-loader.config' import vueLoaderConfig from './vue-loader.config'
import { styleLoader, extractStyles } from './helpers' import { styleLoader, extractStyles } from './helpers'
@ -15,12 +17,15 @@ import { styleLoader, extractStyles } from './helpers'
*/ */
export default function webpackBaseConfig ({ isClient, isServer }) { export default function webpackBaseConfig ({ isClient, isServer }) {
const nodeModulesDir = join(__dirname, '..', 'node_modules') const nodeModulesDir = join(__dirname, '..', 'node_modules')
let config = {
devtool: (this.options.dev ? 'cheap-module-source-map' : false), const config = {
devtool: this.options.dev ? 'cheap-module-source-map' : false,
entry: { entry: {
vendor: ['vue', 'vue-router', 'vue-meta'] vendor: ['vue', 'vue-router', 'vue-meta']
}, },
output: { output: {
path: resolve(this.options.buildDir, 'dist'),
filename: this.options.build.filenames.app,
publicPath: (isUrl(this.options.build.publicPath) publicPath: (isUrl(this.options.build.publicPath)
? this.options.build.publicPath ? this.options.build.publicPath
: urlJoin(this.options.router.base, this.options.build.publicPath)) : urlJoin(this.options.router.base, this.options.build.publicPath))
@ -28,7 +33,7 @@ export default function webpackBaseConfig ({ isClient, isServer }) {
performance: { performance: {
maxEntrypointSize: 300000, maxEntrypointSize: 300000,
maxAssetSize: 300000, maxAssetSize: 300000,
hints: (this.options.dev ? false : 'warning') hints: this.options.dev ? false : 'warning'
}, },
resolve: { resolve: {
extensions: ['.js', '.json', '.vue', '.ts'], extensions: ['.js', '.json', '.vue', '.ts'],
@ -57,6 +62,7 @@ export default function webpackBaseConfig ({ isClient, isServer }) {
] ]
}, },
module: { module: {
noParse: /es6-promise\.js$/, // avoid webpack shimming process
rules: [ rules: [
{ {
test: /\.vue$/, test: /\.vue$/,
@ -98,12 +104,33 @@ export default function webpackBaseConfig ({ isClient, isServer }) {
}, },
plugins: this.options.build.plugins plugins: this.options.build.plugins
} }
// CSS extraction // CSS extraction
if (extractStyles.call(this)) { if (extractStyles.call(this)) {
config.plugins.push( config.plugins.push(
new ExtractTextPlugin({ filename: this.options.build.filenames.css }) new ExtractTextPlugin({ filename: this.options.build.filenames.css })
) )
} }
// Return config
return config // --------------------------------------
// Dev specific config
// --------------------------------------
if (this.options.dev) {
//
}
// --------------------------------------
// Production specific config
// --------------------------------------
if (!this.options.dev) {
// This is needed in webpack 2 for minify CSS
config.plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
}
// Clone deep avoid leaking config between Client and Server
return cloneDeep(config)
} }

View File

@ -22,89 +22,108 @@ import base from './base.config.js'
export default function webpackClientConfig () { export default function webpackClientConfig () {
let config = base.call(this, { isClient: true }) let config = base.call(this, { isClient: true })
config.name = 'client'
// Entry // Entry
config.entry.app = resolve(this.options.buildDir, 'client.js') config.entry.app = resolve(this.options.buildDir, 'client.js')
// Add vendors // Add vendors
if (this.options.store) { if (!this.options.dev) {
config.entry.vendor.push('vuex') if (this.options.store) {
config.entry.vendor.push('vuex')
}
config.entry.vendor = config.entry.vendor.concat(this.options.build.vendor)
// Extract vendor chunks for better caching
config.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
filename: this.options.build.filenames.vendor,
minChunks (module) {
// A module is extracted into the vendor chunk when...
return (
// If it's inside node_modules
/node_modules/.test(module.context) &&
// Do not externalize if the request is a CSS file
!/\.(css|less|scss|sass|styl|stylus)$/.test(module.request)
)
}
})
)
} }
config.entry.vendor = config.entry.vendor.concat(this.options.build.vendor)
// Output // Env object defined in nuxt.config.js
config.output.path = resolve(this.options.buildDir, 'dist')
config.output.filename = this.options.build.filenames.app
// env object defined in nuxt.config.js
let env = {} let env = {}
each(this.options.env, (value, key) => { each(this.options.env, (value, key) => {
env['process.env.' + key] = (typeof value === 'string' ? JSON.stringify(value) : value) env['process.env.' + key] = (typeof value === 'string' ? JSON.stringify(value) : value)
}) })
// Webpack plugins
config.plugins = (config.plugins || []).concat([ // Webpack common plugins
// Strip comments in Vue code if (!Array.isArray(config.plugins)) {
new webpack.DefinePlugin(Object.assign(env, { config.plugins = []
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')), }
'process.BROWSER_BUILD': true,
'process.SERVER_BUILD': false, // Generate output HTML
'process.browser': true, config.plugins.push(
'process.server': true new HTMLPlugin({
})), template: this.options.appTemplatePath,
// Extract vendor chunks for better caching inject: false
new webpack.optimize.CommonsChunkPlugin({ })
name: 'vendor', )
filename: this.options.build.filenames.vendor,
minChunks (module) { // Generate vue-ssr-client-manifest
// A module is extracted into the vendor chunk when... config.plugins.push(
return ( new VueSSRClientPlugin({
// If it's inside node_modules filename: 'vue-ssr-client-manifest.json'
/node_modules/.test(module.context) && })
// Do not externalize if the request is a CSS file )
!/\.(css|less|scss|sass|styl|stylus)$/.test(module.request)
) // Extract webpack runtime & manifest
} config.plugins.push(
}),
// Extract webpack runtime & manifest
new webpack.optimize.CommonsChunkPlugin({ new webpack.optimize.CommonsChunkPlugin({
name: 'manifest', name: 'manifest',
minChunks: Infinity, minChunks: Infinity,
filename: this.options.build.filenames.manifest filename: this.options.build.filenames.manifest
}),
// Generate output HTML
new HTMLPlugin({
template: this.options.appTemplatePath,
inject: false // <- Resources will be injected using vue server renderer
}),
// Generate client manifest json
new VueSSRClientPlugin({
filename: 'client-manifest.json'
}) })
]) )
// client bundle progress bar
// Define Env
config.plugins.push(
new webpack.DefinePlugin(Object.assign(env, {
'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')),
'process.env.VUE_ENV': JSON.stringify('client'),
'process.browser': true,
'process.server': false
}))
)
// Build progress bar
config.plugins.push( config.plugins.push(
new ProgressBarPlugin() new ProgressBarPlugin()
) )
// Add friendly error plugin
// --------------------------------------
// Dev specific config
// --------------------------------------
if (this.options.dev) { if (this.options.dev) {
// Add friendly error plugin
config.plugins.push(new FriendlyErrorsWebpackPlugin()) config.plugins.push(new FriendlyErrorsWebpackPlugin())
}
// Dev client build
if (this.options.dev) {
// Add HMR support // Add HMR support
config.entry.app = flatten(['webpack-hot-middleware/client?name=$client&reload=true', config.entry.app]) config.entry.app = ['webpack-hot-middleware/client?name=$client&reload=true', config.entry.app]
config.output.filename = '[name].js'
config.plugins.push( config.plugins.push(
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin() new webpack.NoEmitOnErrorsPlugin()
) )
} }
// Production client build
// --------------------------------------
// Production specific config
// --------------------------------------
if (!this.options.dev) { if (!this.options.dev) {
// Minify JS
config.plugins.push( config.plugins.push(
// This is needed in webpack 2 for minifying CSS
new webpack.LoaderOptionsPlugin({
minimize: true
}),
// Minify JS
new webpack.optimize.UglifyJsPlugin({ new webpack.optimize.UglifyJsPlugin({
sourceMap: true, sourceMap: true,
compress: { compress: {
@ -112,7 +131,15 @@ export default function webpackClientConfig () {
} }
}) })
) )
// Webpack Bundle Analyzer
if (this.options.build.analyze) {
config.plugins.push(
new BundleAnalyzerPlugin(Object.assign({}, this.options.build.analyze))
)
}
} }
// Extend config // Extend config
if (typeof this.options.build.extend === 'function') { if (typeof this.options.build.extend === 'function') {
this.options.build.extend.call(this, config, { this.options.build.extend.call(this, config, {
@ -120,22 +147,6 @@ export default function webpackClientConfig () {
isClient: true isClient: true
}) })
} }
// Offline-plugin integration
if (!this.options.dev && this.options.offline) {
const offlineOpts = typeof this.options.offline === 'object' ? this.options.offline : {}
config.plugins.push(
new OfflinePlugin(defaults(offlineOpts, {}))
)
}
// Webpack Bundle Analyzer
if (!this.options.dev && this.options.build.analyze) {
let options = {}
if (typeof this.options.build.analyze === 'object') {
options = this.options.build.analyze
}
config.plugins.push(
new BundleAnalyzerPlugin(options)
)
}
return config return config
} }

View File

@ -13,6 +13,8 @@ import base from './base.config.js'
export default function webpackServerConfig () { export default function webpackServerConfig () {
let config = base.call(this, { isServer: true }) let config = base.call(this, { isServer: true })
config.name = 'server'
// env object defined in nuxt.config.js // env object defined in nuxt.config.js
let env = {} let env = {}
each(this.options.env, (value, key) => { each(this.options.env, (value, key) => {
@ -24,7 +26,6 @@ export default function webpackServerConfig () {
devtool: (this.options.dev ? 'source-map' : false), devtool: (this.options.dev ? 'source-map' : false),
entry: resolve(this.options.buildDir, 'server.js'), entry: resolve(this.options.buildDir, 'server.js'),
output: Object.assign({}, config.output, { output: Object.assign({}, config.output, {
path: resolve(this.options.buildDir, 'dist'),
filename: 'server-bundle.js', filename: 'server-bundle.js',
libraryTarget: 'commonjs2' libraryTarget: 'commonjs2'
}), }),
@ -32,6 +33,8 @@ export default function webpackServerConfig () {
hints: false hints: false
}, },
externals: [ externals: [
// https://webpack.js.org/configuration/externals/#externals
// https://github.com/liady/webpack-node-externals
nodeExternals({ nodeExternals({
// load non-javascript files with extensions, presumably via loaders // load non-javascript files with extensions, presumably via loaders
whitelist: [/\.(?!(?:js|json)$).{1,5}$/i] whitelist: [/\.(?!(?:js|json)$).{1,5}$/i]
@ -42,21 +45,19 @@ export default function webpackServerConfig () {
filename: 'server-bundle.json' filename: 'server-bundle.json'
}), }),
new webpack.DefinePlugin(Object.assign(env, { new webpack.DefinePlugin(Object.assign(env, {
'process.env.NODE_ENV': JSON.stringify(this.options.dev ? 'development' : 'production'), 'process.env.NODE_ENV': JSON.stringify(env.NODE_ENV || (this.options.dev ? 'development' : 'production')),
'process.BROWSER_BUILD': false, // deprecated 'process.env.VUE_ENV': JSON.stringify('server'),
'process.SERVER_BUILD': true, // deprecated
'process.browser': false, 'process.browser': false,
'process.server': true 'process.server': true
})) }))
]) ])
}) })
// This is needed in webpack 2 for minifying CSS
// --------------------------------------
// Production specific config
// --------------------------------------
if (!this.options.dev) { if (!this.options.dev) {
config.plugins.push(
new webpack.LoaderOptionsPlugin({
minimize: true
})
)
} }
// Extend config // Extend config
@ -66,5 +67,6 @@ export default function webpackServerConfig () {
isServer: true isServer: true
}) })
} }
return config return config
} }

View File

@ -9,7 +9,7 @@ export default function ({ isClient }) {
})) }))
// https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations // https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations
let config = { const config = {
postcss: this.options.build.postcss, postcss: this.options.build.postcss,
loaders: { loaders: {
'js': 'babel-loader?' + babelOptions, 'js': 'babel-loader?' + babelOptions,
@ -23,6 +23,7 @@ export default function ({ isClient }) {
preserveWhitespace: false, preserveWhitespace: false,
extractCSS: extractStyles.call(this) extractCSS: extractStyles.call(this)
} }
// Return the config // Return the config
return config return config
} }