diff --git a/package-lock.json b/package-lock.json index 8b0b511..7283371 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "get-stack-trace", "version": "0.0.0", + "dependencies": { + "stacktrace-parser": "^0.1.10" + }, "devDependencies": { "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/github": "^9.1.0", @@ -13915,6 +13918,25 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "engines": { + "node": ">=8" + } + }, "node_modules/std-env": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", diff --git a/package.json b/package.json index 210d1aa..546fdc7 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,9 @@ "name": "Gajus Kuizinas", "url": "https://gajus.com" }, + "dependencies": { + "stacktrace-parser": "^0.1.10" + }, "description": "V8 stack traces", "devDependencies": { "@semantic-release/commit-analyzer": "^11.0.0", diff --git a/src/getStackTrace.test.ts b/src/getStackTrace.test.ts index 9f87a94..cf5d272 100644 --- a/src/getStackTrace.test.ts +++ b/src/getStackTrace.test.ts @@ -4,5 +4,7 @@ import { expect, test } from 'vitest'; test('gets stack trace', () => { const stackTrace = getStackTrace(); - expect(stackTrace.callSites[0].fileName).toMatch(/getStackTrace/u); + expect(stackTrace[0].fileName).toMatch(/getStackTrace/u); + expect(stackTrace[0].lineNumber).toBe(5); + expect(stackTrace[0].columnNumber).toBe(22); }); diff --git a/src/getStackTrace.ts b/src/getStackTrace.ts index 2921e47..3c0426a 100644 --- a/src/getStackTrace.ts +++ b/src/getStackTrace.ts @@ -1,34 +1,29 @@ import { type StackTrace } from './types'; +import { parse as parseStackTrace } from 'stacktrace-parser'; export const getStackTrace = (): StackTrace => { - const oldStackTraceLimit = Error.stackTraceLimit; - const oldPrepareStackTrace = Error.prepareStackTrace; - - Error.prepareStackTrace = (error, structuredStackTrace) => { - return structuredStackTrace; - }; - - const honeypot: { stack: NodeJS.CallSite[] } = { - stack: [], - }; - - Error.captureStackTrace(honeypot); - - const callSites = honeypot.stack; - - Error.stackTraceLimit = oldStackTraceLimit; - Error.prepareStackTrace = oldPrepareStackTrace; - - const trail: readonly NodeJS.CallSite[] = callSites.slice(1); - - return { - callSites: trail.map((callSite) => { + // The reason we are parsing the stack trace rather than + // using captureStackTrace is because captureStackTrace + // does not resolve source maps, i.e. the stack trace + // will contain the compiled code references rather than + // the original source code references. + // + // eslint-disable-next-line unicorn/error-message + const stackTrace = new Error().stack; + + if (!stackTrace) { + throw new Error('Could not get stack trace'); + } + + return parseStackTrace(stackTrace) + .map((stackFrame) => { return { - columnNumber: callSite.getColumnNumber(), - fileName: callSite.getFileName() ?? null, - functionName: callSite.getFunctionName(), - lineNumber: callSite.getLineNumber(), + arguments: stackFrame.arguments, + columnNumber: stackFrame.column, + fileName: stackFrame.file, + functionName: stackFrame.methodName, + lineNumber: stackFrame.lineNumber, }; - }), - }; + }) + .slice(1); }; diff --git a/src/index.ts b/src/index.ts index 38238cd..7609435 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,3 @@ export { getStackTrace } from './getStackTrace'; export { serializeStackTrace } from './serializeStackTrace'; -export { CallSite, StackTrace } from './types'; +export { StackFrame, StackTrace } from './types'; diff --git a/src/serializeStackTrace.ts b/src/serializeStackTrace.ts index db56df2..be24aa1 100644 --- a/src/serializeStackTrace.ts +++ b/src/serializeStackTrace.ts @@ -10,7 +10,7 @@ export const serializeStackTrace = ( ': ' + errorMessage + '\n' + - stackTrace.callSites + stackTrace .map((stackFrame) => { const { columnNumber, fileName, functionName, lineNumber } = stackFrame; diff --git a/src/types.ts b/src/types.ts index 622fd77..9333225 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,10 +1,9 @@ -export type CallSite = { +export type StackFrame = { + arguments: readonly string[]; columnNumber: number | null; fileName: string | null; functionName: string | null; lineNumber: number | null; }; -export type StackTrace = { - callSites: CallSite[]; -}; +export type StackTrace = StackFrame[];