mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-27 16:12:12 +00:00
many improvements
This commit is contained in:
parent
d68b4f0c00
commit
5722a92c4c
@ -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
|
||||||
|
107
lib/builder.js
107
lib/builder.js
@ -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()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
53
lib/nuxt.js
53
lib/nuxt.js
@ -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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user