Skip to content
This repository was archived by the owner on Aug 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"extends": "oclif"
"extends": [
"oclif",
"oclif-typescript"
],
"rules": {}
}
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"wrap-ansi": "^4.0.0"
},
"devDependencies": {
"@oclif/tslint": "^3.1.0",
"@types/chai": "^4.1.6",
"@types/clean-stack": "^1.3.0",
"@types/fs-extra": "^5.0.4",
Expand All @@ -23,11 +22,13 @@
"@types/wrap-ansi": "^3.0.0",
"chai": "^4.2.0",
"chalk": "^2.4.1",
"eslint": "^6.6.0",
"eslint-config-oclif": "^3.1.0",
"eslint-config-oclif-typescript": "^0.1.0",
"fancy-test": "^1.4.1",
"mocha": "^5.2.0",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"typescript": "^3.1.3"
"typescript": "^3.2.1"
},
"engines": {
"node": ">=8.0.0"
Expand All @@ -45,8 +46,8 @@
"repository": "oclif/errors",
"scripts": {
"build": "rm -rf lib && tsc",
"lint": "tsc -p test --noEmit && tslint -p test -t stylish",
"posttest": "yarn run lint",
"lint": "tsc -p test --noEmit && eslint . --ext .ts --config .eslintrc",
"posttest": "yarn lint",
"prepublishOnly": "yarn run build",
"pretest": "yarn run build",
"test": "mocha --forbid-only \"test/**/*.test.ts\""
Expand Down
11 changes: 7 additions & 4 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// tslint:disable no-console

import {Logger} from './logger'

// eslint-disable-next-line no-multi-assign
const g = (global as any).oclif = (global as any).oclif || {}

function displayWarnings() {
Expand All @@ -14,12 +13,16 @@ function displayWarnings() {

export const config = {
errorLogger: undefined as Logger | undefined,
get debug(): boolean { return !!g.debug },
get debug(): boolean {
return Boolean(g.debug)
},
set debug(enabled: boolean) {
g.debug = enabled
if (enabled) displayWarnings()
},
get errlog(): string | undefined { return g.errlog },
get errlog(): string | undefined {
return g.errlog
},
set errlog(errlog: string | undefined) {
g.errlog = errlog
if (errlog) this.errorLogger = new Logger(errlog)
Expand Down
16 changes: 11 additions & 5 deletions src/errors/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import {config} from '../config'

export class CLIError extends Error {
oclif: any

code?: string

constructor(error: string | Error, options: {code?: string, exit?: number | false} = {}) {
constructor(error: string | Error, options: {code?: string; exit?: number | false} = {}) {
const addExitCode = (error: any) => {
error.oclif = error.oclif || {}
error.oclif.exit = options.exit === undefined ? 2 : options.exit
Expand All @@ -32,10 +33,11 @@ export class CLIError extends Error {
if (config.debug) {
return this.stack
}
let wrap: typeof Wrap = require('wrap-ansi')
let indent: typeof Indent = require('indent-string')
const wrap: typeof Wrap = require('wrap-ansi')
const indent: typeof Indent = require('indent-string')

let output = `${this.name}: ${this.message}`
// eslint-disable-next-line node/no-missing-require
output = wrap(output, require('../screen').errtermwidth - 6, {trim: false, hard: true} as any)
output = indent(output, 3)
output = indent(output, 1, {indent: this.bang, includeEmptyLines: true} as any)
Expand All @@ -45,7 +47,9 @@ export class CLIError extends Error {

protected get bang() {
let red: typeof Chalk.red = ((s: string) => s) as any
try { red = require('chalk').red } catch {}
try {
red = require('chalk').red
} catch {}
return red(process.platform === 'win32' ? '»' : '›')
}
}
Expand All @@ -59,7 +63,9 @@ export namespace CLIError {

protected get bang() {
let yellow: typeof Chalk.yellow = ((s: string) => s) as any
try { yellow = require('chalk').yellow } catch {}
try {
yellow = require('chalk').yellow
} catch {}
return yellow(process.platform === 'win32' ? '»' : '›')
}
}
Expand Down
5 changes: 4 additions & 1 deletion src/errors/exit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import {CLIError} from './cli'

export class ExitError extends CLIError {
oclif!: { exit: number }

code = 'EEXIT'

constructor(exitCode = 0) {
super(`EEXIT: ${exitCode}`, {exit: exitCode})
}

render(): string { return '' }
render(): string {
return ''
}
}
6 changes: 3 additions & 3 deletions src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
declare namespace NodeJS {
interface Global {
oclif?: {
debug?: boolean
errlog?: string
}
debug?: boolean;
errlog?: string;
};
}
}
13 changes: 7 additions & 6 deletions src/handle.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// tslint:disable no-console
/* eslint-disable no-process-exit */
/* eslint-disable unicorn/no-process-exit */

import clean = require('clean-stack')

Expand All @@ -8,20 +9,20 @@ export const handle = (err: any) => {
try {
if (!err) err = new Error('no error?')
if (err.message === 'SIGINT') process.exit(1)
let stack = clean(err.stack || '', {pretty: true})
const stack = clean(err.stack || '', {pretty: true})
let message = stack
if (err.oclif && typeof err.render === 'function') message = err.render()
if (message) console.error(message)
const exitCode = (err.oclif && err.oclif.exit !== undefined) ? err.oclif.exit : 1
if (config.errorLogger && err.code !== 'EEXIT') {
config.errorLogger.log(stack)
config.errorLogger.flush()
.then(() => process.exit(exitCode))
.catch(console.error)
.then(() => process.exit(exitCode))
.catch(console.error)
} else process.exit(exitCode)
} catch (e) {
} catch (error) {
console.error(err.stack)
console.error(e.stack)
console.error(error.stack)
process.exit(1)
}
}
8 changes: 4 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export function exit(code = 0): never {
throw new ExitError(code)
}

export function error(input: string | Error, options: {code?: string, exit: false}): void
export function error(input: string | Error, options?: {code?: string, exit?: number}): never
export function error(input: string | Error, options: {code?: string, exit?: number | false} = {}) {
export function error(input: string | Error, options: {code?: string; exit: false}): void
export function error(input: string | Error, options?: {code?: string; exit?: number}): never
export function error(input: string | Error, options: {code?: string; exit?: number | false} = {}) {
const err = new CLIError(input, options)
if (options.exit === false) {
console.error(err.render ? err.render() : `Error ${err.message}`)
Expand All @@ -25,7 +25,7 @@ export function error(input: string | Error, options: {code?: string, exit?: num
}

export function warn(input: string | Error) {
let err = new CLIError.Warn(input)
const err = new CLIError.Warn(input)
console.error(err.render ? err.render() : `Warning: ${err.message}`)
if (config.errorLogger) config.errorLogger.log(err.stack)
}
6 changes: 4 additions & 2 deletions src/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ function chomp(s: string): string {

export class Logger {
protected flushing: Promise<void> = Promise.resolve()

protected buffer: string[] = []

// eslint-disable-next-line no-useless-constructor
constructor(public file: string) {}

log(msg: string) {
let stripAnsi: typeof StripAnsi = require('strip-ansi')
const stripAnsi: typeof StripAnsi = require('strip-ansi')
msg = stripAnsi(chomp(msg))
let lines = msg.split('\n').map(l => `${timestamp()} ${l}`.trimRight())
const lines = msg.split('\n').map(l => `${timestamp()} ${l}`.trimRight())
this.buffer.push(...lines)
// tslint:disable-next-line no-console
this.flush(50).catch(console.error)
Expand Down
4 changes: 2 additions & 2 deletions src/screen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ function termwidth(stream: any): number {

const columns: number | null = (global as any).columns

export let stdtermwidth = columns || termwidth(process.stdout)
export let errtermwidth = columns || termwidth(process.stderr)
export const stdtermwidth = columns || termwidth(process.stdout)
export const errtermwidth = columns || termwidth(process.stderr)
102 changes: 54 additions & 48 deletions test/handle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,68 +12,74 @@ const exit = process.exit

describe('handle', () => {
beforeEach(() => {
(process as any).exit = (code: any) => { process.exitCode = code }
(process as any).exit = (code: any) => {
process.exitCode = code
}
})
afterEach(() => {
(process as any).exit = exit
})

fancy
.stderr()
.finally(() => delete process.exitCode)
.it('displays an error from root handle module', ctx => {
require('../handle')(new Error('x'))
expect(ctx.stderr).to.contain('Error: x')
expect(process.exitCode).to.equal(1)
})
.stderr()
.finally(() => delete process.exitCode)
.it('displays an error from root handle module', ctx => {
require('../handle')(new Error('x'))
expect(ctx.stderr).to.contain('Error: x')
expect(process.exitCode).to.equal(1)
})

fancy
.stderr()
.finally(() => delete process.exitCode)
.it('shows an unhandled error', ctx => {
handle(new Error('x'))
expect(ctx.stderr).to.contain('Error: x')
expect(process.exitCode).to.equal(1)
})
.stderr()
.finally(() => delete process.exitCode)
.it('shows an unhandled error', ctx => {
handle(new Error('x'))
expect(ctx.stderr).to.contain('Error: x')
expect(process.exitCode).to.equal(1)
})

fancy
.stderr()
.finally(() => delete process.exitCode)
.it('handles a badly formed error object', () => {
handle({status: 400})
expect(process.exitCode).to.equal(1)
})
.stderr()
.finally(() => delete process.exitCode)
.it('handles a badly formed error object', () => {
handle({status: 400})
expect(process.exitCode).to.equal(1)
})

fancy
.stderr()
.finally(() => delete process.exitCode)
.it('shows a cli error', ctx => {
handle(new CLIError('x'))
expect(ctx.stderr).to.equal(` ${x} Error: x\n`)
expect(process.exitCode).to.equal(2)
})
.stderr()
.finally(() => delete process.exitCode)
.it('shows a cli error', ctx => {
handle(new CLIError('x'))
expect(ctx.stderr).to.equal(` ${x} Error: x\n`)
expect(process.exitCode).to.equal(2)
})

fancy
.stdout()
.stderr()
.finally(() => delete process.exitCode)
.it('hides an exit error', ctx => {
handle(new ExitError())
expect(ctx.stdout).to.equal('')
expect(ctx.stderr).to.equal('')
expect(process.exitCode).to.equal(0)
})
.stdout()
.stderr()
.finally(() => delete process.exitCode)
.it('hides an exit error', ctx => {
handle(new ExitError())
expect(ctx.stdout).to.equal('')
expect(ctx.stderr).to.equal('')
expect(process.exitCode).to.equal(0)
})

fancy
.stderr()
.do(() => config.errlog = errlog)
.finally(() => config.errlog = undefined)
.finally(() => delete process.exitCode)
.it('logs when errlog is set', async ctx => {
handle(new CLIError('uh oh!'))
expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`)
await config.errorLogger!.flush()
expect(fs.readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!')
expect(process.exitCode).to.equal(2)
})
.stderr()
.do(() => {
config.errlog = errlog
})
.finally(() => {
config.errlog = undefined
})
.finally(() => delete process.exitCode)
.it('logs when errlog is set', async ctx => {
handle(new CLIError('uh oh!'))
expect(ctx.stderr).to.equal(` ${x} Error: uh oh!\n`)
await config.errorLogger!.flush()
expect(fs.readFileSync(errlog, 'utf8')).to.contain('Error: uh oh!')
expect(process.exitCode).to.equal(2)
})
})
24 changes: 14 additions & 10 deletions test/warn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,18 @@ const errlog = path.join(__dirname, '../tmp/mytest/warn.log')

describe('warn', () => {
fancy
.stderr()
.do(() => config.errlog = errlog)
.finally(() => config.errlog = undefined)
.it('warns', async ctx => {
warn('foo!')
expect(ctx.stderr).to.contain('Warning: foo!')
expect(process.exitCode).to.be.undefined
await config.errorLogger!.flush()
expect(fs.readFileSync(errlog, 'utf8')).to.contain('Warning: foo!')
})
.stderr()
.do(() => {
config.errlog = errlog
})
.finally(() => {
config.errlog = undefined
})
.it('warns', async ctx => {
warn('foo!')
expect(ctx.stderr).to.contain('Warning: foo!')
expect(process.exitCode).to.be.undefined
await config.errorLogger!.flush()
expect(fs.readFileSync(errlog, 'utf8')).to.contain('Warning: foo!')
})
})
3 changes: 0 additions & 3 deletions tslint.json

This file was deleted.

Loading