File: README.md

package info (click to toggle)
node-vlq 2.0.4-4
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 288 kB
  • sloc: javascript: 101; makefile: 2
file content (187 lines) | stat: -rw-r--r-- 5,681 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# Using vlq.js with source maps

This library doesn't include any special magic for dealing with source maps, just the low-level encoding/decoding. But it's actually fairly straightforward to convert an incomprehensible-looking string like this...

```
AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA
```

...into an array of mappings. Suppose we had some CoffeeScript code:

**helloworld.coffee**
```coffee
console.log 'hello world'
```

It would get transpiled into this:

**helloworld.js**
```js
(function() {
  console.log('hello world');

}).call(this);
```

And CoffeeScript would (if you asked it to) generate a sourcemap like this:

**helloworld.js.map**
```js
{
  "version": 3,
  "file": "helloworld.js",
  "sources": [
    "helloworld.coffee"
],
  "names": [],
  "mappings": "AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA"
}
```

(A source map simply a JSON object that adheres to a particular specification, [which you can find here](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1).)

Each line in the generated JavaScript (`helloworld.js`) is represented as a series of VLQ-encoded *segments*, separated by the `,` character. The lines themselves are separated by `;` characters. So we could represent the mapping like so:

```js
mappings = 'AAAA;AAAA,EAAA,OAAO,CAAC,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA';
vlqs = mappings.split(';').map(line => line.split(','));

[
  // line 0 of helloworld.js (everything is zero-based)
  ['AAAA'],

  // line 1
  ['AAAA', 'EAAA', 'OAAO', 'CAAC', 'GAAR', 'CAAY', 'aAAZ', 'CAAA', 'CAAA'],

  // line 2
  ['AAAA']
]
```

Using vlq.js to decode each segment, we can convert that into the following:

```js
decoded = vlqs.map(line => line.map(vlq.decode));

[
  // line 0
  [[0, 0, 0, 0]],

  // line 1
  [
    [0, 0, 0, 0],
    [2, 0, 0, 0],
    [7, 0, 0, 7],
    [1, 0, 0, 1],
    [3, 0, 0, -8],
    [1, 0, 0, 12],
    [13, 0, 0, -12],
    [1, 0, 0, 0],
    [1, 0, 0, 0]
  ],

  // line 2
  [[0, 0, 0, 0]]
]
```

Each segment has 4 *fields* in this case, though the [spec](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?hl=en_US&pli=1&pli=1) allows segments to have either 1, 4 or 5 fields (in other words, 2, 3, 4 and 5 below are optional - in our CoffeeScript example, the fifth field is never used). They are:

1. The zero-based starting column of the current line. If this is the first segment of the line, it's absolute, otherwise it's relative to the same field in the previous segment.
2. The zero-based index of the original **source file**, as listed in the source map object's `sources` array (since the generated code may be the result of combining several files), *relative to the previous value*.
3. The zero-based starting **line** in the original source code that this segment corresponds to, *relative to the previous value*.
4. The zero-based starting **column** in the original source code that this segment corresponds to, *relative to the previous value*.
5. The zero-based index of the **name**, as listed in the source map object's `names` array, that this mapping corresponds to, *relative to the previous value*. (This isn't used here because no names are changed, but it's useful when minifying JavaScript, since `myVeryLongVarName` will get changed to `a` or similar.)

We can now decode our mappings a bit further:

```js
let sourceFileIndex = 0;   // second field
let sourceCodeLine = 0;    // third field
let sourceCodeColumn = 0;  // fourth field
let nameIndex = 0;         // fifth field

decoded = decoded.map(line => {
  let generatedCodeColumn = 0; // first field - reset each time

  return line.map(segment => {
    generatedCodeColumn += segment[0];

    const result = [generatedCodeColumn];

    if (segment.length === 1) {
      // only one field!
      return result;
    }

    sourceFileIndex  += segment[1];
    sourceCodeLine   += segment[2];
    sourceCodeColumn += segment[3];

    result.push(sourceFileIndex, sourceCodeLine, sourceCodeColumn);

    if (segment.length === 5) {
      nameIndex += segment[4];
      result.push(nameIndex);
    }

    return result;
  });
});

[
  // line 0
  [[0, 0, 0, 0]],

  // line 1
  [
    [0, 0, 0, 0],
    [2, 0, 0, 0],
    [9, 0, 0, 7],
    [10, 0, 0, 8],
    [13, 0, 0, 0],
    [14, 0, 0, 12],
    [27, 0, 0, 0],
    [28, 0, 0, 0],
    [29, 0, 0, 0]
  ],

  // line 2
  [[0, 0, 0, 0]]
]
```

The first and third lines don't really contain any interesting information. But the second line does. Let's take the first three segments:

```js
// line 1 (the second line - still zero-based, remember)
[
  // Column 0 of line 1 corresponds to source file 0, line 0, column 0
  [0, 0, 0, 0],

  // Column 2 of line 1 also corresponds to 0, 0, 0! In other words, the
  // two spaces before `console` in helloworld.js don't correspond to
  // anything in helloworld.coffee
  [2, 0, 0, 0],

  // Column 9 of line 1 corresponds to 0, 0, 7. Taken together with the
  // previous segment, this means that columns 2-9 of line 1 in the
  // generated helloworld.js file correspond to columns 0-7 of line 0
  // in the original helloworld.coffee
  [9, 0, 0, 7],

  ...
]
```

It's through this fairly convoluted process that your browser (assuming it's one of the good ones) is able to read `helloworld.js` and an accompanying source map (typically `helloworld.js.map`) and do this:

### Chrome

![](https://github.com/Rich-Harris/vlq/blob/master/sourcemaps/Chrome.png)

### Firefox

![](https://github.com/Rich-Harris/vlq/blob/master/sourcemaps/Firefox.png)

You can try this for yourself by cloning this repo and opening the `sourcemaps/index.html` file.