Understand SourceMap and improve the efficiency of front-end development by 100%

1, What is a Source Map

Generally speaking, Source Map is an information file that stores the location information after code packaging and conversion. In essence, it is a json description file that maintains the code mapping relationship before and after packaging. For an explanation of # source maps # see # Introduction to JavaScript Source Maps[7].

Our online code is usually packaged. If the online code reports an error and wants to debug, it's really hard. For example, the following example:

Use the packaging tool Webpack to compile this code

console.log('source map!!!')
console.log(a); //This line is bound to report mistakes

Effect after the browser is opened:

Click to enter the error file:

There is no way to find the specific location and reason, so at this time, the role of Source Map , comes. In the Webpack , construction code, open , Source Map:

Then perform the build again and open the browser again:

It can be found that you can successfully locate the specific error reporting location, which is the function of "Source Map". It should be noted that Source Map , is not unique to Webpack , and other packaging tools also support , Source Map. The packaging tool only introduces , Source Map , technology through configuration. Packaging tools are described below.

2, Function of Source Map

The above case is just the first experience of "Source Map". Now let's talk about its role. Why do we need "Source Map"?

Ruan Yifeng's detailed explanation of JavaScript Source Map [8] points out that JavaScript scripts are becoming more and more complex. Most of the source code (especially various function libraries and frameworks) must be transformed before they can be put into production environment.

Common source code conversion mainly includes the following three situations:

  • Compress, reduce volume

  • Merge multiple files to reduce the number of HTTP requests

  • Compile other languages into JavaScript

In these three cases, the actual running code is different from the development code, and debugging becomes very difficult, so the Source Map is needed. Combined with the above example, even after the packaged code, we can find the specific error location, which makes our debug code easy and simple. This is the problem that Source Map wants to solve.

3, How to generate Source Map

Various mainstream front-end task management tools and packaging tools support generating source maps.

3.1 UglifyJS

UglifyJS , is a command line tool used to compress JavaScript , code

Install UglifyJS:

npm install uglify - js - g

