Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit ae3543e

Browse files
committed
feat(error): add a helper method to get non zone aware stack trace
1 parent f3547cc commit ae3543e

10 files changed

+422
-6
lines changed

Diff for: dist/zone-helper.js

+159
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
(function (global, factory) {
9+
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
10+
typeof define === 'function' && define.amd ? define(factory) :
11+
(factory());
12+
}(this, (function () { 'use strict';
13+
14+
/**
15+
* @license
16+
* Copyright Google Inc. All Rights Reserved.
17+
*
18+
* Use of this source code is governed by an MIT-style license that can be
19+
* found in the LICENSE file at https://angular.io/license
20+
*/
21+
((function (_global) {
22+
var ErrorType = _global['Error'];
23+
var blackListedStackFrames = {};
24+
var zoneAwareFunctionNames = ['Zone', 'drainMicrotask', 'getStacktraceWithUncaughtError', 'new LongStackTrace', 'Object.onScheduleTask'];
25+
var detectZone = Zone.root.fork(Zone['longStackTraceZoneSpec']).fork({
26+
name: 'detectZone',
27+
onHandleError: function (parentDelegate, currentZone, targetZone, error) {
28+
parentDelegate.handleError(targetZone, error);
29+
var frames = error.stack.split(/\n/);
30+
var runFrame = false, runGuardedFrame = false, runTaskFrame = false;
31+
var _loop_1 = function () {
32+
var frame = frames.shift();
33+
// On safari it is possible to have stack frame with no line number.
34+
// This check makes sure that we don't filter frames on name only (must have
35+
// linenumber)
36+
if (/:\d+:\d+/.test(frame)) {
37+
var f_1 = frame.split(' [')[0];
38+
if (zoneAwareFunctionNames.filter(function (zf) { return f_1.toLowerCase().indexOf(f_1.toLowerCase()) !== -1; }).length > 0) {
39+
blackListedStackFrames[f_1] = f_1;
40+
}
41+
}
42+
};
43+
while (frames.length) {
44+
_loop_1();
45+
}
46+
return false;
47+
}
48+
});
49+
var detectZoneWithCallbacks = Zone.root.fork(Zone['longStackTraceZoneSpec']).fork({
50+
name: 'detectZone',
51+
onFork: function (parentDelegate, currentZone, targetZone, zoneSpec) {
52+
return parentDelegate.fork(targetZone, zoneSpec);
53+
},
54+
onIntercept: function (parentDelegate, currentZone, targetZone, delegate, source) {
55+
return parentDelegate.intercept(targetZone, delegate, source);
56+
},
57+
onInvoke: function (parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) {
58+
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
59+
},
60+
onScheduleTask: function (parentZoneDelegate, currentZone, targetZone, task) {
61+
return parentZoneDelegate.scheduleTask(targetZone, task);
62+
},
63+
onInvokeTask: function (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) {
64+
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
65+
},
66+
onCancelTask: function (parentZoneDelegate, currentZone, targetZone, task) {
67+
return parentZoneDelegate.cancelTask(targetZone, task);
68+
},
69+
onHasTask: function (delegate, current, target, hasTaskState) {
70+
return delegate.hasTask(target, hasTaskState);
71+
},
72+
onHandleError: function (parentDelegate, currentZone, targetZone, error) {
73+
parentDelegate.handleError(targetZone, error);
74+
var frames = error.stack.split(/\n/);
75+
var runFrame = false, runGuardedFrame = false, runTaskFrame = false;
76+
while (frames.length) {
77+
var frame = frames.shift();
78+
// On safari it is possible to have stack frame with no line number.
79+
// This check makes sure that we don't filter frames on name only (must have
80+
// linenumber)
81+
if (/:\d+:\d+/.test(frame)) {
82+
var f = frame.split(' [')[0];
83+
if (f.indexOf('Zone') !== -1 || f.toLowerCase().indexOf('zoneaware') !== -1 || f.indexOf('drainMicroTaskQueue') !== -1) {
84+
blackListedStackFrames[f] = f;
85+
}
86+
}
87+
}
88+
return false;
89+
}
90+
});
91+
var detectFn = function () {
92+
throw new Error('zoneAwareFrames');
93+
};
94+
var detectPromiseFn = function () {
95+
var p = new Promise(function (resolve, reject) {
96+
reject(new Error('zoneAwareFrames'));
97+
});
98+
};
99+
var detectPromiseCaughtFn = function () {
100+
var p = new Promise(function (resolve, reject) {
101+
reject(new Error('zoneAwareFrames'));
102+
});
103+
p.catch(function (err) {
104+
throw err;
105+
});
106+
};
107+
// Cause the error to extract the stack frames.
108+
detectZone.runTask(detectZone.scheduleEventTask('detect', detectFn, null, function () { return null; }, null));
109+
detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleEventTask('detect', detectFn, null, function () { return null; }, null));
110+
detectZone.runTask(detectZone.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null));
111+
detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMacroTask('detect', detectFn, null, function () { return null; }, null));
112+
detectZone.runTask(detectZone.scheduleMicroTask('detect', detectFn, null, function () { return null; }));
113+
detectZoneWithCallbacks.runTask(detectZoneWithCallbacks.scheduleMicroTask('detect', detectFn, null, function () { return null; }));
114+
detectZone.runGuarded(function () {
115+
detectZone.run(detectFn);
116+
});
117+
detectZoneWithCallbacks.runGuarded(function () {
118+
detectZone.run(detectFn);
119+
});
120+
detectZone.runGuarded(detectPromiseFn);
121+
detectZoneWithCallbacks.runGuarded(detectPromiseFn);
122+
detectZone.runGuarded(detectPromiseCaughtFn);
123+
detectZoneWithCallbacks.runGuarded(detectPromiseCaughtFn);
124+
var otherZoneAwareFunctionNames = ['ZoneTask.invoke', 'ZoneAware'];
125+
Object.defineProperty(ErrorType, 'getNonZoneAwareStack', {
126+
value: function (err) {
127+
if (err.stack) {
128+
var frames_1 = err.stack.split('\n');
129+
var simplifiedFrames = [];
130+
var _loop_2 = function (i) {
131+
var frame = frames_1[i].split(' [')[0];
132+
var frameWithoutZone = frame.split(' [')[0];
133+
if (blackListedStackFrames.hasOwnProperty(frameWithoutZone) &&
134+
blackListedStackFrames[frameWithoutZone]) {
135+
frames_1.splice(i, 1);
136+
i--;
137+
}
138+
else if (otherZoneAwareFunctionNames.filter(function (f) { return frame.toLowerCase().indexOf(f.toLowerCase()) !== -1; }).length > 0) {
139+
frames_1.splice(i, 1);
140+
i--;
141+
}
142+
else {
143+
simplifiedFrames.push(frame);
144+
}
145+
out_i_1 = i;
146+
};
147+
var out_i_1;
148+
for (var i = 0; i < frames_1.length; i++) {
149+
_loop_2(i);
150+
i = out_i_1;
151+
}
152+
return simplifiedFrames.join('\n');
153+
}
154+
return err.stack;
155+
}
156+
});
157+
})(typeof window === 'object' && window || typeof self === 'object' && self || global));
158+
159+
})));

