misc: improve CLI errors

This commit is contained in:
Pooya Parsa 2018-01-11 19:41:50 +03:30
parent 58279166e2
commit 13166bcf78
7 changed files with 121 additions and 84 deletions

View File

@ -6,7 +6,7 @@ process.env.DEBUG = process.env.DEBUG || 'nuxt:*'
const fs = require('fs') const fs = require('fs')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const { Nuxt, Builder, Generator } = require('../') const { Nuxt, Builder, Generator, Utils } = require('../')
const resolve = require('path').resolve const resolve = require('path').resolve
const debug = require('debug')('nuxt:build') const debug = require('debug')('nuxt:build')
debug.color = 2 // Force green color debug.color = 2 // Force green color
@ -49,8 +49,7 @@ var options = {}
if (fs.existsSync(nuxtConfigFile)) { if (fs.existsSync(nuxtConfigFile)) {
options = require(nuxtConfigFile) options = require(nuxtConfigFile)
} else if (argv['config-file'] !== 'nuxt.config.js') { } else if (argv['config-file'] !== 'nuxt.config.js') {
console.error(`> Could not load config file ${argv['config-file']}`) Utils.fatalError(`Could not load config file`, argv['config-file'])
process.exit(1)
} }
if (typeof options.rootDir !== 'string') { if (typeof options.rootDir !== 'string') {
options.rootDir = rootDir options.rootDir = rootDir
@ -59,7 +58,8 @@ if (typeof options.rootDir !== 'string') {
options.dev = false options.dev = false
// Nuxt Mode // Nuxt Mode
options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode options.mode =
(argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode
// Analyze option // Analyze option
options.build = options.build || {} options.build = options.build || {}
@ -71,11 +71,15 @@ debug('Building...')
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
const builder = new Builder(nuxt) const builder = new Builder(nuxt)
// Setup hooks
nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from))
if (options.mode !== 'spa') { if (options.mode !== 'spa') {
// Build for SSR app // Build for SSR app
builder.build() builder
.build()
.then(() => debug('Building done')) .then(() => debug('Building done'))
.catch((err) => { .catch(err => {
console.error(err) console.error(err)
process.exit(1) process.exit(1)
}) })
@ -108,7 +112,7 @@ if (options.mode !== 'spa') {
return `Route: '${route}' thrown an error: \n` + JSON.stringify(error) return `Route: '${route}' thrown an error: \n` + JSON.stringify(error)
} }
}) })
console.error('==== Error report ==== \n' + report.join('\n\n')) // eslint-disable-line no-console Utils.printError(report.join('\n\n'), 'Generate errors')
} }
}) })
@ -116,8 +120,6 @@ if (options.mode !== 'spa') {
nuxt.options.generate.minify = false nuxt.options.generate.minify = false
// Generate on spa mode // Generate on spa mode
new Generator(nuxt, builder).generate({ build: true }).then(() => { new Generator(nuxt, builder).generate({ build: true }).then(() => {
if (!nuxt.options.dev) { process.exit(0)
console.log(`✓ You can now directly upload ${nuxt.options.generate.dir}/ or start server using "nuxt start"`)
}
}) })
} }

View File

