• Column address: Front-end compilation and engineering
  • Series of articles: Babel stuff, a preview of the Vue file CLI tool, learning about the Babel plug-in through a “snazzy” example, and getting into vue-Loader custom blocks
  • Author: Front-end Xiao Dong

What is Source Map

In plain English, a Source Map is an information file that stores the location information of the code after packaging and transformation. 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.

The code on our line is usually packaged, and if the code on our line is wrong, it can be very difficult to debug, as in the following example:

Compile this code using the packaging tool Webpack

console.log('source map!!! ')
console.log(a); // This line will definitely get an error
Copy the code

After the browser is opened:

Click to enter the error file:

There’s no way to find out where or why, so here’s where Source Map comes in. In the Webpack build code, open Source Map:

Then re-build and open the browser again:

It can be seen that the specific error location can be successfully located, which is the purpose of the Source Map. It is important to note that Source Map is not unique to Webpack. Other packaging tools also support Source Map, and the packaging tool just introduces the Source Map technology in a configuration way. The packaging tool is described below.

Ii. The role of Source Map

The above example is just the first experience of Source Map. Now let’s talk about its function. Why do we need Source Map?

Ruan yifeng’s JavaScript Source Map points out that JavaScript scripts are becoming more and more complex. Much of the source code, especially the various libraries and frameworks, has to be transformed before it can be put into production.

Common source code conversion, mainly the following three cases:

  • Compress, reduce the volume
  • Multiple files are merged to reduce the number of HTTP requests
  • Other languages compile to JavaScript

In all three cases, the actual running code is different from the development code, and debugging becomes difficult, which is why the Source Map is needed. With the above example, even the packaged code can find the specific error location, which makes it easy to debug the code, which is the problem Source Map is trying to solve.

3. How to generate Source Map

Source Map generation is supported by all major front-end task management tools and packaging tools.

3.1 UglifyJS

UglifyJS is a command-line tool for compressing JavaScript code

Install UglifyJS:

npm install uglify - js - g
Copy the code

Generate Source Map while compressing code:

uglifyjs app.js - o app.min.js--source - map app.min.js.map
Copy the code

Source Map options:

--source - map Source Map-- source-map-root Specifies the path of the source file -- source-map-url//#sourceMappingURL path. The default is the value specified by --source-map.-- source-map-include-sources whether to add source code content to sourcesContent array -- source-map-inline whether to include sourceMapWrite the last line of compressed code --in- source-map Indicates sourceMapIs used when the source file has been transformedCopy the code
3.2 Grunt

Grunt is a JavaScript project builder

Configure grunt-contrib-uglify to generate the Source Map:

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

When grunt-usemin is used to package source code, grunt-usemin calls grunt-contrib-concat and grunt-contrib-uglify in sequence to package and compress the source code. Therefore, both need to be configured:

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

Gulp is a JavaScript project building tool

Generate Source Map using gulp-sourcemaps:

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'));
});
Copy the code
3.4 SystemJS

SystemJS is the module loader

Generate Source Map with SystemJS Build Tool:

builder.bundle('myModule.js'.'outfile.js', {
    minify: true.sourceMaps: true
});
Copy the code
  • sourceMapContentsOption to specify whether to write the source codeSource Mapfile
3.5 Webpack

Webpack is the front-end packaging tool that will be used in the examples in this article. Set devtool in its configuration file webpack.config.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"
};
Copy the code
  • devtoolThere are more than 20 different values that generate different types ofSource MapCan be configured as required. It will be described in more detail below and will not be repeated here.
3.6 a Closure Compiler

Generated using Closure Compiler

How to use Source Map

After the Source Map is generated, it is generally used for debugging in the browser. The prerequisite is to enable the function. Take Chrome as an example:

Open developer tools and find Settins:

Check the following two options:

Going back to the above example, the Source file becomes index.js, and clicking on it shows the actual Source code, indicating that the Source Map was successfully opened and used

5. How Source Map works

Dist /bundld.js:

You can see this comment at the end:

//# sourceMappingURL=bundle.js.map
Copy the code

Because of this comment, the Source Map address of the file is marked so that the browser can correctly locate the Source code. SourceMappingURL Points to the URL of the Source Map file.

In addition to this approach, MDN states that this can be indicated by the SourceMap: < URL > field of the Response header.

