The solution to this problem is a nifty browser feature called Source Maps. Let’s learn more.
Here’s an example source map:
You’ll probably never have to create these files yourself, but it can’t hurt to understand what’s inside:
- version – the version of the source map spec this file represents (should be “3”)
- file – the generated filename this source map is associated with
- sourceRoot – the url root from which all sources are relative (optional)
- sources – an array of URLs to the original source files
- names – an array of variable/method names found in your code
- mappings – the actual source code mappings, represented as base64-encoded VLQ values
If this seems like a lot to know – don’t worry. We’ll explain how to use tools to generate these files for you.
To indicate to browsers that a source map is available for a transpiled file, the
sourceMappingURL directive needs to be added to the end of that file:
When modern browsers see the
sourceMappingURL directive, they download the source map from the provided location and use the mapping information inside to corroborate the running client-code with the original source code.
Above: Stepping through Sentry’s original ES6 + JSX code in Firefox using Source Maps.
Note: Browsers only download and apply source maps when developer tools are open. There is no performance impact for regular users.
Generating the Source Map
Okay, we know how source maps roughly work, and how to get the browser to download and use them. But how do we go about actually generating them and referencing them from our transpiled files?
UglifyJS is a popular tool for minifying your source code for production. It can dramatically reduce the size of your files by eliminating whitespace, rewriting variable names, removing dead code branches, and more.
If you are using UglifyJS to minify your source code, the following command will additionally generate a source map mapping the minified code back to the original source:
$ uglifyjs app.js -o app.min.js --source-map app.min.js.map
If you take a look at the generated output file (app.min.js), you’ll notice that the final line includes the sourceMappingURL directive pointed to our newly generated source map.
Note that this is a relative URL. In order for the browser to download the associated source map, it must be uploaded to (and served from) the same destination directory as the Uglified file (app.min.js). That means if app.min.js is served from http://example.org/static/app.min.js, so too must your source map be served from http://example.org/static/app.min.js.map.
Relative URLs aren’t the only way of specifying sourceMappingURL. You can give Uglify an absolute URL via the
--source-map-url <url> option. Or you can even include the entire source map inline (not recommended). Take a look at Uglify’s command-line options for more information.
Generating source maps with Webpack is super simple. Simply specify the
outputSourcemapFilename property in your
output configuration (docs):
Now when you run the
Private Source Maps
To prevent this, instead of providing a publicly-accessible
sourceMappingURL, you can instead serve your source maps from a server that is only accessible to your development team. For example, a server that is only reachable from your company’s VPN.
//# sourceMappingURL: http://company.intranet/app/static/app.min.js.map
When a non-team member visits your application with developer tools open, they will attempt to download this source map but get a 404 (or 403) HTTP error, and the source map will not be applied.
Source Maps and Sentry
Above: A stack trace in Sentry mapped back to its original ES6 + JSX source code.
Basically, if you’ve followed the steps in this little guide, and your deploy now has:
- Generated, uploaded source maps
- Transpiled files with
sourceMappingURLdirectives pointing to those source maps
Then there’s nothing more to do. Sentry will do the rest.
Sentry and offline Source Maps
Alternatively, instead of hosting source maps yourself, you can upload them directly to Sentry.
Why would you want to do that? A few reasons:
- In case Sentry has difficulty reaching your servers (e.g. source maps are hosted on VPN)
- Latency; source maps are inside Sentry before an exception is thrown
- You’re developing an application that runs natively on a device, e.g. using React Native or PhoneGap, whose source code/maps cannot be reached over the internet
- Avoids version mismatches where fetched source map doesn’t match code where error was thrown
For more information on uploading source maps to Sentry, check out our official documentation.
You just learned how Source Maps can save your skin by making your transpiled code easier to debug in production. Since your build tools likely already support source map generation, it won’t take very long to configure, and the results are very much worth it.