Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80620 views
1
/* -*- Mode: js; js-indent-level: 2; -*- */
2
/*
3
* Copyright 2011 Mozilla Foundation and contributors
4
* Licensed under the New BSD license. See LICENSE or:
5
* http://opensource.org/licenses/BSD-3-Clause
6
*/
7
if (typeof define !== 'function') {
8
var define = require('amdefine')(module, require);
9
}
10
define(function (require, exports, module) {
11
12
var util = require('./util');
13
var binarySearch = require('./binary-search');
14
var SourceMapConsumer = require('./source-map-consumer').SourceMapConsumer;
15
var BasicSourceMapConsumer = require('./basic-source-map-consumer').BasicSourceMapConsumer;
16
17
/**
18
* An IndexedSourceMapConsumer instance represents a parsed source map which
19
* we can query for information. It differs from BasicSourceMapConsumer in
20
* that it takes "indexed" source maps (i.e. ones with a "sections" field) as
21
* input.
22
*
23
* The only parameter is a raw source map (either as a JSON string, or already
24
* parsed to an object). According to the spec for indexed source maps, they
25
* have the following attributes:
26
*
27
* - version: Which version of the source map spec this map is following.
28
* - file: Optional. The generated file this source map is associated with.
29
* - sections: A list of section definitions.
30
*
31
* Each value under the "sections" field has two fields:
32
* - offset: The offset into the original specified at which this section
33
* begins to apply, defined as an object with a "line" and "column"
34
* field.
35
* - map: A source map definition. This source map could also be indexed,
36
* but doesn't have to be.
37
*
38
* Instead of the "map" field, it's also possible to have a "url" field
39
* specifying a URL to retrieve a source map from, but that's currently
40
* unsupported.
41
*
42
* Here's an example source map, taken from the source map spec[0], but
43
* modified to omit a section which uses the "url" field.
44
*
45
* {
46
* version : 3,
47
* file: "app.js",
48
* sections: [{
49
* offset: {line:100, column:10},
50
* map: {
51
* version : 3,
52
* file: "section.js",
53
* sources: ["foo.js", "bar.js"],
54
* names: ["src", "maps", "are", "fun"],
55
* mappings: "AAAA,E;;ABCDE;"
56
* }
57
* }],
58
* }
59
*
60
* [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
61
*/
62
function IndexedSourceMapConsumer(aSourceMap) {
63
var sourceMap = aSourceMap;
64
if (typeof aSourceMap === 'string') {
65
sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
66
}
67
68
var version = util.getArg(sourceMap, 'version');
69
var sections = util.getArg(sourceMap, 'sections');
70
71
if (version != this._version) {
72
throw new Error('Unsupported version: ' + version);
73
}
74
75
var lastOffset = {
76
line: -1,
77
column: 0
78
};
79
this._sections = sections.map(function (s) {
80
if (s.url) {
81
// The url field will require support for asynchronicity.
82
// See https://github.com/mozilla/source-map/issues/16
83
throw new Error('Support for url field in sections not implemented.');
84
}
85
var offset = util.getArg(s, 'offset');
86
var offsetLine = util.getArg(offset, 'line');
87
var offsetColumn = util.getArg(offset, 'column');
88
89
if (offsetLine < lastOffset.line ||
90
(offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {
91
throw new Error('Section offsets must be ordered and non-overlapping.');
92
}
93
lastOffset = offset;
94
95
return {
96
generatedOffset: {
97
// The offset fields are 0-based, but we use 1-based indices when
98
// encoding/decoding from VLQ.
99
generatedLine: offsetLine + 1,
100
generatedColumn: offsetColumn + 1
101
},
102
consumer: new SourceMapConsumer(util.getArg(s, 'map'))
103
}
104
});
105
}
106
107
IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
108
IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;
109
110
/**
111
* The version of the source mapping spec that we are consuming.
112
*/
113
IndexedSourceMapConsumer.prototype._version = 3;
114
115
/**
116
* The list of original sources.
117
*/
118
Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
119
get: function () {
120
var sources = [];
121
for (var i = 0; i < this._sections.length; i++) {
122
for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {
123
sources.push(this._sections[i].consumer.sources[j]);
124
}
125
};
126
return sources;
127
}
128
});
129
130
/**
131
* Returns the original source, line, and column information for the generated
132
* source's line and column positions provided. The only argument is an object
133
* with the following properties:
134
*
135
* - line: The line number in the generated source.
136
* - column: The column number in the generated source.
137
*
138
* and an object is returned with the following properties:
139
*
140
* - source: The original source file, or null.
141
* - line: The line number in the original source, or null.
142
* - column: The column number in the original source, or null.
143
* - name: The original identifier, or null.
144
*/
145
IndexedSourceMapConsumer.prototype.originalPositionFor =
146
function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
147
var needle = {
148
generatedLine: util.getArg(aArgs, 'line'),
149
generatedColumn: util.getArg(aArgs, 'column')
150
};
151
152
// Find the section containing the generated position we're trying to map
153
// to an original position.
154
var sectionIndex = binarySearch.search(needle, this._sections,
155
function(needle, section) {
156
var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
157
if (cmp) {
158
return cmp;
159
}
160
161
return (needle.generatedColumn -
162
section.generatedOffset.generatedColumn);
163
}, binarySearch.GREATEST_LOWER_BOUND);
164
var section = this._sections[sectionIndex];
165
166
if (!section) {
167
return {
168
source: null,
169
line: null,
170
column: null,
171
name: null
172
};
173
}
174
175
return section.consumer.originalPositionFor({
176
line: needle.generatedLine -
177
(section.generatedOffset.generatedLine - 1),
178
column: needle.generatedColumn -
179
(section.generatedOffset.generatedLine === needle.generatedLine
180
? section.generatedOffset.generatedColumn - 1
181
: 0)
182
});
183
};
184
185
/**
186
* Returns the original source content. The only argument is the url of the
187
* original source file. Returns null if no original source content is
188
* available.
189
*/
190
IndexedSourceMapConsumer.prototype.sourceContentFor =
191
function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
192
for (var i = 0; i < this._sections.length; i++) {
193
var section = this._sections[i];
194
195
var content = section.consumer.sourceContentFor(aSource, true);
196
if (content) {
197
return content;
198
}
199
}
200
if (nullOnMissing) {
201
return null;
202
}
203
else {
204
throw new Error('"' + aSource + '" is not in the SourceMap.');
205
}
206
};
207
208
/**
209
* Returns the generated line and column information for the original source,
210
* line, and column positions provided. The only argument is an object with
211
* the following properties:
212
*
213
* - source: The filename of the original source.
214
* - line: The line number in the original source.
215
* - column: The column number in the original source.
216
*
217
* and an object is returned with the following properties:
218
*
219
* - line: The line number in the generated source, or null.
220
* - column: The column number in the generated source, or null.
221
*/
222
IndexedSourceMapConsumer.prototype.generatedPositionFor =
223
function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
224
for (var i = 0; i < this._sections.length; i++) {
225
var section = this._sections[i];
226
227
// Only consider this section if the requested source is in the list of
228
// sources of the consumer.
229
if (section.consumer.sources.indexOf(util.getArg(aArgs, 'source')) === -1) {
230
continue;
231
}
232
var generatedPosition = section.consumer.generatedPositionFor(aArgs);
233
if (generatedPosition) {
234
var ret = {
235
line: generatedPosition.line +
236
(section.generatedOffset.generatedLine - 1),
237
column: generatedPosition.column +
238
(section.generatedOffset.generatedLine === generatedPosition.line
239
? section.generatedOffset.generatedColumn - 1
240
: 0)
241
};
242
return ret;
243
}
244
}
245
246
return {
247
line: null,
248
column: null
249
};
250
};
251
252
/**
253
* Parse the mappings in a string in to a data structure which we can easily
254
* query (the ordered arrays in the `this.__generatedMappings` and
255
* `this.__originalMappings` properties).
256
*/
257
IndexedSourceMapConsumer.prototype._parseMappings =
258
function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
259
this.__generatedMappings = [];
260
this.__originalMappings = [];
261
for (var i = 0; i < this._sections.length; i++) {
262
var section = this._sections[i];
263
var sectionMappings = section.consumer._generatedMappings;
264
for (var j = 0; j < sectionMappings.length; j++) {
265
var mapping = sectionMappings[i];
266
267
var source = mapping.source;
268
var sourceRoot = section.consumer.sourceRoot;
269
270
if (source != null && sourceRoot != null) {
271
source = util.join(sourceRoot, source);
272
}
273
274
// The mappings coming from the consumer for the section have
275
// generated positions relative to the start of the section, so we
276
// need to offset them to be relative to the start of the concatenated
277
// generated file.
278
var adjustedMapping = {
279
source: source,
280
generatedLine: mapping.generatedLine +
281
(section.generatedOffset.generatedLine - 1),
282
generatedColumn: mapping.column +
283
(section.generatedOffset.generatedLine === mapping.generatedLine)
284
? section.generatedOffset.generatedColumn - 1
285
: 0,
286
originalLine: mapping.originalLine,
287
originalColumn: mapping.originalColumn,
288
name: mapping.name
289
};
290
291
this.__generatedMappings.push(adjustedMapping);
292
if (typeof adjustedMapping.originalLine === 'number') {
293
this.__originalMappings.push(adjustedMapping);
294
}
295
};
296
};
297
298
this.__generatedMappings.sort(util.compareByGeneratedPositions);
299
this.__originalMappings.sort(util.compareByOriginalPositions);
300
};
301
302
exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
303
});
304
305