From 5631c047709bf7025fb9399e1764a2cb45e2daf9 Mon Sep 17 00:00:00 2001 From: Jeff Dickey <216188+jdxcode@users.noreply.github.com> Date: Sat, 20 Jan 2018 13:25:14 -0800 Subject: [PATCH] fix: only display error when unhandled --- src/action/spinner.ts | 3 +- src/deps.ts | 2 -- src/errors.ts | 71 ++++++++++++++++++++++++------------------- src/prompt.ts | 8 +++-- src/styled/object.ts | 5 ++- test/errors.test.ts | 1 - 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/action/spinner.ts b/src/action/spinner.ts index 45f43e6f..7d8c36ff 100644 --- a/src/action/spinner.ts +++ b/src/action/spinner.ts @@ -1,5 +1,6 @@ // tslint:disable restrict-plus-operands +import chalk from 'chalk' import * as supportsColor from 'supports-color' import deps from '../deps' @@ -10,7 +11,7 @@ const spinners = require('./spinners') function color(s: string): string { if (!supportsColor) return s let has256 = supportsColor.has256 || (process.env.TERM || '').indexOf('256') !== -1 - return has256 ? `\u001b[38;5;104m${s}${deps.ansiStyles.reset.open}` : deps.chalk.magenta(s) + return has256 ? `\u001b[38;5;104m${s}${deps.ansiStyles.reset.open}` : chalk.magenta(s) } export default class SpinnerAction extends ActionBase { diff --git a/src/deps.ts b/src/deps.ts index 511fc746..2803ffa3 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -1,6 +1,5 @@ import screen = require('@dxcli/screen') import ansiStyles = require('ansi-styles') -import {Chalk} from 'chalk' import stripAnsi = require('strip-ansi') import prompt = require('./prompt') @@ -10,7 +9,6 @@ import styledObject = require('./styled/object') import table = require('./styled/table') export const deps = { - get chalk(): Chalk { return fetch('chalk') }, get stripAnsi(): typeof stripAnsi { return fetch('strip-ansi') }, get ansiStyles(): typeof ansiStyles { return fetch('ansi-styles') }, get ansiEscapes(): any { return fetch('ansi-escapes') }, diff --git a/src/errors.ts b/src/errors.ts index 85753b48..37dab751 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -1,5 +1,6 @@ // tslint:disable no-console +import chalk from 'chalk' import {inspect} from 'util' import CLI from '.' @@ -11,7 +12,7 @@ export interface Message { type: 'error' scope: string | undefined severity: 'fatal' | 'error' | 'warn' - error: Error + error: CLIError } export interface Options { @@ -48,23 +49,35 @@ export function getErrorMessage(err: any): string { return message || inspect(err) } -function wrap(msg: string): string { - const linewrap = require('@heroku/linewrap') - return linewrap(6, deps.screen.errtermwidth, { - skip: /^\$ .*$/, - skipScheme: 'ansi-color', - })(msg) -} +function displayError(err: CLIError) { + function wrap(msg: string): string { + const linewrap = require('@heroku/linewrap') + return linewrap(6, deps.screen.errtermwidth, { + skip: /^\$ .*$/, + skipScheme: 'ansi-color', + })(msg) + } + + function render(): string { + let bang = chalk.red(arrow) + let msg = err['cli-ux'].scope ? `${err['cli-ux'].scope}: ${getErrorMessage(err)}` : getErrorMessage(err) + if (err['cli-ux'].severity === 'fatal') { + bang = chalk.bgRed.bold.white(' FATAL ') + msg += `\n${inspect(err)}` + } + if (err['cli-ux'].severity === 'warn') bang = chalk.yellow(arrow) + return bangify(wrap(msg), bang) + } -function render(message: Message): string { - let bang = deps.chalk.red(arrow) - let msg = message.scope ? `${message.scope}: ${getErrorMessage(message.error)}` : getErrorMessage(message.error) - if (message.severity === 'fatal') { - bang = deps.chalk.bgRed.bold.white(' FATAL ') - msg += `\n${inspect(message.error)}` + function getBang(): string { + if (err['cli-ux'].severity === 'warn') return chalk.yellowBright('!') + if (err['cli-ux'].severity === 'fatal') return chalk.bold.bgRedBright('!!!') + return chalk.bold.redBright('!') } - if (message.severity === 'warn') bang = deps.chalk.yellow(arrow) - return bangify(wrap(msg), bang) + + config.action.pause(() => { + console.error(render()) + }, getBang()) } export class CLIError extends Error { @@ -76,6 +89,13 @@ export class CLIError extends Error { } constructor(input: any, severity: 'fatal' | 'error' | 'warn', scope: string | undefined, opts: Options) { + function getExitCode(options: Options): false | number { + let exit = options.exit === undefined ? (options as any).exitCode : options.exit + if (exit === false) return false + if (exit === undefined) return 1 + return exit + } + const err: CLIError = typeof input === 'string' ? super(input) && this : input err['cli-ux'] = err['cli-ux'] || { severity, @@ -86,23 +106,11 @@ export class CLIError extends Error { } } -function getExitCode(options: Options): false | number { - let exit = options.exit === undefined ? (options as any).exitCode : options.exit - if (exit === false) return false - if (exit === undefined) return 1 - return exit -} - export default (e: IEventEmitter) => { e.on('output', m => { if (m.type !== 'error') return - const bang = (m.severity === 'warn' && deps.chalk.yellowBright('!')) - || (m.severity === 'fatal' && deps.chalk.bold.bgRedBright('!!!')) - || deps.chalk.bold.redBright('!') try { - config.action.pause(() => { - console.error(render(m)) - }, bang) + if (m.error['cli-ux'].exit === false) displayError(m.error) } catch (newErr) { console.error(newErr) console.error(m.error) @@ -120,12 +128,11 @@ export default (e: IEventEmitter) => { const cli: typeof CLI = require('.').cli.scope(scope) if (err.code === 'EPIPE') return if (err['cli-ux'] && typeof err['cli-ux'].exit === 'number') { + displayError(err) await cli.done().catch(cli.debug) process.exit(err['cli-ux'].exit as number) } else { - cli.fatal(err, {exit: false}) - await cli.done().catch(cli.debug) - process.exit(100) + cli.fatal(err) } } catch (newError) { console.error(err) diff --git a/src/prompt.ts b/src/prompt.ts index 74429ba0..a45d2f33 100644 --- a/src/prompt.ts +++ b/src/prompt.ts @@ -1,3 +1,5 @@ +import chalk from 'chalk' + import config from './config' import deps from './deps' @@ -17,7 +19,7 @@ export default { prompt(name: string, options: IPromptOptions = {}) { return config.action.pauseAsync(() => { return _prompt(name, options) - }, deps.chalk.cyan('?')) + }, chalk.cyan('?')) }, confirm(message: string): Promise { return config.action.pauseAsync(async () => { @@ -28,14 +30,14 @@ export default { return confirm() } return confirm() - }, deps.chalk.cyan('?')) + }, chalk.cyan('?')) } } function _prompt(name: string, inputOptions: Partial = {}): Promise { const options: IPromptConfig = { isTTY: !!(process.env.TERM !== 'dumb' && process.stdin.isTTY), name, - prompt: name ? deps.chalk.dim(`${name}: `) : deps.chalk.dim('> '), + prompt: name ? chalk.dim(`${name}: `) : chalk.dim('> '), type: 'normal', ...inputOptions, } diff --git a/src/styled/object.ts b/src/styled/object.ts index c343dffe..42ee95ef 100644 --- a/src/styled/object.ts +++ b/src/styled/object.ts @@ -1,9 +1,8 @@ // tslint:disable +import chalk from 'chalk' import * as util from 'util' -import deps from '../deps' - export default function styledObject(obj: any, keys?: string[]) { let keyLengths = Object.keys(obj).map(key => key.toString().length) let maxKeyLength = Math.max.apply(Math, keyLengths) + 2 @@ -18,7 +17,7 @@ export default function styledObject(obj: any, keys?: string[]) { } } let logKeyValue = (key: string, value: any) => { - console.log(`${deps.chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value)) + console.log(`${chalk.blue(key)}:` + ' '.repeat(maxKeyLength - key.length - 1) + pp(value)) } for (let key of keys || Object.keys(obj).sort()) { let value = obj[key] diff --git a/test/errors.test.ts b/test/errors.test.ts index 4b418674..96ca5849 100644 --- a/test/errors.test.ts +++ b/test/errors.test.ts @@ -15,6 +15,5 @@ describe.stderr('errors', () => { it('errors', () => { expect(() => cli.error('foobar')).to.throw(/foobar/) - expect(output.stderr).to.equal(' ▸ foobar\n') }) })