A practical guide to source maps
Q: What are sourcemaps (source maps?) anyways?
A: Source maps refer to extra information the browser uses to make debugging easier. They allow you to inspect and debug through source code that looks like the code in your editor, and not the goo that the browser runs after your build system gets its grubby hands on your beautiful code.


Q: It’s not working, what do I do?
A: I’ll get into that, but first, here’s some quick ways to debug source code without source maps (as you may have to do from time to time). First, on most browsers’ developer tools window, there is a “pretty print” button that at least adds spaces and newlines to minified code. It looks like {} in most browsers.

With that, you can set breakpoints on real lines, and have a better chance of stepping through the code. Sometimes I’ll write on a scratchpad what the minimized variable names stand for. So in the above case, e refers to options, and t refers to toPage.
Q: You didn’t answer my question.
A: That question would take too long to answer. Would you like to know what components make up source maps?
Q: Sure… Wait, components? I thought there was just a source map?
A: …Sometimes. There are fundamentally three parts to getting source maps to work. First, there’s the generated code. This is what you serve to the browser (usually minified like the first picture). Then, there’s the source file(s). These are the files that you modify in your editor (The second picture). Lastly, there is the source map itself. At a high level, the source map file itself knows nothing about either the generated file OR the source file. You can think of it as function that takes a line number and column number (corresponding to the generated file), and returning a file name, line number, and column number corresponding to the correct code location in the source file.
Q: Okay, so the browser is running my generated code, but in the debugger it uses the source map to figure out the equivalent line of my source code and “pretends” it’s running that code instead?
A: Yeah, basically. Now, the reason I said sometimes above is that instead of having three files, you can inline this information into two, or even one file.
Q: How do browsers even know where to load the source maps anyways?
A: Your generated file needs to have a comment at the bottom that looks like this: //# sourceMappingURL=/path/to/script.js.map where the URL is any relative (to the generated file location) or absolute path. The browser then downloads that file and parses it. The two that matter right now are sourceRoot and sources. Sources is an array of files like this: sources: ["first source file.js", "src/components/otherfile.js"] and sourceRoot is a prefix URL location that gets prepended to each source file. So, basically, if you have a sourceRoot of /sources/ with the above sources array, then it is equivalent to having an empty sourceRoot, but a sources array of sources: ["/sources/first source file.js", "/sources/components/otherfile.js"].
Lastly, the map file has a mappings field which does the actual translation. Here is how it works. Each entry in the mappings file basically says something like “line 1, column 20482 belongs to line 7, column 89 of the source file in the 1st index of the sources file”. So the browser will then go to the sources array, see that it needs to download /sources/src/components/otherfile.js, and then it highlights line 7 column 89.
Q: I think that makes sense. Now can you tell me why my source maps aren’t working?
A: Yeah, so the point I want to emphasize is that *all* of the paths above are relative to your web server where your files are hosted. So, if my source file is located at /Users/anil/code/myrepo/components/otherfile.js, that doesn’t necessarily correspond to the location on your server. So that’s the first debugging step. Start with your generated file, and look for a sourceMappingURL comment. See if you can download the file referenced in that comment (manually adding the correct relative path if need be). If that doesn’t work, then there’s your (first) problem. Once that works, look at the sources and sourceRoot entries in your map file. Use the algorithm in the above question to see where the source file should be located. Then try to download that file. If it still doesn’t work, then that’s a more complicated question.
Q: But my sourceMappingURL looks something like this: //# sourceMappingURL=data:application/json;base64,eyJ2ZXJza...
A: Yeah. Well, that was the simple version. Remember how I said that there were ways to combine the three files into two or one? Instead of hosting a separate .map file, you can also inline the contents of your source file as Base64 encoded string. Same basic idea, except that the browser is going to decode the rest of that line using Base64 instead of downloading a separate file.
A: And before you ask: The way you get down to one file is to use the sourcesContent entry of the map file. It’s basically the same idea. Instead of making the browser go and download /sources/src/components/otherfile.js from your webserver, it can just inline the source code into the map file itself. As you can imagine, this would cause your .map file to become quite large as it is a concatenation of all of your source files
Q: Is inlining a good or a bad thing?
A: There are pros & cons. On the pro side, if you inline your sources file AND do the Base64 encoding, then you no longer have to worry about URL paths! The browser just parses your giant comment and voila, it works. The downside to this approach is that you’ve just embedded your entire source code as a comment into your javascript file. The filesize is going to increase and if your code is proprietary, well now you’ve just released it to everyone. I would suggest not inlining your final code.
Q: My brain is fried. Can you drop me some links for future reading?
A: Here’s the official? spec. If you want to know more specifics on how the implementation works, read this great tutorial. If that’s too much info, try this one.
Q: What about tooling?
A: To debug and parse your source maps, hands down you want to use the source-map package on npm. We use the sorcery package to chain compilation steps (e.g. to go from ES2015 -> ES5 -> uglify -> concat) while producing a map file that maps the final generated file to the original source, and not just the file created before the final transformation.
Q: Thank you
A: You won’t be once other people realize you’re the expert in the subject. It’s one of the lesser known blunders after starting a land war in Asia, but before “being an expert in installers”
