Introduction to Javascript Source Maps

Original author: Ryan Seddon
  • Transfer
Have you ever thought how cool it would be if the code merged into a single file and minified javascript in a production environment could be conveniently read and even debugged without sacrificing performance? Now this is possible if you use a thing called source maps .

In short, this is a way to associate a minified / merged file with the files from which it came from. During assembly, for a combat environment, in addition to minifying and combining files, a mapper file is also generated that contains information about the source files. When an appeal is made to a specific place in a minified file, then a search is made in the mapper, which calculates the line and character in the source file. Developer Tools (WebKit nightly builds or Google Chrome Canary) can parse this file automatically and transparently replace files, as if working with the source files. At the time of writing ( original article - approx. Transl. ), Firefox has blocked the development of support for Source Map. Read more on the MozillaWiki Source Map .

An example is the correct determination of the place in the source code.

In this example, you can poke anywhere on the textarea with the right button and select "Get original location". In this case, an appeal will be made to the mapper file with the transfer of the line and character number in the minified code, and the corresponding piece of code from the source file will be shown. The console will display the line number and character number in the source file and other interesting information.

image

Real use


Before looking at the following example, you need to activate the source maps view in Chrome Canary or WebKit nightly. To do this, activate the “Enable source maps” item in the properties (see screenshot)
image

. Continue. The previous example was interesting, but how can this be used? Go to dev.fontdragr.com with your customized Google Chrome browser and you will see that the javascripts on the page are not compiled and you can watch individual js files. This is all thanks to the use of the mapper, but in fact the code on the page is compiled. All errors, log entries and breakpoints will be mapped to the source code, and it will be very convenient to debug the code. As a result, you can work with the production site as a test one.

Example - look at the console at fontdragr.com

Why do we need Source Maps?


Mapping now works only between the source files and the compressed / merged version, but there is talk of making mapping for languages ​​compiled in JavaScript (e.g. CoffeeScript), and even support for CSS preprocessors such as SASS and LESS.
In the future, we could easily use almost any language, as if it were natively supported by the browser:
  • CoffeeScript
  • ECMAScript 6 and above
  • SASS / LESS etc.
  • Almost any language that compiles in JavaScript

Check out the screencast in which CoffeeScript is debugged in an experimental build of the Firefox console:


The Google Web Toolkit (GWT) recently added support for Source Maps and Ray Cromwell of GWT made an excellent screencast showing how Source Map works in action.


Another example uses the Google Traceur library , which allows you to write in ES6 (ECMAScript 6) and compile into ES3-compatible code. The Traceur compiler also generates a source map. Look at an example of using ES6 features (classes and traits), as if they were natively supported by the browser. Textarea in the example also allows you to write ES6 code that will compile on the fly in ES3 and will also create a file-mapper. Example - you can write code on ES6 and immediately look in the debugger
image


How it works?


The only compiler / minifikator with Source Map support is the Closure compiler (how to generate a mapper during compilation is described below). When minifying JavaScript, a mapper file will also be created. So far, the Closure compiler does not add a special comment for Google Chrome Canary dev tools to the end of the file that a mapper file is available:
//@ sourceMappingURL=/path/to/file.js.map

Such a comment allows the browser to search for the right place in the source file using the mapper file. If you don’t like the idea of ​​using strange comments, you can add a special header to the compiled file:
X-SourceMap: /path/to/file.js.map

Like a comment, this will tell the client where to look for the mapper for this file. Using a header also allows you to work with languages ​​that do not support single-line comments.
image
The mapper file will be downloaded only if the property is enabled and the console is open. Well, of course, you will need to fill in the source files so that they are available in the paths indicated in the mapper.

How to generate a mapper file?


As mentioned above, you will need a Closure compiler for minifying, gluing and generating a mapper file for the necessary JavaScript files. To do this, run the command:
java -jar compiler.jar \ 
     --js script.js \
     --create_source_map ./script-min.js.map \
     --source_map_format=V3 \
     --js_output_file script-min.js

The necessary flags are --create_source_mapand --source_map_format. The latter is needed, because By default, the mapper is created in V2 format, and we need V3.

Source Map Internal Device


