30

我已经编写了一个 AngularJS 应用程序,但它被证明是调试的一场噩梦。我正在使用 Grunt + uglify 来连接和缩小我的应用程序代码。它还会在缩小的 JS 文件旁边创建一个源映射。

当文件中存在 JS 错误但在 AngularJS 应用程序之外时,源映射似乎可以正常工作。例如,如果我写console.log('a.b');在其中一个文件的顶部,Chrome 调试器中记录的错误会显示原始文件的行+文件信息,而不是缩小的文件。

当 Angular 自己运行的代码(例如在控制器代码中)出现问题时,就会出现问题。我从 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)
4

6 回答 6

10

Larrifax 的回答很好,但在同一问题报告中记录了该功能的改进版本:

.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 所指出的,这将生成两个堆栈跟踪:一个由 Angular 格式化,第二个由浏览器格式化。第二个跟踪将应用源映射。禁用重复项可能不是一个好主意,因为您可能有其他 Angular 模块也可以处理可以在此之后通过委托调用的异常。

于 2015-11-30T04:05:19.270 回答
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 中完全可以接受。缺点是您在代码库中添加了相当大的依赖项,并且您从非常快速的同步错误报告转移到了相当慢的异步错误报告。

于 2014-08-01T13:45:52.130 回答
6

我刚刚遇到了同样的问题,并且一直在寻找解决方案 - 显然这是一个 Chrome 问题,通常是堆栈跟踪,并且恰好适用于 Angular,因为它在错误报告中使用堆栈跟踪。看:

谷歌浏览器中的源映射是否会推送到 Error.stack

于 2013-11-09T20:28:46.440 回答
3

我会看看以下项目:https ://github.com/novocaine/sourcemapped-stacktrace

它与@jakub-hampl 的答案基本相同,但可能有用。

于 2014-08-15T18:00:05.927 回答
0

根据这个问题,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;
    };
  });
});
于 2014-09-03T11:04:46.220 回答
0

由于该错误在 Chrome 中修复(但该问题在 Angular 中仍然存在),因此不会两次打印堆栈跟踪的解决方法是:

app.factory('$exceptionHandler', function() {
    return function(exception, cause) {
        console.error(exception.stack);
    };
});
于 2016-10-30T10:44:06.847 回答