@ -9,7 +9,7 @@ const debug = require('debug')('nuxt:build')
debug.color = 2 // force green color debug.color = 2 // force green color
const fs = require('fs') const fs = require('fs')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const { Nuxt, Builder } = require('../') const { Nuxt, Builder, Utils } = require('../')
const chokidar = require('chokidar') const chokidar = require('chokidar')
const path = require('path') const path = require('path')
const resolve = path.resolve const resolve = path.resolve
@ -38,8 +38,7 @@ if (argv.version) {
} }
if (argv.hostname === '') { if (argv.hostname === '') {
console.error(`> Provided hostname argument has no value`) Utils.fatalError('Provided hostname argument has no value')
process.exit(1)
} }
if (argv.help) { if (argv.help) {
@ -72,29 +71,31 @@ let dev = startDev()
let needToRestart = false let needToRestart = false
// Start watching for nuxt.config.js changes // Start watching for nuxt.config.js changes
chokidar chokidar.watch(nuxtConfigFile, nuxtConfig.watchers.chokidar).on('all', () => {
.watch(nuxtConfigFile, nuxtConfig.watchers.chokidar) debug('[nuxt.config.js] changed')
.on('all', () => { needToRestart = true
debug('[nuxt.config.js] changed')
needToRestart = true
dev = dev.then((instance) => { dev = dev.then(instance => {
if (needToRestart === false) return instance if (needToRestart === false) return instance
needToRestart = false needToRestart = false
debug('Rebuilding the app...') debug('Rebuilding the app...')
return startDev(instance) return startDev(instance)
})
}) })
})
function startDev(oldInstance) { function startDev(oldInstance) {
// Get latest environment variables // Get latest environment variables
const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port const port =
const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port
const host =
argv.hostname ||
process.env.HOST ||
process.env.npm_package_config_nuxt_host
// Error handler // Error handler
const onError = (err, instance) => { const onError = (err, instance) => {
debug('Error while reloading [nuxt.config.js]', err) Utils.printError(err)
return Promise.resolve(instance) // Wait for next reload return Promise.resolve(instance) // Wait for next reload
} }
@ -118,18 +119,30 @@ function startDev(oldInstance) {
return onError(err, instance || oldInstance) return onError(err, instance || oldInstance)
} }
return Promise.resolve() return (
.then(() => oldInstance && oldInstance.builder ? oldInstance.builder.unwatch() : Promise.resolve()) Promise.resolve()
// Start build .then(
.then(() => builder.build()) () =>
// Close old nuxt after successful build oldInstance && oldInstance.builder
.then(() => oldInstance && oldInstance.nuxt ? oldInstance.nuxt.close() : Promise.resolve()) ? oldInstance.builder.unwatch()
// Start listening : Promise.resolve()
.then(() => nuxt.listen(port, host)) )
// Pass new nuxt to watch chain // Start build
.then(() => instance) .then(() => builder.build())
// Handle errors // Close old nuxt after successful build
.catch((err) => onError(err, instance)) .then(
() =>
oldInstance && oldInstance.nuxt
? oldInstance.nuxt.close()
: Promise.resolve()
)
// Start listening
.then(() => nuxt.listen(port, host))
// Pass new nuxt to watch chain
.then(() => instance)
// Handle errors
.catch(err => onError(err, instance))
)
} }
function loadNuxtConfig() { function loadNuxtConfig() {
@ -139,8 +152,7 @@ function loadNuxtConfig() {
delete require.cache[nuxtConfigFile] delete require.cache[nuxtConfigFile]
options = require(nuxtConfigFile) options = require(nuxtConfigFile)
} else if (argv['config-file'] !== 'nuxt.config.js') { } else if (argv['config-file'] !== 'nuxt.config.js') {
console.error(`> Could not load config file ${argv['config-file']}`) Utils.fatalError('Could not load config file: ' + argv['config-file'])
process.exit(1)
} }
if (typeof options.rootDir !== 'string') { if (typeof options.rootDir !== 'string') {
@ -151,7 +163,8 @@ function loadNuxtConfig() {
options.dev = true options.dev = true
// Nuxt Mode // Nuxt Mode
options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode options.mode =
(argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode
return options return options
} }

View File

@ -8,7 +8,7 @@ const fs = require('fs')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const debug = require('debug')('nuxt:generate') const debug = require('debug')('nuxt:generate')
const { Nuxt, Builder, Generator } = require('../') const { Nuxt, Builder, Generator, Utils } = require('../')
const resolve = require('path').resolve const resolve = require('path').resolve
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
@ -49,8 +49,7 @@ var options = {}
if (fs.existsSync(nuxtConfigFile)) { if (fs.existsSync(nuxtConfigFile)) {
options = require(nuxtConfigFile) options = require(nuxtConfigFile)
} else if (argv['config-file'] !== 'nuxt.config.js') { } else if (argv['config-file'] !== 'nuxt.config.js') {
console.error(`> Could not load config file ${argv['config-file']}`) Utils.fatalError('Could not load config file: ' + argv['config-file'])
process.exit(1)
} }
if (typeof options.rootDir !== 'string') { if (typeof options.rootDir !== 'string') {
options.rootDir = rootDir options.rootDir = rootDir
@ -58,7 +57,8 @@ if (typeof options.rootDir !== 'string') {
options.dev = false // Force production mode (no webpack middleware called) options.dev = false // Force production mode (no webpack middleware called)
// Nuxt Mode // Nuxt Mode
options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode options.mode =
(argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode
debug('Generating...') debug('Generating...')
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
@ -72,19 +72,17 @@ const generateOptions = {
const s = Date.now() const s = Date.now()
nuxt.hook('generate:distRemoved', function () { // Setup hooks
debug('Destination folder cleaned')
})
nuxt.hook('generate:distCopied', function () { nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from))
debug('Static & build files copied')
})
nuxt.hook('generate:page', function (page) { nuxt.hook('generate:distRemoved', () => debug('Destination folder cleaned'))
debug('Generate file: ' + page.path)
})
nuxt.hook('generate:done', function (generator, errors) { nuxt.hook('generate:distCopied', () => debug('Static & build files copied'))
nuxt.hook('generate:page', page => debug('Generate file: ' + page.path))
nuxt.hook('generate:done', (generator, errors) => {
const duration = Math.round((Date.now() - s) / 100) / 10 const duration = Math.round((Date.now() - s) / 100) / 10
debug(`HTML Files generated in ${duration}s`) debug(`HTML Files generated in ${duration}s`)
@ -98,16 +96,16 @@ nuxt.hook('generate:done', function (generator, errors) {
return `Route: '${route}' thrown an error: \n` + JSON.stringify(error) return `Route: '${route}' thrown an error: \n` + JSON.stringify(error)
} }
}) })
console.error('==== Error report ==== \n' + report.join('\n\n')) // eslint-disable-line no-console Utils.printError(report.join('\n\n'), 'generate')
} }
}) })
generator.generate(generateOptions) generator
.generate(generateOptions)
.then(() => { .then(() => {
debug('Generate done') debug('Generate done')
process.exit(0) process.exit(0)
}) })
.catch((err) => { .catch(err => {
console.error(err) Utils.fatalError(err)
process.exit(1)
}) })

