react / wstein / node_modules / react / node_modules / envify / node_modules / jstransform / 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: 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// Pass `true` below to allow duplicate names and sources. While source maps69// are intended to be compressed and deduplicated, the TypeScript compiler70// sometimes generates source maps with duplicates in them. See Github issue71// #72 and bugzil.la/889492.72this._names = ArraySet.fromArray(names, true);73this._sources = ArraySet.fromArray(sources, true);7475this.sourceRoot = sourceRoot;76this.sourcesContent = sourcesContent;77this._mappings = mappings;78this.file = file;79}8081/**82* Create a SourceMapConsumer from a SourceMapGenerator.83*84* @param SourceMapGenerator aSourceMap85* The source map that will be consumed.86* @returns SourceMapConsumer87*/88SourceMapConsumer.fromSourceMap =89function SourceMapConsumer_fromSourceMap(aSourceMap) {90var smc = Object.create(SourceMapConsumer.prototype);9192smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);93smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);94smc.sourceRoot = aSourceMap._sourceRoot;95smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),96smc.sourceRoot);97smc.file = aSourceMap._file;9899smc.__generatedMappings = aSourceMap._mappings.slice()100.sort(util.compareByGeneratedPositions);101smc.__originalMappings = aSourceMap._mappings.slice()102.sort(util.compareByOriginalPositions);103104return smc;105};106107/**108* The version of the source mapping spec that we are consuming.109*/110SourceMapConsumer.prototype._version = 3;111112/**113* The list of original sources.114*/115Object.defineProperty(SourceMapConsumer.prototype, 'sources', {116get: function () {117return this._sources.toArray().map(function (s) {118return this.sourceRoot ? util.join(this.sourceRoot, s) : s;119}, this);120}121});122123// `__generatedMappings` and `__originalMappings` are arrays that hold the124// parsed mapping coordinates from the source map's "mappings" attribute. They125// are lazily instantiated, accessed via the `_generatedMappings` and126// `_originalMappings` getters respectively, and we only parse the mappings127// and create these arrays once queried for a source location. We jump through128// these hoops because there can be many thousands of mappings, and parsing129// them is expensive, so we only want to do it if we must.130//131// Each object in the arrays is of the form:132//133// {134// generatedLine: The line number in the generated code,135// generatedColumn: The column number in the generated code,136// source: The path to the original source file that generated this137// chunk of code,138// originalLine: The line number in the original source that139// corresponds to this chunk of generated code,140// originalColumn: The column number in the original source that141// corresponds to this chunk of generated code,142// name: The name of the original symbol which generated this chunk of143// code.144// }145//146// All properties except for `generatedLine` and `generatedColumn` can be147// `null`.148//149// `_generatedMappings` is ordered by the generated positions.150//151// `_originalMappings` is ordered by the original positions.152153SourceMapConsumer.prototype.__generatedMappings = null;154Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {155get: function () {156if (!this.__generatedMappings) {157this.__generatedMappings = [];158this.__originalMappings = [];159this._parseMappings(this._mappings, this.sourceRoot);160}161162return this.__generatedMappings;163}164});165166SourceMapConsumer.prototype.__originalMappings = null;167Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {168get: function () {169if (!this.__originalMappings) {170this.__generatedMappings = [];171this.__originalMappings = [];172this._parseMappings(this._mappings, this.sourceRoot);173}174175return this.__originalMappings;176}177});178179/**180* Parse the mappings in a string in to a data structure which we can easily181* query (the ordered arrays in the `this.__generatedMappings` and182* `this.__originalMappings` properties).183*/184SourceMapConsumer.prototype._parseMappings =185function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {186var generatedLine = 1;187var previousGeneratedColumn = 0;188var previousOriginalLine = 0;189var previousOriginalColumn = 0;190var previousSource = 0;191var previousName = 0;192var mappingSeparator = /^[,;]/;193var str = aStr;194var mapping;195var temp;196197while (str.length > 0) {198if (str.charAt(0) === ';') {199generatedLine++;200str = str.slice(1);201previousGeneratedColumn = 0;202}203else if (str.charAt(0) === ',') {204str = str.slice(1);205}206else {207mapping = {};208mapping.generatedLine = generatedLine;209210// Generated column.211temp = base64VLQ.decode(str);212mapping.generatedColumn = previousGeneratedColumn + temp.value;213previousGeneratedColumn = mapping.generatedColumn;214str = temp.rest;215216if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {217// Original source.218temp = base64VLQ.decode(str);219mapping.source = this._sources.at(previousSource + temp.value);220previousSource += temp.value;221str = temp.rest;222if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {223throw new Error('Found a source, but no line and column');224}225226// Original line.227temp = base64VLQ.decode(str);228mapping.originalLine = previousOriginalLine + temp.value;229previousOriginalLine = mapping.originalLine;230// Lines are stored 0-based231mapping.originalLine += 1;232str = temp.rest;233if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {234throw new Error('Found a source and line, but no column');235}236237// Original column.238temp = base64VLQ.decode(str);239mapping.originalColumn = previousOriginalColumn + temp.value;240previousOriginalColumn = mapping.originalColumn;241str = temp.rest;242243if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {244// Original name.245temp = base64VLQ.decode(str);246mapping.name = this._names.at(previousName + temp.value);247previousName += temp.value;248str = temp.rest;249}250}251252this.__generatedMappings.push(mapping);253if (typeof mapping.originalLine === 'number') {254this.__originalMappings.push(mapping);255}256}257}258259this.__originalMappings.sort(util.compareByOriginalPositions);260};261262/**263* Find the mapping that best matches the hypothetical "needle" mapping that264* we are searching for in the given "haystack" of mappings.265*/266SourceMapConsumer.prototype._findMapping =267function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,268aColumnName, aComparator) {269// To return the position we are searching for, we must first find the270// mapping for the given position and then return the opposite position it271// points to. Because the mappings are sorted, we can use binary search to272// find the best mapping.273274if (aNeedle[aLineName] <= 0) {275throw new TypeError('Line must be greater than or equal to 1, got '276+ aNeedle[aLineName]);277}278if (aNeedle[aColumnName] < 0) {279throw new TypeError('Column must be greater than or equal to 0, got '280+ aNeedle[aColumnName]);281}282283return binarySearch.search(aNeedle, aMappings, aComparator);284};285286/**287* Returns the original source, line, and column information for the generated288* source's line and column positions provided. The only argument is an object289* with the following properties:290*291* - line: The line number in the generated source.292* - column: The column number in the generated source.293*294* and an object is returned with the following properties:295*296* - source: The original source file, or null.297* - line: The line number in the original source, or null.298* - column: The column number in the original source, or null.299* - name: The original identifier, or null.300*/301SourceMapConsumer.prototype.originalPositionFor =302function SourceMapConsumer_originalPositionFor(aArgs) {303var needle = {304generatedLine: util.getArg(aArgs, 'line'),305generatedColumn: util.getArg(aArgs, 'column')306};307308var mapping = this._findMapping(needle,309this._generatedMappings,310"generatedLine",311"generatedColumn",312util.compareByGeneratedPositions);313314if (mapping) {315var source = util.getArg(mapping, 'source', null);316if (source && this.sourceRoot) {317source = util.join(this.sourceRoot, source);318}319return {320source: source,321line: util.getArg(mapping, 'originalLine', null),322column: util.getArg(mapping, 'originalColumn', null),323name: util.getArg(mapping, 'name', null)324};325}326327return {328source: null,329line: null,330column: null,331name: null332};333};334335/**336* Returns the original source content. The only argument is the url of the337* original source file. Returns null if no original source content is338* availible.339*/340SourceMapConsumer.prototype.sourceContentFor =341function SourceMapConsumer_sourceContentFor(aSource) {342if (!this.sourcesContent) {343return null;344}345346if (this.sourceRoot) {347aSource = util.relative(this.sourceRoot, aSource);348}349350if (this._sources.has(aSource)) {351return this.sourcesContent[this._sources.indexOf(aSource)];352}353354var url;355if (this.sourceRoot356&& (url = util.urlParse(this.sourceRoot))) {357// XXX: file:// URIs and absolute paths lead to unexpected behavior for358// many users. We can help them out when they expect file:// URIs to359// behave like it would if they were running a local HTTP server. See360// https://bugzilla.mozilla.org/show_bug.cgi?id=885597.361var fileUriAbsPath = aSource.replace(/^file:\/\//, "");362if (url.scheme == "file"363&& this._sources.has(fileUriAbsPath)) {364return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]365}366367if ((!url.path || url.path == "/")368&& this._sources.has("/" + aSource)) {369return this.sourcesContent[this._sources.indexOf("/" + aSource)];370}371}372373throw new Error('"' + aSource + '" is not in the SourceMap.');374};375376/**377* Returns the generated line and column information for the original source,378* line, and column positions provided. The only argument is an object with379* the following properties:380*381* - source: The filename of the original source.382* - line: The line number in the original source.383* - column: The column number in the original source.384*385* and an object is returned with the following properties:386*387* - line: The line number in the generated source, or null.388* - column: The column number in the generated source, or null.389*/390SourceMapConsumer.prototype.generatedPositionFor =391function SourceMapConsumer_generatedPositionFor(aArgs) {392var needle = {393source: util.getArg(aArgs, 'source'),394originalLine: util.getArg(aArgs, 'line'),395originalColumn: util.getArg(aArgs, 'column')396};397398if (this.sourceRoot) {399needle.source = util.relative(this.sourceRoot, needle.source);400}401402var mapping = this._findMapping(needle,403this._originalMappings,404"originalLine",405"originalColumn",406util.compareByOriginalPositions);407408if (mapping) {409return {410line: util.getArg(mapping, 'generatedLine', null),411column: util.getArg(mapping, 'generatedColumn', null)412};413}414415return {416line: null,417column: null418};419};420421SourceMapConsumer.GENERATED_ORDER = 1;422SourceMapConsumer.ORIGINAL_ORDER = 2;423424/**425* Iterate over each mapping between an original source/line/column and a426* generated line/column in this source map.427*428* @param Function aCallback429* The function that is called with each mapping.430* @param Object aContext431* Optional. If specified, this object will be the value of `this` every432* time that `aCallback` is called.433* @param aOrder434* Either `SourceMapConsumer.GENERATED_ORDER` or435* `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to436* iterate over the mappings sorted by the generated file's line/column437* order or the original's source/line/column order, respectively. Defaults to438* `SourceMapConsumer.GENERATED_ORDER`.439*/440SourceMapConsumer.prototype.eachMapping =441function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {442var context = aContext || null;443var order = aOrder || SourceMapConsumer.GENERATED_ORDER;444445var mappings;446switch (order) {447case SourceMapConsumer.GENERATED_ORDER:448mappings = this._generatedMappings;449break;450case SourceMapConsumer.ORIGINAL_ORDER:451mappings = this._originalMappings;452break;453default:454throw new Error("Unknown order of iteration.");455}456457var sourceRoot = this.sourceRoot;458mappings.map(function (mapping) {459var source = mapping.source;460if (source && sourceRoot) {461source = util.join(sourceRoot, source);462}463return {464source: source,465generatedLine: mapping.generatedLine,466generatedColumn: mapping.generatedColumn,467originalLine: mapping.originalLine,468originalColumn: mapping.originalColumn,469name: mapping.name470};471}).forEach(aCallback, context);472};473474exports.SourceMapConsumer = SourceMapConsumer;475476});477478479