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.
Actually generate a source map
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
Verify the sourceMappingURL Directive
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.
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.
$ 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.)
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.”
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.