123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- (function(root, factory) {
- 'use strict';
- // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js, Rhino, and browsers.
- /* istanbul ignore next */
- if (typeof define === 'function' && define.amd) {
- define('error-stack-parser', ['stackframe'], factory);
- } else if (typeof exports === 'object') {
- module.exports = factory(require('stackframe'));
- } else {
- root.ErrorStackParser = factory(root.StackFrame);
- }
- }(this, function ErrorStackParser(StackFrame) {
- 'use strict';
- var FIREFOX_SAFARI_STACK_REGEXP = /(^|@)\S+:\d+/;
- var CHROME_IE_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
- var SAFARI_NATIVE_CODE_REGEXP = /^(eval@)?(\[native code])?$/;
- return {
- /**
- * Given an Error object, extract the most information from it.
- *
- * @param {Error} error object
- * @return {Array} of StackFrames
- */
- parse: function ErrorStackParser$$parse(error) {
- if (typeof error.stacktrace !== 'undefined' || typeof error['opera#sourceloc'] !== 'undefined') {
- return this.parseOpera(error);
- } else if (error.stack && error.stack.match(CHROME_IE_STACK_REGEXP)) {
- return this.parseV8OrIE(error);
- } else if (error.stack) {
- return this.parseFFOrSafari(error);
- } else {
- throw new Error('Cannot parse given Error object');
- }
- },
- // Separate line and column numbers from a string of the form: (URI:Line:Column)
- extractLocation: function ErrorStackParser$$extractLocation(urlLike) {
- // Fail-fast but return locations like "(native)"
- if (urlLike.indexOf(':') === -1) {
- return [urlLike];
- }
- var regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
- var parts = regExp.exec(urlLike.replace(/[()]/g, ''));
- return [parts[1], parts[2] || undefined, parts[3] || undefined];
- },
- parseV8OrIE: function ErrorStackParser$$parseV8OrIE(error) {
- var filtered = error.stack.split('\n').filter(function(line) {
- return !!line.match(CHROME_IE_STACK_REGEXP);
- }, this);
- return filtered.map(function(line) {
- if (line.indexOf('(eval ') > -1) {
- // Throw away eval information until we implement stacktrace.js/stackframe#8
- line = line.replace(/eval code/g, 'eval').replace(/(\(eval at [^()]*)|(,.*$)/g, '');
- }
- var sanitizedLine = line.replace(/^\s+/, '').replace(/\(eval code/g, '(').replace(/^.*?\s+/, '');
- // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in
- // case it has spaces in it, as the string is split on \s+ later on
- var location = sanitizedLine.match(/ (\(.+\)$)/);
- // remove the parenthesized location from the line, if it was matched
- sanitizedLine = location ? sanitizedLine.replace(location[0], '') : sanitizedLine;
- // if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine
- // because this line doesn't have function name
- var locationParts = this.extractLocation(location ? location[1] : sanitizedLine);
- var functionName = location && sanitizedLine || undefined;
- var fileName = ['eval', '<anonymous>'].indexOf(locationParts[0]) > -1 ? undefined : locationParts[0];
- return new StackFrame({
- functionName: functionName,
- fileName: fileName,
- lineNumber: locationParts[1],
- columnNumber: locationParts[2],
- source: line
- });
- }, this);
- },
- parseFFOrSafari: function ErrorStackParser$$parseFFOrSafari(error) {
- var filtered = error.stack.split('\n').filter(function(line) {
- return !line.match(SAFARI_NATIVE_CODE_REGEXP);
- }, this);
- return filtered.map(function(line) {
- // Throw away eval information until we implement stacktrace.js/stackframe#8
- if (line.indexOf(' > eval') > -1) {
- line = line.replace(/ line (\d+)(?: > eval line \d+)* > eval:\d+:\d+/g, ':$1');
- }
- if (line.indexOf('@') === -1 && line.indexOf(':') === -1) {
- // Safari eval frames only have function names and nothing else
- return new StackFrame({
- functionName: line
- });
- } else {
- var functionNameRegex = /((.*".+"[^@]*)?[^@]*)(?:@)/;
- var matches = line.match(functionNameRegex);
- var functionName = matches && matches[1] ? matches[1] : undefined;
- var locationParts = this.extractLocation(line.replace(functionNameRegex, ''));
- return new StackFrame({
- functionName: functionName,
- fileName: locationParts[0],
- lineNumber: locationParts[1],
- columnNumber: locationParts[2],
- source: line
- });
- }
- }, this);
- },
- parseOpera: function ErrorStackParser$$parseOpera(e) {
- if (!e.stacktrace || (e.message.indexOf('\n') > -1 &&
- e.message.split('\n').length > e.stacktrace.split('\n').length)) {
- return this.parseOpera9(e);
- } else if (!e.stack) {
- return this.parseOpera10(e);
- } else {
- return this.parseOpera11(e);
- }
- },
- parseOpera9: function ErrorStackParser$$parseOpera9(e) {
- var lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
- var lines = e.message.split('\n');
- var result = [];
- for (var i = 2, len = lines.length; i < len; i += 2) {
- var match = lineRE.exec(lines[i]);
- if (match) {
- result.push(new StackFrame({
- fileName: match[2],
- lineNumber: match[1],
- source: lines[i]
- }));
- }
- }
- return result;
- },
- parseOpera10: function ErrorStackParser$$parseOpera10(e) {
- var lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
- var lines = e.stacktrace.split('\n');
- var result = [];
- for (var i = 0, len = lines.length; i < len; i += 2) {
- var match = lineRE.exec(lines[i]);
- if (match) {
- result.push(
- new StackFrame({
- functionName: match[3] || undefined,
- fileName: match[2],
- lineNumber: match[1],
- source: lines[i]
- })
- );
- }
- }
- return result;
- },
- // Opera 10.65+ Error.stack very similar to FF/Safari
- parseOpera11: function ErrorStackParser$$parseOpera11(error) {
- var filtered = error.stack.split('\n').filter(function(line) {
- return !!line.match(FIREFOX_SAFARI_STACK_REGEXP) && !line.match(/^Error created at/);
- }, this);
- return filtered.map(function(line) {
- var tokens = line.split('@');
- var locationParts = this.extractLocation(tokens.pop());
- var functionCall = (tokens.shift() || '');
- var functionName = functionCall
- .replace(/<anonymous function(: (\w+))?>/, '$2')
- .replace(/\([^)]*\)/g, '') || undefined;
- var argsRaw;
- if (functionCall.match(/\(([^)]*)\)/)) {
- argsRaw = functionCall.replace(/^[^(]+\(([^)]*)\)$/, '$1');
- }
- var args = (argsRaw === undefined || argsRaw === '[arguments not available]') ?
- undefined : argsRaw.split(',');
- return new StackFrame({
- functionName: functionName,
- args: args,
- fileName: locationParts[0],
- lineNumber: locationParts[1],
- columnNumber: locationParts[2],
- source: line
- });
- }, this);
- }
- };
- }));
|