0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-03 09:31:22 -05:00

fix(cli/js/error_stack): Expose Error.captureStackTrace (#5254)

This commit is contained in:
Nayeem Rahman 2020-05-29 13:02:36 +01:00 committed by GitHub
parent ce246d8d85
commit 49c7077401
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 56 additions and 41 deletions

View file

@ -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

View file

@ -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.
}

View file

@ -948,7 +948,6 @@ export class Console {
name: "Trace",
message,
};
// @ts-expect-error
Error.captureStackTrace(err, this.trace);
this.error((err as Error).stack);
};

View file

@ -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",