forked from jashkenas/coffeescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsourcemap.js
More file actions
211 lines (183 loc) · 7.83 KB
/
Copy pathsourcemap.js
File metadata and controls
211 lines (183 loc) · 7.83 KB
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Generated by CoffeeScript 2.3.2
(function() {
// Source maps allow JavaScript runtimes to match running JavaScript back to
// the original source code that corresponds to it. This can be minified
// JavaScript, but in our case, we're concerned with mapping pretty-printed
// JavaScript back to CoffeeScript.
// In order to produce maps, we must keep track of positions (line number, column number)
// that originated every node in the syntax tree, and be able to generate a
// [map file](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit)
// — which is a compact, VLQ-encoded representation of the JSON serialization
// of this information — to write out alongside the generated JavaScript.
// LineMap
// -------
// A **LineMap** object keeps track of information about original line and column
// positions for a single line of output JavaScript code.
// **SourceMaps** are implemented in terms of **LineMaps**.
var LineMap, SourceMap;
LineMap = class LineMap {
constructor(line1) {
this.line = line1;
this.columns = [];
}
add(column, [sourceLine, sourceColumn], options = {}) {
if (this.columns[column] && options.noReplace) {
return;
}
return this.columns[column] = {
line: this.line,
column,
sourceLine,
sourceColumn
};
}
sourceLocation(column) {
var mapping;
while (!((mapping = this.columns[column]) || (column <= 0))) {
column--;
}
return mapping && [mapping.sourceLine, mapping.sourceColumn];
}
};
SourceMap = (function() {
var BASE64_CHARS, VLQ_CONTINUATION_BIT, VLQ_SHIFT, VLQ_VALUE_MASK;
// SourceMap
// ---------
// Maps locations in a single generated JavaScript file back to locations in
// the original CoffeeScript source file.
// This is intentionally agnostic towards how a source map might be represented on
// disk. Once the compiler is ready to produce a "v3"-style source map, we can walk
// through the arrays of line and column buffer to produce it.
class SourceMap {
constructor() {
this.lines = [];
}
// Adds a mapping to this SourceMap. `sourceLocation` and `generatedLocation`
// are both `[line, column]` arrays. If `options.noReplace` is true, then if there
// is already a mapping for the specified `line` and `column`, this will have no
// effect.
add(sourceLocation, generatedLocation, options = {}) {
var base, column, line, lineMap;
[line, column] = generatedLocation;
lineMap = ((base = this.lines)[line] || (base[line] = new LineMap(line)));
return lineMap.add(column, sourceLocation, options);
}
// Look up the original position of a given `line` and `column` in the generated
// code.
sourceLocation([line, column]) {
var lineMap;
while (!((lineMap = this.lines[line]) || (line <= 0))) {
line--;
}
return lineMap && lineMap.sourceLocation(column);
}
// V3 SourceMap Generation
// -----------------------
// Builds up a V3 source map, returning the generated JSON as a string.
// `options.sourceRoot` may be used to specify the sourceRoot written to the source
// map. Also, `options.sourceFiles` and `options.generatedFile` may be passed to
// set "sources" and "file", respectively.
generate(options = {}, code = null) {
var buffer, i, j, lastColumn, lastSourceColumn, lastSourceLine, len, len1, lineMap, lineNumber, mapping, needComma, ref, ref1, sources, v3, writingline;
writingline = 0;
lastColumn = 0;
lastSourceLine = 0;
lastSourceColumn = 0;
needComma = false;
buffer = "";
ref = this.lines;
for (lineNumber = i = 0, len = ref.length; i < len; lineNumber = ++i) {
lineMap = ref[lineNumber];
if (lineMap) {
ref1 = lineMap.columns;
for (j = 0, len1 = ref1.length; j < len1; j++) {
mapping = ref1[j];
if (!(mapping)) {
continue;
}
while (writingline < mapping.line) {
lastColumn = 0;
needComma = false;
buffer += ";";
writingline++;
}
// Write a comma if we've already written a segment on this line.
if (needComma) {
buffer += ",";
needComma = false;
}
// Write the next segment. Segments can be 1, 4, or 5 values. If just one, then it
// is a generated column which doesn't match anything in the source code.
// The starting column in the generated source, relative to any previous recorded
// column for the current line:
buffer += this.encodeVlq(mapping.column - lastColumn);
lastColumn = mapping.column;
// The index into the list of sources:
buffer += this.encodeVlq(0);
// The starting line in the original source, relative to the previous source line.
buffer += this.encodeVlq(mapping.sourceLine - lastSourceLine);
lastSourceLine = mapping.sourceLine;
// The starting column in the original source, relative to the previous column.
buffer += this.encodeVlq(mapping.sourceColumn - lastSourceColumn);
lastSourceColumn = mapping.sourceColumn;
needComma = true;
}
}
}
// Produce the canonical JSON object format for a "v3" source map.
sources = options.sourceFiles ? options.sourceFiles : options.filename ? [options.filename] : ['<anonymous>'];
v3 = {
version: 3,
file: options.generatedFile || '',
sourceRoot: options.sourceRoot || '',
sources: sources,
names: [],
mappings: buffer
};
if (options.sourceMap || options.inlineMap) {
v3.sourcesContent = [code];
}
return v3;
}
encodeVlq(value) {
var answer, nextChunk, signBit, valueToEncode;
answer = '';
// Least significant bit represents the sign.
signBit = value < 0 ? 1 : 0;
// The next bits are the actual value.
valueToEncode = (Math.abs(value) << 1) + signBit;
// Make sure we encode at least one character, even if valueToEncode is 0.
while (valueToEncode || !answer) {
nextChunk = valueToEncode & VLQ_VALUE_MASK;
valueToEncode = valueToEncode >> VLQ_SHIFT;
if (valueToEncode) {
nextChunk |= VLQ_CONTINUATION_BIT;
}
answer += this.encodeBase64(nextChunk);
}
return answer;
}
encodeBase64(value) {
return BASE64_CHARS[value] || (function() {
throw new Error(`Cannot Base64 encode value: ${value}`);
})();
}
};
// Base64 VLQ Encoding
// -------------------
// Note that SourceMap VLQ encoding is "backwards". MIDI-style VLQ encoding puts
// the most-significant-bit (MSB) from the original value into the MSB of the VLQ
// encoded value (see [Wikipedia](https://en.wikipedia.org/wiki/File:Uintvar_coding.svg)).
// SourceMap VLQ does things the other way around, with the least significat four
// bits of the original value encoded into the first byte of the VLQ encoded value.
VLQ_SHIFT = 5;
VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT; // 0010 0000
VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - 1; // 0001 1111
// Regular Base64 Encoding
// -----------------------
BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
return SourceMap;
}).call(this);
// Our API for source maps is just the `SourceMap` class.
module.exports = SourceMap;
}).call(this);