You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					417 lines
				
				14 KiB
			
		
		
			
		
	
	
					417 lines
				
				14 KiB
			| 
											3 years ago
										 | /* -*- 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 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; |