preface

The code of our project after compiled packaged, will develop multiple files code combined to the same file, but also through a variety of compression, merger, code uglification etc, this conversion after the resulting code will only be used for the online environment, so we online actual running code with us to develop the code is have very big different, If a bug occurs at this point, we can only locate the converted code, which is already unrecognizable

The transformed code looks like this

Although this kind of code is very computer friendly, our debugging will become very difficult, which is where sourcemap is needed

What is a SourceMap

In simple terms, Sourcemap is an information file, which stores the corresponding location information of the code before and after conversion, that is, the corresponding source location of the converted compressed code before conversion. It is the mapping between the source code and the production code. Sourcemap solves the problem that during the packaging process, the code is compressed. After removing Spaces and Babel compiling and converting, debug difficult problems due to large differences between codes

After the project is developed and built, you must have seen.js.map file in the package folder besides js, CSS, images and other resources, which is called sourcemap file

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

With this line, you can enable sourcemap. The sourceMappingURL is the address of the Sourcemap that marks the file. The sourcemap file can be placed locally or on the network

Open another.js.map file and see if it has a bunch of gibberish. Let’s look at a simpler one

The generation of SourceMap

There are many ways to generate sourcemap, and there are many front-end tools support, such as Webpack, Uglifyjs, gulp, etc., here will not be detailed, how to generate sourcemap can be interested in this article

Code.tutsplus.com/tutorials/s…

The properties of SourceMap

Let’s look at a simpler code

const value = 123;
console.log(value);
Copy the code

Code packaged with Webpack

console.log(123);
//# sourceMappingURL=bundle.js.map
Copy the code

The generated sourcemap file

{
   version : 3.file :  bundle.js ,
   mappings :  AACAA,QAAQC,IADM ,
   sources : [
     webpack://studysourcemap/./test.js ].sourcesContent : [
     const value = 123; \nconsole.log(value); ] .names : [
     console ,
     log 
  ],
   sourceRoot:}Copy the code

The meaning of each attribute is as follows

  • Version: Which sourcemap version of the specification is followed (discussed briefly below)
  • Sources: array of source file urls before conversion (array because there are multiple file merge cases)
  • Names: An array of identifiers referenced in mappings (which can be interpreted as all the variable and attribute names of the pre-transformation code)
  • SourceRoot: The root path of the source file
  • SourcesContent: The original content of the source file before conversion, also an array
  • Mappings: Base64 VLQ string that records source and compiled code location information and is the most important content
  • File: The generated file name associated with the Sourcemap file, which is the packaged and compiled file name

SourceMap version

Version of Sourcemap

  • In 2009, when Google introduced its Cloure Compiler, it also introduced a debugging plug-in called Closure Inspector, which made it easy to debug compiled code. Sourcemap was a prototype
  • In 2010, with Closure Compiler Source Map 2.0, we worked on some standards and decided to use Base64 encoding, but the resulting Map files were much larger than they are now
  • In 2011, the third generation came out, Source Map Revision 3 Proposal, which is the current version of sourcemap, which is why version=3 of the Map file above, which optimized the algorithm, Greatly reduces the size of the map file

The map files generated in the first version were about 10 times the size of the converted files, the second version was reduced in size by 20 to 30 percent, and the third version was reduced in size by half

Because of the 3rd generation Source Map Revision 3 Proposal standard, different packaging tools and browsers can use Sourcemap, A github library that generates sourcemap based on this standard github.com/mozilla/sou…

The principle of SourceMap

The focus here is on the Mappings and Names attribute, which is a long string that is divided into three parts

  1. The semicolon (;) Each line of the generated file is a semicolon (;). A semicolon represents a line of converted source code
  1. Comma (,), position corresponding, each paragraph is separated by comma (,), a comma corresponds to a position of the source code after conversion
  1. English letters, each consisting of 1,4 or 5 variable-length fields, record the location of the original code

As a simple example, suppose you have the following mappings attribute

mappings : AACAA; QAAQC,IADM ,Copy the code

A semicolon indicates two lines of code, preceded by AACAA on the first line, followed by QAAQC and IADM on the second

The second line has a comma indicating that the line is divided into two paragraphs, QAAQC and IADM

Semicolon and comma we should have no doubt, mainly is the English letter this piece of meaning position corresponding principle

Each paragraph has a maximum of five sections

  • The first part, which indicates which column the position is in
  • The second part indicates which file the location belongs to in the Sources property
  • The third part, indicating which line of code this position belongs to before the transformation
  • The fourth part, which indicates which column of the code this position belongs to before the transformation
  • The fifth part indicates which variable in the NAMES property the position belongs to

So where do these five parts come from, step by step

Suppose the file A. js has a line of code I Love SourceMap, and the final output file is bundle.js with Javascript is awesome as follows