> SourceMap: /path/to/file.js.map > ``` `dist 'folder, in addition to' bundle.js' and 'bundle.js.map', this file is the 'Source map' file, Also the URL to which 'sourceMappingURL' points! [] (https://files.mdnice.com/user/20608/9124506c-2e1d-410e-b141-97062b36fc3f.png) * ` version ` : ` Source map ` version, now provides ` v3 `. * 'sources' : the file before conversion. The item is an array indicating that there may be multiple file merges. * 'names' : names of all variables and attributes before conversion. * 'mappings' : a string that records location information, as described in the following sections. * 'file' : converted file name. * 'sourceRoot' : directory of the file before conversion. This item is empty if it is in the same directory as the file before the conversion. * 'sourcesContent' : the original content of the file before conversion. ##### 5.1 About the Source Map version In a 2009 Google article, when introducing the 'Cloure Compiler', Google also introduced a debugging thing: Firefox plugin 'Closure Inspector' to facilitate debugging of compiled code. This is the first generation of the Source Map! > 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. In 2010, in the second generation, 'Closure Compiler Source Map 2.0', 'Source Map' acknowledged the common 'JSON' format and other standards, almost in its current form. The biggest difference is the 'mapping' algorithm, which is also the key address of the 'Source Map'. The 'mapping' in the second generation has decided to use the 'base 64' encoding, but the algorithm is now outweighed, so the resulting '.map 'is now much larger. In 2011, **Source Map Revision 3 Proposal] (https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#) from the * *, This is also the 'Source Map' version we are using now. As the document's name suggests, the 'Source Map' has evolved out of the 'Clousre Compiler' and into a separate thing, supported by browsers. The biggest change in this version is the downsizing of the 'mapping' algorithm. Use [VLQ] (https://en.wikipedia.org/wiki/Variable-length_quantity) before the code generation [base64] (https://zh.wikipedia.org/zh-cn/Base64) 'mapping' greatly reduces the size of the '. Map 'file. The irony of the history of the Source Map is that it was developed as an add-on. After all, the policy that it facilitated was fading away, and it became the body of skill, written into the browser. > Source Map V1 the initial Source Map file is about 10 times the size of the converted file. Source Map V2 reduces this by 50%, and V3 reduces it by 50%. Therefore, the size of Source Map file corresponding to 133K files is about 300K. ##### 5.2 About the mappings attribute to avoid interference, change the case to the following: ' 'js var a = 1; console.log(a); `Copy the code

Package the compiled bundle.js file:

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

Package the 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": ""
}
Copy the code

You can see that the value of the mappings attribute is: AAAA; AACA, C, in order to explain this thing, we need to explain the structure of it. This is a string that has three layers:

  • The first layer is row correspondence, with a semicolon (;). Represents, each semicolon corresponds to a line of converted source code. So, everything before the first semicolon corresponds to the first line of the source code, and so on.
  • The second layer is position correspondence, represented by a comma (,), each comma corresponds to a position of the source code after conversion. So, whatever comes before the first comma corresponds to the first location of the source code, and so on.
  • The third layer is the position conversion, represented by VLQ code, which represents the corresponding source position before the conversion.

After going back to the source code, you can analyze:

  1. Because there are two lines in the source code, there is a semicolon that precedes the first and second lines. namelymappingsIn theAAAAandAACA,c.
  2. The semicolon follows the second line, the codeconsole.log(a);I can split it into two positionsconsoleandlog(a), so there is a comma. namelyAACA,cIn theAACAandc.

Summary, is converted source into two lines, the first line has a position, the second line has two positions.

As for this AAAA, AAcA and other letters are how to come, can refer to teacher Ruan Yifeng JavaScript Source Map explanation has made a detailed introduction. The author’s own understanding is:

AAAA, AAcA, and C all represent positions. Normally, each position consists of a maximum of five letters. The meanings of five letters are as follows:

  • First, which indicates the number of columns in which the position is located.
  • The second, indicating which file the location belongs to in the Sources property.
  • The third bit, indicating which line of code this position belongs to before the conversion.
  • The fourth bit, which indicates the number of columns in the code before the transformation.
  • The fifth digit, indicating which variable in the NAMES property this position belongs to.

There are only four letters at most, because there are nonenamesProperties.

Each position can be converted using VLQ code to form a mapping relationship. You can convert the test yourself at this site, to be AAAA; AACA, C results after conversion:

Two sets of data can be obtained:

[0.0.0.0]
[0.0.1.0], [14]
Copy the code

For example, the position AAAA is converted to [0, 0, 0, 0].

  1. Compress the first column of the code.
  2. The first source code file, i.eindex.js.
  3. The first line of the source code.
  4. Source code column 1

Var a = 1; var a = 1; This is where bundle.js is located in the packaged file.

Source Map in Webpack

The function and principle of Source Map are introduced above. Now let’s talk about the use of Source Map in the packaging tool WebPack. After all, we all need it in development.

To use Source Map, you only need to configure devtool in the webpack.config.js file

Of course, we can’t remember all of them. We can remember a few key ones:

