426 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			426 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* -*- Mode: js; js-indent-level: 2; -*- */
 | 
						|
/*
 | 
						|
 * Copyright 2011 Mozilla Foundation and contributors
 | 
						|
 * Licensed under the New BSD license. See LICENSE or:
 | 
						|
 * http://opensource.org/licenses/BSD-3-Clause
 | 
						|
 */
 | 
						|
 | 
						|
var base64VLQ = require('./base64-vlq');
 | 
						|
var util = require('./util');
 | 
						|
var ArraySet = require('./array-set').ArraySet;
 | 
						|
var MappingList = require('./mapping-list').MappingList;
 | 
						|
 | 
						|
/**
 | 
						|
 * An instance of the SourceMapGenerator represents a source map which is
 | 
						|
 * being built incrementally. You may pass an object with the following
 | 
						|
 * properties:
 | 
						|
 *
 | 
						|
 *   - file: The filename of the generated source.
 | 
						|
 *   - sourceRoot: A root for all relative URLs in this source map.
 | 
						|
 */
 | 
						|
function SourceMapGenerator(aArgs) {
 | 
						|
  if (!aArgs) {
 | 
						|
    aArgs = {};
 | 
						|
  }
 | 
						|
  this._file = util.getArg(aArgs, 'file', null);
 | 
						|
  this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
 | 
						|
  this._skipValidation = util.getArg(aArgs, 'skipValidation', false);
 | 
						|
  this._sources = new ArraySet();
 | 
						|
  this._names = new ArraySet();
 | 
						|
  this._mappings = new MappingList();
 | 
						|
  this._sourcesContents = null;
 | 
						|
}
 | 
						|
 | 
						|
SourceMapGenerator.prototype._version = 3;
 | 
						|
 | 
						|
/**
 | 
						|
 * Creates a new SourceMapGenerator based on a SourceMapConsumer
 | 
						|
 *
 | 
						|
 * @param aSourceMapConsumer The SourceMap.
 | 
						|
 */
 | 
						|
