From aac8286e46eec79f318b12f623bee401fafcd73d Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Wed, 6 Nov 2024 10:25:54 +0530 Subject: [PATCH] feat: add raw property to the parsed error --- package.json | 1 + src/parser.ts | 12 +++++++++--- src/types.ts | 5 +++++ tests/parser.spec.ts | 46 ++++++++++++++++++++++++++------------------ 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 3d31b61..e91e8f0 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ }, "prettier": "@adonisjs/prettier-config", "dependencies": { + "@poppinss/exception": "^1.1.0", "error-stack-parser-es": "^0.1.5" } } diff --git a/src/parser.ts b/src/parser.ts index 3a2ada7..0ed283b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -7,6 +7,8 @@ * file that was distributed with this source code. */ +import { inspect } from 'node:util' +import { Exception } from '@poppinss/exception' import { parse, StackFrame as ESFrame } from 'error-stack-parser-es' import debug from './debug.js' @@ -64,7 +66,10 @@ export class ErrorParser { return source as Error } - return new Error(JSON.stringify(source), { cause: source }) + const error = new Exception(JSON.stringify(source)) + error.help = + 'To get as much information as possible from your errors, make sure to throw Error objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error for more information.' + return error } /** @@ -290,10 +295,11 @@ export class ErrorParser { name: error.name, metadata: [], frames: await this.#enhanceFrames(esFrames), - hint: 'hint' in error ? (error.hint as string) : undefined, - code: 'code' in error ? (error.code as string) : undefined, + hint: 'hint' in error ? String(error.hint) : 'help' in error ? String(error.help) : undefined, + code: 'code' in error ? String(error.code) : undefined, cause: error.cause, stack: error.stack, + raw: inspect(error), } satisfies ParsedError for (let transformer of this.#transformers) { diff --git a/src/types.ts b/src/types.ts index 6a1d675..504ccde 100644 --- a/src/types.ts +++ b/src/types.ts @@ -26,6 +26,11 @@ export interface ParsedError { name: string frames: StackFrame[] metadata: MetadataGroup[] + /** + * Referenced to the raw error formatted using util.inspect + * method. + */ + raw: string cause?: unknown hint?: string code?: string diff --git a/tests/parser.spec.ts b/tests/parser.spec.ts index d57338c..d15346d 100644 --- a/tests/parser.spec.ts +++ b/tests/parser.spec.ts @@ -16,18 +16,20 @@ import { fileURLToPath } from 'node:url' import { readFile } from 'node:fs/promises' import { httpServer } from './helpers.js' import { ErrorParser } from '../src/parser.js' +import { inspect } from 'node:util' test.group('Error parser', () => { test('should parse error', async ({ assert }) => { const error = new Error('Something went wrong') - const { frames } = await new ErrorParser().parse(error) + const { frames, raw } = await new ErrorParser().parse(error) + assert.equal(raw, inspect(error)) assert.equal(frames[0].fileName, fileURLToPath(import.meta.url)) - assert.equal(frames[0].lineNumber, 22) + assert.equal(frames[0].lineNumber, 23) assert.equal(frames[0].type, 'app') assert.equal(frames[0].fileType, 'fs') assert.equal( - frames[0].source!.find(({ lineNumber }) => lineNumber === 22)?.chunk, + frames[0].source!.find(({ lineNumber }) => lineNumber === 23)?.chunk, ` const error = new Error('Something went wrong')` ) }) @@ -39,7 +41,7 @@ test.group('Error parser', () => { const { frames } = await new ErrorParser().parse(error) assert.equal(frames[0].fileName, 'invalid-path') - assert.equal(frames[0].lineNumber, 36) + assert.equal(frames[0].lineNumber, 38) assert.equal(frames[0].type, 'app') assert.equal(frames[0].fileType, 'fs') assert.isUndefined(frames[0].source) @@ -54,7 +56,7 @@ test.group('Error parser', () => { const { frames } = await new ErrorParser().parse(error) assert.equal(frames[0].fileName, join(fileURLToPath(import.meta.url), 'dist', 'webpack:')) - assert.equal(frames[0].lineNumber, 49) + assert.equal(frames[0].lineNumber, 51) assert.equal(frames[0].type, 'app') assert.equal(frames[0].fileType, 'fs') assert.isUndefined(frames[0].source) @@ -158,11 +160,11 @@ test.group('Error parser', () => { } catch (error) { const { frames } = await new ErrorParser().parse(error) assert.equal(frames[2].fileName, fileURLToPath(import.meta.url)) - assert.equal(frames[2].lineNumber, 157) + assert.equal(frames[2].lineNumber, 159) assert.equal(frames[2].type, 'app') assert.equal(frames[2].fileType, 'fs') assert.equal( - frames[2].source!.find(({ lineNumber }) => lineNumber === 157)?.chunk, + frames[2].source!.find(({ lineNumber }) => lineNumber === 159)?.chunk, ` await unidici.fetch('http://locahost:8100')` ) @@ -178,12 +180,12 @@ test.group('Error parser', () => { } catch (error) { const { frames } = await new ErrorParser({ offset: 2 }).parse(error) assert.equal(frames[0].fileName, fileURLToPath(import.meta.url)) - assert.equal(frames[0].lineNumber, 177) + assert.equal(frames[0].lineNumber, 179) assert.equal(frames[0].type, 'app') assert.equal(frames[0].fileType, 'fs') assert.equal(frames[1].type, 'module') assert.equal( - frames[0].source!.find(({ lineNumber }) => lineNumber === 177)?.chunk, + frames[0].source!.find(({ lineNumber }) => lineNumber === 179)?.chunk, ` await unidici.fetch('http://locahost:8100')` ) } @@ -204,11 +206,11 @@ test.group('Error parser', () => { console.log(error) const { frames } = await new ErrorParser().parse(error) assert.equal(frames[2].fileName, fileURLToPath(import.meta.url)) - assert.equal(frames[2].lineNumber, 202) + assert.equal(frames[2].lineNumber, 204) assert.equal(frames[2].type, 'app') assert.equal(frames[2].fileType, 'fs') assert.equal( - frames[2].source!.find(({ lineNumber }) => lineNumber === 202)?.chunk, + frames[2].source!.find(({ lineNumber }) => lineNumber === 204)?.chunk, ` await unidici.fetch('http://localhost:3000')` ) @@ -232,11 +234,11 @@ test.group('Error parser', () => { } catch (error) { const { frames } = await new ErrorParser().parse(error) assert.equal(frames[2].fileName, fileURLToPath(import.meta.url)) - assert.equal(frames[2].lineNumber, 231) + assert.equal(frames[2].lineNumber, 233) assert.equal(frames[2].type, 'app') assert.equal(frames[2].fileType, 'fs') assert.equal( - frames[2].source!.find(({ lineNumber }) => lineNumber === 231)?.chunk, + frames[2].source!.find(({ lineNumber }) => lineNumber === 233)?.chunk, ` await unidici.fetch('http://localhost:3000')` ) @@ -249,14 +251,20 @@ test.group('Error parser', () => { test('normalize boolean thrown as an error', async ({ assert }) => { const error = await new ErrorParser().parse(true) assert.equal(error.message, 'true') - assert.equal(error.cause, true) + assert.equal( + error.hint, + 'To get as much information as possible from your errors, make sure to throw Error objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error for more information.' + ) }) test('normalize promise thrown as an error', async ({ assert }) => { const p = new Promise(() => {}) const error = await new ErrorParser().parse(p) assert.equal(error.message, '{}') - assert.equal(error.cause, p) + assert.equal( + error.hint, + 'To get as much information as possible from your errors, make sure to throw Error objects. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error for more information.' + ) }) test('define custom parser', async ({ assert }) => { @@ -269,11 +277,11 @@ test.group('Error parser', () => { const error = await parser.parse(p) assert.equal(error.message, 'Promise cannot be thrown') assert.equal(error.frames[0].fileName, fileURLToPath(import.meta.url)) - assert.equal(error.frames[0].lineNumber, 266) + assert.equal(error.frames[0].lineNumber, 274) assert.equal(error.frames[0].type, 'app') assert.equal(error.frames[0].fileType, 'fs') assert.equal( - error.frames[0].source!.find(({ lineNumber }) => lineNumber === 266)?.chunk, + error.frames[0].source!.find(({ lineNumber }) => lineNumber === 274)?.chunk, ` return value instanceof Promise ? new Error('Promise cannot be thrown') : value` ) }) @@ -292,11 +300,11 @@ test.group('Error parser', () => { const parsedError = await parser.parse(error) assert.equal(error.message, 'Something went wrong') assert.equal(parsedError.frames[0].fileName, fileURLToPath(import.meta.url)) - assert.equal(parsedError.frames[0].lineNumber, 282) + assert.equal(parsedError.frames[0].lineNumber, 290) assert.equal(parsedError.frames[0].type, 'app') assert.equal(parsedError.frames[0].fileType, 'fs') assert.equal( - parsedError.frames[0].source!.find(({ lineNumber }) => lineNumber === 282)?.chunk, + parsedError.frames[0].source!.find(({ lineNumber }) => lineNumber === 290)?.chunk, ` const error = new Error('Something went wrong')` ) })