source: imaps-frontend/node_modules/source-map-js/lib/source-map-consumer.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 40.5 KB
Line 
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
8var util = require('./util');
9var binarySearch = require('./binary-search');
10var ArraySet = require('./array-set').ArraySet;
11var base64VLQ = require('./base64-vlq');
12var quickSort = require('./quick-sort').quickSort;
13
14function SourceMapConsumer(aSourceMap, aSourceMapURL) {
15 var sourceMap = aSourceMap;
16 if (typeof aSourceMap === 'string') {
17 sourceMap = util.parseSourceMapInput(aSourceMap);
18 }
19
20 return sourceMap.sections != null
21 ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
22 : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
23}
24
25SourceMapConsumer.fromSourceMap = function(aSourceMap, aSourceMapURL) {
26 return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
27}
28
29/**
30 * The version of the source mapping spec that we are consuming.
31 */
32SourceMapConsumer.prototype._version = 3;
33
34// `__generatedMappings` and `__originalMappings` are arrays that hold the
35// parsed mapping coordinates from the source map's "mappings" attribute. They
36// are lazily instantiated, accessed via the `_generatedMappings` and
37// `_originalMappings` getters respectively, and we only parse the mappings
38// and create these arrays once queried for a source location. We jump through
39// these hoops because there can be many thousands of mappings, and parsing
40// them is expensive, so we only want to do it if we must.
41//
42// Each object in the arrays is of the form:
43//
44// {
45// generatedLine: The line number in the generated code,
46// generatedColumn: The column number in the generated code,
47// source: The path to the original source file that generated this
48// chunk of code,
49// originalLine: The line number in the original source that
50// corresponds to this chunk of generated code,
51// originalColumn: The column number in the original source that
52// corresponds to this chunk of generated code,
53// name: The name of the original symbol which generated this chunk of
54// code.
55// }
56//
57// All properties except for `generatedLine` and `generatedColumn` can be
58// `null`.
59//
60// `_generatedMappings` is ordered by the generated positions.
61//
62// `_originalMappings` is ordered by the original positions.
63
64SourceMapConsumer.prototype.__generatedMappings = null;
65Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
66 configurable: true,
67 enumerable: true,
68 get: function () {
69 if (!this.__generatedMappings) {
70 this._parseMappings(this._mappings, this.sourceRoot);
71 }
72
73 return this.__generatedMappings;
74 }
75});
76
77SourceMapConsumer.prototype.__originalMappings = null;
78Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
79 configurable: true,
80 enumerable: true,
81 get: function () {
82 if (!this.__originalMappings) {
83 this._parseMappings(this._mappings, this.sourceRoot);
84 }
85
86 return this.__originalMappings;
87 }
88});
89
90SourceMapConsumer.prototype._charIsMappingSeparator =
91 function SourceMapConsumer_charIsMappingSeparator(aStr, index) {
92 var c = aStr.charAt(index);
93 return c === ";" || c === ",";
94 };
95
96/**
97 * Parse the mappings in a string in to a data structure which we can easily
98 * query (the ordered arrays in the `this.__generatedMappings` and
99 * `this.__originalMappings` properties).
100 */
101SourceMapConsumer.prototype._parseMappings =
102 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
103 throw new Error("Subclasses must implement _parseMappings");
104 };
105
106SourceMapConsumer.GENERATED_ORDER = 1;
107SourceMapConsumer.ORIGINAL_ORDER = 2;
108
109SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
110SourceMapConsumer.LEAST_UPPER_BOUND = 2;
111
112/**
113 * Iterate over each mapping between an original source/line/column and a
114 * generated line/column in this source map.
115 *
116 * @param Function aCallback
117 * The function that is called with each mapping.
118 * @param Object aContext
119 * Optional. If specified, this object will be the value of `this` every
120 * time that `aCallback` is called.
121 * @param aOrder
122 * Either `SourceMapConsumer.GENERATED_ORDER` or
123 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
124 * iterate over the mappings sorted by the generated file's line/column
125 * order or the original's source/line/column order, respectively. Defaults to
126 * `SourceMapConsumer.GENERATED_ORDER`.
127 */
128SourceMapConsumer.prototype.eachMapping =
129 function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
130 var context = aContext || null;
131 var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
132
133 var mappings;
134 switch (order) {
135 case SourceMapConsumer.GENERATED_ORDER:
136 mappings = this._generatedMappings;
137 break;
138 case SourceMapConsumer.ORIGINAL_ORDER:
139 mappings = this._originalMappings;
140 break;
141 default:
142 throw new Error("Unknown order of iteration.");
143 }
144
145 var sourceRoot = this.sourceRoot;
146 var boundCallback = aCallback.bind(context);
147 var names = this._names;
148 var sources = this._sources;
149 var sourceMapURL = this._sourceMapURL;
150
151 for (var i = 0, n = mappings.length; i < n; i++) {
152 var mapping = mappings[i];
153 var source = mapping.source === null ? null : sources.at(mapping.source);
154 source = util.computeSourceURL(sourceRoot, source, sourceMapURL);
155 boundCallback({
156 source: source,
157 generatedLine: mapping.generatedLine,
158 generatedColumn: mapping.generatedColumn,
159 originalLine: mapping.originalLine,
160 originalColumn: mapping.originalColumn,
161 name: mapping.name === null ? null : names.at(mapping.name)
162 });
163 }
164 };
165
166/**
167 * Returns all generated line and column information for the original source,
168 * line, and column provided. If no column is provided, returns all mappings
169 * corresponding to a either the line we are searching for or the next
170 * closest line that has any mappings. Otherwise, returns all mappings
171 * corresponding to the given line and either the column we are searching for
172 * or the next closest column that has any offsets.
173 *
174 * The only argument is an object with the following properties:
175 *
176 * - source: The filename of the original source.
177 * - line: The line number in the original source. The line number is 1-based.
178 * - column: Optional. the column number in the original source.
179 * The column number is 0-based.
180 *
181 * and an array of objects is returned, each with the following properties:
182 *
183 * - line: The line number in the generated source, or null. The
184 * line number is 1-based.
185 * - column: The column number in the generated source, or null.
186 * The column number is 0-based.
187 */
188SourceMapConsumer.prototype.allGeneratedPositionsFor =
189 function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {
190 var line = util.getArg(aArgs, 'line');
191
192 // When there is no exact match, BasicSourceMapConsumer.prototype._findMapping
193 // returns the index of the closest mapping less than the needle. By
194 // setting needle.originalColumn to 0, we thus find the last mapping for
195 // the given line, provided such a mapping exists.
196 var needle = {
197 source: util.getArg(aArgs, 'source'),
198 originalLine: line,
199 originalColumn: util.getArg(aArgs, 'column', 0)
200 };
201
202 needle.source = this._findSourceIndex(needle.source);
203 if (needle.source < 0) {
204 return [];
205 }
206
207 var mappings = [];
208
209 var index = this._findMapping(needle,
210 this._originalMappings,
211 "originalLine",
212 "originalColumn",
213 util.compareByOriginalPositions,
214 binarySearch.LEAST_UPPER_BOUND);
215 if (index >= 0) {
216 var mapping = this._originalMappings[index];
217
218 if (aArgs.column === undefined) {
219 var originalLine = mapping.originalLine;
220
221 // Iterate until either we run out of mappings, or we run into
222 // a mapping for a different line than the one we found. Since
223 // mappings are sorted, this is guaranteed to find all mappings for
224 // the line we found.
225 while (mapping && mapping.originalLine === originalLine) {
226 mappings.push({
227 line: util.getArg(mapping, 'generatedLine', null),
228 column: util.getArg(mapping, 'generatedColumn', null),
229 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
230 });
231
232 mapping = this._originalMappings[++index];
233 }
234 } else {
235 var originalColumn = mapping.originalColumn;
236
237 // Iterate until either we run out of mappings, or we run into
238 // a mapping for a different line than the one we were searching for.
239 // Since mappings are sorted, this is guaranteed to find all mappings for
240 // the line we are searching for.
241 while (mapping &&
242 mapping.originalLine === line &&
243 mapping.originalColumn == originalColumn) {
244 mappings.push({
245 line: util.getArg(mapping, 'generatedLine', null),
246 column: util.getArg(mapping, 'generatedColumn', null),
247 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
248 });
249
250 mapping = this._originalMappings[++index];
251 }
252 }
253 }
254
255 return mappings;
256 };
257
258exports.SourceMapConsumer = SourceMapConsumer;
259
260/**
261 * A BasicSourceMapConsumer instance represents a parsed source map which we can
262 * query for information about the original file positions by giving it a file
263 * position in the generated source.
264 *
265 * The first parameter is the raw source map (either as a JSON string, or
266 * already parsed to an object). According to the spec, source maps have the
267 * following attributes:
268 *
269 * - version: Which version of the source map spec this map is following.
270 * - sources: An array of URLs to the original source files.
271 * - names: An array of identifiers which can be referrenced by individual mappings.
272 * - sourceRoot: Optional. The URL root from which all sources are relative.
273 * - sourcesContent: Optional. An array of contents of the original source files.
274 * - mappings: A string of base64 VLQs which contain the actual mappings.
275 * - file: Optional. The generated file this source map is associated with.
276 *
277 * Here is an example source map, taken from the source map spec[0]:
278 *
279 * {
280 * version : 3,
281 * file: "out.js",
282 * sourceRoot : "",
283 * sources: ["foo.js", "bar.js"],
284 * names: ["src", "maps", "are", "fun"],
285 * mappings: "AA,AB;;ABCDE;"
286 * }
287 *
288 * The second parameter, if given, is a string whose value is the URL
289 * at which the source map was found. This URL is used to compute the
290 * sources array.
291 *
292 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
293 */
294function BasicSourceMapConsumer(aSourceMap, aSourceMapURL) {
295 var sourceMap = aSourceMap;
296 if (typeof aSourceMap === 'string') {
297 sourceMap = util.parseSourceMapInput(aSourceMap);
298 }
299
300 var version = util.getArg(sourceMap, 'version');
301 var sources = util.getArg(sourceMap, 'sources');
302 // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
303 // requires the array) to play nice here.
304 var names = util.getArg(sourceMap, 'names', []);
305 var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
306 var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
307 var mappings = util.getArg(sourceMap, 'mappings');
308 var file = util.getArg(sourceMap, 'file', null);
309
310 // Once again, Sass deviates from the spec and supplies the version as a
311 // string rather than a number, so we use loose equality checking here.
312 if (version != this._version) {
313 throw new Error('Unsupported version: ' + version);
314 }
315
316 if (sourceRoot) {
317 sourceRoot = util.normalize(sourceRoot);
318 }
319
320 sources = sources
321 .map(String)
322 // Some source maps produce relative source paths like "./foo.js" instead of
323 // "foo.js". Normalize these first so that future comparisons will succeed.
324 // See bugzil.la/1090768.
325 .map(util.normalize)
326 // Always ensure that absolute sources are internally stored relative to
327 // the source root, if the source root is absolute. Not doing this would
328 // be particularly problematic when the source root is a prefix of the
329 // source (valid, but why??). See github issue #199 and bugzil.la/1188982.
330 .map(function (source) {
331 return sourceRoot && util.isAbsolute(sourceRoot) && util.isAbsolute(source)
332 ? util.relative(sourceRoot, source)
333 : source;
334 });
335
336 // Pass `true` below to allow duplicate names and sources. While source maps
337 // are intended to be compressed and deduplicated, the TypeScript compiler
338 // sometimes generates source maps with duplicates in them. See Github issue
339 // #72 and bugzil.la/889492.
340 this._names = ArraySet.fromArray(names.map(String), true);
341 this._sources = ArraySet.fromArray(sources, true);
342
343 this._absoluteSources = this._sources.toArray().map(function (s) {
344 return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
345 });
346
347 this.sourceRoot = sourceRoot;
348 this.sourcesContent = sourcesContent;
349 this._mappings = mappings;
350 this._sourceMapURL = aSourceMapURL;
351 this.file = file;
352}
353
354BasicSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
355BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
356
357/**
358 * Utility function to find the index of a source. Returns -1 if not
359 * found.
360 */
361BasicSourceMapConsumer.prototype._findSourceIndex = function(aSource) {
362 var relativeSource = aSource;
363 if (this.sourceRoot != null) {
364 relativeSource = util.relative(this.sourceRoot, relativeSource);
365 }
366
367 if (this._sources.has(relativeSource)) {
368 return this._sources.indexOf(relativeSource);
369 }
370
371 // Maybe aSource is an absolute URL as returned by |sources|. In
372 // this case we can't simply undo the transform.
373 var i;
374 for (i = 0; i < this._absoluteSources.length; ++i) {
375 if (this._absoluteSources[i] == aSource) {
376 return i;
377 }
378 }
379
380 return -1;
381};
382
383/**
384 * Create a BasicSourceMapConsumer from a SourceMapGenerator.
385 *
386 * @param SourceMapGenerator aSourceMap
387 * The source map that will be consumed.
388 * @param String aSourceMapURL
389 * The URL at which the source map can be found (optional)
390 * @returns BasicSourceMapConsumer
391 */
392BasicSourceMapConsumer.fromSourceMap =
393 function SourceMapConsumer_fromSourceMap(aSourceMap, aSourceMapURL) {
394 var smc = Object.create(BasicSourceMapConsumer.prototype);
395
396 var names = smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
397 var sources = smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
398 smc.sourceRoot = aSourceMap._sourceRoot;
399 smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
400 smc.sourceRoot);
401 smc.file = aSourceMap._file;
402 smc._sourceMapURL = aSourceMapURL;
403 smc._absoluteSources = smc._sources.toArray().map(function (s) {
404 return util.computeSourceURL(smc.sourceRoot, s, aSourceMapURL);
405 });
406
407 // Because we are modifying the entries (by converting string sources and
408 // names to indices into the sources and names ArraySets), we have to make
409 // a copy of the entry or else bad things happen. Shared mutable state
410 // strikes again! See github issue #191.
411
412 var generatedMappings = aSourceMap._mappings.toArray().slice();
413 var destGeneratedMappings = smc.__generatedMappings = [];
414 var destOriginalMappings = smc.__originalMappings = [];
415
416 for (var i = 0, length = generatedMappings.length; i < length; i++) {
417 var srcMapping = generatedMappings[i];
418 var destMapping = new Mapping;
419 destMapping.generatedLine = srcMapping.generatedLine;
420 destMapping.generatedColumn = srcMapping.generatedColumn;
421
422 if (srcMapping.source) {
423 destMapping.source = sources.indexOf(srcMapping.source);
424 destMapping.originalLine = srcMapping.originalLine;
425 destMapping.originalColumn = srcMapping.originalColumn;
426
427 if (srcMapping.name) {
428 destMapping.name = names.indexOf(srcMapping.name);
429 }
430
431 destOriginalMappings.push(destMapping);
432 }
433
434 destGeneratedMappings.push(destMapping);
435 }
436
437 quickSort(smc.__originalMappings, util.compareByOriginalPositions);
438
439 return smc;
440 };
441
442/**
443 * The version of the source mapping spec that we are consuming.
444 */
445BasicSourceMapConsumer.prototype._version = 3;
446
447/**
448 * The list of original sources.
449 */
450Object.defineProperty(BasicSourceMapConsumer.prototype, 'sources', {
451 get: function () {
452 return this._absoluteSources.slice();
453 }
454});
455
456/**
457 * Provide the JIT with a nice shape / hidden class.
458 */
459function Mapping() {
460 this.generatedLine = 0;
461 this.generatedColumn = 0;
462 this.source = null;
463 this.originalLine = null;
464 this.originalColumn = null;
465 this.name = null;
466}
467
468/**
469 * Parse the mappings in a string in to a data structure which we can easily
470 * query (the ordered arrays in the `this.__generatedMappings` and
471 * `this.__originalMappings` properties).
472 */
473
474const compareGenerated = util.compareByGeneratedPositionsDeflatedNoLine;
475function sortGenerated(array, start) {
476 let l = array.length;
477 let n = array.length - start;
478 if (n <= 1) {
479 return;
480 } else if (n == 2) {
481 let a = array[start];
482 let b = array[start + 1];
483 if (compareGenerated(a, b) > 0) {
484 array[start] = b;
485 array[start + 1] = a;
486 }
487 } else if (n < 20) {
488 for (let i = start; i < l; i++) {
489 for (let j = i; j > start; j--) {
490 let a = array[j - 1];
491 let b = array[j];
492 if (compareGenerated(a, b) <= 0) {
493 break;
494 }
495 array[j - 1] = b;
496 array[j] = a;
497 }
498 }
499 } else {
500 quickSort(array, compareGenerated, start);
501 }
502}
503BasicSourceMapConsumer.prototype._parseMappings =
504 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
505 var generatedLine = 1;
506 var previousGeneratedColumn = 0;
507 var previousOriginalLine = 0;
508 var previousOriginalColumn = 0;
509 var previousSource = 0;
510 var previousName = 0;
511 var length = aStr.length;
512 var index = 0;
513 var cachedSegments = {};
514 var temp = {};
515 var originalMappings = [];
516 var generatedMappings = [];
517 var mapping, str, segment, end, value;
518
519 let subarrayStart = 0;
520 while (index < length) {
521 if (aStr.charAt(index) === ';') {
522 generatedLine++;
523 index++;
524 previousGeneratedColumn = 0;
525
526 sortGenerated(generatedMappings, subarrayStart);
527 subarrayStart = generatedMappings.length;
528 }
529 else if (aStr.charAt(index) === ',') {
530 index++;
531 }
532 else {
533 mapping = new Mapping();
534 mapping.generatedLine = generatedLine;
535
536 for (end = index; end < length; end++) {
537 if (this._charIsMappingSeparator(aStr, end)) {
538 break;
539 }
540 }
541 str = aStr.slice(index, end);
542
543 segment = [];
544 while (index < end) {
545 base64VLQ.decode(aStr, index, temp);
546 value = temp.value;
547 index = temp.rest;
548 segment.push(value);
549 }
550
551 if (segment.length === 2) {
552 throw new Error('Found a source, but no line and column');
553 }
554
555 if (segment.length === 3) {
556 throw new Error('Found a source and line, but no column');
557 }
558
559 // Generated column.
560 mapping.generatedColumn = previousGeneratedColumn + segment[0];
561 previousGeneratedColumn = mapping.generatedColumn;
562
563 if (segment.length > 1) {
564 // Original source.
565 mapping.source = previousSource + segment[1];
566 previousSource += segment[1];
567
568 // Original line.
569 mapping.originalLine = previousOriginalLine + segment[2];
570 previousOriginalLine = mapping.originalLine;
571 // Lines are stored 0-based
572 mapping.originalLine += 1;
573
574 // Original column.
575 mapping.originalColumn = previousOriginalColumn + segment[3];
576 previousOriginalColumn = mapping.originalColumn;
577
578 if (segment.length > 4) {
579 // Original name.
580 mapping.name = previousName + segment[4];
581 previousName += segment[4];
582 }
583 }
584
585 generatedMappings.push(mapping);
586 if (typeof mapping.originalLine === 'number') {
587 let currentSource = mapping.source;
588 while (originalMappings.length <= currentSource) {
589 originalMappings.push(null);
590 }
591 if (originalMappings[currentSource] === null) {
592 originalMappings[currentSource] = [];
593 }
594 originalMappings[currentSource].push(mapping);
595 }
596 }
597 }
598
599 sortGenerated(generatedMappings, subarrayStart);
600 this.__generatedMappings = generatedMappings;
601
602 for (var i = 0; i < originalMappings.length; i++) {
603 if (originalMappings[i] != null) {
604 quickSort(originalMappings[i], util.compareByOriginalPositionsNoSource);
605 }
606 }
607 this.__originalMappings = [].concat(...originalMappings);
608 };
609
610/**
611 * Find the mapping that best matches the hypothetical "needle" mapping that
612 * we are searching for in the given "haystack" of mappings.
613 */
614BasicSourceMapConsumer.prototype._findMapping =
615 function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
616 aColumnName, aComparator, aBias) {
617 // To return the position we are searching for, we must first find the
618 // mapping for the given position and then return the opposite position it
619 // points to. Because the mappings are sorted, we can use binary search to
620 // find the best mapping.
621
622 if (aNeedle[aLineName] <= 0) {
623 throw new TypeError('Line must be greater than or equal to 1, got '
624 + aNeedle[aLineName]);
625 }
626 if (aNeedle[aColumnName] < 0) {
627 throw new TypeError('Column must be greater than or equal to 0, got '
628 + aNeedle[aColumnName]);
629 }
630
631 return binarySearch.search(aNeedle, aMappings, aComparator, aBias);
632 };
633
634/**
635 * Compute the last column for each generated mapping. The last column is
636 * inclusive.
637 */
638BasicSourceMapConsumer.prototype.computeColumnSpans =
639 function SourceMapConsumer_computeColumnSpans() {
640 for (var index = 0; index < this._generatedMappings.length; ++index) {
641 var mapping = this._generatedMappings[index];
642
643 // Mappings do not contain a field for the last generated columnt. We
644 // can come up with an optimistic estimate, however, by assuming that
645 // mappings are contiguous (i.e. given two consecutive mappings, the
646 // first mapping ends where the second one starts).
647 if (index + 1 < this._generatedMappings.length) {
648 var nextMapping = this._generatedMappings[index + 1];
649
650 if (mapping.generatedLine === nextMapping.generatedLine) {
651 mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;
652 continue;
653 }
654 }
655
656 // The last mapping for each line spans the entire line.
657 mapping.lastGeneratedColumn = Infinity;
658 }
659 };
660
661/**
662 * Returns the original source, line, and column information for the generated
663 * source's line and column positions provided. The only argument is an object
664 * with the following properties:
665 *
666 * - line: The line number in the generated source. The line number
667 * is 1-based.
668 * - column: The column number in the generated source. The column
669 * number is 0-based.
670 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
671 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
672 * closest element that is smaller than or greater than the one we are
673 * searching for, respectively, if the exact element cannot be found.
674 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
675 *
676 * and an object is returned with the following properties:
677 *
678 * - source: The original source file, or null.
679 * - line: The line number in the original source, or null. The
680 * line number is 1-based.
681 * - column: The column number in the original source, or null. The
682 * column number is 0-based.
683 * - name: The original identifier, or null.
684 */
685BasicSourceMapConsumer.prototype.originalPositionFor =
686 function SourceMapConsumer_originalPositionFor(aArgs) {
687 var needle = {
688 generatedLine: util.getArg(aArgs, 'line'),
689 generatedColumn: util.getArg(aArgs, 'column')
690 };
691
692 var index = this._findMapping(
693 needle,
694 this._generatedMappings,
695 "generatedLine",
696 "generatedColumn",
697 util.compareByGeneratedPositionsDeflated,
698 util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
699 );
700
701 if (index >= 0) {
702 var mapping = this._generatedMappings[index];
703
704 if (mapping.generatedLine === needle.generatedLine) {
705 var source = util.getArg(mapping, 'source', null);
706 if (source !== null) {
707 source = this._sources.at(source);
708 source = util.computeSourceURL(this.sourceRoot, source, this._sourceMapURL);
709 }
710 var name = util.getArg(mapping, 'name', null);
711 if (name !== null) {
712 name = this._names.at(name);
713 }
714 return {
715 source: source,
716 line: util.getArg(mapping, 'originalLine', null),
717 column: util.getArg(mapping, 'originalColumn', null),
718 name: name
719 };
720 }
721 }
722
723 return {
724 source: null,
725 line: null,
726 column: null,
727 name: null
728 };
729 };
730
731/**
732 * Return true if we have the source content for every source in the source
733 * map, false otherwise.
734 */
735BasicSourceMapConsumer.prototype.hasContentsOfAllSources =
736 function BasicSourceMapConsumer_hasContentsOfAllSources() {
737 if (!this.sourcesContent) {
738 return false;
739 }
740 return this.sourcesContent.length >= this._sources.size() &&
741 !this.sourcesContent.some(function (sc) { return sc == null; });
742 };
743
744/**
745 * Returns the original source content. The only argument is the url of the
746 * original source file. Returns null if no original source content is
747 * available.
748 */
749BasicSourceMapConsumer.prototype.sourceContentFor =
750 function SourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
751 if (!this.sourcesContent) {
752 return null;
753 }
754
755 var index = this._findSourceIndex(aSource);
756 if (index >= 0) {
757 return this.sourcesContent[index];
758 }
759
760 var relativeSource = aSource;
761 if (this.sourceRoot != null) {
762 relativeSource = util.relative(this.sourceRoot, relativeSource);
763 }
764
765 var url;
766 if (this.sourceRoot != null
767 && (url = util.urlParse(this.sourceRoot))) {
768 // XXX: file:// URIs and absolute paths lead to unexpected behavior for
769 // many users. We can help them out when they expect file:// URIs to
770 // behave like it would if they were running a local HTTP server. See
771 // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
772 var fileUriAbsPath = relativeSource.replace(/^file:\/\//, "");
773 if (url.scheme == "file"
774 && this._sources.has(fileUriAbsPath)) {
775 return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
776 }
777
778 if ((!url.path || url.path == "/")
779 && this._sources.has("/" + relativeSource)) {
780 return this.sourcesContent[this._sources.indexOf("/" + relativeSource)];
781 }
782 }
783
784 // This function is used recursively from
785 // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we
786 // don't want to throw if we can't find the source - we just want to
787 // return null, so we provide a flag to exit gracefully.
788 if (nullOnMissing) {
789 return null;
790 }
791 else {
792 throw new Error('"' + relativeSource + '" is not in the SourceMap.');
793 }
794 };
795
796/**
797 * Returns the generated line and column information for the original source,
798 * line, and column positions provided. The only argument is an object with
799 * the following properties:
800 *
801 * - source: The filename of the original source.
802 * - line: The line number in the original source. The line number
803 * is 1-based.
804 * - column: The column number in the original source. The column
805 * number is 0-based.
806 * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
807 * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
808 * closest element that is smaller than or greater than the one we are
809 * searching for, respectively, if the exact element cannot be found.
810 * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
811 *
812 * and an object is returned with the following properties:
813 *
814 * - line: The line number in the generated source, or null. The
815 * line number is 1-based.
816 * - column: The column number in the generated source, or null.
817 * The column number is 0-based.
818 */
819BasicSourceMapConsumer.prototype.generatedPositionFor =
820 function SourceMapConsumer_generatedPositionFor(aArgs) {
821 var source = util.getArg(aArgs, 'source');
822 source = this._findSourceIndex(source);
823 if (source < 0) {
824 return {
825 line: null,
826 column: null,
827 lastColumn: null
828 };
829 }
830
831 var needle = {
832 source: source,
833 originalLine: util.getArg(aArgs, 'line'),
834 originalColumn: util.getArg(aArgs, 'column')
835 };
836
837 var index = this._findMapping(
838 needle,
839 this._originalMappings,
840 "originalLine",
841 "originalColumn",
842 util.compareByOriginalPositions,
843 util.getArg(aArgs, 'bias', SourceMapConsumer.GREATEST_LOWER_BOUND)
844 );
845
846 if (index >= 0) {
847 var mapping = this._originalMappings[index];
848
849 if (mapping.source === needle.source) {
850 return {
851 line: util.getArg(mapping, 'generatedLine', null),
852 column: util.getArg(mapping, 'generatedColumn', null),
853 lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)
854 };
855 }
856 }
857
858 return {
859 line: null,
860 column: null,
861 lastColumn: null
862 };
863 };
864
865exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
866
867/**
868 * An IndexedSourceMapConsumer instance represents a parsed source map which
869 * we can query for information. It differs from BasicSourceMapConsumer in
870 * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
871 * input.
872 *
873 * The first parameter is a raw source map (either as a JSON string, or already
874 * parsed to an object). According to the spec for indexed source maps, they
875 * have the following attributes:
876 *
877 * - version: Which version of the source map spec this map is following.
878 * - file: Optional. The generated file this source map is associated with.
879 * - sections: A list of section definitions.
880 *
881 * Each value under the "sections" field has two fields:
882 * - offset: The offset into the original specified at which this section
883 * begins to apply, defined as an object with a "line" and "column"
884 * field.
885 * - map: A source map definition. This source map could also be indexed,
886 * but doesn't have to be.
887 *
888 * Instead of the "map" field, it's also possible to have a "url" field
889 * specifying a URL to retrieve a source map from, but that's currently
890 * unsupported.
891 *
892 * Here's an example source map, taken from the source map spec[0], but
893 * modified to omit a section which uses the "url" field.
894 *
895 * {
896 * version : 3,
897 * file: "app.js",
898 * sections: [{
899 * offset: {line:100, column:10},
900 * map: {
901 * version : 3,
902 * file: "section.js",
903 * sources: ["foo.js", "bar.js"],
904 * names: ["src", "maps", "are", "fun"],
905 * mappings: "AAAA,E;;ABCDE;"
906 * }
907 * }],
908 * }
909 *
910 * The second parameter, if given, is a string whose value is the URL
911 * at which the source map was found. This URL is used to compute the
912 * sources array.
913 *
914 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
915 */
916function IndexedSourceMapConsumer(aSourceMap, aSourceMapURL) {
917 var sourceMap = aSourceMap;
918 if (typeof aSourceMap === 'string') {
919 sourceMap = util.parseSourceMapInput(aSourceMap);
920 }
921
922 var version = util.getArg(sourceMap, 'version');
923 var sections = util.getArg(sourceMap, 'sections');
924
925 if (version != this._version) {
926 throw new Error('Unsupported version: ' + version);
927 }
928
929 this._sources = new ArraySet();
930 this._names = new ArraySet();
931
932 var lastOffset = {
933 line: -1,
934 column: 0
935 };
936 this._sections = sections.map(function (s) {
937 if (s.url) {
938 // The url field will require support for asynchronicity.
939 // See https://github.com/mozilla/source-map/issues/16
940 throw new Error('Support for url field in sections not implemented.');
941 }
942 var offset = util.getArg(s, 'offset');
943 var offsetLine = util.getArg(offset, 'line');
944 var offsetColumn = util.getArg(offset, 'column');
945
946 if (offsetLine < lastOffset.line ||
947 (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)) {
948 throw new Error('Section offsets must be ordered and non-overlapping.');
949 }
950 lastOffset = offset;
951
952 return {
953 generatedOffset: {
954 // The offset fields are 0-based, but we use 1-based indices when
955 // encoding/decoding from VLQ.
956 generatedLine: offsetLine + 1,
957 generatedColumn: offsetColumn + 1
958 },
959 consumer: new SourceMapConsumer(util.getArg(s, 'map'), aSourceMapURL)
960 }
961 });
962}
963
964IndexedSourceMapConsumer.prototype = Object.create(SourceMapConsumer.prototype);
965IndexedSourceMapConsumer.prototype.constructor = SourceMapConsumer;
966
967/**
968 * The version of the source mapping spec that we are consuming.
969 */
970IndexedSourceMapConsumer.prototype._version = 3;
971
972/**
973 * The list of original sources.
974 */
975Object.defineProperty(IndexedSourceMapConsumer.prototype, 'sources', {
976 get: function () {
977 var sources = [];
978 for (var i = 0; i < this._sections.length; i++) {
979 for (var j = 0; j < this._sections[i].consumer.sources.length; j++) {
980 sources.push(this._sections[i].consumer.sources[j]);
981 }
982 }
983 return sources;
984 }
985});
986
987/**
988 * Returns the original source, line, and column information for the generated
989 * source's line and column positions provided. The only argument is an object
990 * with the following properties:
991 *
992 * - line: The line number in the generated source. The line number
993 * is 1-based.
994 * - column: The column number in the generated source. The column
995 * number is 0-based.
996 *
997 * and an object is returned with the following properties:
998 *
999 * - source: The original source file, or null.
1000 * - line: The line number in the original source, or null. The
1001 * line number is 1-based.
1002 * - column: The column number in the original source, or null. The
1003 * column number is 0-based.
1004 * - name: The original identifier, or null.
1005 */
1006IndexedSourceMapConsumer.prototype.originalPositionFor =
1007 function IndexedSourceMapConsumer_originalPositionFor(aArgs) {
1008 var needle = {
1009 generatedLine: util.getArg(aArgs, 'line'),
1010 generatedColumn: util.getArg(aArgs, 'column')
1011 };
1012
1013 // Find the section containing the generated position we're trying to map
1014 // to an original position.
1015 var sectionIndex = binarySearch.search(needle, this._sections,
1016 function(needle, section) {
1017 var cmp = needle.generatedLine - section.generatedOffset.generatedLine;
1018 if (cmp) {
1019 return cmp;
1020 }
1021
1022 return (needle.generatedColumn -
1023 section.generatedOffset.generatedColumn);
1024 });
1025 var section = this._sections[sectionIndex];
1026
1027 if (!section) {
1028 return {
1029 source: null,
1030 line: null,
1031 column: null,
1032 name: null
1033 };
1034 }
1035
1036 return section.consumer.originalPositionFor({
1037 line: needle.generatedLine -
1038 (section.generatedOffset.generatedLine - 1),
1039 column: needle.generatedColumn -
1040 (section.generatedOffset.generatedLine === needle.generatedLine
1041 ? section.generatedOffset.generatedColumn - 1
1042 : 0),
1043 bias: aArgs.bias
1044 });
1045 };
1046
1047/**
1048 * Return true if we have the source content for every source in the source
1049 * map, false otherwise.
1050 */
1051IndexedSourceMapConsumer.prototype.hasContentsOfAllSources =
1052 function IndexedSourceMapConsumer_hasContentsOfAllSources() {
1053 return this._sections.every(function (s) {
1054 return s.consumer.hasContentsOfAllSources();
1055 });
1056 };
1057
1058/**
1059 * Returns the original source content. The only argument is the url of the
1060 * original source file. Returns null if no original source content is
1061 * available.
1062 */
1063IndexedSourceMapConsumer.prototype.sourceContentFor =
1064 function IndexedSourceMapConsumer_sourceContentFor(aSource, nullOnMissing) {
1065 for (var i = 0; i < this._sections.length; i++) {
1066 var section = this._sections[i];
1067
1068 var content = section.consumer.sourceContentFor(aSource, true);
1069 if (content || content === '') {
1070 return content;
1071 }
1072 }
1073 if (nullOnMissing) {
1074 return null;
1075 }
1076 else {
1077 throw new Error('"' + aSource + '" is not in the SourceMap.');
1078 }
1079 };
1080
1081/**
1082 * Returns the generated line and column information for the original source,
1083 * line, and column positions provided. The only argument is an object with
1084 * the following properties:
1085 *
1086 * - source: The filename of the original source.
1087 * - line: The line number in the original source. The line number
1088 * is 1-based.
1089 * - column: The column number in the original source. The column
1090 * number is 0-based.
1091 *
1092 * and an object is returned with the following properties:
1093 *
1094 * - line: The line number in the generated source, or null. The
1095 * line number is 1-based.
1096 * - column: The column number in the generated source, or null.
1097 * The column number is 0-based.
1098 */
1099IndexedSourceMapConsumer.prototype.generatedPositionFor =
1100 function IndexedSourceMapConsumer_generatedPositionFor(aArgs) {
1101 for (var i = 0; i < this._sections.length; i++) {
1102 var section = this._sections[i];
1103
1104 // Only consider this section if the requested source is in the list of
1105 // sources of the consumer.
1106 if (section.consumer._findSourceIndex(util.getArg(aArgs, 'source')) === -1) {
1107 continue;
1108 }
1109 var generatedPosition = section.consumer.generatedPositionFor(aArgs);
1110 if (generatedPosition) {
1111 var ret = {
1112 line: generatedPosition.line +
1113 (section.generatedOffset.generatedLine - 1),
1114 column: generatedPosition.column +
1115 (section.generatedOffset.generatedLine === generatedPosition.line
1116 ? section.generatedOffset.generatedColumn - 1
1117 : 0)
1118 };
1119 return ret;
1120 }
1121 }
1122
1123 return {
1124 line: null,
1125 column: null
1126 };
1127 };
1128
1129/**
1130 * Parse the mappings in a string in to a data structure which we can easily
1131 * query (the ordered arrays in the `this.__generatedMappings` and
1132 * `this.__originalMappings` properties).
1133 */
1134IndexedSourceMapConsumer.prototype._parseMappings =
1135 function IndexedSourceMapConsumer_parseMappings(aStr, aSourceRoot) {
1136 this.__generatedMappings = [];
1137 this.__originalMappings = [];
1138 for (var i = 0; i < this._sections.length; i++) {
1139 var section = this._sections[i];
1140 var sectionMappings = section.consumer._generatedMappings;
1141 for (var j = 0; j < sectionMappings.length; j++) {
1142 var mapping = sectionMappings[j];
1143
1144 var source = section.consumer._sources.at(mapping.source);
1145 source = util.computeSourceURL(section.consumer.sourceRoot, source, this._sourceMapURL);
1146 this._sources.add(source);
1147 source = this._sources.indexOf(source);
1148
1149 var name = null;
1150 if (mapping.name) {
1151 name = section.consumer._names.at(mapping.name);
1152 this._names.add(name);
1153 name = this._names.indexOf(name);
1154 }
1155
1156 // The mappings coming from the consumer for the section have
1157 // generated positions relative to the start of the section, so we
1158 // need to offset them to be relative to the start of the concatenated
1159 // generated file.
1160 var adjustedMapping = {
1161 source: source,
1162 generatedLine: mapping.generatedLine +
1163 (section.generatedOffset.generatedLine - 1),
1164 generatedColumn: mapping.generatedColumn +
1165 (section.generatedOffset.generatedLine === mapping.generatedLine
1166 ? section.generatedOffset.generatedColumn - 1
1167 : 0),
1168 originalLine: mapping.originalLine,
1169 originalColumn: mapping.originalColumn,
1170 name: name
1171 };
1172
1173 this.__generatedMappings.push(adjustedMapping);
1174 if (typeof adjustedMapping.originalLine === 'number') {
1175 this.__originalMappings.push(adjustedMapping);
1176 }
1177 }
1178 }
1179
1180 quickSort(this.__generatedMappings, util.compareByGeneratedPositionsDeflated);
1181 quickSort(this.__originalMappings, util.compareByOriginalPositions);
1182 };
1183
1184exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
Note: See TracBrowser for help on using the repository browser.