ITKarma picture

The Source Maps mechanism is used to map the source code of the program to the scripts generated on their basis. Despite the fact that the topic is not new and a number of articles have already been written on it (for example, this , this and this ) some aspects still need clarification. This article is an attempt to streamline and systematize everything that is known on this topic in a short and accessible form.

The article Source Maps is considered in relation to client development in the environment of popular browsers (for example, DevTools Google Chrome), although their scope is not tied to any particular language or environment. The main source for Source Maps is, of course, the standard , although it has not yet been accepted (status - proposal), but nonetheless widely supported by browsers.

Work on Source Maps was started at the end of the 2000s; the first version was created for the Firebug Closure Inspector plugin. The second version was released in 2010 and contained changes in terms of reducing the size of the map-file. The third version was developed as part of the collaboration between Google and Mozilla and was introduced in 2011 (last revision in 2013).

Currently, there is a situation in the client development environment where the source code is almost never integrated directly into the web page, but it goes through various stages of processing: minification, optimization, concatenation, moreover, the source code itself can be written in languages ​​that require transpilation. In this case, for debugging purposes, you need a mechanism that allows you to observe in the debugger exactly the original, human-readable code.

Source Maps requires the following files to work:

  • actually generated JavaScript file
  • a set of source files used to create it
  • map file mapping them onto each other

Map File


All work of Source Maps is based on a map-file, which may look, for example, like this:

{ "version":3, "file":"index.js", "sourceRoot":"", "sources":["../src/index.ts"], "names":[], "mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,SAAS,SAAS;IACd,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;", "sourcesContent": [] } 

Usually, the name of the map file is the sum of the name of the script to which it belongs, with the addition of the extension ".map", bundle.js - bundle.js.map. This is a regular json file with the following fields:

  • “version” - version of Source Maps;
  • "file" - (optionally) the name of the generated file to which the current map-file belongs;
  • "sourceRoot" - (optional) prefix for the paths to the source files;
  • “sources” - a list of paths to the source files (resolved similarly to the src addresses of the script tag, you can use file://.);
  • "names" - a list of the names of variables and functions that have undergone changes in the generated file;
  • "mappings" - coordinates of the mapping of variables and functions of the source files to the generated file in Base64 VLQ format;
  • "sourcesContent" - (optional) in the case of a self-contained map file, a list of lines, each of which contains the source text of the file from sources;

Download Source Maps


In order for the browser to load the map file, one of the following methods can be used:

  • The JavaScript file came with an HTTP header: SourceMap: & lt; url > (previously obsolete X-SourceMap was used: & lt; url >)
  • there is a special comment in the generated JavaScript file of the form:

//# sourceMappingURL=<url> (для CSS/*# sourceMappingURL=<url> */) 

Thus, downloading the map-file, the browser will pull up the sources from the “sources” field and use the data in the “mappings” field to display them on the generated script. Both options can be found in the Sources DevTools tab.

The pseudo-protocol file://can be used to indicate the path. Also in & lt; url > all contents of a base64 encoded map file may be included. In Webpack terminology, similar Source Maps are called inline source maps.

//# sourceMappingURL=data:application/json;charset=utf-8;base64,<source maps Base64 code> 

Source Maps loading errors
It should be noted that map files are not part of the web page, so you will not see information about downloading them in the Network DevTools tab. However, if the generated file contains a link to a non-existent map file, the Console DevTools will display a warning like: “DevTools failed to load SourceMap:.”. Also, if there is a link to a nonexistent source, instead of it there will be a message of the form: “Could not load content for.”.

Self-contained map files


The source file code can be included directly in the map file in the "sourcesContent" field, if this field is available, there is no need to download them separately. In this case, the file names in “sources” do not reflect their real address and can be completely arbitrary. That is why, you can see such strange “protocols” in the Sources DevTools tab: webpack://, ng://, etc.

Mappings


The essence of the mapping mechanism is that the coordinates (row/column) of the names of variables and functions in the generated file are mapped to the coordinates in the corresponding source code file. The following information is required for the display engine to work:

(# 1) line number in the generated file;
(# 2) column number in the generated file;
(# 3) source index in "sources";
(# 4) source line number;
(# 5) source column number;

All this data is in the “mappings” field, the value of which is a long string with a special structure and values ​​encoded in Base64 VLQ.

The line is separated by semicolons (;) into sections corresponding to the lines in the generated file (# 1).

Each section is separated by commas (,) into segments, each of which may contain 1.4 or 5 values:

  • column number in the generated file (# 2);
  • source index in "sources" (# 3);
  • source line number (# 4);
  • source column number (# 5);
  • index of the name of the variable/function from the list of names;

The values ​​of the row and column numbers are relative, indicate the offset relative to the previous coordinates and only the first from the beginning of the file or section.

Each value is a Base64 VLQ number. VLQ (Variable-length quantity) is the principle of coding an arbitrarily large number using an arbitrary number of binary blocks of fixed length.

Source Maps uses six-bit blocks that follow in order from the lowest part of the number to the highest. The highest 6th bit of each block (continuation bit) is reserved, if it is set, then the current block is followed by the next block of the same number, if reset, the sequence is completed.

Since in Source Maps the value must have a sign, the least significant 1-bit (sign bit) is also reserved for it, but only in the first block of the sequence. As expected, the set sign bit means a negative number.

Thus, if a number can be encoded in a single block, it cannot be modulo more than 15 (1111 2 ), since in the first six-bit block of the sequence two bits are reserved: the continuation bit will always be reset, the sign bit will be set depending on the sign of the number.

Six-bit VLQ blocks are mapped to Base64, where each six-bit sequence corresponds to a specific ASCII character.

ITKarma picture

Decode the number mE. Invert the order, the last part is Em. We decode the numbers from Base64: E - 000100, m - 100110. In the first, we discard the highest continuation bit and two leading zeros - 100. In the second, we discard the highest continuation and low sign bits (the sign bit is cleared - the number is positive) - 0011. As a result, we get 100 0011 2 , which corresponds to the decimal 67.

It is also possible in the opposite direction, we encode 41. Its binary code 101001 2 , we divide into two blocks: the oldest part - 10, the youngest part (always 4-bit) - 1001. We add the most significant continuation bit to the older part (reset) and the three leading zeros - 000010.To the younger part we add the most significant continuation bit (set) and the least significant bit (reset - positive) - 110010. We encode the numbers in Base64: 000010 - C, 110010 - y. We invert the order and, as a result, we obtain yC.

For working with VLQ, the library is very useful.

Source