SourceMapGenerator.fromSourceMap =
 | 
						|
  function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
 | 
						|
    var sourceRoot = aSourceMapConsumer.sourceRoot;
 | 
						|
    var generator = new SourceMapGenerator({
 | 
						|
      file: aSourceMapConsumer.file,
 | 
						|
      sourceRoot: sourceRoot
 | 
						|
    });
 | 
						|
    aSourceMapConsumer.eachMapping(function (mapping) {
 | 
						|
      var newMapping = {
 | 
						|
        generated: {
 | 
						|
          line: mapping.generatedLine,
 | 
						|
          column: mapping.generatedColumn
 | 
						|
        }
 | 
						|
      };
 | 
						|
 | 
						|
      if (mapping.source != null) {
 | 
						|
        newMapping.source = mapping.source;
 | 
						|
        if (sourceRoot != null) {
 | 
						|
          newMapping.source = util.relative(sourceRoot, newMapping.source);
 | 
						|
        }
 | 
						|
 | 
						|
        newMapping.original = {
 | 
						|
          line: mapping.originalLine,
 | 
						|
          column: mapping.originalColumn
 | 
						|
        };
 | 
						|
 | 
						|
        if (mapping.name != null) {
 | 
						|
          newMapping.name = mapping.name;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      generator.addMapping(newMapping);
 | 
						|
    });
 | 
						|
    aSourceMapConsumer.sources.forEach(function (sourceFile) {
 | 
						|
      var sourceRelative = sourceFile;
 | 
						|
      if (sourceRoot !== null) {
 | 
						|
        sourceRelative = util.relative(sourceRoot, sourceFile);
 | 
						|
      }
 | 
						|
 | 
						|
      if (!generator._sources.has(sourceRelative)) {
 | 
						|
        generator._sources.add(sourceRelative);
 | 
						|
      }
 | 
						|
 | 
						|
      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 | 
						|
      if (content != null) {
 | 
						|
        generator.setSourceContent(sourceFile, content);
 | 
						|
      }
 | 
						|
    });
 | 
						|
    return generator;
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Add a single mapping from original source line and column to the generated
 | 
						|
 * source's line and column for this source map being created. The mapping
 | 
						|
 * object should have the following properties:
 | 
						|
 *
 | 
						|
 *   - generated: An object with the generated line and column positions.
 | 
						|
 *   - original: An object with the original line and column positions.
 | 
						|
 *   - source: The original source file (relative to the sourceRoot).
 | 
						|
 *   - name: An optional original token name for this mapping.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype.addMapping =
 | 
						|
  function SourceMapGenerator_addMapping(aArgs) {
 | 
						|
    var generated = util.getArg(aArgs, 'generated');
 | 
						|
    var original = util.getArg(aArgs, 'original', null);
 | 
						|
    var source = util.getArg(aArgs, 'source', null);
 | 
						|
    var name = util.getArg(aArgs, 'name', null);
 | 
						|
 | 
						|
    if (!this._skipValidation) {
 | 
						|
      this._validateMapping(generated, original, source, name);
 | 
						|
    }
 | 
						|
 | 
						|
    if (source != null) {
 | 
						|
      source = String(source);
 | 
						|
      if (!this._sources.has(source)) {
 | 
						|
        this._sources.add(source);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (name != null) {
 | 
						|
      name = String(name);
 | 
						|
      if (!this._names.has(name)) {
 | 
						|
        this._names.add(name);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    this._mappings.add({
 | 
						|
      generatedLine: generated.line,
 | 
						|
      generatedColumn: generated.column,
 | 
						|
      originalLine: original != null && original.line,
 | 
						|
      originalColumn: original != null && original.column,
 | 
						|
      source: source,
 | 
						|
      name: name
 | 
						|
    });
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Set the source content for a source file.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype.setSourceContent =
 | 
						|
  function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
 | 
						|
    var source = aSourceFile;
 | 
						|
    if (this._sourceRoot != null) {
 | 
						|
      source = util.relative(this._sourceRoot, source);
 | 
						|
    }
 | 
						|
 | 
						|
    if (aSourceContent != null) {
 | 
						|
      // Add the source content to the _sourcesContents map.
 | 
						|
      // Create a new _sourcesContents map if the property is null.
 | 
						|
      if (!this._sourcesContents) {
 | 
						|
        this._sourcesContents = Object.create(null);
 | 
						|
      }
 | 
						|
      this._sourcesContents[util.toSetString(source)] = aSourceContent;
 | 
						|
    } else if (this._sourcesContents) {
 | 
						|
      // Remove the source file from the _sourcesContents map.
 | 
						|
      // If the _sourcesContents map is empty, set the property to null.
 | 
						|
      delete this._sourcesContents[util.toSetString(source)];
 | 
						|
      if (Object.keys(this._sourcesContents).length === 0) {
 | 
						|
        this._sourcesContents = null;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Applies the mappings of a sub-source-map for a specific source file to the
 | 
						|
 * source map being generated. Each mapping to the supplied source file is
 | 
						|
 * rewritten using the supplied source map. Note: The resolution for the
 | 
						|
 * resulting mappings is the minimium of this map and the supplied map.
 | 
						|
 *
 | 
						|
 * @param aSourceMapConsumer The source map to be applied.
 | 
						|
 * @param aSourceFile Optional. The filename of the source file.
 | 
						|
 *        If omitted, SourceMapConsumer's file property will be used.
 | 
						|
 * @param aSourceMapPath Optional. The dirname of the path to the source map
 | 
						|
 *        to be applied. If relative, it is relative to the SourceMapConsumer.
 | 
						|
 *        This parameter is needed when the two source maps aren't in the same
 | 
						|
 *        directory, and the source map to be applied contains relative source
 | 
						|
 *        paths. If so, those relative source paths need to be rewritten
 | 
						|
 *        relative to the SourceMapGenerator.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype.applySourceMap =
 | 
						|
  function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
 | 
						|
    var sourceFile = aSourceFile;
 | 
						|
    // If aSourceFile is omitted, we will use the file property of the SourceMap
 | 
						|
    if (aSourceFile == null) {
 | 
						|
      if (aSourceMapConsumer.file == null) {
 | 
						|
        throw new Error(
 | 
						|
          'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +
 | 
						|
          'or the source map\'s "file" property. Both were omitted.'
 | 
						|
        );
 | 
						|
      }
 | 
						|
      sourceFile = aSourceMapConsumer.file;
 | 
						|
    }
 | 
						|
    var sourceRoot = this._sourceRoot;
 | 
						|
    // Make "sourceFile" relative if an absolute Url is passed.
 | 
						|
    if (sourceRoot != null) {
 | 
						|
      sourceFile = util.relative(sourceRoot, sourceFile);
 | 
						|
    }
 | 
						|
    // Applying the SourceMap can add and remove items from the sources and
 | 
						|
    // the names array.
 | 
						|
    var newSources = new ArraySet();
 | 
						|
    var newNames = new ArraySet();
 | 
						|
 | 
						|
    // Find mappings for the "sourceFile"
 | 
						|
    this._mappings.unsortedForEach(function (mapping) {
 | 
						|
      if (mapping.source === sourceFile && mapping.originalLine != null) {
 | 
						|
        // Check if it can be mapped by the source map, then update the mapping.
 | 
						|
        var original = aSourceMapConsumer.originalPositionFor({
 | 
						|
          line: mapping.originalLine,
 | 
						|
          column: mapping.originalColumn
 | 
						|
        });
 | 
						|
        if (original.source != null) {
 | 
						|
          // Copy mapping
 | 
						|
          mapping.source = original.source;
 | 
						|
          if (aSourceMapPath != null) {
 | 
						|
            mapping.source = util.join(aSourceMapPath, mapping.source)
 | 
						|
          }
 | 
						|
          if (sourceRoot != null) {
 | 
						|
            mapping.source = util.relative(sourceRoot, mapping.source);
 | 
						|
          }
 | 
						|
          mapping.originalLine = original.line;
 | 
						|
          mapping.originalColumn = original.column;
 | 
						|
          if (original.name != null) {
 | 
						|
            mapping.name = original.name;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      var source = mapping.source;
 | 
						|
      if (source != null && !newSources.has(source)) {
 | 
						|
        newSources.add(source);
 | 
						|
      }
 | 
						|
 | 
						|
      var name = mapping.name;
 | 
						|
      if (name != null && !newNames.has(name)) {
 | 
						|
        newNames.add(name);
 | 
						|
      }
 | 
						|
 | 
						|
    }, this);
 | 
						|
    this._sources = newSources;
 | 
						|
    this._names = newNames;
 | 
						|
 | 
						|
    // Copy sourcesContents of applied map.
 | 
						|
    aSourceMapConsumer.sources.forEach(function (sourceFile) {
 | 
						|
      var content = aSourceMapConsumer.sourceContentFor(sourceFile);
 | 
						|
      if (content != null) {
 | 
						|
        if (aSourceMapPath != null) {
 | 
						|
          sourceFile = util.join(aSourceMapPath, sourceFile);
 | 
						|
        }
 | 
						|
        if (sourceRoot != null) {
 | 
						|
          sourceFile = util.relative(sourceRoot, sourceFile);
 | 
						|
        }
 | 
						|
        this.setSourceContent(sourceFile, content);
 | 
						|
      }
 | 
						|
    }, this);
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * A mapping can have one of the three levels of data:
 | 
						|
 *
 | 
						|
 *   1. Just the generated position.
 | 
						|
 *   2. The Generated position, original position, and original source.
 | 
						|
 *   3. Generated and original position, original source, as well as a name
 | 
						|
 *      token.
 | 
						|
 *
 | 
						|
 * To maintain consistency, we validate that any new mapping being added falls
 | 
						|
 * in to one of these categories.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype._validateMapping =
 | 
						|
  function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
 | 
						|
                                              aName) {
 | 
						|
    // When aOriginal is truthy but has empty values for .line and .column,
 | 
						|
    // it is most likely a programmer error. In this case we throw a very
 | 
						|
    // specific error message to try to guide them the right way.
 | 
						|
    // For example: https://github.com/Polymer/polymer-bundler/pull/519
 | 
						|
    if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {
 | 
						|
        throw new Error(
 | 
						|
            'original.line and original.column are not numbers -- you probably meant to omit ' +
 | 
						|
            'the original mapping entirely and only map the generated position. If so, pass ' +
 | 
						|
            'null for the original mapping instead of an object with empty or null values.'
 | 
						|
        );
 | 
						|
    }
 | 
						|
 | 
						|
    if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 | 
						|
        && aGenerated.line > 0 && aGenerated.column >= 0
 | 
						|
        && !aOriginal && !aSource && !aName) {
 | 
						|
      // Case 1.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
 | 
						|
             && aOriginal && 'line' in aOriginal && 'column' in aOriginal
 | 
						|
             && aGenerated.line > 0 && aGenerated.column >= 0
 | 
						|
             && aOriginal.line > 0 && aOriginal.column >= 0
 | 
						|
             && aSource) {
 | 
						|
      // Cases 2 and 3.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
      throw new Error('Invalid mapping: ' + JSON.stringify({
 | 
						|
        generated: aGenerated,
 | 
						|
        source: aSource,
 | 
						|
        original: aOriginal,
 | 
						|
        name: aName
 | 
						|
      }));
 | 
						|
    }
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Serialize the accumulated mappings in to the stream of base 64 VLQs
 | 
						|
 * specified by the source map format.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype._serializeMappings =
 | 
						|
  function SourceMapGenerator_serializeMappings() {
 | 
						|
    var previousGeneratedColumn = 0;
 | 
						|
    var previousGeneratedLine = 1;
 | 
						|
    var previousOriginalColumn = 0;
 | 
						|
    var previousOriginalLine = 0;
 | 
						|
    var previousName = 0;
 | 
						|
    var previousSource = 0;
 | 
						|
    var result = '';
 | 
						|
    var next;
 | 
						|
    var mapping;
 | 
						|
    var nameIdx;
 | 
						|
    var sourceIdx;
 | 
						|
 | 
						|
    var mappings = this._mappings.toArray();
 | 
						|
    for (var i = 0, len = mappings.length; i < len; i++) {
 | 
						|
      mapping = mappings[i];
 | 
						|
      next = ''
 | 
						|
 | 
						|
      if (mapping.generatedLine !== previousGeneratedLine) {
 | 
						|
        previousGeneratedColumn = 0;
 | 
						|
        while (mapping.generatedLine !== previousGeneratedLine) {
 | 
						|
          next += ';';
 | 
						|
          previousGeneratedLine++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        if (i > 0) {
 | 
						|
          if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) {
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
          next += ',';
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      next += base64VLQ.encode(mapping.generatedColumn
 | 
						|
                                 - previousGeneratedColumn);
 | 
						|
      previousGeneratedColumn = mapping.generatedColumn;
 | 
						|
 | 
						|
      if (mapping.source != null) {
 | 
						|
        sourceIdx = this._sources.indexOf(mapping.source);
 | 
						|
        next += base64VLQ.encode(sourceIdx - previousSource);
 | 
						|
        previousSource = sourceIdx;
 | 
						|
 | 
						|
        // lines are stored 0-based in SourceMap spec version 3
 | 
						|
        next += base64VLQ.encode(mapping.originalLine - 1
 | 
						|
                                   - previousOriginalLine);
 | 
						|
        previousOriginalLine = mapping.originalLine - 1;
 | 
						|
 | 
						|
        next += base64VLQ.encode(mapping.originalColumn
 | 
						|
                                   - previousOriginalColumn);
 | 
						|
        previousOriginalColumn = mapping.originalColumn;
 | 
						|
 | 
						|
        if (mapping.name != null) {
 | 
						|
          nameIdx = this._names.indexOf(mapping.name);
 | 
						|
          next += base64VLQ.encode(nameIdx - previousName);
 | 
						|
          previousName = nameIdx;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      result += next;
 | 
						|
    }
 | 
						|
 | 
						|
    return result;
 | 
						|
  };
 | 
						|
 | 
						|
SourceMapGenerator.prototype._generateSourcesContent =
 | 
						|
  function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
 | 
						|
    return aSources.map(function (source) {
 | 
						|
      if (!this._sourcesContents) {
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      if (aSourceRoot != null) {
 | 
						|
        source = util.relative(aSourceRoot, source);
 | 
						|
      }
 | 
						|
      var key = util.toSetString(source);
 | 
						|
      return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
 | 
						|
        ? this._sourcesContents[key]
 | 
						|
        : null;
 | 
						|
    }, this);
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Externalize the source map.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype.toJSON =
 | 
						|
  function SourceMapGenerator_toJSON() {
 | 
						|
    var map = {
 | 
						|
      version: this._version,
 | 
						|
      sources: this._sources.toArray(),
 | 
						|
      names: this._names.toArray(),
 | 
						|
      mappings: this._serializeMappings()
 | 
						|
    };
 | 
						|
    if (this._file != null) {
 | 
						|
      map.file = this._file;
 | 
						|
    }
 | 
						|
    if (this._sourceRoot != null) {
 | 
						|
      map.sourceRoot = this._sourceRoot;
 | 
						|
    }
 | 
						|
    if (this._sourcesContents) {
 | 
						|
      map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
 | 
						|
    }
 | 
						|
 | 
						|
    return map;
 | 
						|
  };
 | 
						|
 | 
						|
/**
 | 
						|
 * Render the source map being generated to a string.
 | 
						|
 */
 | 
						|
SourceMapGenerator.prototype.toString =
 | 
						|
  function SourceMapGenerator_toString() {
 | 
						|
    return JSON.stringify(this.toJSON());
 | 
						|
  };
 | 
						|
 | 
						|
exports.SourceMapGenerator = SourceMapGenerator;
 |