Why do YOU need Source Map

According to the Google Developer Documentation, Source Map is typically used with the following types of preprocessors:

  • Translator (Babel)
  • Compiler (TypeScript)
  • Minifiers (UglifyJS)

Why is that? Because often the code we run in the browser is processed, the processed code may be very different from the code we developed, which makes development debugging and in-line debugging difficult. This is where the Source Map comes in, allowing the browser to navigate directly from the transformed code to the pre-transformed code. In WebPack, the Source Map can be configured with the DevTool option.

Configuration items

Now that we know why Source maps are needed, let’s take a look at what types of Source maps WebPack can generate.

According to the Webpack website, there are 20+ devTool values.

It sounds scary, but fortunately it has a pattern.

[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map

By combining keywords, Source maps can be generated for various scenarios.

To understand the quality

Before we look at the values, we need to look at the quality column in the table. When we configure a value for a property, we do so for a purpose, and quality is one of those purposes.

So what is it? It describes what we can see in the source code while debugging.

So let’s see what it means and what it means

quality meaning
bundled code Module not separated
generated code Module separation, code not processed by loader
transformed code Module separation, code processed by loader
original source Code that you wrote yourself
without source content The generated Source Map does not contain the sourcesContent
(lines only) Contains row information but does not contain column information

Understand devtool

Quality determines the source content we can see when debugging, so the devTool value needs to be selected according to the actual project with quality. While DevTool has 20+ optional values, we need to further understand the principles of its composition.

Note: When specifying DevTool, use it in conjunction with mode.Copy the code

First of all, there are three key words in the above model:

  • Inline, hidden, or eval
  • nosources
  • cheap[module]

As the saying goes, practice is the only standard to test the truth, the best way to learn a thing, there is a 🌰 to practice.

Here is a simple demo with several files, where the main file main.js introduces an a.js. To better simulate the actual scenario, we also use the Babel-Loader in WebPack to process JS files.

The details are as follows:

Here is a practical wave ~

Inline, hidden, or eval

These patterns are mutually exclusive and describe how the Source Map is introduced.

inline

Source Map content is imported through base64 in a JS file.

hidden

Without the sourceMappingURL in the code, the browser does not automatically import the Source Map.

eval

The generated code is mixed up with the Source Map content and output via eval.

nosources

The Source Map using this keyword does not contain the sourcesContent, so you can only see the file information and line information, but not the Source code.

cheap[module]

This keyword is used to specify the integrity of debugging information

cheap

Contains no column information and the source code has been processed by the loader

You can see here that when you click on the corresponding file, it jumps to the corresponding file, but the cursor is in the first column (missing column information), and the arrow function is converted to function.

cheap-module

Contains no column information. The source code is development-time code

Again, only column information can be seen, but the code is the original code written during development.

How does Source Map work

The Source Map specification

According to the Source Map V3 specification, the recommended format is:

{
    "version": 3.    "file": "out.js".    "sourceRoot": "".    "sources": ["foo.js"."bar.js"]. "sourcesContent": [null, null],  "names": ["src"."maps"."are"."fun"]. "mappings": "A,AAAB;; ABCDE;" } Copy the code

Here’s an explanation of what each attribute represents.

attribute Lower limit of driving force
version Source Map Indicates the version of the file
file The name of the file corresponding to the Source Map
sourceRoot The root directory of source files. This value is added before each source file
sources List of source files, used for mappings
sourcesContent List of source code strings, used to show source files during debugging, each item in the list corresponds to sources
names Source file variable and property names for mappdings
mappings The location information

Browser and source-map

Take Chrome as an example (other browsers are similar).

Load the source – the map

Browsers load source-map via sourceMappingRUL in js files, and sourceMapping supports two forms: file path or base64 format.

After loading source-map, you can see the corresponding information in the Sources TAB in the browser dev Tool.

The focus here is on the sources and sourcesContent fields in the map file.

The sources field corresponds to file information, and the corresponding directory structure is generated in the browser’s Sources. Then fill the generated file with the corresponding content from sourcesContent. Why we see file information and source content when debugging is the result of the combination of sources and sourcesContent.

To take a look at this, I added the file hello.js in sources and the corresponding content hello ~ in sourcesContent. Here is what happens when the map file is loaded by the browser.

Mappdings,

For example, 🌰 :

Source: input. Jsi am handsome  
you are ugly
Copy the code
The converted code is output.jsi am handsome you are
ugly
Copy the code

How are we going to keep track of this location? Here you can refer to this article [1].

The location information must include Output location, source file Input, source source location, and source code.

So from the chestnuts above, we can draw the following table.

Output location Input Input location Character
L1, C0 index.js L1, C0 i
L1, C2 index.js L1, C2 a
L1, C3 index.js L1, C3 m
L1, C5 index.js L1, C5 h
L1, C6 index.js L1, C6 a
L1, C7 index.js L1, C7 n
L1, C8 index.js L1, C8 d
L1, C9 index.js L1, C9 s
L1, C10 index.js L1, C10 o
L1, C11 index.js L1, C11 m
L1, C12 index.js L1, C12 e
L1, C14 index.js L2, C0 y
L1, C15 index.js L2, C1 o
L1, C16 index.js L2, C2 u
L1, C18 index.js L2, C4 a
L1, C19 index.js L2, C5 r
L1, C20 index.js L2, C6 e
L2, C0 index.js L2, C8 u
L2, C1 index.js L2, C9 g
L2, C2 index.js L2, C10 l
L2, C3 index.js L2, C11 y

This table describes the corresponding position information of each character before and after processing. With this table, the position mapping can be done.

As you can see, that’s a lot of information to store for just a few characters, so this recording method needs to be optimized.

The optimization points are as follows:

  1. You can put the input file into a list so that the list index can be used in the location information.
  2. For the source code, there is no need to record the corresponding information of each character, we only need to record the variable, attribute name (word), you can use a list to save the word, the position information only record the position of the first character of the word.
  3. The line information for the output file is relatively repetitive, so it can be used;To split each line of output code, use.To split the location information for each output code.
  4. Each word in the source code can use the position relative to the previous word, and the location information (number) can be smaller.

After optimization, the following table is shown:

sources: [index.js]
names: [i, am, handsome, you, are, ugly]
Copy the code
Output location Input index Input location Name index
L1, C0 0 L1, C0 0
L1, C + 2 + 0 L1, C+2 + 1
L1, C + 3 + 0 L1, C+3 + 1
L1, C + 9 + 0 L2, C-5 + 1
L1, C + 4 + 0 L2, C+4 + 1
L2, C – 18 + 0 L2, C+4 + 1

In this case, the location information can be recorded as

mappings: "0 | | | | | 1 0 0, 2 0 0 | 1 | 2 | 3 | 1 0 | 1 | 3 | 1, 9 | 0 | | | - 5 1, 4 | 0 2 | 2 | | 1 4; 18 | 0 | 2 | | 4 1"
Remark:1. Digital use separator "|" division2. 0 | | | | 1 0 0 0 indicating - > 0, (the output word column), 0 (input file sources index), 10 ranks (input words, 0 index names (word)Copy the code

Use VLQ for optimization

In the above the mappings, because each position is not necessarily a digital, so must use | separator to partition Numbers. The mappings size can be greatly optimized if the delimiter is omitted. Then it is necessary to introduce VLQ (Variable Length Quantities).

The great thing about VLQ is that it can represent very large numbers very simply. The principle is to use six binary bits to represent a character, where the highest bit indicates whether it is continuous (0: discontinuous, 1: continuous), and the lowest bit indicates whether it is positive or negative (0: positive, 1: negative).

Take two chestnuts 🌰🌰 : the numbers 1 and -23.

The Numbers: 1Binary: 1VLQ code: 000010Base64 VLQ: C
Copy the code
The Numbers: - 23Absolute value: 23Binary: 10111VLQ code: 101111 000001Base64 VLQ: vB
Copy the code

So let’s take minus 23 and break it down a little bit.

Step 1: Go to binary 23 -> 10111Step 2: Divide 10111 into two parts, the first part is the last four parts, and the last part is the five parts -> 1, 0111 -> 00001, 0111Step 3: Splicing in VLQ format -> 101111 000001Where, 101111 is 1 [continuous identification bit] + 0111 + 1 [positive and negative identification bit]000001 is 0 [continuous identifier bit] + 00001Step 4: Compare the Base64 index table (table below)Copy the code

Finally, take a look at what the above mappings look like after optimization.

Before optimization:mappings: "0 | | | | | 1 0 0, 2 0 0 | 1 | 2 | 3 | 1 0 | 1 | 3 | 1, 9 | 0 | | | - 5 1, 4 | 0 2 | 2 | | 1 4; 18 | 0 | 2 | | 4 1"
The optimizedmappings: "AACAA,EACEC,GACGC,SAELC,IAEIC,lBAEIC"
Copy the code

How is it used in projects

By the above theory. Now that we know a bit about source-Map and the DevTool configuration items in WebPack, let’s take a practical look at how WebPack should be configured in different environments of a project.

How is WebPack recommended

Webpack devTool [2]

Development

Recommended use:

  • eval
  • eval-source-map
  • eval-cheap-source-map
  • eval-cheap-module-source-map

In a development environment, we care more about the development experience, so here’s a comparison from source level, build speed, and column information.

devtool The source code level Build speed Column information
eval Webpack + Loader after processing the code fast +
eval-source-map The source code slow +
eval-cheap-source-map Loader processed code In the
eval-cheap-module-source-map The source code In the

Production

Recommended use:

  • none
  • source-map
  • hidden-source-map
  • nosources-source-map

In a production environment, we care more about security, so here’s a comparison from source level, security, and column information.

devtool The source code level security Column information
none
source-map The source code The browser loads source-map, and debugging exposes the source code +
hidden-source-map The source code The map file is generated, but the browser does not load source-map. Map files can be used in combination with error reporting tools +
nosources-source-map Source stack Without sourcesContent, debugging can only see the module information and line information, not the source code

How is the actual project configured

Looking over the projects in the project team, the development environment uses eval-cheap-mod-source-map, while the production environment only needs to know the module and line number of the error, so the nosource-source-map is used.

The last


Focus on “stroll big front”, the first time to get quality articles.

The resources

[1]

Source Maps under the hood: https://docs.microsoft.com/zh-cn/archive/blogs/davidni/source-maps-under-the-hood-vlq-base64-and-yoda#comment-626


[2]

webpack: https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/6/28/172f8f3771f18da1~tplv-t2oaga2asx-image.image