What are JavaScript source maps? A detailed guide from Raygun
Posted Jan 8, 2018 | 6 min. (1226 words)What are JavaScript source maps? And more importantly, how can you know if they are formatted correctly so you can develop better software?
JavaScript source maps are a useful tool for debugging and reading minified JavaScript code without having to translate between the minified and source code by hand. In essence a source map is simply a big lookup table which allows you to translate a position within a minified JavaScript file and the source code from which that minified file was compiled.
What do JavaScript source maps look like?
A source map is simply a JSON document with a few fields which include all the information required to translate between minified and source code.
A very basic source map could look something like this:
{
"file": "example.min.js",
"mappings": "AAAA,GAAIA,QAASC,QAAQ,SAErB,IAAIC,KAEC,KAAM,IAAIC,OAAM,iBAOf",
"names": [
"apple",
"banana",
"chicken",
"document",
"init"
],
"sources": [
"example.js"
]
"version": 3
}The basic properties are:
- file which specifies the file the source map provides a mapping for
- mappings which contains an encoded set of values which specify the mappings from various locations in the minified file to locations in the source file(s)
- names which provides the names of various objects within the source code. This allows a source mapping name to translate variable names without access to the source code
- version the version of the source map specification ( version three is the latest)
What is JavaScript source map validation?
Source maps can be complicated and the tools used to generate them can sometimes be unwieldy and a bit inconsistent. This is especially true if you are using an older tool or using language features which have just been introduced.
A source map validator checks whether the source map has the required fields and allows you to see that the source map contains the mappings you want to have. Sometimes it will even allow you to validate mappings from minified code to source.
It’s important to validate source maps when you are first setting them up to ensure they were correctly generated before using them in a production environment. Typically you will only validate source maps when first implementing or updating your JavaScript minification process.
You can validate your source maps first hand in most modern web browsers. If the source map is available to use often times the browser will automatically present a source mapped copy of the minified file to the user when the user clicks through from an error notification. Ensuring the source mapping aligns with what you would expect given your knowledge of the source code and an error message.
In addition, various validators exist online. When provided with your source map, they can provide some information about which files the map pertains to, as well as allowing you to run map queries.
Raygun provides a free JavaScript source map validator tool which allows you to enter the URL of a known source map or upload a source map to be checked.
How do I create a source map?
Source maps are created by the same utility or library you use to minify your JavaScript code. They will usually be output if you specify a specific option when creating the minified file. For example, the popular minifier UglifyJS2 creates a source map if an option is provided and a filename is specified:
uglifyjs example.js --source-map example.js.mapSource maps in action
To give an example of how source maps can be useful here is a small demonstration.
Let’s say we have the source code below which we wish to deploy on our website:
var apiKey = 'BxyGQtWjGVMsgRxMoJsJxw==';function profit() { } function swingPickaxe() { } function openEyes() { brain.trigger(“eyes.open”); } function checkForCreepers() { console.log(‘Checking for those horrid green dudes’); openEyes(); } function scanForDiamonds() { var i; i = 0; checkForCreepers(i); swingPickaxe(); // … profit(9001); } function dig() { var foo, bar; var baz; foo = “foo!”; scanForDiamonds(foo); } // Attach Raygun to send me details of any errors Raygun.init(apiKey, { allowInsecureSubmissions: true }).attach(); // Now when I am down in the mines looking for diamond I need to.. dig();
We give this file as an input to our minification tool:
uglifyjs example.js --source-map example.js.mapAnd we are given two new files, a minified file example.min.js:
var apiKey="BxyGQtWjGVMsgRxMoJsJxw==";function profit(){}function swingPickaxe(){}function openEyes(){brain.trigger("eyes.open")}function checkForCreepers(){console.log("Checking for those horrid green dudes");openEyes()}function scanForDiamonds(){var i;i=0;checkForCreepers(i);swingPickaxe();profit(9001)}function dig(){var foo,bar;var baz;foo="foo!";scanForDiamonds(foo)}Raygun.init(apiKey,{allowInsecureSubmissions:true}).attach();dig();
//# sourceMappingURL=sm.js.map
and our map file:
{"version":3,"file":"sm.min.js","sources":["sm.js"],"names":["apiKey","profit","swingPickaxe","openEyes","brain","trigger","checkForCreepers","console","log","scanForDiamonds","i","dig","foo","bar","baz","Raygun","init","allowInsecureSubmissions","attach"],"mappings":"AAAA,GAAIA,QAAS,0BAEb,SAASC,WAIT,QAASC,iBAIT,QAASC,YACPC,MAAMC,QAAQ,aAGhB,QAASC,oBACPC,QAAQC,IAAI,wCACZL,YAGF,QAASM,mBACR,GAAIC,EAEJA,GAAI,CACJJ,kBAAiBI,EACjBR,eAEAD,QAAO,MAGR,QAASU,OACR,GAAIC,KAAKC,GACT,IAAIC,IACJF,KAAM,MACNH,iBAAgBG,KAIjBG,OAAOC,KAAKhB,QAAUiB,yBAA0B,OAAQC,QAGxDP","sourcesContent":["var apiKey = 'BxyGQtWjGVMsgRxMoJsJxw==';\r\n\r\nfunction profit()\r\n{\r\n}\r\n\r\nfunction swingPickaxe()\r\n{\r\n}\r\n\r\nfunction openEyes() {\r\n brain.trigger(\"eyes.open\");\r\n}\r\n\r\nfunction checkForCreepers() {\r\n console.log('Checking for those horrid green dudes');\r\n openEyes();\r\n}\r\n\r\nfunction scanForDiamonds() {\r\n var i;\r\n \r\n i = 0; \r\n checkForCreepers(i);\r\n swingPickaxe();\r\n // ...\r\n profit(9001);\r\n}\r\n\r\nfunction dig() {\r\n var foo, bar;\r\n var baz; \r\n foo = \"foo!\";\r\n scanForDiamonds(foo);\r\n}\r\n\r\n// Attach Raygun to send me details of any errors\r\nRaygun.init(apiKey, { allowInsecureSubmissions: true }).attach();\r\n\r\n// Now when I am down in the mines looking for diamond I need to..\r\ndig();\r\n"]}We can now deploy the minified version of our code to our website.
Unfortunately the code has a bug. After it’s deployed you see an error message in your console:
Uncaught ReferenceError: brain is not definedat openEyes (example.min.js:1) at checkForCreepers (example.min.js:1) at scanForDiamonds (example.min.js:1) at dig (example.min.js:1) at example.min.js:1
All the line numbers are the same. This is because the minified code only has a single line. When you refer back to your source code you might be able to spot the origin of the code just based on the method names, however sometimes even this is impossible especially if your method names are local and are minified to single characters in which case your error message might look more like this:
Uncaught ReferenceError: a is not definedat b (example.min.js:1) at c (example.min.js:1) at d (example.min.js:1) at e (example.min.js:1) at example.min.js:1
To determine where the bug has occurred you could hunt through the source code based on the vague clues given, but the better option is to utilize the source map that was generated along with the minified code.
Making the source map available to your internet browser by serving it alongside the minified file allows Chrome to automatically retrieve the map file and provide a mapped stacktrace for you.
The error message now looks like this:
Uncaught ReferenceError: brain is not definedat openEyes (example.js:12) at checkForCreepers (example.js:17) at scanForDiamonds (example.js:24) at dig (example.js:34) at example.js:41
Line numbers now reference the source code instead of the minified file, this makes finding the bug very easy:
function openEyes() {
brain.trigger("eyes.open"); // causes reference error, we need to ensure brain is initialized before calling
}Other software can also make use of source maps to make errors more readable. For instance Raygun Crash Reporting can automatically fetch source map files for minified files allowing you to debug errors reported from clients all over the world. It even provides a means to store source maps privately if you don’t want to expose your JavaScript source code to the public.
**Hopefully this article has answered your question “what are JavaScript source maps?” Feel free to utilize our JavaScript source map validator here once you have written your code to double check your formatting. **
Have any questions about JavaScript source maps? Leave a comment below.