How to set up source maps to help debug production JavaScript

Share
  • April 8, 2020

According to GitHub, JavaScript is the most popular programming language in the world. That’s because developers turn to JavaScript to build compelling user experiences that can only be delivered in the browser. To support these complex applications, new tools and frameworks have emerged that help make web development faster: React, TypeScript, and Webpack are a few you might have heard of.

These emerging tools are great at boosting productivity, but they bring new challenges – particularly when it comes to debugging production JavaScript applications. Developers now have to debug live software using compiled code that bears little resemblance to the original source code they’re familiar with. This makes remediating production issues slow, which likely results in poor experiences for users.

The good news is that developers can use source maps to map transpiled code back to their original source code. When source maps are configured properly, web browsers and observability tools convert stack traces, source files, and other valuable debugging information into their original form.

This all hinges on setting up source maps correctly, which can be more challenging than you’d expect. Below are five tips to help developers get source maps working so they can spend less time debugging and more time doing what they love: writing software.

SEE ALSO: How to read and write in Console app in C#

Actually generate a source map

Source maps sound complicated, but they’re just JSON files that contain mapping definitions that are understood by browsers and other tools. But they don’t suddenly appear – it’s up to developers to generate these source maps as part of their application’s build process. Luckily, almost every JavaScript-generating compiler has options for generating them.

For example, if your original source code is in TypeScript, you can use the TypeScript compiler to generate an accompanying source map during compilation.

$ tsc example.ts --outFile=example.js --source-map

The example command above sends example.ts through the TypeScript compiler, outputs a JavaScript file named example.js, and also outputs an accompanying source map file named example.js.map.

Whether you’re using TypeScript, a different language that compiles to JavaScript like Elm, or a bundler like Webpack, check your tool’s documentation for how to generate source maps. It’s usually as simple as a single command-line argument like the example above.

//# sourceMappingURL=example.js.map

When browsers see this line, they download the source map to corroborate code running in the browser with the original source code that generated it. It can be declared as a fully qualified URL (i.e., it includes http:// or https://), or it can be resolved relative to the source file that contains the directive.

JavaScript-generating compilers will automatically insert this line into your final compiled file when you enable source map generation. But there’s usually a long journey from when your code is initially transformed until it gets to your web server or CDN to be downloaded by users. For example, a subsequent build tool might move the location of this line or remove it. Some CDNs, to minimize bytes-over-the-wire, are even known to strip this line from JavaScript files automatically.

To be sure you’re doing things correctly, it’s important to check that the sourceMappingURL is not only generated at build time but also present when users download this file over a network. You can use your browser, or even a tool like cURL, to verify it’s there:

$ curl -s http://example.com/static/example.js | tail -1
//# sourceMappingURL=example.js.map

Once you’ve verified the directive is present, resolve the URL it points to in order to make sure the source map is present and accessible over the network.

Embed your source files in the source map

Source maps can be problematic because they can be built with different levels of granularity. For example, it’s possible to create a “minimal” source map without referencing any of your original source files, which leaves browsers only capable of transforming basic metadata like filenames and line/column locations.

If you want to get the most value out of source maps, it’s recommended to embed your original source code inside the source map. This way you’ll not only get the benefit of basic location transformation but also give browsers and other monitoring tools the capability to step through your original source code. This is crucial when you’re debugging production JavaScript – otherwise, you’ll find yourself stepping through compiled and minified code that looks closer to assembly than human-readable code.

ost JavaScript-generating compilers provide the capability to embed source code inside source maps out of the box. For example, UglifyJS, a popular JavaScript minification tool, lets you specify this option on the command line.

$ uglifyjs --output=example.min.js example.js --source-map includeSources

Running this command not only generates a source map and specifies a sourceMappingURL directive, but it also embeds the entire input file – example.js – into the source map itself. This gives browsers the clearest picture of how to work backward to your original code.

Check your tool’s source map documentation to figure out how to embed your source code into your source map. (Note: If you don’t want to share your source code with the world, make sure you don’t share your source map on the public internet – consider protecting it behind a VPN.)

Use a single tool to manage JavaScript transformations

It’s not uncommon for JavaScript developers to use multiple compilers to produce a final compiled file. For example, a developer might first run their TypeScript code through the TypeScript compiler, then minify that output using UglifyJS. Each of these tools generates a source map. What happens when you feed the output of one tool into the other?

The answer: usually the transformations become mangled, and while it appears that you’ve generated a “valid” source map, the reality is that the mappings point to incorrect locations in your source code. The browser will let you step through completely incorrect source code because it doesn’t understand that the mappings are incorrect – only that they are “valid.”

he easiest solution is to use a single tool that manages all your transformations in one place to produce a single, final, correct source map. One such tool that does this is Webpack, a popular JavaScript bundler.

Webpack configuration can get a little gnarly, so I’ve not included an example here. If you’re suffering from incorrect mappings, my recommendation is to verify that you’re not using multiple tools back-to-back. And if so, explore using a single tool like Webpack to manage your transformations instead of using JavaScript-outputting compilers individually.

Version files and source maps

Even if you’ve done everything we’ve talked about, there’s one final way you can be bitten by broken source maps: version mismatches. This happens when a browser downloads one version of a transformed file, then downloads a newer, incompatible version of its accompanying source map. Because the source map was generated from a different version of the file, even if the file was changed slightly, the browser will end up showing incorrect transformations.

This might seem like an uncommon situation, but it can occur in a variety of ways. For example, it can occur if a code deploy is triggered while debugging a file that was cached earlier by the browser.

To stop this from occurring, developers need to version files and source maps by either versioning each filename, the URL’s query string, or the file’s parent directory. Each compiled file and source map should share the same version and version scheme to ensure that each browser downloads the source map that belongs to each compiled file.

// example.v1337.js

*code goes here*


//# sourceMappingURL=example.v1337.js.map

In the example above, a compiled file has a version embedded in its filename (v1337). The sourceMappingURL directive points to a source map with the same version embedded. As long as new versions of this file have a bumped version number in the filename, it’s impossible for the browser to incorrectly download the wrong accompanying source map.

SEE ALSO: Pitfalls in designing the application’s business layer

Wrapping up

JavaScript continues to grow in popularity. Not only is it used to power dynamic web pages, but now also mobile apps, games, and server-side code, assisted by a myriad of new frameworks and technologies. Developers using source maps are better poised to efficiently debug compiled JavaScript code in production, letting them remediate application issues faster and reduce the impact on users.

The post How to set up source maps to help debug production JavaScript appeared first on JAXenter.

Source : JAXenter