Skip to content

Commit 833ebcd

Browse files
committed
Better error stack traces
1 parent cd44c9b commit 833ebcd

File tree

4 files changed

+57
-11
lines changed

4 files changed

+57
-11
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"devDependencies": {
4040
"chai": "4.2.0",
4141
"conditional-type-checks": "1.0.5",
42+
"error-stack-parser": "^2.1.4",
4243
"esbuild": "^0.17.15",
4344
"husky": "4.2.5",
4445
"karma": "^6.4.1",

src/task.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,36 +69,42 @@ export class SynclinkTask<T> {
6969
});
7070
}
7171

72-
schedule_async(): this {
72+
_schedule_async(): Promise<T> {
7373
if (this.mode === "async") {
7474
// already scheduled
75-
return this;
75+
return this._promise;
7676
}
7777
if (this.mode === "sync") {
7878
throw new Error("Already synchronously scheduled");
7979
}
8080
this.mode = "async";
81-
this.do_async().then(
81+
const p = this.do_async().then(
8282
(value) => {
8383
// console.log("resolving", this.taskId, "value", value);
8484
this._resolved = true;
8585
this._result = value;
8686
this._resolve(value);
87+
return value;
8788
},
8889
(reason) => {
8990
this._exception = reason;
9091
this._reject(reason);
92+
throw reason;
9193
},
9294
);
95+
return p;
96+
}
97+
98+
schedule_async(): this {
99+
this._schedule_async();
93100
return this;
94101
}
95102

96-
async then<S>(
103+
then<S>(
97104
onfulfilled: (value: T) => S,
98105
onrejected: (reason: any) => S,
99106
): Promise<S> {
100-
this.schedule_async();
101-
return this._promise.then(onfulfilled, onrejected);
107+
return this._schedule_async().then(onfulfilled, onrejected);
102108
}
103109

104110
catch<S>(onrejected: (reason: any) => S): Promise<S> {

src/transfer_handlers.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { generateUUID } from "./request_response";
33
import { createProxy, expose, wrap } from "./async_task";
44
import { FakeMessageChannel } from "./fake_message_channel";
55
import { ProxyMarked, proxyMarker } from "./types";
6+
import * as ErrorStackParser from "error-stack-parser";
67

78
export const throwMarker = Symbol("Synclink.thrown");
89

@@ -103,6 +104,47 @@ type SerializedThrownValue =
103104
| { isError: true; value: Error }
104105
| { isError: false; value: unknown };
105106

107+
const CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
108+
109+
function renderChromeStack(s: ErrorStackParser.StackFrame): string {
110+
const locationInfo = [s.fileName, s.lineNumber, s.columnNumber].join(":");
111+
if (s.functionName) {
112+
return ` at ${s.functionName} (${locationInfo})`;
113+
} else {
114+
return ` at ${locationInfo}`;
115+
}
116+
}
117+
118+
function renderFirefoxStack(s: ErrorStackParser.StackFrame): string {
119+
const locationInfo = [s.fileName, s.lineNumber, s.columnNumber].join(":");
120+
return `${s.functionName || ""}@${locationInfo}`;
121+
}
122+
123+
function fixStackTrace(origError: Error): Error {
124+
const e = new Error(origError.message);
125+
e.name = origError.name;
126+
const isChromeStack = origError.stack?.match(CHROME_IE_STACK_REGEXP);
127+
const stackList = ErrorStackParser.parse(origError)
128+
.concat(ErrorStackParser.parse(e))
129+
.filter((s) => {
130+
const fileName = s.getFileName();
131+
const funcName = s.getFunctionName();
132+
return (
133+
!fileName ||
134+
!/synclink\.m?js$/.test(fileName) ||
135+
funcName?.startsWith("SynclinkTask.") ||
136+
funcName?.startsWith("then")
137+
);
138+
})
139+
.map(isChromeStack ? renderChromeStack : renderFirefoxStack);
140+
if (isChromeStack) {
141+
stackList.unshift(e.stack!.split("\n", 2)[0]);
142+
}
143+
const stack = stackList.join("\n");
144+
e.stack = stack;
145+
return e;
146+
}
147+
106148
/**
107149
* Internal transfer handler to handle thrown exceptions.
108150
*/
@@ -130,10 +172,7 @@ export const throwTransferHandler: TransferHandler<
130172
},
131173
deserialize(serialized) {
132174
if (serialized.isError) {
133-
throw Object.assign(
134-
new Error(serialized.value.message),
135-
serialized.value,
136-
);
175+
throw fixStackTrace(serialized.value);
137176
}
138177
throw serialized.value;
139178
},

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"compilerOptions": {
33
/* Basic Options */
4-
"target": "es2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
4+
"target": "ES2017" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */,
55
"module": "esnext" /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
66
"lib": [
77
"esnext",

0 commit comments

Comments
 (0)