imrpove nuxt-generate error report summary

This commit is contained in:
Pooya Parsa 2018-01-11 21:50:35 +03:30
parent 061059beb4
commit 118d3fb8c2
5 changed files with 97 additions and 44 deletions

View File

@ -104,15 +104,8 @@ if (options.mode !== 'spa') {
debug(`HTML Files generated in ${duration}s`) debug(`HTML Files generated in ${duration}s`)
if (errors.length) { if (errors.length) {
const report = errors.map(({ type, route, error }) => { /* eslint-disable no-console */
/* istanbul ignore if */ console.log('\n' + errors.toString())
if (type === 'unhandled') {
return `Route: '${route}'\n${error.stack}`
} else {
return `Route: '${route}' thrown an error: \n` + JSON.stringify(error)
}
})
Utils.printError(report.join('\n\n'), 'Generate errors summary:')
} }
}) })

View File

@ -86,17 +86,11 @@ 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`)
console.log(errors.length)
if (errors.length) { if (errors.length) {
const report = errors.map(({ type, route, error }) => { /* eslint-disable no-console */
/* istanbul ignore if */ console.log('\n Generate errors summary:')
if (type === 'unhandled') { console.log('\n' + errors.toString())
return `Route: '${route}'\n${error.stack}`
} else {
return `Route: '${route}' thrown an error: \n` + JSON.stringify(error)
}
})
Utils.printError(report.join('\n\n'), 'Generate errors summary:')
} }
}) })

View File

@ -1,8 +1,23 @@
const { copy, remove, writeFile, mkdirp, removeSync, existsSync } = require('fs-extra') const {
copy,
remove,
writeFile,
mkdirp,
removeSync,
existsSync
} = require('fs-extra')
const _ = require('lodash') const _ = require('lodash')
const { resolve, join, dirname, sep } = require('path') const { resolve, join, dirname, sep } = require('path')
const { minify } = require('html-minifier') const { minify } = require('html-minifier')
const { isUrl, promisifyRoute, waitFor, flatRoutes } = require('../common/utils') const Chalk = require('chalk')
const {
isUrl,
promisifyRoute,
waitFor,
flatRoutes,
pe
} = require('../common/utils')
module.exports = class Generator { module.exports = class Generator {
constructor(nuxt, builder) { constructor(nuxt, builder) {
@ -14,7 +29,10 @@ module.exports = class Generator {
this.staticRoutes = resolve(this.options.srcDir, 'static') this.staticRoutes = resolve(this.options.srcDir, 'static')
this.srcBuiltPath = resolve(this.options.buildDir, 'dist') this.srcBuiltPath = resolve(this.options.buildDir, 'dist')
this.distPath = resolve(this.options.rootDir, this.options.generate.dir) this.distPath = resolve(this.options.rootDir, this.options.generate.dir)
this.distNuxtPath = join(this.distPath, (isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath)) this.distNuxtPath = join(
this.distPath,
isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath
)
} }
async generate({ build = true, init = true } = {}) { async generate({ build = true, init = true } = {}) {
@ -25,7 +43,7 @@ module.exports = class Generator {
const errors = await this.generateRoutes(routes) const errors = await this.generateRoutes(routes)
await this.afterGenerate() await this.afterGenerate()
// done hook // Done hook
await this.nuxt.callHook('generate:done', this, errors) await this.nuxt.callHook('generate:done', this, errors)
return { errors } return { errors }
@ -57,14 +75,20 @@ module.exports = class Generator {
let generateRoutes = [] let generateRoutes = []
if (this.options.router.mode !== 'hash') { if (this.options.router.mode !== 'hash') {
try { try {
generateRoutes = await promisifyRoute(this.options.generate.routes || [], ...args) generateRoutes = await promisifyRoute(
this.options.generate.routes || [],
...args
)
} catch (e) { } catch (e) {
console.error('Could not resolve routes') // eslint-disable-line no-console console.error('Could not resolve routes') // eslint-disable-line no-console
throw e // eslint-disable-line no-unreachable throw e // eslint-disable-line no-unreachable
} }
} }
// Generate only index.html for router.mode = 'hash' // Generate only index.html for router.mode = 'hash'
let routes = (this.options.router.mode === 'hash') ? ['/'] : flatRoutes(this.options.router.routes) let routes =
this.options.router.mode === 'hash'
? ['/']
: flatRoutes(this.options.router.routes)
routes = this.decorateWithPayloads(routes, generateRoutes) routes = this.decorateWithPayloads(routes, generateRoutes)
// extendRoutes hook // extendRoutes hook
@ -79,15 +103,43 @@ module.exports = class Generator {
// Start generate process // Start generate process
while (routes.length) { while (routes.length) {
let n = 0 let n = 0
await Promise.all(routes.splice(0, this.options.generate.concurrency).map(async ({ route, payload }) => { await Promise.all(
await waitFor(n++ * this.options.generate.interval) routes
await this.generateRoute({ route, payload, errors }) .splice(0, this.options.generate.concurrency)
})) .map(async ({ route, payload }) => {
await waitFor(n++ * this.options.generate.interval)
await this.generateRoute({ route, payload, errors })
})
)
} }
// Improve string representation for errors
errors.toString = () => this._formatErrors(errors)
return errors return errors
} }
_formatErrors(errors) {
return errors
.map(({ type, route, error }) => {
const isHandled = type === 'handled'
const bgColor = isHandled ? 'bgYellow' : 'bgRed'
const color = isHandled ? 'yellow' : 'red'
let line =
Chalk.black[bgColor](' ROUTE ') + Chalk[color](` ${route}\n\n`)
if (isHandled) {
line += Chalk.grey(JSON.stringify(error, undefined, 2) + '\n')
} else {
line += Chalk.grey(pe.render(error))
}
return line
})
.join('\n')
}
async afterGenerate() { async afterGenerate() {
const indexPath = join(this.distPath, 'index.html') const indexPath = join(this.distPath, 'index.html')
if (existsSync(indexPath)) { if (existsSync(indexPath)) {
@ -138,14 +190,14 @@ module.exports = class Generator {
decorateWithPayloads(routes, generateRoutes) { decorateWithPayloads(routes, generateRoutes) {
let routeMap = {} let routeMap = {}
// Fill routeMap for known routes // Fill routeMap for known routes
routes.forEach((route) => { routes.forEach(route => {
routeMap[route] = { routeMap[route] = {
route, route,
payload: null payload: null
} }
}) })
// Fill routeMap with given generate.routes // Fill routeMap with given generate.routes
generateRoutes.forEach((route) => { generateRoutes.forEach(route => {
// route is either a string or like { route : '/my_route/1', payload: {} } // route is either a string or like { route : '/my_route/1', payload: {} }
const path = _.isString(route) ? route : route.route const path = _.isString(route) ? route : route.route
routeMap[path] = { routeMap[path] = {
@ -161,7 +213,10 @@ module.exports = class Generator {
const pageErrors = [] const pageErrors = []
try { try {
const res = await this.nuxt.renderer.renderRoute(route, { _generate: true, payload }) const res = await this.nuxt.renderer.renderRoute(route, {
_generate: true,
payload
})
html = res.html html = res.html
if (res.error) { if (res.error) {
pageErrors.push({ type: 'handled', route, error: res.error }) pageErrors.push({ type: 'handled', route, error: res.error })
@ -171,7 +226,10 @@ module.exports = class Generator {
pageErrors.push({ type: 'unhandled', route, error: err }) pageErrors.push({ type: 'unhandled', route, error: err })
Array.prototype.push.apply(errors, pageErrors) Array.prototype.push.apply(errors, pageErrors)
await this.nuxt.callHook('generate:routeFailed', { route, errors: pageErrors }) await this.nuxt.callHook('generate:routeFailed', {
route,
errors: pageErrors
})
return false return false
} }
@ -180,7 +238,9 @@ module.exports = class Generator {
try { try {
html = minify(html, this.options.generate.minify) html = minify(html, this.options.generate.minify)
} catch (err) /* istanbul ignore next */ { } catch (err) /* istanbul ignore next */ {
const minifyErr = new Error(`HTML minification failed. Make sure the route generates valid HTML. Failed HTML:\n ${html}`) const minifyErr = new Error(
`HTML minification failed. Make sure the route generates valid HTML. Failed HTML:\n ${html}`
)
pageErrors.push({ type: 'unhandled', route, error: minifyErr }) pageErrors.push({ type: 'unhandled', route, error: minifyErr })
} }
} }
@ -205,7 +265,11 @@ module.exports = class Generator {
await mkdirp(dirname(page.path)) await mkdirp(dirname(page.path))
await writeFile(page.path, page.html, 'utf8') await writeFile(page.path, page.html, 'utf8')
await this.nuxt.callHook('generate:routeCreated', { route, path: page.path, errors: pageErrors }) await this.nuxt.callHook('generate:routeCreated', {
route,
path: page.path,
errors: pageErrors
})
if (pageErrors.length) { if (pageErrors.length) {
Array.prototype.push.apply(errors, pageErrors) Array.prototype.push.apply(errors, pageErrors)

View File

@ -3,26 +3,28 @@ const _ = require('lodash')
const PrettyError = require('pretty-error') const PrettyError = require('pretty-error')
const Chalk = require('chalk') const Chalk = require('chalk')
const pe = new PrettyError() exports.pe = new PrettyError()
exports.printWarn = function (msg, from) { exports.printWarn = function (msg, from) {
/* eslint-disable no-console */ /* eslint-disable no-console */
const fromStr = from ? Chalk.yellow(` ${from}\n\n`) : ' ' const fromStr = from ? Chalk.yellow(` ${from}\n\n`) : ' '
console.error('\n' + Chalk.bgYellow.black(' WARN ') + fromStr + msg + '\n') console.error('\n' + Chalk.bgYellow.black(' WARN ') + fromStr + msg + '\n')
} }
exports.printError = function (_error, from) { exports.renderError = function (_error, from) {
/* eslint-disable no-console */ const errStr = exports.pe.render(_error)
const errStr = pe.render(_error)
const fromStr = from ? Chalk.red(` ${from}`) : '' const fromStr = from ? Chalk.red(` ${from}`) : ''
return '\n' + Chalk.bgRed.black(' ERROR ') + fromStr + '\n' + errStr + '\n'
}
console.error('\n' + Chalk.bgRed.black(' ERROR ') + fromStr + '\n') exports.printError = function () {
console.error(errStr + '\n') /* eslint-disable no-console */
console.error(exports.renderError(...arguments))
} }
exports.fatalError = function () { exports.fatalError = function () {
exports.printError(...arguments) /* eslint-disable no-console */
console.error(exports.renderError(...arguments))
process.exit(1) process.exit(1)
} }

View File

@ -93,6 +93,6 @@ test.serial('bin/nuxt-generate', async t => {
t.true(stderr.includes('Destination folder cleaned')) t.true(stderr.includes('Destination folder cleaned'))
t.true(stderr.includes('Static & build files copied')) t.true(stderr.includes('Static & build files copied'))
t.true(stderr.includes(`Generate file: ${sep}users${sep}1${sep}index.html`)) t.true(stderr.includes(`Generate file: ${sep}users${sep}1${sep}index.html`))
t.true(stderr.includes('Generate errors summary:')) t.true(stdout.includes('Generate errors summary:'))
t.true(stderr.includes('Generate done')) t.true(stderr.includes('Generate done'))
}) })