Generate {Source Map while compressing code:

uglifyjs app.js - o app.min.js--source - map app.min.js.map

Source Map # related options:

--source - map Source Map The path and name of the file
    --source - map - root Path to source file
    --source - map - url //#The path to the sourceMappingURL. Default to the value specified by -- source map.
    --source - map - include - sources Add the contents of source code to sourcesContent array
    --source - map - inline Will Source Map Write to the last line of compressed code
    -- in -source - map input Source Map, Used when the source file has been transformed

3.2 Grunt

Grunt # is a JavaScript # project building tool

Configure the # grunt contrib uglify # plug-in to generate a # Source Map:

grunt.initConfig({
    uglify: {
        options: {
            sourceMap: true
        }
    }
});

When you package the source code with grunt usemin, grunt usemin will call grunt contrib concat [9] and grunt contrib uglify [10] successively to package and compress the source code. Therefore, it needs to be configured:

grunt.initConfig({
    concat: {
        options: {
            sourceMap: true
        }
    },
    uglify: {
        options: {
            sourceMap: true,
            sourceMapIn: function(uglifySource) {
                return uglifySource + '.map';
            },
        }
    }
});

3.3 Gulp

Gulp # is a JavaScript # project building tool

Generate a {Source Map using gulp sourcemaps [11]:

var gulp = require('gulp');
var plugin1 = require('gulp-plugin1');
var plugin2 = require('gulp-plugin2');
var sourcemaps = require('gulp-sourcemaps');

gulp.task('javascript', function() {
    gulp.src('src/**/*.js')
        .pipe(sourcemaps.init())
        .pipe(plugin1())
        .pipe(plugin2())
        .pipe(sourcemaps.write('../maps'))
        .pipe(gulp.dest('dist'));
});

3.4 SystemJS

SystemJS # is a module loader

Generate {Source Map using SystemJS Build Tool[12]:

builder.bundle('myModule.js', 'outfile.js', {
    minify: true,
    sourceMaps: true
});
  • The sourceMapContents option specifies whether to write the source code to the Source Map file

3.5 Webpack

Webpack # is a front-end packaging tool (this packaging tool will be used in this case). In its configuration file, @ webpack config. Set devtool[13] in. JS to generate the {Source Map} file:

const path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    },
    devtool: "source-map"
};
  • devtool has more than 20 different values and generates different types of source maps, which can be configured as needed. It will be described in detail below and will not be repeated here.

3.6 Closure Compiler

Generated by {Closure Compiler[14]

4, How to use Source Map

After generating Source Map, it is usually debugged in browser. The premise is that you need to turn on the function. Take Chrome as an example.

Open the developer tool and find} settings:

Check the following two options:

Back to the above case, the source code file becomes index JS, click to enter and display the real source code, that is, it indicates that the {Source Map has been successfully opened and used

5, How Source Map works

In the above case, after packaging, generate the # dist # folder and open # dist / bundld js :

You can see this note at the end:

//# sourceMappingURL=bundle.js.map

It is precisely because of this comment that the "Source Map" address of the file is marked, and the browser can correctly find the location of the source code. sourceMappingURL refers to the URL of the Source Map file.

In addition to this method, MDN[15] points out that it can be indicated through the sourcemap: < URL > field of response header.

> SourceMap: /path/to/file.js.map
> ```

`dist` Folder, except `bundle.js` also `bundle.js.map` ,This file is `Source Map` Documents, too `sourceMappingURL` directive `URL`

![](https://files.mdnice.com/user/20608/9124506c-2e1d-410e-b141-97062b36fc3f.png)

* `version`: `Source map`Version of, currently`v3`. 
* `sources`: File before conversion. This item is an array indicating that there may be multiple file merges.
* `names`: All variable names and attribute names before conversion.
* `mappings`: A string that records location information, as described below.
* `file`: Converted file name.
* `sourceRoot`: Directory of the file before conversion. If it is in the same directory as the file before conversion, this item is empty.
* `sourcesContent`: The original content of the file before conversion.

#####5.1. About the version of Source map

In 2009 `Google` In an article on `Cloure Compiler` When, `Google` It also launched a debugging tool: `Firefox` plug-in unit `Closure Inspector` ,To facilitate debugging compiled code. This is `Source Map` First generation!

> You can use the compiler with Closure Inspector , a Firebug extension that makes debugging the obfuscated code almost as easy as debugging the human-readable source.

2010 In the second generation `Closure Compiler Source Map 2.0` In, `Source Map` Confessed a common `JSON` Format and other standards have almost taken shape. The biggest difference is `mapping` Algorithm, too `Source Map` Key address of. In the second generation `mapping` Decided to use `base 64` Coding, but the algorithm has revenue and expenditure, so it is generated `.map` Much larger than now.
2011 The third generation[**Source Map Revision 3 Proposal**](https://docs. google. com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_ 2gc6fh0ky0k / edit #) has been released, which is also the 'Source Map' version we use now. From the naming of the document, the 'Source Map' has evolved from the 'Clousre Compiler' to an independent thing, which is also supported by the browser. Compared with the second generation, the biggest change in this version is the compact replacement of the 'mapping' algorithm, using [vLQ]( https://en.wikipedia.org/wiki/Variable-length_quantity )Code generation [Base64]( https://zh.wikipedia.org/zh-cn/Base64 )The former 'mapping' is greatly reduced Map ` the volume of the file.

`Source Map` The humor of development history is that it was developed as an auxiliary thing. After all, the policy it assists is declining, but it has become the subject of skills and has been written into the browser.

> Source Map V1 Most initially generated Source Map The file is about 10 times the size of the converted file. Source Map V2 Reduce it by 50%,V3 Again V2 Reduced by 50% on the basis of%. So now 133 k File corresponding to Source Map The size of the document is about 300 k about.

#####5.2 # about mappings attribute

In order to avoid interference, the case is changed into the following case without error reporting:

```js
var a = 1;
console.log(a);
`

Packaged and compiled bundle JS file:

/******/
(() => { // webpackBootstrap
    var __webpack_exports__ = {};
    /*!**********************!*\
      !*** ./src/index.js ***!
      \**********************/
    var a = 1;
    console.log(a);
    /******/
})();
//# sourceMappingURL=bundle.js.map

Packaged and compiled bundle js. Map file:

{
    "version": 3,
    "sources": [
        "webpack://learn-source-map/./src/index.js"
    ],
    "names": [],
    "mappings": "AAAA;AACA,c",
    "file": "bundle.js",
    "sourcesContent": [
        "var a = 1;\r\nconsole.log(a);"
    ],
    "sourceRoot": ""
}

You can see that the value of the "mappings" attribute is: AAAA; AACA, c, if you want to clarify this thing, you need to explain its composition and structure first. This is a string, which is divided into three layers:

  • The first layer is row correspondence, with semicolon (;) Indicates that each semicolon corresponds to a line of converted source code. Therefore, the content before the first semicolon corresponds to the first line of the source code, and so on.

  • The second layer is location correspondence, which is represented by commas (,), and each comma corresponds to a location of the converted source code. Therefore, the content before the first comma should correspond to the first position of the line source code, and so on.

  • The third layer is location conversion, represented by VLQ code [16], which represents the source code location corresponding to the location before conversion.

After returning to the source code, you can analyze:

  1. Because there are two lines in the source code, there is a semicolon, which indicates the first and second lines. AAAA and AACA in mappings, C.

  2. The semicolon is followed by the second line, which is the code console log(a); Two positions can be split, console and log (a), so there is a comma. AAC in and AAC.

In summary, the converted source code is divided into two lines. The first line has one position and the second line has two positions.

As for how the letters "AAAA", "AAcA" and so on come from, you can refer to the detailed explanation of Mr. Ruan Yifeng's JavaScript Source Map [17] for a detailed introduction. The author's own understanding is:

AAAA , AAcA , and , c , represent positions. Normally, each position is composed of up to 5 letters. The meanings of 5 letters are:

  • The first digit indicates which column this position is in (of the converted code).

  • The second digit indicates which file in the sources attribute this location belongs to.

  • The third digit indicates that this position belongs to the first line of the code before conversion.

  • The fourth digit indicates which column this position belongs to before conversion.

  • The fifth digit indicates which variable in the names attribute this location belongs to.

There are only 4 letters at most after conversion, because there is no {names} attribute.

Each position can be converted by VLQ coding [18] to form a mapping relationship. You can conduct your own conversion test on this website [19] and convert it to AAAA; AACA, c# converted results:

Two sets of data can be obtained:

[0, 0, 0, 0]
[0, 0, 1, 0], [14]

Numbers start from , 0 , take the position , AAAA , for example, and get [0, 0, 0, 0] after conversion, so the meanings of the representatives are;

  1. Compress the first column of the code.

  2. The first source code file is index js.

  3. The first line of the source code.

  4. First column of source code

Through the above analysis, we can know that var a = 1 in the source code; In the packaged file, i.e. bundle JS.

6, Source Map in Webpack

The function and principle of "Source Map" are introduced above. Now let's talk about the application of "Source Map" in the packaging tool "WebPack". After all, we can't do without it in our development.

As mentioned above, it only needs to be in webpack config. JS file, you can use the # Source Map by configuring # devtool # in the # file. Please refer to webpack devtool[20] for the specific values of this # devtool

According to the introduction of, the official lists 20 types. Of course, we can't remember them all. We can remember several key ones:

The following 7 options are recommended:

  • Source map: external. You can view the accurate information of the error code and the error location of the source code.

  • Inline Source Map: inline. Only one inline} Source Map is generated to view the accurate information of the error code and the error location of the source code

  • Hidden source map: external. You can view the accurate information of the error code, but you can't track the error of the source code. You can only prompt the error location of the built code.

  • eval Source Map: inline. Each file generates a corresponding , Source Map, which is in , eval , where you can view the accurate information of the error code and the error location of the source code.

  • Nosources source map: external. You can view the error code and the cause of the error, but you cannot view the accurate information of the error code, and there is no source code information.

  • Soap source map: external. You can view the accurate information of the error code and the error location of the source code. You can only accurate the error to the whole line and ignore the column.

  • Soap module Source Map: external. You can the accurate information of the error code and the error location of the source code. Module , will be added to , loader , Source Map.

Difference between inline and external:

  1. A file (. map) was generated externally, but not inline.

  2. Inline builds are faster.

The following examples demonstrate the above seven types:

First, change the case into error reporting status. In order to reflect the situation of the column, modify the source code as follows:

console.log('source map!!!')
var a = 1;
console.log(a, b); //This line is bound to report mistakes

6.1 source-map

devtool: 'source-map'

After compilation, you can view the accurate information of the error code and the error location of the source code:

Generated map file:

6.2 inline-source-map

devtool: 'inline-source-map'

After compilation, you can view the accurate information of the error code and the error location of the source code:

But not generated Instead, the map file is inserted into the "sourceMappingURL" in the form of "base64":

6.3 hidden-source-map

devtool: 'hidden-source-map'

After compilation, you can view the accurate information of the error code, but you cannot view the location of the source code:

Generated map file:

6.4 eval-source-map

devtool: 'eval-source-map'

After compilation, you can view the accurate information of the error code and the error location of the source code:

But not generated map file, but in the eval function , including the , sourceMappingURL:

6.5 nosources-source-map

devtool: 'nosources-source-map'

After compilation, you can view the exact location of the error code and the error location of the source code. You can only prompt the error reason:

Generated map file:

6.6 cheap-source-map

devtool: 'cheap-source-map'

After compilation, you can check the accurate information of the error code and the error location of the source code, but ignore the specific column (because it is b that causes the error):

Generated map file:

6.7 cheap-module-source-map

Because of the need for # module, add # loader in the case:

module: {
    rules: [{
        test: /\.css$/,
        use: [
            // Style loader: create a style tag, insert the style resource in js, and add it to the head to take effect
            'style-loader',
            // css loader: change the css file into a commonjs module and load it into js. The content inside is the style string
            'css-loader'
        ]
    }]
}

Create a new # index. In the # src # directory CSS} file, add style code:

body {
    margin: 0;
    padding: 0;
    height: 100%;
    background-color: pink;
}

Then in Src / index JS css :

//Introduce index css
import './index.css';

console.log('source map!!!')
var a = 1;
console.log(a, b); //This line is bound to report mistakes

Modify devtool:

devtool: 'cheap-module-source-map'

After packaging, open the browser and the style takes effect, indicating that the "loader" is successfully imported. You can view the exact information of the error code and the error location of the source code, but ignore the specific column (because it is b that causes the error):

Generated map , file, and package the information of , loader , together:

6.8 summary

(1) Development environment: it needs to be considered that the speed is fast and the debugging is more friendly

  • Fast (eval > inline > soap >...)

    1. eval-cheap-souce-map

    2. eval-source-map

  • More friendly debugging

    1. souce-map

    2. cheap-module-souce-map

    3. cheap-souce-map

Finally, the best two schemes are obtained -- > Eval source map (high integrity and fast inlining speed) / Eval soap module soap map (error prompt ignores columns but contains other information, fast inlining speed)

(2) Production environment: we need to consider whether the source code should be hidden and whether the debugging should be more friendly

  • Inlining will make the code larger, so you don't need to inline in the production environment

  • Hide source code

    1. Nosources source map # all hidden (packaged code and source code)

    2. Hidden source map only hides the source code and will prompt the error message of the code after construction

Finally, the best two schemes are obtained -- > source map (the most complete) / soap module soap map (error prompt: ignore a whole row of columns)

7, Summary

Source Map # is essential in our daily development process. It can help us debug and locate errors. Although it involves many knowledge points, such as VLQ[21], base64[22], etc., our core concern is its working principle and the application of {Source Map} in packaging tools, such as {webpack}.

Source Map , is very powerful. It is not only applied to daily development, but also can do more things, such as , performance anomaly monitoring platform. For example, FunDebug[23] is a website that restores the compressed code in the production environment through "Source Map", provides complete stack information, accurately locates the wrong source code, and helps users quickly repair "bugs". There are many cases like this.

In short, it is very necessary to learn # Source Map #.

Keywords: Javascript Front-end

Added by Indersingh on Tue, 15 Feb 2022 04:05:22 +0200