The following seven alternatives are recommended:

  • Source-map: external. You can view error code exact information and source code error location.
  • inline-source-map: inline. Only one inline is generatedSource Map, you can view the error code exact information and source code error location
  • Hidden-source-map: external. You can see the exact information about the error code, but you can’t trace the source code error, only the error location of the built code.
  • eval-source-map: inline. For each file, the correspondingSource Map, all ineval, you can view the exact information about the error code and the error location of the source code.
  • Nosource-source-map: external. You can view the cause of the error code, but not the exact information about the error code, and there is no source code information.
  • Cheap -source-map: external. You can view the exact information of the error code and the error location of the source code, and can only pinpoint the error to the entire line, ignoring the column.
  • cheap-module-source-map: external. Can error code exact information and source code error location,moduleWill joinloaderSource Map.

The difference between inline and external:

  1. External generated files (.map), inline is not.
  2. Inline builds are faster.

The following are specific examples of the above seven types:

First, change the case to the error state. To reflect the column, change the source code to the following:

console.log('source map!!! ')
var a = 1;
console.log(a, b); // This line will definitely get an error
Copy the code
6.1 the source – the map
devtool: 'source-map'
Copy the code

After compiling, you can view the exact information about the error code and the error location of the source code:

A.map file is generated:

6.2 the inline – source – the map
devtool: 'inline-source-map'
Copy the code

After compiling, you can view the exact information about the error code and the error location of the source code:

Instead of generating a.map file, the sourceMappingURL is inserted as base64:

6.3 hidden – source – the map
devtool: 'hidden-source-map'
Copy the code

After compiling, you can see the exact information about the error code, but you cannot see the location of the source code:

A.map file is generated:

6.4 the eval – source – the map
devtool: 'eval-source-map'
Copy the code

After compiling, you can view the exact information about the error code and the error location of the source code:

Instead of generating a.map file, the eval function includes sourceMappingURL:

6.5 nosources – source – the map
devtool: 'nosources-source-map'
Copy the code

Error: error: error: error: error: error: error: error: error: error: error

A.map file is generated:

6.6 being – the source – the map
devtool: 'cheap-source-map'
Copy the code

After compiling, you can see the exact information of the error code and the error location of the source code, but ignore the specific column (because b causes the error) :

A.map file is generated:

6.7 being – the module – the source – the map

Since module is required, add loader to the case:

module: {
    rules: [{
        test: /\.css$/,
        use: [
            // style-loader: create style tag, insert style resource in js, add it to head
            'style-loader'.// css-loader: load a CSS file into a commonJS module. The content is a style string
            'css-loader']]}}Copy the code

Create a new index.css file in the SRC directory and add the style code:

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

Then add index.css to SRC /index.js:

/ / the introduction of the index. The CSS
import './index.css';

console.log('source map!!! ')
var a = 1;
console.log(a, b); // This line will definitely get an error
Copy the code

Modify devtool:

devtool: 'cheap-module-source-map'
Copy the code

After the loader is packaged, open the browser and the loader style takes effect. You can see the exact information of the error code and the error location of the source code, but ignore the specific column (because b causes the error) :

The.map file is generated, and loader information is packaged as well:

6.8 summarize

(1) Development environment: fast speed and friendly debugging should be considered

  • Speed (eval > inline > cheap>…).
    1. eval-cheap-souce-map
    2. eval-source-map
  • More debug friendly
    1. souce-map
    2. cheap-module-souce-map
    3. cheap-souce-map

> eval-source-map (high integrity, fast inline)/eval-cheap-module-souce-map (error message ignores columns but contains other information, fast inline)

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

  • Inlining makes code bigger, so it’s not used in production
  • Hiding source code
    1. nosources-source-mapAll hidden (packaged code and source code)
    2. hidden-source-mapHiding only the source code prompts post-build code error messages

Source-map (most complete)/cheap-module-souce-map (error: whole row ignored column)

Seven,

Source Map is an essential part of our daily development process. It helps us debug and locate errors. Although it involves a lot of knowledge, such as VLQ, Base64, etc., our core focus is on how it works and how Source Map can be used in packaging tools such as Webpack.

Source Map is powerful enough to do more than just daily development, such as a performance exception monitoring platform. For example, FunDebug uses Source Map to restore compressed code in production environment, provide complete stack information, locate error Source code accurately, and help users quickly fix bugs. There are many cases like this.

In short, it is necessary to learn Source Map.

Eight, reference

  • Introduction to JavaScript Source Maps
  • MDN
  • JavaScript Source Map details
  • VLQ
  • base64
  • base64vlq
  • FunDebug
  • Amazing, I didn’t expect a Source map to involve so many blind spots
  • Talk about how I get the front-end source of Zhihu