So how do you represent the mapping

For example, Love, whose original position is (0,2), outputs awesome at (0,14), can be mapped like this

Write it like this in a fixed format that contains the original position and the output position, the word, and also the original file name, because you can process multiple files, and if you don’t write the file name, you don’t know which file the input position came from

The output word The mapping relationship
Javascript 0 | 0 | a.js | 0 | 7 | SourceMap
is 0, 11, a, js, 0, 0, I
awesome 0 | 14 | a.js | 0 | 2 | Love

We can optimize it by putting a.js and the last words in an array, using sources to record all the original names, names to record all the words in the original file, and then using subscripts to represent them, for example, Love

Most of the time, the output file is actually only one line, so we can omit the line number of the output file, and become

Considering that columns and columns can be extremely large if the file is extremely large, consider using relative position instead of absolute position, using absolute position only for the position of the first word, and using the position relative to the previous word for the rest

The original word The input position The output word Output location mapping
I 0,0, absolute position is 0,11, absolute position 11, 0, 0, 0, 0
Love 0,2, relative to I awesome The position of 0,3 relative to is 3, 0, 0, 2, 1
SourceMap 0,5, relative to Love Javascript (0,-14) position relative to awesome -14, 0, 0, 5, 2

So we now have a preliminary map file

{names: ['I', 'Love', 'SourceMap'], Sources: ['a.js'], mappings: [11 | 0 | 0 | 0 | 0 | 0 | 1, -14 | 0 | 0 | 5 | 2]}Copy the code

But the mappings here is very ugly, but also need to use | to separate, accounts for more than one location, with VLQ coding can solve the problem of dividing Numbers, is the core of his thought on the number of consecutive marking, we first to understand, take the mappings on the properties of the first, for example, remove |, A mark is then added to the successive characters

110000
Copy the code

We read from left to right, the number 1 is marked, which means there’s continuity, and then we take the next one, which is 1, which is unmarked, and the first one ends, so the first one is 11

If you keep going, 0 is not marked, that means it’s a complete number, and the second number is 0

And so on…

You end up with 11,0,0,0,0

VLQ uses 6 binary bits for storage, in which the first bit indicates whether the identifier bit is continuous, and the last bit indicates whether the number is positive or negative (0 positive,1 negative). There are only 4 bits in the middle, so the range of a cell is [-15,15]. If the number exceeds that, the continuous identifier bit is used

Let’s do a couple of examples, and I’ve color-coded each step

Step 3 (Press… 5554), the rightmost 4 bits because it needs to represent an extra sign bit, the rest can be represented by 5 bits

The penultimate step is in reverse order because VLQ indicates that the order of data bytes is reversed

Finally, we can get their VLQ code

Decimal value VLQ coding VLQ encodes the corresponding value of each segment
5 001010 10
– 19 100111, 000001, 39 and 1

And then switch it to Base64 encoding, which you can look up in the table below

Therefore, the base64 VLQ encoding of 5 and -19 can be obtained. Since the VLQ encoding value of 5 is 10, K can be obtained by looking up the table above. Similarly, N and B can be obtained by -19, and finally, K and nB can be obtained respectively

There is a web site can transform our verify www.murzwin.com/base64vlq.h…

Then we’ll go back and manually generate the map file for our original simple JS file to verify

const value = 123;
console.log(value);
Copy the code

Packaged code

console.log(123);
//# sourceMappingURL=bundle.js.map
Copy the code

Sources and names can be identified first

{
     sources : [ a.js ],
     names : [ console ,  log ],
}
Copy the code

The base64 VLQ code is obtained

The original location Output location Sources index Index of names mapping VLQ encoding for each part Base64 VLQ coding
console Absolute (1, 0) Absolute (0, 0) 0 0 0 0 1
log Relative to (0, 8) Relative to (0, 8) 0 1 8 0 0
123 A (1, 6) Relative to (0, 4) 0 There is no 4 0 – 1

So we can get the final map file

{ sources : [ a.js ], names : [ console , log ], mappings : AACAA,QAAQC,IADM , // ... Other}Copy the code

In turn, the original location can be derived from the Sourcemap file, which is not demonstrated here

SourceMap summary

  • Map the relationship between transformed code and source code
  • Enable //# sourceMappingURL=xxx.js.map in code
  • The Source Map solves the problem of inconsistency between the source code and the running code
  • Not only js files have it, CSS files also have it
  • The core principle is Base64 VLQ encoding

Thanks to the giant

Juejin. Cn/post / 702353…

Juejin. Cn/post / 696307…

Docs.google.com/document/d/…

www.ruanyifeng.com/blog/2013/0…

www.html5rocks.com/en/tutorial…

www.qiutianaimeili.com/html/page/2…