View File

@ -3,7 +3,7 @@
const fs = require('fs') const fs = require('fs')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const { Nuxt } = require('../') const { Nuxt, Utils } = require('../')
const { resolve } = require('path') const { resolve } = require('path')
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
@ -23,8 +23,7 @@ const argv = parseArgs(process.argv.slice(2), {
}) })
if (argv.hostname === '') { if (argv.hostname === '') {
console.error(`> Provided hostname argument has no value`) Utils.fatalError('Provided hostname argument has no value')
process.exit(1)
} }
if (argv.help) { if (argv.help) {
@ -53,8 +52,7 @@ let options = {}
if (fs.existsSync(nuxtConfigFile)) { if (fs.existsSync(nuxtConfigFile)) {
options = require(nuxtConfigFile) options = require(nuxtConfigFile)
} else if (argv['config-file'] !== 'nuxt.config.js') { } else if (argv['config-file'] !== 'nuxt.config.js') {
console.error(`> Could not load config file ${argv['config-file']}`) Utils.fatalError('Could not load config file: ' + argv['config-file'])
process.exit(1)
} }
if (typeof options.rootDir !== 'string') { if (typeof options.rootDir !== 'string') {
@ -65,27 +63,39 @@ if (typeof options.rootDir !== 'string') {
options.dev = false options.dev = false
// Nuxt Mode // Nuxt Mode
options.mode = (argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode options.mode =
(argv['spa'] && 'spa') || (argv['universal'] && 'universal') || options.mode
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
// Setup hooks
nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from))
// Check if project is built for production // Check if project is built for production
const distDir = resolve(nuxt.options.rootDir, nuxt.options.buildDir || '.nuxt', 'dist') const distDir = resolve(
nuxt.options.rootDir,
nuxt.options.buildDir || '.nuxt',
'dist'
)
if (!fs.existsSync(distDir)) { if (!fs.existsSync(distDir)) {
console.error('> No build files found, please run `nuxt build` before launching `nuxt start`') Utils.fatalError(
process.exit(1) 'No build files found, please run `nuxt build` before launching `nuxt start`'
)
} }
// Check if SSR Bundle is required // Check if SSR Bundle is required
if (nuxt.options.render.ssr === true) { if (nuxt.options.render.ssr === true) {
const ssrBundlePath = resolve(distDir, 'server-bundle.json') const ssrBundlePath = resolve(distDir, 'server-bundle.json')
if (!fs.existsSync(ssrBundlePath)) { if (!fs.existsSync(ssrBundlePath)) {
console.error('> No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`') Utils.fatalError(
process.exit(1) 'No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`'
)
} }
} }
const port = argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port const port =
const host = argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host argv.port || process.env.PORT || process.env.npm_package_config_nuxt_port
const host =
argv.hostname || process.env.HOST || process.env.npm_package_config_nuxt_host
nuxt.listen(port, host) nuxt.listen(port, host)

View File

@ -15,12 +15,17 @@ exports.printWarn = function (msg, from) {
exports.printError = function (_error, from) { exports.printError = function (_error, from) {
/* eslint-disable no-console */ /* eslint-disable no-console */
const errStr = pe.render(_error) const errStr = pe.render(_error)
const fromStr = from ? Chalk.red(` ${from}\n`) : ' ' const fromStr = from ? Chalk.red(` ${from}`) : ''
console.error('\n' + Chalk.bgRed.black(' ERROR ') + fromStr) console.error('\n' + Chalk.bgRed.black(' ERROR ') + fromStr + '\n')
console.error(errStr + '\n') console.error(errStr + '\n')
} }
exports.fatalError = function () {
exports.printError(...arguments)
process.exit(1)
}
exports.encodeHtml = function encodeHtml(str) { exports.encodeHtml = function encodeHtml(str) {
return str.replace(/</g, '&lt;').replace(/>/g, '&gt;') return str.replace(/</g, '&lt;').replace(/>/g, '&gt;')
} }

View File

@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs') const fs = require('fs')
const { uniq } = require('lodash') const { uniq } = require('lodash')
const hash = require('hash-sum') const hash = require('hash-sum')
const { chainFn, sequence, printError, printWarn } = require('../common/utils') const { chainFn, sequence, printWarn } = require('../common/utils')
module.exports = class ModuleContainer { module.exports = class ModuleContainer {
constructor(nuxt) { constructor(nuxt) {
@ -116,8 +116,7 @@ module.exports = class ModuleContainer {
try { try {
handler = require(this.nuxt.resolvePath(src)) handler = require(this.nuxt.resolvePath(src))
} catch (err) { } catch (err) {
printError(err) this.nuxt.onError(err, src)
throw new Error('Error while resolving module: ' + src)
} }
} }

View File

@ -18,6 +18,7 @@ module.exports = class Nuxt {
this.options = Options.from(options) this.options = Options.from(options)
this.initialized = false this.initialized = false
this.onError = this.onError.bind(this)
// Hooks // Hooks
this._hooks = {} this._hooks = {}
@ -28,16 +29,14 @@ module.exports = class Nuxt {
this.renderer = new Renderer(this) this.renderer = new Renderer(this)
// Backward compatibility // Backward compatibility
this.errorHandler = this.onError
this.render = this.renderer.app this.render = this.renderer.app
this.renderRoute = this.renderer.renderRoute.bind(this.renderer) this.renderRoute = this.renderer.renderRoute.bind(this.renderer)
this.renderAndGetWindow = this.renderer.renderAndGetWindow.bind( this.renderAndGetWindow = this.renderer.renderAndGetWindow.bind(
this.renderer this.renderer
) )
this._ready = this.ready().catch(err => { this._ready = this.ready().catch(err => this.onError(err))
this.callHook('error', err)
printError(err)
})
} }
static get version() { static get version() {
@ -102,6 +101,17 @@ module.exports = class Nuxt {
this._hooks[name].push(fn) this._hooks[name].push(fn)
} }
onError(err, from = 'Nuxt error') {
// Log error to the console if there is not any error listener
if (!this._hooks['error']) {
printError(err, from)
return
}
// Call error hooks
this.callHook('error', err, from)
}
async callHook(name, ...args) { async callHook(name, ...args) {
if (!this._hooks[name]) { if (!this._hooks[name]) {
return return
@ -110,7 +120,7 @@ module.exports = class Nuxt {
try { try {
await sequence(this._hooks[name], fn => fn(...args)) await sequence(this._hooks[name], fn => fn(...args))
} catch (err) { } catch (err) {
printError(err, name) this.onError(err, name)
} }
} }