Diff for: gulpfile.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ gulp.task('build/sync-test.js', ['compile-esm'], function(cb) {
159159
return generateScript('./lib/zone-spec/sync-test.ts', 'sync-test.js', false, cb);
160160
});
161161

162+
gulp.task('build/zone-helper.js', ['compile-esm'], function(cb) {
163+
return generateScript('./lib/helper/zone-helper.ts', 'zone-helper.js', false, cb);
164+
});
165+
162166
gulp.task('build', [
163167
'build/zone.js',
164168
'build/zone.js.d.ts',
@@ -181,7 +185,8 @@ gulp.task('build', [
181185
'build/wtf.min.js',
182186
'build/async-test.js',
183187
'build/fake-async-test.js',
184-
'build/sync-test.js'
188+
'build/sync-test.js',
189+
'build/zone-helper.js'
185190
]);
186191

187192
gulp.task('test/node', ['compile'], function(cb) {

Diff for: karma-dist.conf.js

+1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ module.exports = function (config) {
1717
config.files.push('dist/sync-test.js');
1818
config.files.push('dist/task-tracking.js');
1919
config.files.push('dist/wtf.js');
20+
config.files.push('dist/zone-helper.js');
2021
config.files.push('build/test/main.js');
2122
};

Diff for: lib/helper/zone-helper.ts

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
((function(_global: any) {
9+
const ErrorType = _global['Error'];
10+
const blackListedStackFrames = {};
11+
const zoneAwareFunctionNames = [
12+
'Zone', 'drainMicrotask', 'getStacktraceWithUncaughtError', 'new LongStackTrace',
13+
'Object.onScheduleTask'
14+
];
15+
16+
function handleError(error: Error) {
17+
let frames = error.stack ? error.stack.split(/\n/) : [];
18+
while (frames.length) {
19+
let frame = frames.shift();
20+
// On safari it is possible to have stack frame with no line number.
21+
// This check makes sure that we don't filter frames on name only (must have
22+
// linenumber)
23+
if (/:\d+:\d+/.test(frame)) {
24+
const f = frame.split(' [')[0];
25+
if (zoneAwareFunctionNames.filter(zf => f.toLowerCase().indexOf(f.toLowerCase()) !== -1)
26+
.length > 0) {
27+
blackListedStackFrames[f] = f;
28+
}
29+
}
30+
}
31+
}
32+
33+
const detectZone = Zone.root.fork(Zone['longStackTraceZoneSpec']).fork({
34+
name: 'detectZone',
35+
onHandleError(parentDelegate, currentZone, targetZone, error) {
36+
parentDelegate.handleError(targetZone, error);
37+
handleError(error);
38+
return false;
39+
}
40+
});
41+
42+
const detectZoneWithCallbacks = Zone.root.fork(Zone['longStackTraceZoneSpec']).fork({
43+
name: 'detectZone',
44+
onFork: (parentDelegate, currentZone, targetZone, zoneSpec) => {
45+
return parentDelegate.fork(targetZone, zoneSpec);
46+
},
47+
onIntercept: (parentDelegate, currentZone, targetZone, delegate, source) => {
48+
return parentDelegate.intercept(targetZone, delegate, source);
49+
},
50+
onInvoke:
51+
(parentZoneDelegate, currentZone, targetZone, delegate, applyThis, applyArgs, source) => {
52+
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
53+
},
54+
onScheduleTask: (parentZoneDelegate, currentZone, targetZone, task) => {
55+
return parentZoneDelegate.scheduleTask(targetZone, task);
56+
},
57+
onInvokeTask: (parentZoneDelegate, currentZone, targetZone, task, applyThis, applyArgs) => {
58+
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
59+
},
60+
onCancelTask: (parentZoneDelegate, currentZone, targetZone, task) => {
61+
return parentZoneDelegate.cancelTask(targetZone, task);
62+
},
63+
64+
onHasTask: (delegate, current, target, hasTaskState) => {
65+
return delegate.hasTask(target, hasTaskState);
66+
},
67+
68+
onHandleError(parentDelegate, currentZone, targetZone, error) {
69+
parentDelegate.handleError(targetZone, error);
70+
handleError(error);
71+
return false;
72+
}
73+
});
74+
75+
let detectFn = () => {
76+
throw new Error('zoneAwareFrames');
77+
};
78+
79+
let detectPromiseFn = () => {
80+
const p = new Promise((resolve, reject) => {
81+
reject(new Error('zoneAwareFrames'));
82+
});
83+
};
84+
85+
let detectPromiseCaughtFn = () => {
86+
const p = new Promise((resolve, reject) => {
87+
reject(new Error('zoneAwareFrames'));
88+
});
89+
p.catch(err => {
90+
throw err;
91+
});
92+
};
93+
94+
// Cause the error to extract the stack frames.
95+
detectZone.runTask(detectZone.scheduleEventTask('detect', detectFn, null, () => null, null));
96+
detectZoneWithCallbacks.runTask(
97+
detectZoneWithCallbacks.scheduleEventTask('detect', detectFn, null, () => null, null));
98+
detectZone.runTask(detectZone.scheduleMacroTask('detect', detectFn, null, () => null, null));
99+
detectZoneWithCallbacks.runTask(
100+
detectZoneWithCallbacks.scheduleMacroTask('detect', detectFn, null, () => null, null));
101+
detectZone.runTask(detectZone.scheduleMicroTask('detect', detectFn, null, () => null));
102+
detectZoneWithCallbacks.runTask(
103+
detectZoneWithCallbacks.scheduleMicroTask('detect', detectFn, null, () => null));
104+
105+
detectZone.runGuarded(() => {
106+
detectZone.run(detectFn);
107+
});
108+
detectZoneWithCallbacks.runGuarded(() => {
109+
detectZone.run(detectFn);
110+
});
111+
112+
detectZone.runGuarded(detectPromiseFn);
113+
detectZoneWithCallbacks.runGuarded(detectPromiseFn);
114+
115+
detectZone.runGuarded(detectPromiseCaughtFn);
116+
detectZoneWithCallbacks.runGuarded(detectPromiseCaughtFn);
117+
118+
const otherZoneAwareFunctionNames = ['ZoneTask.invoke', 'ZoneAware'];
119+
120+
Object.defineProperty(ErrorType, 'getNonZoneAwareStack', {
121+
value: function(err: Error) {
122+
if (err.stack) {
123+
let frames = err.stack.split('\n');
124+
const simplifiedFrames: string[] = [];
125+
for (let i = 0; i < frames.length; i++) {
126+
const frame = frames[i].split(' [')[0];
127+
const frameWithoutZone = frame.split(' [')[0];
128+
if (blackListedStackFrames.hasOwnProperty(frameWithoutZone) &&
129+
blackListedStackFrames[frameWithoutZone]) {
130+
frames.splice(i, 1);
131+
i--;
132+
} else if (
133+
otherZoneAwareFunctionNames
134+
.filter(f => frame.toLowerCase().indexOf(f.toLowerCase()) !== -1)
135+
.length > 0) {
136+
frames.splice(i, 1);
137+
i--;
138+
} else {
139+
simplifiedFrames.push(frame);
140+
}
141+
}
142+
return simplifiedFrames.join('\n');
143+
}
144+
return err.stack;
145+
}
146+
});
147+
})(typeof window === 'object' && window || typeof self === 'object' && self || global));

Diff for: lib/zone.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1668,8 +1668,8 @@ const Zone: ZoneType = (function(global: any) {
16681668
// This check makes sure that we don't filter frames on name only (must have
16691669
// linenumber)
16701670
if (/:\d+:\d+/.test(frame)) {
1671-
// Get rid of the path so that we don't accidintely find function name in path.
1672-
// In chrome the seperator is `(` and `@` in FF and safari
1671+
// Get rid of the path so that we don't accidentally find function name in path.
1672+
// In chrome the separator is `(` and `@` in FF and safari
16731673
// Chrome: at Zone.run (zone.js:100)
16741674
// Chrome: at Zone.run (http://localhost:9876/base/build/lib/zone.js:100:24)
16751675
// FireFox: Zone.prototype.run@http://localhost:9876/base/build/lib/zone.js:101:24

Diff for: test/browser-zone-setup.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ import '../lib/zone-spec/long-stack-trace';
1313
import '../lib/zone-spec/proxy';
1414
import '../lib/zone-spec/sync-test';
1515
import '../lib/zone-spec/task-tracking';
16-
import '../lib/zone-spec/wtf';
16+
import '../lib/zone-spec/wtf';
17+
import '../lib/helper/zone-helper';

Diff for: test/common_tests.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ import './zone-spec/sync-test.spec';
1919
import './zone-spec/fake-async-test.spec';
2020
import './zone-spec/proxy.spec';
2121
import './zone-spec/task-tracking.spec';
22+
import './helper/zone-helper.spec';
2223

2324
Error.stackTraceLimit = Number.POSITIVE_INFINITY;

0 commit comments

Comments
 (0)