Skip to content

Commit a8cb2aa

Browse files
author
Eric Wendelin
committed
Name functions uniquely so they can be matched easily by stacktrace.js
1 parent ac12835 commit a8cb2aa

File tree

5 files changed

+228
-10
lines changed

5 files changed

+228
-10
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ dist:
2222
./node_modules/.bin/uglifyjs2 node_modules/stackframe/stackframe.js error-stack-parser.js \
2323
-o error-stack-parser.min.js --source-map error-stack-parser.js.map
2424
mv error-stack-parser.min.js error-stack-parser.js.map dist/
25+
cp error-stack-parser.js dist/
2526

2627
.PHONY: clean test dist

dist/error-stack-parser.js

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/* global StackFrame: false */
2+
(function (root, factory) {
3+
'use strict';
4+
// Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.
5+
if (typeof define === 'function' && define.amd) {
6+
define(['stackframe'], factory);
7+
} else if (typeof exports === 'object') {
8+
module.exports = factory(require('stackframe'));
9+
} else {
10+
root.ErrorStackParser = factory(root.StackFrame);
11+
}
12+
}(this, function ErrorStackParser(StackFrame) {
13+
'use strict';
14+
15+
// ES5 Polyfills
16+
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
17+
if (!Function.prototype.bind) {
18+
Function.prototype.bind = function (oThis) {
19+
if (typeof this !== 'function') {
20+
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
21+
}
22+
23+
var aArgs = Array.prototype.slice.call(arguments, 1);
24+
var fToBind = this;
25+
var NoOp = function () {
26+
};
27+
var fBound = function () {
28+
return fToBind.apply(this instanceof NoOp && oThis ? this : oThis,
29+
aArgs.concat(Array.prototype.slice.call(arguments)));
30+
};
31+
32+
NoOp.prototype = this.prototype;
33+
fBound.prototype = new NoOp();
34+
35+
return fBound;
36+
};
37+
}
38+
39+
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
40+
if (!Array.prototype.map) {
41+
Array.prototype.map = function(callback, thisArg) {
42+
if (this === void 0 || this === null) {
43+
throw new TypeError("this is null or not defined");
44+
}
45+
var O = Object(this);
46+
var len = O.length >>> 0;
47+
var T;
48+
if (typeof callback !== "function") {
49+
throw new TypeError(callback + " is not a function");
50+
}
51+
if (arguments.length > 1) {
52+
T = thisArg;
53+
}
54+
55+
var A = new Array(len);
56+
var k = 0;
57+
58+
while (k < len) {
59+
var kValue, mappedValue;
60+
if (k in O) {
61+
kValue = O[k];
62+
mappedValue = callback.call(T, kValue, k, O);
63+
A[k] = mappedValue;
64+
}
65+
k++;
66+
}
67+
68+
return A;
69+
};
70+
}
71+
72+
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
73+
if (!Array.prototype.filter) {
74+
Array.prototype.filter = function(callback/*, thisArg*/) {
75+
if (this === void 0 || this === null) {
76+
throw new TypeError("this is null or not defined");
77+
}
78+
79+
var t = Object(this);
80+
var len = t.length >>> 0;
81+
if (typeof callback !== "function") {
82+
throw new TypeError(callback + " is not a function");
83+
}
84+
85+
var res = [];
86+
var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
87+
for (var i = 0; i < len; i++) {
88+
if (i in t) {
89+
var val = t[i];
90+
if (callback.call(thisArg, val, i, t)) {
91+
res.push(val);
92+
}
93+
}
94+
}
95+
96+
return res;
97+
};
98+
}
99+
100+
var FIREFOX_SAFARI_STACK_REGEXP = /\S+\:\d+/;
101+
var CHROME_IE_STACK_REGEXP = /\s+at /;
102+
103+
return {
104+
/**
105+
* Given an Error object, extract the most information from it.
106+
* @param error {Error}
107+
* @return Array[StackFrame]
108+
*/
109+
parse: function ErrorStackParser$$parse(error) {
110+
if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') {
111+
return this.parseOpera(error);
112+
} else if (error.stack.match(CHROME_IE_STACK_REGEXP)) {
113+
return this.parseV8OrIE(error);
114+
} else if (error.stack.match(FIREFOX_SAFARI_STACK_REGEXP)) {
115+
return this.parseFFOrSafari(error);
116+
} else {
117+
throw new Error('Cannot parse given Error object');
118+
}
119+
},
120+
121+
/**
122+
* Separate line and column numbers from a URL-like string.
123+
* @param urlLike String
124+
* @return Array[String]
125+
*/
126+
extractLocation: function ErrorStackParser$$extractLocation(urlLike) {
127+
var locationParts = urlLike.split(':');
128+
var lastNumber = locationParts.pop();
129+
var possibleNumber = locationParts[locationParts.length - 1];
130+
if (!isNaN(parseFloat(possibleNumber)) && isFinite(possibleNumber)) {
131+
var lineNumber = locationParts.pop();
132+
return [locationParts.join(':'), lineNumber, lastNumber];
133+
} else {
134+
return [locationParts.join(':'), lastNumber, undefined];
135+
}
136+
},
137+
138+
parseV8OrIE: function ErrorStackParser$$parseV8OrIE(error) {
139+
return error.stack.split('\n').slice(1).map(function (line) {
140+
var tokens = line.replace(/^\s+/, '').split(/\s+/).slice(1);
141+
var locationParts = this.extractLocation(tokens.pop().replace(/[\(\)\s]/g, ''));
142+
var functionName = (!tokens[0] || tokens[0] === 'Anonymous') ? undefined : tokens[0];
143+
return new StackFrame(functionName, undefined, locationParts[0], locationParts[1], locationParts[2]);
144+
}.bind(this));
145+
},
146+
147+
parseFFOrSafari: function ErrorStackParser$$parseFFOrSafari(error) {
148+
return error.stack.split('\n').filter(function (line) {
149+
return !!line.match(FIREFOX_SAFARI_STACK_REGEXP);
150+
}.bind(this)).map(function (line) {
151+
var tokens = line.split('@');
152+
var locationParts = this.extractLocation(tokens.pop());
153+
var functionName = tokens.shift() || undefined;
154+
return new StackFrame(functionName, undefined, locationParts[0], locationParts[1], locationParts[2]);
155+
}.bind(this));
156+
},
157+
158+
parseOpera: function ErrorStackParser$$parseOpera(e) {
159+
if (!e.stacktrace || (e.message.indexOf('\n') > -1 &&
160+
e.message.split('\n').length > e.stacktrace.split('\n').length)) {
161+
return this.parseOpera9(e);
162+
} else if (!e.stack) {
163+
return this.parseOpera10a(e);
164+
} else if (e.stacktrace.indexOf("called from line") < 0) {
165+
return this.parseOpera10b(e);
166+
} else {
167+
return this.parseOpera11(e);
168+
}
169+
},
170+
171+
parseOpera9: function ErrorStackParser$$parseOpera9(e) {
172+
var lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
173+
var lines = e.message.split('\n');
174+
var result = [];
175+
176+
for (var i = 2, len = lines.length; i < len; i += 2) {
177+
var match = lineRE.exec(lines[i]);
178+
if (match) {
179+
result.push(new StackFrame(undefined, undefined, match[2], match[1]));
180+
}
181+
}
182+
183+
return result;
184+
},
185+
186+
parseOpera10a: function ErrorStackParser$$parseOpera10a(e) {
187+
var lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
188+
var lines = e.stacktrace.split('\n');
189+
var result = [];
190+
191+
for (var i = 0, len = lines.length; i < len; i += 2) {
192+
var match = lineRE.exec(lines[i]);
193+
if (match) {
194+
result.push(new StackFrame(match[3] || undefined, undefined, match[2], match[1]));
195+
}
196+
}
197+
198+
return result;
199+
},
200+
201+
// Opera 10.65+ Error.stack very similar to FF/Safari
202+
parseOpera11: function ErrorStackParser$$parseOpera11(error) {
203+
return error.stack.split('\n').filter(function (line) {
204+
return !!line.match(FIREFOX_SAFARI_STACK_REGEXP);
205+
}.bind(this)).map(function (line) {
206+
var tokens = line.split('@');
207+
var location = tokens.pop().split(':');
208+
var functionCall = (tokens.shift() || '');
209+
var functionName = functionCall.replace(/<anonymous function: (\w+)>/, '$1').replace(/\([^\)]*\)/, '') || undefined;
210+
var argsRaw = functionCall.replace(/^[^\(]+\(([^\)]*)\)$/, '$1') || undefined;
211+
var args = (argsRaw === undefined || argsRaw === '[arguments not available]') ? undefined : argsRaw.split(',');
212+
return new StackFrame(functionName, args, location[0] + ':' + location[1], location[2], location[3]);
213+
});
214+
}
215+
};
216+
}));
217+

0 commit comments

Comments
 (0)