diff --git a/src/errors/pretty-print.ts b/src/errors/pretty-print.ts new file mode 100644 index 0000000..3b20536 --- /dev/null +++ b/src/errors/pretty-print.ts @@ -0,0 +1,58 @@ +import clean = require('clean-stack') + +export interface PrettyPrintableError { + /** + * messsage to display related to the error + */ + message?: string; + + /** + * a unique error code for this instance of the error + */ + code?: string; + + /** + * a url to find out more information related to this error + * or fixing the error + */ + ref?: string; + + /** + * a suggestion that may be useful or provide additional context + */ + suggestion?: string; + + /** + * a string representation of a stack trace for the error + */ + stack?: string; +} + +function formatStack(error: PrettyPrintableError) { + let stack = error.stack ?? '' + stack = clean(error.stack || '', {pretty: true}) + + // remove message from top of stack if one exists + // this will be displayed elsewhere in the pretty printed + // error + if (error.message) { + const split = error.stack?.split('\n') ?? [] + split.shift() + stack = split.join('\n') + } + + return stack +} + +export default function prettyPrint(error: PrettyPrintableError) { + const {message, code, suggestion, ref, stack} = error + + const formattedCode = code ? `Code: ${code}` : undefined + const formattedSuggestion = suggestion ? `Suggestion: ${suggestion}` : undefined + const formattedReference = ref ? `Reference: ${ref}` : undefined + const formattedStack = stack ? formatStack(error) : undefined + + return [message, formattedCode, formattedSuggestion, formattedReference, formattedStack] + .filter(Boolean) + .join('\n') +} diff --git a/test/pretty-print.test.ts b/test/pretty-print.test.ts new file mode 100644 index 0000000..6d27890 --- /dev/null +++ b/test/pretty-print.test.ts @@ -0,0 +1,31 @@ +import {expect, fancy} from 'fancy-test' +import prettyPrint, {PrettyPrintableError} from '../src/errors/pretty-print' +const stripAnsi = require('strip-ansi') + +describe('pretty-print', () => { + fancy + .it('pretty prints an error', async () => { + const sampleError: Error & PrettyPrintableError = new Error('Something very serious has gone wrong with the flags!') + sampleError.ref = 'https://oclif.io/docs/flags' + sampleError.code = 'OCLIF_BAD_FLAG' + sampleError.suggestion = 'Try using using a good flag' + + expect( + stripAnsi(prettyPrint(sampleError)), + ).to.contain(`Something very serious has gone wrong with the flags! +Code: OCLIF_BAD_FLAG +Suggestion: Try using using a good flag +Reference: https://oclif.io/docs/flags + at`) + }) + + fancy + .it('handles omitted fields', async () => { + const sampleError = new Error('Something very serious has gone wrong with the flags!') + + expect( + stripAnsi(prettyPrint(sampleError)), + ).to.contain(`Something very serious has gone wrong with the flags! + at`) + }) +})