diff options
-rw-r--r-- | cli/js/error_stack.ts | 74 | ||||
-rw-r--r-- | cli/js/lib.deno.shared_globals.d.ts | 8 | ||||
-rw-r--r-- | cli/js/web/console.ts | 1 | ||||
-rw-r--r-- | cli/tests/unit/error_stack_test.ts | 14 |
4 files changed, 56 insertions, 41 deletions
diff --git a/cli/js/error_stack.ts b/cli/js/error_stack.ts index 8a3d0530b..daf983ba1 100644 --- a/cli/js/error_stack.ts +++ b/cli/js/error_stack.ts @@ -214,51 +214,47 @@ function evaluateCallSite(callSite: CallSite): CallSiteEval { }; } -function prepareStackTrace( - error: Error, - structuredStackTrace: CallSite[] -): string { +function prepareStackTrace(error: Error, callSites: CallSite[]): string { + const mappedCallSites = callSites.map( + (callSite): CallSite => { + const fileName = callSite.getFileName(); + const lineNumber = callSite.getLineNumber(); + const columnNumber = callSite.getColumnNumber(); + if (fileName && lineNumber != null && columnNumber != null) { + return patchCallSite( + callSite, + applySourceMap({ + fileName, + lineNumber, + columnNumber, + }) + ); + } + return callSite; + } + ); Object.defineProperties(error, { - __callSiteEvals: { value: [] }, - __formattedFrames: { value: [] }, + __callSiteEvals: { value: [], configurable: true }, + __formattedFrames: { value: [], configurable: true }, }); - const errorString = - `${error.name}: ${error.message}\n` + - structuredStackTrace - .map( - (callSite): CallSite => { - const fileName = callSite.getFileName(); - const lineNumber = callSite.getLineNumber(); - const columnNumber = callSite.getColumnNumber(); - if (fileName && lineNumber != null && columnNumber != null) { - return patchCallSite( - callSite, - applySourceMap({ - fileName, - lineNumber, - columnNumber, - }) - ); - } - return callSite; - } - ) - .map((callSite): string => { - // @ts-expect-error - error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite))); - const isInternal = - callSite.getFileName()?.startsWith("$deno$") ?? false; - const string = callSiteToString(callSite, isInternal); - // @ts-expect-error - error.__formattedFrames.push(string); - return ` at ${colors.stripColor(string)}`; - }) - .join("\n"); + for (const callSite of mappedCallSites) { + // @ts-expect-error + error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite))); + const isInternal = callSite.getFileName()?.startsWith("$deno$") ?? false; + // @ts-expect-error + error.__formattedFrames.push(callSiteToString(callSite, isInternal)); + } // @ts-expect-error Object.freeze(error.__callSiteEvals); // @ts-expect-error Object.freeze(error.__formattedFrames); - return errorString; + return ( + `${error.name}: ${error.message}\n` + + // @ts-expect-error + error.__formattedFrames + .map((s: string) => ` at ${colors.stripColor(s)}`) + .join("\n") + ); } // @internal diff --git a/cli/js/lib.deno.shared_globals.d.ts b/cli/js/lib.deno.shared_globals.d.ts index f3a565668..2ad39545b 100644 --- a/cli/js/lib.deno.shared_globals.d.ts +++ b/cli/js/lib.deno.shared_globals.d.ts @@ -1481,3 +1481,11 @@ declare const AbortSignal: { prototype: AbortSignal; new (): AbortSignal; }; + +interface ErrorConstructor { + /** See https://v8.dev/docs/stack-trace-api#stack-trace-collection-for-custom-exceptions. */ + // eslint-disable-next-line @typescript-eslint/ban-types + captureStackTrace(error: Object, constructor?: Function): void; + // TODO(nayeemrmn): Support `Error.prepareStackTrace()`. We currently use this + // internally in a way that makes it unavailable for users. +} diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts index 0e17d2efb..4b5423631 100644 --- a/cli/js/web/console.ts +++ b/cli/js/web/console.ts @@ -948,7 +948,6 @@ export class Console { name: "Trace", message, }; - // @ts-expect-error Error.captureStackTrace(err, this.trace); this.error((err as Error).stack); }; diff --git a/cli/tests/unit/error_stack_test.ts b/cli/tests/unit/error_stack_test.ts index eb0a5c0e6..052cb9591 100644 --- a/cli/tests/unit/error_stack_test.ts +++ b/cli/tests/unit/error_stack_test.ts @@ -1,5 +1,5 @@ // Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. -import { unitTest, assert } from "./test_util.ts"; +import { assert, assertEquals, unitTest } from "./test_util.ts"; // @ts-expect-error TypeScript (as of 3.7) does not support indexing namespaces by symbol const { setPrepareStackTrace } = Deno[Deno.internal]; @@ -96,6 +96,18 @@ unitTest(function prepareStackTrace(): void { assert(result.includes(".ts:"), "should remap to something in 'js/'"); }); +unitTest(function captureStackTrace(): void { + function foo(): void { + const error = new Error(); + const stack1 = error.stack!; + Error.captureStackTrace(error, foo); + const stack2 = error.stack!; + // stack2 should be stack1 without the first frame. + assertEquals(stack2, stack1.replace(/(?<=^[^\n]*\n)[^\n]*\n/, "")); + } + foo(); +}); + unitTest(function applySourceMap(): void { const result = Deno.applySourceMap({ fileName: "CLI_SNAPSHOT.js", |