react / wstein / node_modules / browserify / node_modules / insert-module-globals / node_modules / combine-source-map / node_modules / source-map / lib / source-map / source-map-consumer.js
80559 views/* -*- Mode: js; js-indent-level: 2; -*- */1/*2* Copyright 2011 Mozilla Foundation and contributors3* Licensed under the New BSD license. See LICENSE or:4* http://opensource.org/licenses/BSD-3-Clause5*/6if (typeof define !== 'function') {7var define = require('amdefine')(module, require);8}9define(function (require, exports, module) {1011var util = require('./util');12var binarySearch = require('./binary-search');13var ArraySet = require('./array-set').ArraySet;14var base64VLQ = require('./base64-vlq');1516/**17* A SourceMapConsumer instance represents a parsed source map which we can18* query for information about the original file positions by giving it a file19* position in the generated source.20*21* The only parameter is the raw source map (either as a JSON string, or22* already parsed to an object). According to the spec, source maps have the23* following attributes:24*25* - version: Which version of the source map spec this map is following.26* - sources: An array of URLs to the original source files.27* - names: An array of identifiers which can be referrenced by individual mappings.28* - sourceRoot: Optional. The URL root from which all sources are relative.29* - sourcesContent: Optional. An array of contents of the original source files.30* - mappings: A string of base64 VLQs which contain the actual mappings.31* - file: Optional. The generated file this source map is associated with.32*33* Here is an example source map, taken from the source map spec[0]:34*35* {36* version : 3,37* file: "out.js",38* sourceRoot : "",39* sources: ["foo.js", "bar.js"],40* names: ["src", "maps", "are", "fun"],41* mappings: "AA,AB;;ABCDE;"42* }43*44* [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#45*/46function SourceMapConsumer(aSourceMap) {47var sourceMap = aSourceMap;48if (typeof aSourceMap === 'string') {49sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));50}5152var version = util.getArg(sourceMap, 'version');53var sources = util.getArg(sourceMap, 'sources');54// Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which55// requires the array) to play nice here.56var names = util.getArg(sourceMap, 'names', []);57var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);58var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);59var mappings = util.getArg(sourceMap, 'mappings');60var file = util.getArg(sourceMap, 'file', null);6162// Once again, Sass deviates from the spec and supplies the version as a63// string rather than a number, so we use loose equality checking here.64if (version != this._version) {65throw new Error('Unsupported version: ' + version);66}6768// Some source maps produce relative source paths like "./foo.js" instead of69// "foo.js". Normalize these first so that future comparisons will succeed.70// See bugzil.la/1090768.71sources = sources.map(util.normalize);7273// Pass `true` below to allow duplicate names and sources. While source maps74// are intended to be compressed and deduplicated, the TypeScript compiler75// sometimes generates source maps with duplicates in them. See Github issue76// #72 and bugzil.la/889492.77this._names = ArraySet.fromArray(names, true);78this._sources = ArraySet.fromArray(sources, true);7980this.sourceRoot = sourceRoot;81this.sourcesContent = sourcesContent;82this._mappings = mappings;83this.file = file;84}8586/**87* Create a SourceMapConsumer from a SourceMapGenerator.88*89* @param SourceMapGenerator aSourceMap90* The source map that will be consumed.91* @returns SourceMapConsumer92*/93SourceMapConsumer.fromSourceMap =94function SourceMapConsumer_fromSourceMap(aSourceMap) {95var smc = Object.create(SourceMapConsumer.prototype);9697smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);98smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);99smc.sourceRoot = aSourceMap._sourceRoot;100smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),101smc.sourceRoot);102smc.file = aSourceMap._file;103104smc.__generatedMappings = aSourceMap._mappings.toArray().slice();105smc.__originalMappings = aSourceMap._mappings.toArray().slice()106.sort(util.compareByOriginalPositions);107108return smc;109};110111/**112* The version of the source mapping spec that we are consuming.113*/114SourceMapConsumer.prototype._version = 3;115116/**117* The list of original sources.118*/119Object.defineProperty(SourceMapConsumer.prototype, 'sources', {120get: function () {121return this._sources.toArray().map(function (s) {122return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s;123}, this);124}125});126127// `__generatedMappings` and `__originalMappings` are arrays that hold the128// parsed mapping coordinates from the source map's "mappings" attribute. They129// are lazily instantiated, accessed via the `_generatedMappings` and130// `_originalMappings` getters respectively, and we only parse the mappings131// and create these arrays once queried for a source location. We jump through132// these hoops because there can be many thousands of mappings, and parsing133// them is expensive, so we only want to do it if we must.134//135// Each object in the arrays is of the form:136//137// {138// generatedLine: The line number in the generated code,139// generatedColumn: The column number in the generated code,140// source: The path to the original source file that generated this141// chunk of code,142// originalLine: The line number in the original source that143// corresponds to this chunk of generated code,144// originalColumn: The column number in the original source that145// corresponds to this chunk of generated code,146// name: The name of the original symbol which generated this chunk of147// code.148// }149//150// All properties except for `generatedLine` and `generatedColumn` can be151// `null`.152//153// `_generatedMappings` is ordered by the generated positions.154//155// `_originalMappings` is ordered by the original positions.156157SourceMapConsumer.prototype.__generatedMappings = null;158Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {159get: function () {160if (!this.__generatedMappings) {161this.__generatedMappings = [];162this.__originalMappings = [];163this._parseMappings(this._mappings, this.sourceRoot);164}165166return this.__generatedMappings;167}168});169170SourceMapConsumer.prototype.__originalMappings = null;171Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {172get: function () {173if (!this.__originalMappings) {174this.__generatedMappings = [];175this.__originalMappings = [];176this._parseMappings(this._mappings, this.sourceRoot);177}178179return this.__originalMappings;180}181});182183SourceMapConsumer.prototype._nextCharIsMappingSeparator =184function SourceMapConsumer_nextCharIsMappingSeparator(aStr) {185var c = aStr.charAt(0);186return c === ";" || c === ",";187};188189/**190* Parse the mappings in a string in to a data structure which we can easily191* query (the ordered arrays in the `this.__generatedMappings` and192* `this.__originalMappings` properties).193*/194SourceMapConsumer.prototype._parseMappings =195function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {196var generatedLine = 1;197var previousGeneratedColumn = 0;198var previousOriginalLine = 0;199var previousOriginalColumn = 0;200var previousSource = 0;201var previousName = 0;202var str = aStr;203var temp = {};204var mapping;205206while (str.length > 0) {207if (str.charAt(0) === ';') {208generatedLine++;209str = str.slice(1);210previousGeneratedColumn = 0;211}212else if (str.charAt(0) === ',') {213str = str.slice(1);214}215else {216mapping = {};217mapping.generatedLine = generatedLine;218219// Generated column.220base64VLQ.decode(str, temp);221mapping.generatedColumn = previousGeneratedColumn + temp.value;222previousGeneratedColumn = mapping.generatedColumn;223str = temp.rest;224225if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {226// Original source.227base64VLQ.decode(str, temp);228mapping.source = this._sources.at(previousSource + temp.value);229previousSource += temp.value;230str = temp.rest;231if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {232throw new Error('Found a source, but no line and column');233}234235// Original line.236base64VLQ.decode(str, temp);237mapping.originalLine = previousOriginalLine + temp.value;238previousOriginalLine = mapping.originalLine;239// Lines are stored 0-based240mapping.originalLine += 1;241str = temp.rest;242if (str.length === 0 || this._nextCharIsMappingSeparator(str)) {243throw new Error('Found a source and line, but no column');244}245246// Original column.247base64VLQ.decode(str, temp);248mapping.originalColumn = previousOriginalColumn + temp.value;249previousOriginalColumn = mapping.originalColumn;250str = temp.rest;251252if (str.length > 0 && !this._nextCharIsMappingSeparator(str)) {253// Original name.254base64VLQ.decode(str, temp);255mapping.name = this._names.at(previousName + temp.value);256previousName += temp.value;257str = temp.rest;258}259}260261this.__generatedMappings.push(mapping);262if (typeof mapping.originalLine === 'number') {263this.__originalMappings.push(mapping);264}265}266}267268this.__generatedMappings.sort(util.compareByGeneratedPositions);269this.__originalMappings.sort(util.compareByOriginalPositions);270};271272/**273* Find the mapping that best matches the hypothetical "needle" mapping that274* we are searching for in the given "haystack" of mappings.275*/276SourceMapConsumer.prototype._findMapping =277function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,278aColumnName, aComparator) {279// To return the position we are searching for, we must first find the280// mapping for the given position and then return the opposite position it281// points to. Because the mappings are sorted, we can use binary search to282// find the best mapping.283284if (aNeedle[aLineName] <= 0) {285throw new TypeError('Line must be greater than or equal to 1, got '286+ aNeedle[aLineName]);287}288if (aNeedle[aColumnName] < 0) {289throw new TypeError('Column must be greater than or equal to 0, got '290+ aNeedle[aColumnName]);291}292293return binarySearch.search(aNeedle, aMappings, aComparator);294};295296/**297* Compute the last column for each generated mapping. The last column is298* inclusive.299*/300SourceMapConsumer.prototype.computeColumnSpans =301function SourceMapConsumer_computeColumnSpans() {302for (var index = 0; index < this._generatedMappings.length; ++index) {303var mapping = this._generatedMappings[index];304305// Mappings do not contain a field for the last generated columnt. We306// can come up with an optimistic estimate, however, by assuming that307// mappings are contiguous (i.e. given two consecutive mappings, the308// first mapping ends where the second one starts).309if (index + 1 < this._generatedMappings.length) {310var nextMapping = this._generatedMappings[index + 1];311312if (mapping.generatedLine === nextMapping.generatedLine) {313mapping.lastGeneratedColumn = nextMapping.generatedColumn - 1;314continue;315}316}317318// The last mapping for each line spans the entire line.319mapping.lastGeneratedColumn = Infinity;320}321};322323/**324* Returns the original source, line, and column information for the generated325* source's line and column positions provided. The only argument is an object326* with the following properties:327*328* - line: The line number in the generated source.329* - column: The column number in the generated source.330*331* and an object is returned with the following properties:332*333* - source: The original source file, or null.334* - line: The line number in the original source, or null.335* - column: The column number in the original source, or null.336* - name: The original identifier, or null.337*/338SourceMapConsumer.prototype.originalPositionFor =339function SourceMapConsumer_originalPositionFor(aArgs) {340var needle = {341generatedLine: util.getArg(aArgs, 'line'),342generatedColumn: util.getArg(aArgs, 'column')343};344345var index = this._findMapping(needle,346this._generatedMappings,347"generatedLine",348"generatedColumn",349util.compareByGeneratedPositions);350351if (index >= 0) {352var mapping = this._generatedMappings[index];353354if (mapping.generatedLine === needle.generatedLine) {355var source = util.getArg(mapping, 'source', null);356if (source != null && this.sourceRoot != null) {357source = util.join(this.sourceRoot, source);358}359return {360source: source,361line: util.getArg(mapping, 'originalLine', null),362column: util.getArg(mapping, 'originalColumn', null),363name: util.getArg(mapping, 'name', null)364};365}366}367368return {369source: null,370line: null,371column: null,372name: null373};374};375376/**377* Returns the original source content. The only argument is the url of the378* original source file. Returns null if no original source content is379* availible.380*/381SourceMapConsumer.prototype.sourceContentFor =382function SourceMapConsumer_sourceContentFor(aSource) {383if (!this.sourcesContent) {384return null;385}386387if (this.sourceRoot != null) {388aSource = util.relative(this.sourceRoot, aSource);389}390391if (this._sources.has(aSource)) {392return this.sourcesContent[this._sources.indexOf(aSource)];393}394395var url;396if (this.sourceRoot != null397&& (url = util.urlParse(this.sourceRoot))) {398// XXX: file:// URIs and absolute paths lead to unexpected behavior for399// many users. We can help them out when they expect file:// URIs to400// behave like it would if they were running a local HTTP server. See401// https://bugzilla.mozilla.org/show_bug.cgi?id=885597.402var fileUriAbsPath = aSource.replace(/^file:\/\//, "");403if (url.scheme == "file"404&& this._sources.has(fileUriAbsPath)) {405return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]406}407408if ((!url.path || url.path == "/")409&& this._sources.has("/" + aSource)) {410return this.sourcesContent[this._sources.indexOf("/" + aSource)];411}412}413414throw new Error('"' + aSource + '" is not in the SourceMap.');415};416417/**418* Returns the generated line and column information for the original source,419* line, and column positions provided. The only argument is an object with420* the following properties:421*422* - source: The filename of the original source.423* - line: The line number in the original source.424* - column: The column number in the original source.425*426* and an object is returned with the following properties:427*428* - line: The line number in the generated source, or null.429* - column: The column number in the generated source, or null.430*/431SourceMapConsumer.prototype.generatedPositionFor =432function SourceMapConsumer_generatedPositionFor(aArgs) {433var needle = {434source: util.getArg(aArgs, 'source'),435originalLine: util.getArg(aArgs, 'line'),436originalColumn: util.getArg(aArgs, 'column')437};438439if (this.sourceRoot != null) {440needle.source = util.relative(this.sourceRoot, needle.source);441}442443var index = this._findMapping(needle,444this._originalMappings,445"originalLine",446"originalColumn",447util.compareByOriginalPositions);448449if (index >= 0) {450var mapping = this._originalMappings[index];451452return {453line: util.getArg(mapping, 'generatedLine', null),454column: util.getArg(mapping, 'generatedColumn', null),455lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)456};457}458459return {460line: null,461column: null,462lastColumn: null463};464};465466/**467* Returns all generated line and column information for the original source468* and line provided. The only argument is an object with the following469* properties:470*471* - source: The filename of the original source.472* - line: The line number in the original source.473*474* and an array of objects is returned, each with the following properties:475*476* - line: The line number in the generated source, or null.477* - column: The column number in the generated source, or null.478*/479SourceMapConsumer.prototype.allGeneratedPositionsFor =480function SourceMapConsumer_allGeneratedPositionsFor(aArgs) {481// When there is no exact match, SourceMapConsumer.prototype._findMapping482// returns the index of the closest mapping less than the needle. By483// setting needle.originalColumn to Infinity, we thus find the last484// mapping for the given line, provided such a mapping exists.485var needle = {486source: util.getArg(aArgs, 'source'),487originalLine: util.getArg(aArgs, 'line'),488originalColumn: Infinity489};490491if (this.sourceRoot != null) {492needle.source = util.relative(this.sourceRoot, needle.source);493}494495var mappings = [];496497var index = this._findMapping(needle,498this._originalMappings,499"originalLine",500"originalColumn",501util.compareByOriginalPositions);502if (index >= 0) {503var mapping = this._originalMappings[index];504505while (mapping && mapping.originalLine === needle.originalLine) {506mappings.push({507line: util.getArg(mapping, 'generatedLine', null),508column: util.getArg(mapping, 'generatedColumn', null),509lastColumn: util.getArg(mapping, 'lastGeneratedColumn', null)510});511512mapping = this._originalMappings[--index];513}514}515516return mappings.reverse();517};518519SourceMapConsumer.GENERATED_ORDER = 1;520SourceMapConsumer.ORIGINAL_ORDER = 2;521522/**523* Iterate over each mapping between an original source/line/column and a524* generated line/column in this source map.525*526* @param Function aCallback527* The function that is called with each mapping.528* @param Object aContext529* Optional. If specified, this object will be the value of `this` every530* time that `aCallback` is called.531* @param aOrder532* Either `SourceMapConsumer.GENERATED_ORDER` or533* `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to534* iterate over the mappings sorted by the generated file's line/column535* order or the original's source/line/column order, respectively. Defaults to536* `SourceMapConsumer.GENERATED_ORDER`.537*/538SourceMapConsumer.prototype.eachMapping =539function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {540var context = aContext || null;541var order = aOrder || SourceMapConsumer.GENERATED_ORDER;542543var mappings;544switch (order) {545case SourceMapConsumer.GENERATED_ORDER:546mappings = this._generatedMappings;547break;548case SourceMapConsumer.ORIGINAL_ORDER:549mappings = this._originalMappings;550break;551default:552throw new Error("Unknown order of iteration.");553}554555var sourceRoot = this.sourceRoot;556mappings.map(function (mapping) {557var source = mapping.source;558if (source != null && sourceRoot != null) {559source = util.join(sourceRoot, source);560}561return {562source: source,563generatedLine: mapping.generatedLine,564generatedColumn: mapping.generatedColumn,565originalLine: mapping.originalLine,566originalColumn: mapping.originalColumn,567name: mapping.name568};569}).forEach(aCallback, context);570};571572exports.SourceMapConsumer = SourceMapConsumer;573574});575576577