refactor: unified logging (#3163)

This commit is contained in:
Pooya Parsa 2018-03-31 21:39:34 +04:30 committed by GitHub
commit 838c88b909
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 169 additions and 337 deletions

View File

@ -1,18 +1,18 @@
#!/usr/bin/env node #!/usr/bin/env node
const { join } = require('path') const { join } = require('path')
const semver = require('semver') const semver = require('semver')
const consola = require('consola')
const { Utils } = require('..')
const { name, engines } = require('../package.json') const { name, engines } = require('../package.json')
const logger = consola.withScope('nuxt')
// Global error handler // Global error handler
process.on('unhandledRejection', _error => { process.on('unhandledRejection', _error => {
Utils.printError(_error) logger.error(_error)
}) })
if (!semver.satisfies(process.version, engines.node)) { if (!semver.satisfies(process.version, engines.node)) {
Utils.fatalError( logger.fatal(
`The engine "node" is incompatible with ${name}. Expected version "${ `The engine "node" is incompatible with ${name}. Expected version "${
engines.node engines.node
}".` }".`

View File

@ -1,13 +1,11 @@
#!/usr/bin/env node #!/usr/bin/env node
/* eslint-disable no-console */
const parseArgs = require('minimist') const parseArgs = require('minimist')
const debug = require('debug')('nuxt:build') const consola = require('consola')
debug.color = 2 // Force green color const { Nuxt, Builder, Generator } = require('..')
const { Nuxt, Builder, Generator, Utils } = require('..')
const { loadNuxtConfig } = require('./common/utils') const { loadNuxtConfig } = require('./common/utils')
const logger = consola.withScope('nuxt:build')
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
alias: { alias: {
h: 'help', h: 'help',
@ -24,7 +22,7 @@ const argv = parseArgs(process.argv.slice(2), {
}) })
if (argv.help) { if (argv.help) {
console.log(` process.stderr.write(`
Description Description
Compiles the application for production deployment Compiles the application for production deployment
Usage Usage
@ -51,12 +49,11 @@ if (argv.analyze) {
options.build.analyze = true options.build.analyze = true
} }
debug('Building...')
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
const builder = new Builder(nuxt) const builder = new Builder(nuxt)
// Setup hooks // Setup hooks
nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from)) nuxt.hook('error', (_err) => logger.fatal(_err))
// Close function // Close function
const close = () => { const close = () => {
@ -74,28 +71,8 @@ if (!argv.generate) {
builder builder
.build() .build()
.then(() => close()) .then(() => close())
.catch(Utils.fatalError) .catch(error => logger.fatal(error))
} else { } else {
// -- Build and generate --
const s = Date.now()
nuxt.hook('generate:distRemoved', () => debug('Destination folder cleaned'))
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
debug(`HTML Files generated in ${duration}s`)
if (errors.length) {
/* eslint-disable no-console */
console.log('\n' + errors.toString())
}
})
// Generate dist for SPA static deployment // Generate dist for SPA static deployment
new Generator(nuxt, builder).generate({ build: true }).then(() => { new Generator(nuxt, builder).generate({ build: true }).then(() => {
close() close()

View File

@ -1,17 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
/* eslint-disable no-console */
const defaultsDeep = require('lodash/defaultsDeep') const defaultsDeep = require('lodash/defaultsDeep')
const debug = require('debug')('nuxt:build')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const chokidar = require('chokidar') const chokidar = require('chokidar')
const consola = require('consola')
const { version } = require('../package.json') const { version } = require('../package.json')
const { Nuxt, Builder, Utils } = require('..') const { Nuxt, Builder } = require('..')
const { loadNuxtConfig, getLatestHost, nuxtConfigFile } = require('./common/utils') const { loadNuxtConfig, getLatestHost, nuxtConfigFile } = require('./common/utils')
debug.color = 2 // force green color const logger = consola.withScope('nuxt:dev')
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
alias: { alias: {
@ -31,16 +27,16 @@ const argv = parseArgs(process.argv.slice(2), {
}) })
if (argv.version) { if (argv.version) {
console.log(version) process.stderr.write(version + '\n')
process.exit(0) process.exit(0)
} }
if (argv.hostname === '') { if (argv.hostname === '') {
Utils.fatalError('Provided hostname argument has no value') logger.fatal('Provided hostname argument has no value')
} }
if (argv.help) { if (argv.help) {
console.log(` process.stderr.write(`
Description Description
Starts the application in development mode (hot-code reloading, error Starts the application in development mode (hot-code reloading, error
reporting, etc) reporting, etc)
@ -69,14 +65,14 @@ let needToRestart = false
chokidar chokidar
.watch(nuxtConfigFile(argv), nuxtConfig.watchers.chokidar) .watch(nuxtConfigFile(argv), nuxtConfig.watchers.chokidar)
.on('all', () => { .on('all', () => {
debug('[nuxt.config.js] changed') logger.debug('[nuxt.config.js] changed')
needToRestart = true 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...') logger.debug('Rebuilding the app...')
return startDev(instance) return startDev(instance)
}) })
}) })
@ -87,7 +83,7 @@ function startDev(oldInstance) {
// Error handler // Error handler
const onError = (err, instance) => { const onError = (err, instance) => {
Utils.printError(err) logger.error(err)
return Promise.resolve(instance) // Wait for next reload return Promise.resolve(instance) // Wait for next reload
} }

View File

@ -1,12 +1,11 @@
#!/usr/bin/env node #!/usr/bin/env node
/* eslint-disable no-console */
const parseArgs = require('minimist') const parseArgs = require('minimist')
const debug = require('debug')('nuxt:generate') const consola = require('consola')
const { Nuxt, Builder, Generator } = require('..')
const { Nuxt, Builder, Generator, Utils } = require('..')
const { loadNuxtConfig } = require('./common/utils') const { loadNuxtConfig } = require('./common/utils')
const logger = consola.withScope('nuxt:generate')
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
alias: { alias: {
h: 'help', h: 'help',
@ -23,7 +22,7 @@ const argv = parseArgs(process.argv.slice(2), {
}) })
if (argv.help) { if (argv.help) {
console.log(` process.stderr.write(`
Description Description
Generate a static web application (server-rendered) Generate a static web application (server-rendered)
Usage Usage
@ -42,7 +41,6 @@ const options = loadNuxtConfig(argv)
options.dev = false // Force production mode (no webpack middleware called) options.dev = false // Force production mode (no webpack middleware called)
debug('Generating...')
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
const builder = new Builder(nuxt) const builder = new Builder(nuxt)
const generator = new Generator(nuxt, builder) const generator = new Generator(nuxt, builder)
@ -52,34 +50,9 @@ const generateOptions = {
build: argv['build'] build: argv['build']
} }
const s = Date.now()
// Setup hooks
nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from))
nuxt.hook('generate:distRemoved', () => debug('Destination folder cleaned'))
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
debug(`HTML Files generated in ${duration}s`)
if (errors.length) {
/* eslint-disable no-console */
console.log('\n Generate errors summary:')
console.log('\n' + errors.toString())
}
})
generator generator
.generate(generateOptions) .generate(generateOptions)
.then(() => { .then(() => {
debug('Generate done')
process.exit(0) process.exit(0)
}) })
.catch(Utils.fatalError) .catch(error => logger.fatal(error))

View File

@ -1,13 +1,13 @@
#!/usr/bin/env node #!/usr/bin/env node
/* eslint-disable no-console */
const fs = require('fs') const fs = require('fs')
const { resolve } = require('path') const { resolve } = require('path')
const parseArgs = require('minimist') const parseArgs = require('minimist')
const consola = require('consola')
const { Nuxt, Utils } = require('..') const { Nuxt } = require('..')
const { loadNuxtConfig, getLatestHost } = require('./common/utils') const { loadNuxtConfig, getLatestHost } = require('./common/utils')
const logger = consola.withScope('nuxt:start')
const argv = parseArgs(process.argv.slice(2), { const argv = parseArgs(process.argv.slice(2), {
alias: { alias: {
h: 'help', h: 'help',
@ -25,11 +25,11 @@ const argv = parseArgs(process.argv.slice(2), {
}) })
if (argv.hostname === '') { if (argv.hostname === '') {
Utils.fatalError('Provided hostname argument has no value') logger.fatal('Provided hostname argument has no value')
} }
if (argv.help) { if (argv.help) {
console.log(` process.stderr.write(`
Description Description
Starts the application in production mode. Starts the application in production mode.
The application should be compiled with \`nuxt build\` first. The application should be compiled with \`nuxt build\` first.
@ -54,7 +54,7 @@ options.dev = false
const nuxt = new Nuxt(options) const nuxt = new Nuxt(options)
// Setup hooks // Setup hooks
nuxt.hook('error', (_err, from) => Utils.fatalError(_err, from)) nuxt.hook('error', (_err) => logger.fatal(_err))
// Check if project is built for production // Check if project is built for production
const distDir = resolve( const distDir = resolve(
@ -63,7 +63,7 @@ const distDir = resolve(
'dist' 'dist'
) )
if (!fs.existsSync(distDir)) { if (!fs.existsSync(distDir)) {
Utils.fatalError( logger.fatal(
'No build files found, please run `nuxt build` before launching `nuxt start`' 'No build files found, please run `nuxt build` before launching `nuxt start`'
) )
} }
@ -72,7 +72,7 @@ if (!fs.existsSync(distDir)) {
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)) {
Utils.fatalError( logger.fatal(
'No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`' 'No SSR build! Please start with `nuxt start --spa` or build using `nuxt build --universal`'
) )
} }

View File

@ -11,23 +11,21 @@ import serialize from 'serialize-javascript'
import MFS from 'memory-fs' import MFS from 'memory-fs'
import webpackDevMiddleware from 'webpack-dev-middleware' import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware' import webpackHotMiddleware from 'webpack-hot-middleware'
import Debug from 'debug'
import Glob from 'glob' import Glob from 'glob'
import upath from 'upath' import upath from 'upath'
import logUpdate from 'log-update' import consola from 'consola'
import { r, wp, wChunk, createRoutes, parallel, sequence, relativeTo, waitFor, createSpinner } from '../common/utils' import { r, wp, wChunk, createRoutes, parallel, sequence, relativeTo, waitFor } from '../common/utils'
import Options from '../common/options' import Options from '../common/options'
import PerfLoader from './webpack/utils/perf-loader' import PerfLoader from './webpack/utils/perf-loader'
import ClientWebpackConfig from './webpack/client' import ClientWebpackConfig from './webpack/client'
import ServerWebpackConfig from './webpack/server' import ServerWebpackConfig from './webpack/server'
const debug = Debug('nuxt:build')
debug.color = 2 // Force green color
const glob = util.promisify(Glob) const glob = util.promisify(Glob)
const logger = consola.withScope('nuxt:builder')
export default class Builder { export default class Builder {
constructor(nuxt) { constructor(nuxt) {
this.nuxt = nuxt this.nuxt = nuxt
@ -43,11 +41,6 @@ export default class Builder {
this.customFilesWatcher = null this.customFilesWatcher = null
this.perfLoader = null this.perfLoader = null
// Shared spinner
this.spinner = createSpinner({ minimal: this.options.minimalCLI })
this.spinner.enabled = !this.options.test
this.logUpdate = logUpdate.create(process.stdout)
// Helper to resolve build paths // Helper to resolve build paths
this.relativeToBuild = (...args) => this.relativeToBuild = (...args) =>
relativeTo(this.options.buildDir, ...args) relativeTo(this.options.buildDir, ...args)
@ -106,7 +99,7 @@ export default class Builder {
} }
this._buildStatus = STATUS.BUILDING this._buildStatus = STATUS.BUILDING
this.spinner.start('Initializing builder') logger.start('Initializing builder')
// Wait for nuxt ready // Wait for nuxt ready
await this.nuxt.ready() await this.nuxt.ready()
@ -131,9 +124,9 @@ export default class Builder {
} }
} }
this.spinner.succeed('Builder initialized') logger.success('Builder initialized')
debug(`App root: ${this.options.srcDir}`) logger.debug(`App root: ${this.options.srcDir}`)
// Create .nuxt/, .nuxt/components and .nuxt/dist folders // Create .nuxt/, .nuxt/components and .nuxt/dist folders
await fsExtra.remove(r(this.options.buildDir)) await fsExtra.remove(r(this.options.buildDir))
@ -158,7 +151,7 @@ export default class Builder {
} }
async generateRoutesAndFiles() { async generateRoutesAndFiles() {
this.spinner.start(`Generating nuxt files...`) logger.start(`Generating nuxt files...`)
// -- Templates -- // -- Templates --
let templatesFiles = [ let templatesFiles = [
@ -253,7 +246,7 @@ export default class Builder {
} }
// -- Routes -- // -- Routes --
debug('Generating routes...') logger.debug('Generating routes...')
// If user defined a custom method to create routes // If user defined a custom method to create routes
if (this._nuxtPages) { if (this._nuxtPages) {
// Use nuxt.js createRoutes bases on pages/ // Use nuxt.js createRoutes bases on pages/
@ -421,7 +414,7 @@ export default class Builder {
}) })
) )
this.spinner.succeed('Nuxt files generated') logger.success('Nuxt files generated')
} }
async webpackBuild() { async webpackBuild() {
@ -429,6 +422,10 @@ export default class Builder {
const compilersOptions = [] const compilersOptions = []
// Client
const clientConfig = new ClientWebpackConfig(this).config()
compilersOptions.push(clientConfig)
// Server // Server
let serverConfig = null let serverConfig = null
if (this.options.build.ssr) { if (this.options.build.ssr) {
@ -436,10 +433,6 @@ export default class Builder {
compilersOptions.push(serverConfig) compilersOptions.push(serverConfig)
} }
// Client
const clientConfig = new ClientWebpackConfig(this).config()
compilersOptions.push(clientConfig)
// Alias plugins to their real path // Alias plugins to their real path
this.plugins.forEach(p => { this.plugins.forEach(p => {
const src = this.relativeToBuild(p.src) const src = this.relativeToBuild(p.src)
@ -470,9 +463,9 @@ export default class Builder {
// Warmup perfLoader before build // Warmup perfLoader before build
if (this.options.build.parallel) { if (this.options.build.parallel) {
this.spinner.start('Warming up worker pools') logger.start('Warming up worker pools')
this.perfLoader.warmup() this.perfLoader.warmup()
this.spinner.succeed('Worker pools ready') logger.success('Worker pools ready')
} }
// Start Builds // Start Builds
@ -487,12 +480,6 @@ export default class Builder {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
const name = compiler.options.name const name = compiler.options.name
const shouldLog = this.options.minimalCLI && !this.options.test && !this.options.dev
if (shouldLog) {
this.spinner.start('Compiling ' + name + '...')
}
await this.nuxt.callHook('build:compile', { name, compiler }) await this.nuxt.callHook('build:compile', { name, compiler })
// Load renderer resources after build // Load renderer resources after build
@ -534,17 +521,9 @@ export default class Builder {
err = stats.toString(this.options.build.stats) err = stats.toString(this.options.build.stats)
} }
if (shouldLog) {
this.spinner.fail('Compiled ' + name + ' with some errors')
}
return reject(err) return reject(err)
} }
if (shouldLog) {
this.spinner.succeed('Compiled ' + name)
}
resolve() resolve()
}) })
} }
@ -552,7 +531,7 @@ export default class Builder {
} }
webpackDev(compiler) { webpackDev(compiler) {
debug('Adding webpack middleware...') logger.debug('Adding webpack middleware...')
// Create webpack dev middleware // Create webpack dev middleware
this.webpackDevMiddleware = util.promisify( this.webpackDevMiddleware = util.promisify(

View File

@ -3,7 +3,10 @@ import _ from 'lodash'
import htmlMinifier from 'html-minifier' import htmlMinifier from 'html-minifier'
import Chalk from 'chalk' import Chalk from 'chalk'
import fsExtra from 'fs-extra' import fsExtra from 'fs-extra'
import { isUrl, promisifyRoute, waitFor, flatRoutes, printWarn, createSpinner } from '../common/utils' import consola from 'consola'
import { isUrl, promisifyRoute, waitFor, flatRoutes } from '../common/utils'
const logger = consola.withScope('nuxt:generator')
export default class Generator { export default class Generator {
constructor(nuxt, builder) { constructor(nuxt, builder) {
@ -19,22 +22,18 @@ export default class Generator {
this.distPath, this.distPath,
isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath isUrl(this.options.build.publicPath) ? '' : this.options.build.publicPath
) )
this.spinner = createSpinner({
minimal: this.options.minimalCLI
})
} }
async generate({ build = true, init = true } = {}) { async generate({ build = true, init = true } = {}) {
this.spinner.start('Initializing generator...') logger.start('Initializing generator...')
await this.initiate({ build, init }) await this.initiate({ build, init })
this.spinner.start('Preparing routes for generate...') logger.start('Preparing routes for generate...')
const routes = await this.initRoutes() const routes = await this.initRoutes()
this.spinner.start('Generating pages...') logger.start('Generating pages...')
const errors = await this.generateRoutes(routes) const errors = await this.generateRoutes(routes)
@ -77,7 +76,7 @@ export default class Generator {
...args ...args
) )
} catch (e) { } catch (e) {
console.error('Could not resolve routes') // eslint-disable-line no-console logger.error('Could not resolve routes')
throw e // eslint-disable-line no-unreachable throw e // eslint-disable-line no-unreachable
} }
} }
@ -147,7 +146,7 @@ export default class Generator {
// Prevent conflicts // Prevent conflicts
if (fsExtra.existsSync(fallbackPath)) { if (fsExtra.existsSync(fallbackPath)) {
printWarn(`SPA fallback was configured, but the configured path (${fallbackPath}) already exists.`) logger.warn(`SPA fallback was configured, but the configured path (${fallbackPath}) already exists.`)
return return
} }
@ -275,10 +274,10 @@ export default class Generator {
}) })
if (pageErrors.length) { if (pageErrors.length) {
this.spinner.fail('Error generating ' + route) logger.error('Error generating ' + route)
Array.prototype.push.apply(errors, pageErrors) Array.prototype.push.apply(errors, pageErrors)
} else { } else {
this.spinner.succeed('Generated ' + route) logger.success('Generated ' + route)
} }
return true return true

View File

@ -20,7 +20,6 @@ export default class WebpackBaseConfig {
this.isStatic = builder.isStatic this.isStatic = builder.isStatic
this.options = builder.options this.options = builder.options
this.spinner = builder.spinner this.spinner = builder.spinner
this.logUpdate = builder.logUpdate
} }
getBabelOptions() { getBabelOptions() {
@ -203,19 +202,17 @@ export default class WebpackBaseConfig {
plugins.push(new WarnFixPlugin()) plugins.push(new WarnFixPlugin())
// Build progress indicator // Build progress indicator
if (!(this.options.test || this.options.minimalCLI)) { plugins.push(new WebpackBar({
plugins.push(new WebpackBar({ profile: this.options.build.profile,
profile: this.options.build.profile, name: this.isServer ? 'server' : 'client',
name: this.isServer ? 'server' : 'client', color: this.isServer ? 'orange' : 'green',
color: this.isServer ? 'orange' : 'green', compiledIn: false,
logUpdate: this.logUpdate, done: () => {
done: () => { if (this.options.dev) {
if (this.options.dev) { this.nuxt.showReady(true)
this.nuxt.showReady(true)
}
} }
})) }
} }))
// Add stats plugin // Add stats plugin
if (!this.options.dev && this.options.build.stats) { if (!this.options.dev && this.options.build.stats) {

View File

@ -1,7 +1,6 @@
import path from 'path' import path from 'path'
import fs from 'fs' import fs from 'fs'
import env from 'std-env'
import isCI from 'is-ci'
const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', 'package.json')) const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', 'package.json'))
? path.resolve(__dirname, '..') // dist ? path.resolve(__dirname, '..') // dist
@ -9,13 +8,8 @@ const nuxtDir = fs.existsSync(path.resolve(__dirname, '..', 'package.json'))
export default { export default {
// Information about running environment // Information about running environment
dev: process.env.NODE_ENV !== 'production', dev: Boolean(env.dev),
production: process.env.NODE_ENV === 'production',
debug: undefined, // = dev debug: undefined, // = dev
test: process.env.NODE_ENV === 'test',
ci: Boolean(isCI),
tty: Boolean(process.stdout.isTTY),
minimalCLI: undefined, // = ci || test || production || !tty
// Mode // Mode
mode: 'universal', mode: 'universal',

View File

@ -129,11 +129,6 @@ Options.from = function (_options) {
options.debug = options.dev options.debug = options.dev
} }
// minimalCLI
if (options.minimalCLI === undefined) {
options.minimalCLI = options.ci || options.test || options.production || !options.tty
}
// Apply default hash to CSP option // Apply default hash to CSP option
if (options.render.csp === true) { if (options.render.csp === true) {
options.render.csp = { hashAlgorithm: 'sha256' } options.render.csp = { hashAlgorithm: 'sha256' }

View File

@ -1,57 +1,5 @@
import path from 'path' import path from 'path'
import _ from 'lodash' import _ from 'lodash'
import Chalk from 'chalk'
import ORA from 'ora'
export const printWarn = function (msg, from) {
/* eslint-disable no-console */
const fromStr = from ? Chalk.yellow(` ${from}\n\n`) : ' '
console.warn('\n' + Chalk.bgYellow.black(' WARN ') + fromStr + msg + '\n')
}
export const renderError = function (_error, from) {
const errStr = _error.stack || String(_error)
const fromStr = from ? Chalk.red(` ${from}`) : ''
return '\n' + Chalk.bgRed.black(' ERROR ') + fromStr + ' ' + errStr
}
export const printError = function () {
/* eslint-disable no-console */
console.error(renderError(...arguments))
}
export const fatalError = function () {
if (arguments[0]) {
/* eslint-disable no-console */
console.error(renderError(...arguments))
}
process.exit(1)
}
export const createSpinner = function ({ minimal = false }) {
// Use ORA by default
if (!minimal) {
return new ORA()
}
// Creare a minimal fallback for test and CI environments
const ctx = { enabled: true }
// eslint-disable-next-line no-console
const createLogger = (tag, ctx) => (...args) => ctx.enabled && console.log(
_.padEnd(`[${tag.toUpperCase()}]`, 9),
`[${new Date().toLocaleTimeString()}]`,
...args
)
return Object.assign(ctx, {
start: createLogger('START', ctx),
fail: createLogger('FAIL', ctx),
succeed: createLogger('SUCCESS', ctx),
info: createLogger('INFO', ctx)
})
}
export const encodeHtml = function encodeHtml(str) { export const encodeHtml = function encodeHtml(str) {
return str.replace(/</g, '&lt;').replace(/>/g, '&gt;') return str.replace(/</g, '&lt;').replace(/>/g, '&gt;')

View File

@ -1,8 +1,11 @@
import path from 'path' import path from 'path'
import consola from 'consola'
import Youch from '@nuxtjs/youch' import Youch from '@nuxtjs/youch'
import fs from 'fs-extra' import fs from 'fs-extra'
const logger = consola.withScope('nuxt:error')
export default function errorMiddleware(err, req, res, next) { export default function errorMiddleware(err, req, res, next) {
// ensure statusCode, message and name fields // ensure statusCode, message and name fields
err.statusCode = err.statusCode || 500 err.statusCode = err.statusCode || 500
@ -11,7 +14,7 @@ export default function errorMiddleware(err, req, res, next) {
// We hide actual errors from end users, so show them on server logs // We hide actual errors from end users, so show them on server logs
if (err.statusCode !== 404) { if (err.statusCode !== 404) {
console.error(err) // eslint-disable-line no-console logger.error(err)
} }
const sendResponse = (content, type = 'text/html') => { const sendResponse = (content, type = 'text/html') => {

View File

@ -1,8 +1,11 @@
import generateETag from 'etag' import generateETag from 'etag'
import fresh from 'fresh' import fresh from 'fresh'
import consola from 'consola'
import { getContext } from '../../common/utils' import { getContext } from '../../common/utils'
const logger = consola.withScope('nuxt:middleware')
export default async function nuxtMiddleware(req, res, next) { export default async function nuxtMiddleware(req, res, next) {
// Get context // Get context
const context = getContext(req, res) const context = getContext(req, res)
@ -103,7 +106,7 @@ export default async function nuxtMiddleware(req, res, next) {
} catch (err) { } catch (err) {
/* istanbul ignore if */ /* istanbul ignore if */
if (context && context.redirected) { if (context && context.redirected) {
console.error(err) // eslint-disable-line no-console logger.error(err)
return err return err
} }

View File

@ -1,34 +1,28 @@
import Module from 'module' import Module from 'module'
import path from 'path' import path from 'path'
import Debug from 'debug'
import enableDestroy from 'server-destroy' import enableDestroy from 'server-destroy'
import _ from 'lodash' import _ from 'lodash'
import chalk from 'chalk'
import fs from 'fs-extra' import fs from 'fs-extra'
import clear from 'clear' import consola from 'consola'
import chalk from 'chalk'
import Options from '../common/options' import Options from '../common/options'
import { sequence, printError } from '../common/utils' import { sequence } from '../common/utils'
import packageJSON from '../../package.json' import packageJSON from '../../package.json'
import moduleUtil from '../common/module' import moduleUtil from '../common/module'
import ModuleContainer from './module' import ModuleContainer from './module'
import Renderer from './renderer' import Renderer from './renderer'
const debug = Debug('nuxt:') const logger = consola.withScope('nuxt')
debug.color = 5
const IS_WINDOWS = /^win/.test(process.platform)
const READY_ICON = IS_WINDOWS ? '' : '🚀'
export default class Nuxt { export default class Nuxt {
constructor(options = {}) { constructor(options = {}) {
this.options = Options.from(options) this.options = Options.from(options)
this.readyMessage = null this.readyMessage = 'Ready'
this.initialized = false this.initialized = false
this.onError = this.onError.bind(this)
// Hooks // Hooks
this._hooks = {} this._hooks = {}
@ -39,14 +33,15 @@ export default 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.onError(err)) this._ready = this.ready().catch(err => {
logger.error(err)
})
} }
static get version() { static get version() {
@ -87,26 +82,16 @@ export default 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
} }
debug(`Call ${name} hooks (${this._hooks[name].length})`) logger.debug(`Call ${name} hooks (${this._hooks[name].length})`)
try { try {
await sequence(this._hooks[name], fn => fn(...args)) await sequence(this._hooks[name], fn => fn(...args))
} catch (err) { } catch (err) {
this.onError(err, name) logger.error(err)
this.callHook('error', err)
} }
} }
@ -121,25 +106,12 @@ export default class Nuxt {
}) })
} }
showReady(doClear = false) { showReady(clear = false) {
if (!this.readyMessage || this.options.test) { logger.ready({
return message: this.readyMessage,
} badge: true,
clear
if (this.options.minimalCLI) { })
// eslint-disable-next-line no-console
console.log('[READY] ', this.readyMessage)
} else {
if (doClear) {
clear()
}
process.stdout.write(
'\n' +
chalk.bgGreen.black(' READY ') +
chalk.green(` ${READY_ICON} ${this.readyMessage}\n`)
)
}
} }
listen(port = 3000, host = 'localhost') { listen(port = 3000, host = 'localhost') {
@ -152,7 +124,8 @@ export default class Nuxt {
return reject(err) return reject(err)
} }
this.readyMessage = `Listening on ${host}:${port}` const hostPort = chalk.underline.blue(`${host}:${port}`)
this.readyMessage = `Listening on ${hostPort}`
// Close server on nuxt close // Close server on nuxt close
this.hook( this.hook(
@ -161,7 +134,7 @@ export default class Nuxt {
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
// Destroy server by forcing every connection to be closed // Destroy server by forcing every connection to be closed
server.destroy(err => { server.destroy(err => {
debug('server closed') logger.debug('server closed')
/* istanbul ignore if */ /* istanbul ignore if */
if (err) { if (err) {
return reject(err) return reject(err)

View File

@ -7,9 +7,9 @@ import compression from 'compression'
import _ from 'lodash' import _ from 'lodash'
import fs from 'fs-extra' import fs from 'fs-extra'
import { createBundleRenderer } from 'vue-server-renderer' import { createBundleRenderer } from 'vue-server-renderer'
import Debug from 'debug'
import connect from 'connect' import connect from 'connect'
import launchMiddleware from 'launch-editor-middleware' import launchMiddleware from 'launch-editor-middleware'
import consola from 'consola'
import { isUrl, waitFor, timeout } from '../common/utils' import { isUrl, waitFor, timeout } from '../common/utils'
import defaults from '../common/nuxt.config' import defaults from '../common/nuxt.config'
@ -18,8 +18,7 @@ import MetaRenderer from './meta'
import errorMiddleware from './middleware/error' import errorMiddleware from './middleware/error'
import nuxtMiddleware from './middleware/nuxt' import nuxtMiddleware from './middleware/nuxt'
const debug = Debug('nuxt:render') const logger = consola.withScope('nuxt:render')
debug.color = 4 // Force blue color
let jsdom = null let jsdom = null
@ -290,7 +289,7 @@ export default class Renderer {
} }
// Log rendered url // Log rendered url
debug(`Rendering url ${url}`) logger.debug(`Rendering url ${url}`)
// Add url and isSever to the context // Add url and isSever to the context
context.url = url context.url = url
@ -400,11 +399,11 @@ export default class Renderer {
try { try {
jsdom = this.nuxt.requireModule('jsdom') jsdom = this.nuxt.requireModule('jsdom')
} catch (e) /* istanbul ignore next */ { } catch (e) /* istanbul ignore next */ {
/* eslint-disable no-console */ consola.error(`
console.error('Fail when calling nuxt.renderAndGetWindow(url)') Fail when calling nuxt.renderAndGetWindow(url)
console.error('jsdom module is not installed') jsdom module is not installed
console.error('Please install jsdom with: npm install --save-dev jsdom') Please install jsdom with: npm install --save-dev jsdom
/* eslint-enable no-console */ `)
throw e throw e
} }
} }
@ -418,7 +417,7 @@ export default class Renderer {
} }
const jsdomErrHandler = err => { throw err } const jsdomErrHandler = err => { throw err }
if (opts.virtualConsole !== false) { if (opts.virtualConsole !== false) {
options.virtualConsole = new jsdom.VirtualConsole().sendTo(console) options.virtualConsole = new jsdom.VirtualConsole().sendTo(consola)
// throw error when window creation failed // throw error when window creation failed
options.virtualConsole.on('jsdomError', jsdomErrHandler) options.virtualConsole.on('jsdomError', jsdomErrHandler)
} }

View File

@ -78,11 +78,10 @@
"caniuse-lite": "^1.0.30000821", "caniuse-lite": "^1.0.30000821",
"chalk": "^2.3.2", "chalk": "^2.3.2",
"chokidar": "^2.0.3", "chokidar": "^2.0.3",
"clear": "^0.1.0",
"compression": "^1.7.1", "compression": "^1.7.1",
"connect": "^3.6.5", "connect": "^3.6.5",
"consola": "^0.1.0",
"css-loader": "^0.28.11", "css-loader": "^0.28.11",
"debug": "^3.1.0",
"es6-promise": "^4.2.4", "es6-promise": "^4.2.4",
"esm": "^3.0.14", "esm": "^3.0.14",
"etag": "^1.8.1", "etag": "^1.8.1",
@ -91,9 +90,8 @@
"fs-extra": "^5.0.0", "fs-extra": "^5.0.0",
"glob": "^7.1.2", "glob": "^7.1.2",
"hash-sum": "^1.0.2", "hash-sum": "^1.0.2",
"html-minifier": "^3.5.12", "html-minifier": "^3.5.13",
"html-webpack-plugin": "^3.1.0", "html-webpack-plugin": "^3.1.0",
"is-ci": "^1.1.0",
"launch-editor-middleware": "^2.2.1", "launch-editor-middleware": "^2.2.1",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"log-update": "^2.3.0", "log-update": "^2.3.0",
@ -113,6 +111,7 @@
"serialize-javascript": "^1.4.0", "serialize-javascript": "^1.4.0",
"serve-static": "^1.13.2", "serve-static": "^1.13.2",
"server-destroy": "^1.0.1", "server-destroy": "^1.0.1",
"std-env": "^1.1.0",
"style-resources-loader": "^1.1.0", "style-resources-loader": "^1.1.0",
"thread-loader": "^1.1.5", "thread-loader": "^1.1.5",
"time-fix-plugin": "^2.0.0", "time-fix-plugin": "^2.0.0",
@ -131,7 +130,7 @@
"webpack-dev-middleware": "^3.1.0", "webpack-dev-middleware": "^3.1.0",
"webpack-hot-middleware": "^2.21.2", "webpack-hot-middleware": "^2.21.2",
"webpack-node-externals": "^1.6.0", "webpack-node-externals": "^1.6.0",
"webpackbar": "^1.5.1" "webpackbar": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {
"babel-eslint": "^8.2.1", "babel-eslint": "^8.2.1",
@ -142,7 +141,7 @@
"eslint-config-standard": "^11.0.0", "eslint-config-standard": "^11.0.0",
"eslint-config-standard-jsx": "^5.0.0", "eslint-config-standard-jsx": "^5.0.0",
"eslint-plugin-html": "^4.0.2", "eslint-plugin-html": "^4.0.2",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.10.0",
"eslint-plugin-jest": "^21.15.0", "eslint-plugin-jest": "^21.15.0",
"eslint-plugin-node": "^6.0.0", "eslint-plugin-node": "^6.0.0",
"eslint-plugin-promise": "^3.7.0", "eslint-plugin-promise": "^3.7.0",

View File

@ -20,7 +20,6 @@ describe('basic generate', () => {
const config = loadFixture('basic', {generate: {dir: '.nuxt-generate'}}) const config = loadFixture('basic', {generate: {dir: '.nuxt-generate'}})
nuxt = new Nuxt(config) nuxt = new Nuxt(config)
generator = new Generator(nuxt) generator = new Generator(nuxt)
generator.spinner.enabled = false
await generator.generate({ build: false }) await generator.generate({ build: false })

View File

@ -19,7 +19,6 @@ describe('fallback generate', () => {
nuxt = new Nuxt(config) nuxt = new Nuxt(config)
generator = new Generator(nuxt) generator = new Generator(nuxt)
generator.spinner.enabled = false
await generator.generate({ build: false }) await generator.generate({ build: false })

View File

@ -1,42 +1,6 @@
import { Utils } from '../utils' import { Utils } from '../utils'
import mockConsole from '../utils/console'
describe('utils', () => { describe('utils', () => {
const console = mockConsole(['warn', 'error'])
test('printWarn', () => {
Utils.printWarn('Testing printWarn', 'utils.test.js')
expect(console.warn).toHaveBeenCalledTimes(1)
expect(console.warn.mock.calls[0][0]).toContain('WARN')
expect(console.warn.mock.calls[0][0]).toContain('Testing printWarn')
})
test('printError', () => {
Utils.printError(new Error('Error object'), 'utils.test.js')
expect(console.error).toHaveBeenCalledTimes(1)
expect(console.error.mock.calls[0][0]).toContain('Error: Error object')
console.error.mockClear()
Utils.printError('Error string', 'utils.test.js')
expect(console.error).toHaveBeenCalledTimes(1)
expect(console.error.mock.calls[0][0]).toContain('Error string')
})
test('fatalError', () => {
const exitSpy = jest.spyOn(process, 'exit').mockImplementation()
Utils.fatalError('Testing fatalError')
expect(console.error).toHaveBeenCalledTimes(1)
expect(console.error.mock.calls[0][0]).toContain('Testing fatalError')
expect(exitSpy).toHaveBeenCalledTimes(1)
expect(exitSpy).toHaveBeenCalledWith(1)
exitSpy.mockRestore()
})
test('encodeHtml', () => { test('encodeHtml', () => {
const html = '<h1>Hello</h1>' const html = '<h1>Hello</h1>'
expect(Utils.encodeHtml(html)).toBe('&lt;h1&gt;Hello&lt;/h1&gt;') expect(Utils.encodeHtml(html)).toBe('&lt;h1&gt;Hello&lt;/h1&gt;')

View File

@ -1,4 +1,10 @@
// eslint-disable // eslint-disable
require('babel-polyfill') require('babel-polyfill')
const consola = require('consola')
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 1000 jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 1000
consola.clear().add({
log: jest.fn()
})

View File

@ -1504,10 +1504,6 @@ clean-css@4.1.x:
dependencies: dependencies:
source-map "0.5.x" source-map "0.5.x"
clear@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/clear/-/clear-0.1.0.tgz#b81b1e03437a716984fd7ac97c87d73bdfe7048a"
cli-cursor@^2.0.0, cli-cursor@^2.1.0: cli-cursor@^2.0.0, cli-cursor@^2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
@ -1723,6 +1719,14 @@ connect@^3.6.5:
parseurl "~1.3.2" parseurl "~1.3.2"
utils-merge "1.0.1" utils-merge "1.0.1"
consola@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/consola/-/consola-0.1.0.tgz#6b627c18dee52e0f61f5222552a250345ba2dedd"
dependencies:
figures "^2.0.0"
ora "^2.0.0"
std-env "^1.1.0"
console-browserify@^1.1.0: console-browserify@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10"
@ -2418,9 +2422,9 @@ eslint-import-resolver-node@^0.3.1:
debug "^2.6.9" debug "^2.6.9"
resolve "^1.5.0" resolve "^1.5.0"
eslint-module-utils@^2.1.1: eslint-module-utils@^2.2.0:
version "2.1.1" version "2.2.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746"
dependencies: dependencies:
debug "^2.6.8" debug "^2.6.8"
pkg-dir "^1.0.0" pkg-dir "^1.0.0"
@ -2431,16 +2435,16 @@ eslint-plugin-html@^4.0.2:
dependencies: dependencies:
htmlparser2 "^3.8.2" htmlparser2 "^3.8.2"
eslint-plugin-import@^2.8.0: eslint-plugin-import@^2.10.0:
version "2.9.0" version "2.10.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.10.0.tgz#fa09083d5a75288df9c6c7d09fe12255985655e7"
dependencies: dependencies:
builtin-modules "^1.1.1" builtin-modules "^1.1.1"
contains-path "^0.1.0" contains-path "^0.1.0"
debug "^2.6.8" debug "^2.6.8"
doctrine "1.5.0" doctrine "1.5.0"
eslint-import-resolver-node "^0.3.1" eslint-import-resolver-node "^0.3.1"
eslint-module-utils "^2.1.1" eslint-module-utils "^2.2.0"
has "^1.0.1" has "^1.0.1"
lodash "^4.17.4" lodash "^4.17.4"
minimatch "^3.0.3" minimatch "^3.0.3"
@ -3296,7 +3300,7 @@ html-entities@^1.2.0:
version "1.2.1" version "1.2.1"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f"
html-minifier@^3.2.3, html-minifier@^3.5.12: html-minifier@^3.2.3:
version "3.5.12" version "3.5.12"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.12.tgz#6bfad4d0327f5b8d2b62f5854654ac3703b9b031" resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.12.tgz#6bfad4d0327f5b8d2b62f5854654ac3703b9b031"
dependencies: dependencies:
@ -3309,6 +3313,18 @@ html-minifier@^3.2.3, html-minifier@^3.5.12:
relateurl "0.2.x" relateurl "0.2.x"
uglify-js "3.3.x" uglify-js "3.3.x"
html-minifier@^3.5.13:
version "3.5.13"
resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.13.tgz#6bca6d533a7f18a476dc6aeb3d113071ab5c165e"
dependencies:
camel-case "3.0.x"
clean-css "4.1.x"
commander "2.15.x"
he "1.1.x"
param-case "2.1.x"
relateurl "0.2.x"
uglify-js "3.3.x"
html-tags@^2.0.0: html-tags@^2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b"
@ -6780,6 +6796,18 @@ statuses@~1.3.1:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
std-env@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/std-env/-/std-env-1.0.0.tgz#7435477a5900f38088f2b021dd2dd98679816b0a"
dependencies:
is-ci "^1.1.0"
std-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/std-env/-/std-env-1.1.0.tgz#f89ded0c3facd5b53cd3cda288779ed88d3b87ad"
dependencies:
is-ci "^1.1.0"
stealthy-require@^1.1.0: stealthy-require@^1.1.0:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
@ -7553,18 +7581,19 @@ webpack@^4.4.1:
watchpack "^1.5.0" watchpack "^1.5.0"
webpack-sources "^1.0.1" webpack-sources "^1.0.1"
webpackbar@^1.5.1: webpackbar@^2.2.0:
version "1.5.1" version "2.2.0"
resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-1.5.1.tgz#05fd64a168ac6cbef763ab074ababeaf9edb72fc" resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-2.2.0.tgz#db08d5f997b6dfd717c070fb1cf9c0c4ae35f1b3"
dependencies: dependencies:
chalk "^2.3.2" chalk "^2.3.2"
consola "^0.1.0"
figures "^2.0.0" figures "^2.0.0"
is-ci "^1.1.0"
loader-utils "^1.1.0" loader-utils "^1.1.0"
lodash "^4.17.5" lodash "^4.17.5"
log-update "^2.3.0" log-update "^2.3.0"
pretty-time "^1.0.0" pretty-time "^1.0.0"
schema-utils "^0.4.5" schema-utils "^0.4.5"
std-env "^1.0.0"
table "^4.0.3" table "^4.0.3"
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: