Skip to content

Instantly share code, notes, and snippets.

@cakoose
Last active January 12, 2021 01:10
Show Gist options
  • Save cakoose/5515a7ddc8b35c3b5f47ccd398c43fb8 to your computer and use it in GitHub Desktop.
Save cakoose/5515a7ddc8b35c3b5f47ccd398c43fb8 to your computer and use it in GitHub Desktop.
// Node used to lose track of stack frames across async gaps. But due to improvements
// in V8, Node 14.x will now preserve stack frames for `async` functions.
//
// Unfortunately, this doesn't work for raw promises and most libraries on NPM either
// use raw promises or are transpiled down to raw promises.
//
// To counter this, you can use this wrapper whenever you're calling into a library
// that returns a raw promise:
// - OLD: const result = await someLibrary.query(...);
// - NEW: const result = await stackTraceFixer.wrap(someLibrary.query(...));
export async function wrap<T>(promise: Promise<T>): Promise<T> {
try {
return await promise;
} catch (err) {
graft(1, err);
throw err;
}
}
// Append the current stack trace to the end of `err.stack`.
// - skipStackFrames: The number of top-most frames of the current stack trace to skip.
export function graft(skipStackFrames: number, err: Error): void {
const stack = new Error().stack;
if (stack === undefined) {
console.log('Warning: Stack trace is undefined.');
return;
}
// The first line is the exception message. The second is our stack frame. Remove both.
const skipLines = skipStackFrames + 2;
let pos = 0;
for (let i = 0; i < skipLines; i++) {
pos = stack.indexOf('\n', pos + 1);
if (pos < 0) {
console.log(`Warning: Tried to trim ${skipLines} lines; only found ${i}.`);
return;
}
}
err.stack = err.stack + stack.substring(pos + 1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment