AngularJS - Stack trace not utilizing source map

Debugging my AngularJS app has become quite a challenge. I've utilized Grunt + uglify to concatenate and minify the application code, with the addition of a source map generated alongside the minified JS file.

The source map functions correctly when there's a JS error in a non-AngularJS part of the code. For instance, if I insert console.log('a.b'); at the beginning of a file, Chrome debugger reports the error with line and file details from the original file rather than the minified version.

However, things take a turn when an error occurs within Angular-related code (like Controller code). While Angular provides a clear stack trace, it only references the minified file instead of the original one.

I'm wondering if there's a solution that would prompt Angular to recognize the source map?

Here's a sample error for reference:

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)

Answer №1

Larrifax's response is well done, but there exists an enhanced iteration of the function detailed within the corresponding issue report:

.config(function($provide) {

  // Correcting 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;
      });
    };
  });
})

This script will result in two stack traces, as acknowledged by Andrew Magee here: one structured by Angular, followed by a second one formatted by the browser. The subsequent trace incorporates sourcemaps. It may not be advisable to deactivate the duplicates, since you could potentially have other Angular modules that also manage exceptions and might get invoked subsequently through delegation.

Answer №2

If you're facing a challenge and struggling to find a solution, sometimes the only option left is to roll up your sleeves and tackle the issue head-on. One way to address source map parsing is by diving into the code yourself. Here's a snippet that can help with this task. To begin, ensure that you have integrated source-map into your webpage. Then, implement the following code:

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 = {};

    // Obtain 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);
  };
});

This approach involves downloading the source, fetching the sourcemaps, parsing them, and replacing the locations in the stack trace with mapped locations. While this method functions well in Chrome and satisfactorily in Firefox, it does come with some drawbacks. You'll be introducing a significant dependency to your codebase and transitioning from swift synchronous error reporting to somewhat slower asynchronous error reporting.

Answer №3

I recently encountered the same problem and have been searching for a solution - turns out it's a Chrome issue related to stack traces in general, which also affects Angular due to its use of stack traces for error reporting. Check out this discussion on Stack Overflow:

Will the source mapping in Google Chrome push to Error.stack

Answer №4

For those interested in exploring further, I recommend checking out this project: https://github.com/novocaine/sourcemapped-stacktrace

This resource provides a similar solution to what @jakub-hampl suggested, offering additional functionality that could prove helpful.

Answer №5

As mentioned in this reported issue, it appears that Angular's usage of $logProvider can disrupt sourcemapping functionality. To address this, a workaround has been proposed:

var customModule = angular.module('source-map-exception-handler', [])

customModule.config(function($provide) {
  $provide.decorator('$exceptionHandler', function($delegate) {
    return function(exception, cause) {
        $delegate(exception, cause);
        throw exception;
    };
  });
});

Answer №6

While the bug in Chrome has been resolved, the problem still remains in Angular. To prevent printing out the stack trace twice, a temporary solution would be to implement the following:

app.factory('$exceptionHandler', function() {
    return function(exception, cause) {
        console.error(exception.stack);
    };
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The Proper Way to Include External CSS in a Next.js Page Without Triggering Any Warnings

I find myself in a scenario where I must dynamically inject CSS into a webpage. The content of this page is constantly changing, and I am provided with raw HTML and a link to a CSS file from a server that needs to be displayed on the page. My attempt to ...

The error message "TypeError: Model.find(...).project is not a function" indicates that

let totalProducts = await RentedProducts.find({ customer: req.params.id, }).select({ barcodes: 1 }) I'm specifically trying to retrieve the barcodes field, but I keep encountering an error. Error: RentedProducts.find(...).project is not recognize ...

Many inhabitants - utilizing mongoosejs

Just a simple question, for example with a double reference in the model. Schema / Model var OrderSchema = new Schema({ user: { type : Schema.Types.ObjectId, ref : 'User', required: true }, meal: { ...

Guide on displaying several items from Laravel to Vue through the JavaScript filter method?

I need help transferring multiple objects from laravel to vue and filling my vue objects with information retrieved from the database. This is the data received from laravel: https://i.sstatic.net/2Hnk5.png Vue objects to be populated: storeName: {}, ...

Transmitting client-side Javascript data to backend server using Express

I am trying to fetch data from my frontend using the DOM and send it to the backend through Express but I'm unsure of how to proceed. I have used a POST method to console.log the data, but now I need help retrieving it in my Express backend. (The cons ...

Generate and delete dynamic iFrames through variable manipulation

I'm currently developing a landing page specifically for our pilots to conveniently access weather information before taking off. However, due to the limitations posed by our computer security measures, I can only utilize iframes to obtain the necessa ...

Despite reaching a video readystate of 4 in HTML5, the video still hangs and does not play smoothly

Using html5, I am currently working with video and audio. I have encountered an issue where sometimes the video hangs even after its readyState === 4. The cause of this problem is unclear to me. I aim for a solution where if the video's readyState = ...

Display a division upon clicking a hyperlink with a specific class

My goal is to display/fade in a <div> with an ID of "signInHold" when the <li> "Sign In" is clicked, utilizing the class signInActive on the <li>. <ul class="nav1"> <li class="nav2"> <a href="http://rocketcss.c ...

An empty response was received after making an Ajax request

Attempting to create an Ajax request and parse the response header to extract the "Location" attribute. Here is the code in question : let request = new XMLHttpRequest(); request.onreadystatechange = function() { if(request.readyState == 4 && ...

What is the appropriate command for "building" a fresh NPM project?

I'm currently working on a JS website with Three.js. I kicked off my project like this: $ npm init $ npm install three Can anyone guide me on where to place my code utilizing Three.js, and which npm command should I use to "compile" my script for dep ...

What is the best way to retrieve the attribute value of the parent tag within a function?

Below is the html structure in question: <script> function process(){ //how to get attribute window.allert(attr); } </script> <div attr="234234"> <div onclick="process()">some content</div> </d ...

In AngularJS, the use of the '+' operator is causing concatenation instead of addition

Looking for assistance with my TypeScript code where I've created a basic calculator. Everything is working as expected except for addition, which seems to be concatenating the numbers instead of adding them together. HTML CODE : <input type="tex ...

Issues with Angular2 causing function to not run as expected

After clicking a button to trigger createPlaylist(), the function fails to execute asd(). I attempted combining everything into one function, but still encountered the same issue. The console.log(resp) statement never logs anything. What could be causing ...

Locate the point at which the two strings no longer match in their indices

Consider having 2 strings: var a = "abcdef", b = "abcdefgh"; I am searching for the first index where the complete match is broken without needing to iterate over both strings and compare each character with a loop. In this instance, I need to identify ...

Creating interactive web applications with Python Flask by utilizing buttons to execute functions

When the button is clicked in my Flask template, I want it to trigger a Python function that I defined in app.py. The function should be accessible within the template by including this code where the function is defined: Here is an example function in ap ...

What is the best way to display the latitude and longitude values stored in my database on a Google Map interface?

Currently, I have some sample code that calls latitude and longitude values and marks them on maps. However, my goal is to display all latitude and longitude records on the map, not just one. I understand that I need to modify my code to fetch all values ...

Transferring an array between Javascript and Django

I am working with an array of objects in JavaScript, like this: Arr = [0: {k;v}, 1: {k,v}] and so on, each containing numerous fields. The challenge I'm facing is in sending these objects to Django. I have attempted using JSON.stringify to send the ...

The show/hide toggle button is malfunctioning and not functioning properly

I'm still learning jQuery and I attempted to create a show/hide toggle button without relying on jQuery's toggle function. However, I can't seem to identify the issue in the code below. Although the Hide button successfully hides the paragr ...

Unusual CSS hierarchy observed post AJAX content load

Currently, I am facing a puzzling issue where my CSS rules seem to be losing precedence on a page loaded via AJAX. Despite placing my custom CSS file last in the main page, allowing it to take precedence over any bootstrap styles, after loading new content ...

Exploring the functionality of window.matchmedia in React while incorporating Typescript

Recently, I have been working on implementing a dark mode toggle switch in React Typescript. In the past, I successfully built one using plain JavaScript along with useState and window.matchmedia('(prefers-color-scheme dark)').matches. However, w ...