-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathstack.ts
122 lines (108 loc) · 3.19 KB
/
stack.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { ErrorLike } from "./types";
const newline = /\r?\n/;
const onoCall = /\bono[ @]/;
/**
* The Property Descriptor of a lazily-computed `stack` property.
*/
interface LazyStack {
configurable: true;
/**
* Lazily computes the error's stack trace.
*/
get(): string | undefined;
}
/**
* Is the property lazily computed?
*/
export function isLazyStack(stackProp: PropertyDescriptor | undefined): stackProp is LazyStack {
return Boolean(
stackProp &&
stackProp.configurable &&
typeof stackProp.get === "function"
);
}
/**
* Is the stack property writable?
*/
export function isWritableStack(stackProp: PropertyDescriptor | undefined): boolean {
return Boolean(
// If there is no stack property, then it's writable, since assigning it will create it
!stackProp ||
stackProp.writable ||
typeof stackProp.set === "function"
);
}
/**
* Appends the original `Error.stack` property to the new Error's stack.
*/
export function joinStacks(newError: ErrorLike, originalError?: ErrorLike): string | undefined {
let newStack = popStack(newError.stack);
let originalStack = originalError ? originalError.stack : undefined;
if (newStack && originalStack) {
return newStack + "\n\n" + originalStack;
}
else {
return newStack || originalStack;
}
}
/**
* Calls `joinStacks` lazily, when the `Error.stack` property is accessed.
*/
export function lazyJoinStacks(lazyStack: LazyStack, newError: ErrorLike, originalError?: ErrorLike): void {
if (originalError) {
Object.defineProperty(newError, "stack", {
get: () => {
let newStack = lazyStack.get.apply(newError);
return joinStacks({ stack: newStack }, originalError);
},
enumerable: false,
configurable: true
});
}
else {
lazyPopStack(newError, lazyStack);
}
}
/**
* Removes Ono from the stack, so that the stack starts at the original error location
*/
function popStack(stack: string | undefined): string | undefined {
if (stack) {
let lines = stack.split(newline);
// Find the Ono call(s) in the stack, and remove them
let onoStart;
for (let i = 0; i < lines.length; i++) {
let line = lines[i];
if (onoCall.test(line)) {
if (onoStart === undefined) {
// We found the first Ono call in the stack trace.
// There may be other subsequent Ono calls as well.
onoStart = i;
}
}
else if (onoStart !== undefined) {
// We found the first non-Ono call after one or more Ono calls.
// So remove the Ono call lines from the stack trace
lines.splice(onoStart, i - onoStart);
break;
}
}
if (lines.length > 0) {
return lines.join("\n");
}
}
// If we get here, then the stack doesn't contain a call to `ono`.
// This may be due to minification or some optimization of the JS engine.
// So just return the stack as-is.
return stack;
}
/**
* Calls `popStack` lazily, when the `Error.stack` property is accessed.
*/
function lazyPopStack(error: ErrorLike, lazyStack: LazyStack): void {
Object.defineProperty(error, "stack", {
get: () => popStack(lazyStack.get.apply(error)),
enumerable: false,
configurable: true
});
}