Spaces:
Runtime error
Runtime error
| /* -*- 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; | |