What Are Source Maps in Javascript?
What Are Source Maps in Javascript?

In this video you will learn about source maps in JavaScript, how the work and why do you need them. Also make sure that I watch this video until the end because I will share but bonus tip about hiding sourcemaps in production so that people don't see your code.
Let's jump right into it.

So the first question "Do we have a problem at all?". For this we need to think once again about how our javascript applications are developed today. Javascript is not a stale language and we are getting new feature there every day. Compared with Es5 which is like (old Javascript) es6, es7, es8 brought us lots of sugar, new possibilities and functions.

But the problem is here that not all browsers support new features at once. We have sometime quite long support process and some browsers don't support some features at all.

This is why we use babel or similar tools to transpile our code with new feature in older versions which will be supported across all browsers.

For example if modern things like arrow functions, spread operators or async await are not supported by the browser babel will transpile it to javascript version that is supported.


And this leads us to source maps. The problem is that we want to read this files while debugging in browser in the same format like we wrote that code. So we don't want to see how code looked for browsers, especially when it's unreadable because of minification. We want to see it in example the same state like in editor. And source maps it a solution to this problem. So it's a JSON respesentation of mapping between filename, lines and column at the moment of converting.

For example when we use babel it supports sourcemaps. So we can get them on the fly. And actually all modern tools support sourcemaps: babel, minify tools, all browsers support sourcemaps.

Let's check on the example.

const add = (...args) => {
  return args.reduce((sum, el) => {
    return sum + el;
  }, 0);
};

console.log(add(1, 2, 3));

So here is a spread function from es6 and we wrote add function which sums all arguments. Now we need to install babel and babel preset to transpile our es6 code to es5.

npm install -g babel-cli
npm i babel-preset-es2015

So here is a command that will transpile our file to es5.

babel main.js --out-file main.dist.js --source-maps --presets=es2015

So as you see I installed babel globally because it's a CLI tool. I also install es2015 preset to convert our es6 code to es5. Here we specify our file that we want to convert, output file, preset and that we need sourcemaps.

As you can see we have main.dist.js now.

"use strict";

var add = function add() {
  for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
    args[_key] = arguments[_key];
  }

  return args.reduce(function (sum, el) {
    return sum + el;
  }, 0);
};

console.log(add(1, 2, 3));

//# sourceMappingURL=main.dist.js.map

This is what we will feed to our browser. It looks not so readable but at least we didn't minify it. Also the last line is what brings sourcemaps support to browser.

Let's check main.dist.js.map now

{"version":3,"sources":["main.js"],"names":[],"mappings":";;AAAA,IAAM,MAAM,SAAN,GAAM,GAAa;AAAA,oCAAT,IAAS;AAAT,QAAS;AAAA;;AACvB,SAAO,KAAK,MAAL,CAAY,UAAC,GAAD,EAAM,EAAN,EAAa;AAC9B,WAAO,MAAM,EAAb;AACD,GAFM,EAEJ,CAFI,CAAP;AAGD,CAJD;;AAMA,QAAQ,GAAR,CAAY,IAAI,CAAJ,EAAO,CAAP,EAAU,CAAV,CAAZ","file":"main.dist.js","sourcesContent":["const add = (...args) => {\n  return args.reduce((sum, el) => {\n    return sum + el;\n  }, 0);\n};\n\nconsole.log(add(1, 2, 3));\n"]}

So this is how sourcemaps look like. This is a special JSON which all needed content, mappings and names.

When you browser parses this javascript file it goes to the last line and sees sourcemaps and it parses it also. And it's not only for javascript. Whatever is interpreted in browser can have sourcemaps. For example you can have sourcemaps for CSS and then in browser instead of CSS you will see your SASS code or whatever preprocessor you are using.

Let's add our dist file to index.html and open it in browser.

<script src="main.dist.js"></script>

Let's open our sourcemaps in browser and check how it looks like. The most important that you enable sourcemaps for javascript in chrome settings. Now if we click on our source files in console we are jumping not in our dist file but in main.js instead. So we see both main.js and main.dist.js in the files list but in console and debugging browser automatically jumps to source file.

As you can see we can also put breakpoints in our source code it browser will magically map lines from source to dist javascript. Doesn't matter if it's minified or not.


So here is my bonus about hiding sourcemaps. From my point of view we want sourcemaps everywhere: locally, on staging and on production. If locally and on staging nobody expect us or a team that you are working in has access then on production people don't usually do sourcemaps because that don't know how to give them. And this is pity because debugging minified code on production becomes simply impossible.

So here are several solutions for that. First of all you can load different generated javascript files with and without sourcemaps based on cookie, user role or IP. Whatever you want. This is not the ideal solution from my point of view because every time we need to generate output twice. And on big projects it takes a lot of time.

The easiest approach to hide sourcemaps by URL which is only allowed from the office or specific IP or only when you are using VPN. Then browser simply can't load sourcemaps and that's it. In sourcemaps we can pass not only local relative path but full path from any domain so it's not a problem.

So in this video you learned what are sourcemaps, how they are generated and why they are so useful.

Also if you want to improve your programming skill I have lots of full courses regarding different web technologies.

📚 Source code of what we've done