我已经编写了一个AngularJS应用程序,但这对于调试来说是一个噩梦。我正在使用Grunt + uglify来连接和缩小我的应用程序代码。它还会在缩小的JS文件旁边创建一个源映射。
当文件中存在JS错误但在AngularJS应用程序之外时,源映射似乎正常工作。例如如果我在其中一个文件的顶部写下console.log('a.b');
,则Chrome调试器中记录的错误会显示原始文件的行+文件信息,而不是缩小的文件。
当Angular自身运行的代码出现问题时(例如在Controller代码中),会出现问题。我从Angular得到一个很好的堆栈跟踪,但它只详细说明了缩小的文件而不是原始文件。
我有什么办法让Angular能够确认源地图吗?
以下示例错误:
TypeError: Cannot call method 'getElement' of undefined
at Object.addMapControls (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:2848)
at Object.g [as init] (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:344)
at new a (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:591)
at d (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.min.js:29:495)
at Object.instantiate (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.min.js:30:123)
答案 0 :(得分:9)
我能找到的唯一解决方案是咬紧牙关并自己解析源地图。以下是一些可以执行此操作的代码。首先,您需要将source-map添加到您的网页。然后添加以下代码:
angular.module('Shared').factory('$exceptionHandler',
function($log, $window, $injector) {
var getSourceMappedStackTrace = function(exception) {
var $q = $injector.get('$q'),
$http = $injector.get('$http'),
SMConsumer = window.sourceMap.SourceMapConsumer,
cache = {};
// Retrieve a SourceMap object for a minified script URL
var getMapForScript = function(url) {
if (cache[url]) {
return cache[url];
} else {
var promise = $http.get(url).then(function(response) {
var m = response.data.match(/\/\/# sourceMappingURL=(.+\.map)/);
if (m) {
var path = url.match(/^(.+)\/[^/]+$/);
path = path && path[1];
return $http.get(path + '/' + m[1]).then(function(response) {
return new SMConsumer(response.data);
});
} else {
return $q.reject();
}
});
cache[url] = promise;
return promise;
}
};
if (exception.stack) { // not all browsers support stack traces
return $q.all(_.map(exception.stack.split(/\n/), function(stackLine) {
var match = stackLine.match(/^(.+)(http.+):(\d+):(\d+)/);
if (match) {
var prefix = match[1], url = match[2], line = match[3], col = match[4];
return getMapForScript(url).then(function(map) {
var pos = map.originalPositionFor({
line: parseInt(line, 10),
column: parseInt(col, 10)
});
var mangledName = prefix.match(/\s*(at)?\s*(.*?)\s*(\(|@)/);
mangledName = (mangledName && mangledName[2]) || '';
return ' at ' + (pos.name ? pos.name : mangledName) + ' ' +
$window.location.origin + pos.source + ':' + pos.line + ':' +
pos.column;
}, function() {
return stackLine;
});
} else {
return $q.when(stackLine);
}
})).then(function (lines) {
return lines.join('\n');
});
} else {
return $q.when('');
}
};
return function(exception) {
getSourceMappedStackTrace(exception).then($log.error);
};
});
此代码将下载源代码,然后下载源映射,解析它们,最后尝试替换堆栈中的位置跟踪映射的位置。这在Chrome中完美运行,在Firefox中非常可接受。缺点是您要为代码库添加相当大的依赖项,并且从非常快速的同步错误报告转移到相当慢的异步错误报告。
答案 1 :(得分:9)
Larrifax的answer很好,但有一个改进版本的函数记录in the same issue report:
.config(function($provide) {
// Fix sourcemaps
// @url https://github.com/angular/angular.js/issues/5217#issuecomment-50993513
$provide.decorator('$exceptionHandler', function($delegate) {
return function(exception, cause) {
$delegate(exception, cause);
setTimeout(function() {
throw exception;
});
};
});
})
这将生成两个堆栈跟踪,如Andrew Magee noted:一个由Angular格式化,另一个由浏览器格式化。第二个跟踪将应用源图。禁用重复项可能不是一个好主意,因为您可能还有其他Angular模块也可以处理可通过委派后调用的异常。
答案 2 :(得分:6)
我只是遇到了同样的问题并一直在寻找解决方案 - 显然这是一个一般的堆栈跟踪的Chrome问题,并且恰好适用于Angular,因为它在错误报告中使用堆栈跟踪。参见:
Will the source mapping in Google Chrome push to Error.stack
答案 3 :(得分:3)
我会看一下以下项目:https://github.com/novocaine/sourcemapped-stacktrace
它与@ jakub-hampl的答案基本相同,但可能有用。
答案 4 :(得分:0)
根据this issue,似乎Angular的$logProvider
打破了源代码制作。问题中建议使用这样的解决方法:
var module = angular.module('source-map-exception-handler', [])
module.config(function($provide) {
$provide.decorator('$exceptionHandler', function($delegate) {
return function(exception, cause) {
$delegate(exception, cause);
throw exception;
};
});
});
答案 5 :(得分:0)
正如Chrome中的错误has been fixed(但问题仍然存在于Angular中),不会打印出两次堆栈跟踪的解决方法就是:
app.factory('$exceptionHandler', function() {
return function(exception, cause) {
console.error(exception.stack);
};
});