To better understand the Source Map, take for example a small file-mapper and analyze in detail how the “addressing” works. Below is a slightly modified example from the V3 spec :
{
    version : 3,
    file: "out.js",
    sourceRoot : "",
    sources: ["foo.js", "bar.js"],
    names: ["src", "maps", "are", "fun"],
    mappings: "AAgBC,SAAQ,CAAEA"
}


You may notice that this is a regular object literal containing all the necessary information:
  • Mapper version
  • The name of the minified / merged file for production
  • sourceRoot allows you to append a prefix to the path to the source files
  • sources contains source file names
  • names contains all real variable / function names from the resulting file
  • a mappingsis the corresponding minified names


BASE64 VLQ or how to make Source Map small


Initially, a very detailed output of all the dependencies was described in the specification, which made the file-mapper 10 times larger than the generated file. The second version reduced the file size by half, and the third version reduced it by half again. Now for a 133kB file ~ 300kB file-mapper is generated. How did you manage to achieve such a decrease and at the same time be able to track complex dependencies?
It uses VLQ (Variable Length Quantity) and Base64 encoding. Propertymappings- This is one very large line. Inside this line, semicolons (;) separate line numbers in the generated file. Inside the resulting string, commas are used to separate code segments. Each of the segments represents 1, 4 or 5 VLQ fields. Some may be longer due to the continuation bit. Each segment is built on the basis of the previous one, which helps to reduce the file size.
image
As stated earlier, each segment can be 1, 4, or five VLQs. The diagram shows 4 VLQs with one continuation bit. Let's analyze it in more detail and show how the mapper calculates the position in the source file. A segment consists of five things:
  • Character number in the generated file
  • Original file
  • Line number in source file
  • Character Number in Source File
  • Original name (if any)

( note perev .: I didn’t manage to fully translate this part of the article, it can be read in full in the original; if you want to help, write, I will be grateful )

Potential Issues with XSSI


The specs talk about potential issues with implementing XSS when using Source Map. You can get rid of it by writing " )]}" at the beginning of your map-file to make this js-file invalid and cause an error. WebKit dev tools already knows how to trap it:
if (response.slice(0, 3) === ")]}") {
    response = response.substring(response.indexOf('\n'));
}

As you can see, the first three characters are truncated and checked for compliance with the invalid code specified in the specification, and in this case everything is truncated to the next line feed character.

@sourceURLand displayNamein action: evaland anonymous functions


Although these two conventions are not yet included in the Source Map specification, they can greatly simplify work with evalanonymous functions.
The first helper is very similar to a property //@ sourceMappingURLand is actually mentioned in the specification (V3). By including this special comment in the code that will later be executed through eval, you can call evals, which will give them more logical names when working in the console. The following is a simple example using the CoffeeScript compiler:

Example - code passed through eval with the generated name
image

Another helper allows you to give names to anonymous functions using the property displayNamespecified in the context of this function. Profile this example to see displayNamein action.

Example - names for anonymous functions through displayName(only WebKit NIghtly)
image
When profiling, beautiful names will be displayed instead (anonymous function). But most likely displayNameit will not be included in the final build of Google Chrome. Although there is still hope, they also suggest renaming the property to debugName .
At the time of writing, naming the code made through evalis supported only by Firefox and Google Chrome. The property displayNameis only available in Google Chrome nightly builds.

Pour in


There is a very long discussion on supporting Source Map in CoffeeScript .
UglifyJS also has a ticket about supporting Source Map .
You can help if you participate in the discussion and give your opinion on the need for Source Map support. The more tools that support this technology, the easier it will be to work, so ask for its support in your favorite OpenSource project.

Source map not perfect


There is one trouble using Source Map for normal debugging. The problem is that when you try to check the value of an argument or variable defined in the context of the source file, the context will not return anything, because it really does not exist. We need some kind of reverse mapping to check the value of the corresponding variable / argument in the minified code and match it to the source code.
The problem is solved, and with due attention to the Source Map, even more interesting applications may appear.

Tools and Resources




Source Map is a powerful developer tool. It allows you to keep the production code as compressed as possible, but at the same time allows you to debug it. It is also useful for novice developers to view code written by experienced developers to learn how to structure and write their code correctly without having to wade through minified code. What are you waiting for? Generate a Source Map for your project!

Also popular now: