Pinsave/counterstrike
1
1/* -*- Mode: js; js-indent-level: 2; -*- */2/*3 * Copyright 2011 Mozilla Foundation and contributors4 * Licensed under the New BSD license. See LICENSE or:5 * http://opensource.org/licenses/BSD-3-Clause6 */7 8var base64VLQ = require('./base64-vlq');9var util = require('./util');10var ArraySet = require('./array-set').ArraySet;11var MappingList = require('./mapping-list').MappingList;12 13/**14 * An instance of the SourceMapGenerator represents a source map which is15 * being built incrementally. You may pass an object with the following16 * properties:17 *18 * - file: The filename of the generated source.19 * - sourceRoot: A root for all relative URLs in this source map.20 */21function SourceMapGenerator(aArgs) {22 if (!aArgs) {23 aArgs = {};24 }25 this._file = util.getArg(aArgs, 'file', null);26 this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);27 this._skipValidation = util.getArg(aArgs, 'skipValidation', false);28 this._ignoreInvalidMapping = util.getArg(aArgs, 'ignoreInvalidMapping', false);29 this._sources = new ArraySet();30 this._names = new ArraySet();31 this._mappings = new MappingList();32 this._sourcesContents = null;33}34 35SourceMapGenerator.prototype._version = 3;36 37/**38 * Creates a new SourceMapGenerator based on a SourceMapConsumer39 *40 * @param aSourceMapConsumer The SourceMap.41 */42SourceMapGenerator.fromSourceMap =43 function SourceMapGenerator_fromSourceMap(aSourceMapConsumer, generatorOps) {44 var sourceRoot = aSourceMapConsumer.sourceRoot;45 var generator = new SourceMapGenerator(Object.assign(generatorOps || {}, {46 file: aSourceMapConsumer.file,47 sourceRoot: sourceRoot48 }));49 aSourceMapConsumer.eachMapping(function (mapping) {50 var newMapping = {51 generated: {52 line: mapping.generatedLine,53 column: mapping.generatedColumn54 }55 };56 57 if (mapping.source != null) {58 newMapping.source = mapping.source;59 if (sourceRoot != null) {60 newMapping.source = util.relative(sourceRoot, newMapping.source);61 }62 63 newMapping.original = {64 line: mapping.originalLine,65 column: mapping.originalColumn66 };67 68 if (mapping.name != null) {69 newMapping.name = mapping.name;70 }71 }72 73 generator.addMapping(newMapping);74 });75 aSourceMapConsumer.sources.forEach(function (sourceFile) {76 var sourceRelative = sourceFile;77 if (sourceRoot !== null) {78 sourceRelative = util.relative(sourceRoot, sourceFile);79 }80 81 if (!generator._sources.has(sourceRelative)) {82 generator._sources.add(sourceRelative);83 }84 85 var content = aSourceMapConsumer.sourceContentFor(sourceFile);86 if (content != null) {87 generator.setSourceContent(sourceFile, content);88 }89 });90 return generator;91 };92 93/**94 * Add a single mapping from original source line and column to the generated95 * source's line and column for this source map being created. The mapping96 * object should have the following properties:97 *98 * - generated: An object with the generated line and column positions.99 * - original: An object with the original line and column positions.100 * - source: The original source file (relative to the sourceRoot).101 * - name: An optional original token name for this mapping.102 */103SourceMapGenerator.prototype.addMapping =104 function SourceMapGenerator_addMapping(aArgs) {105 var generated = util.getArg(aArgs, 'generated');106 var original = util.getArg(aArgs, 'original', null);107 var source = util.getArg(aArgs, 'source', null);108 var name = util.getArg(aArgs, 'name', null);109 110 if (!this._skipValidation) {111 if (this._validateMapping(generated, original, source, name) === false) {112 return;113 }114 }115 116 if (source != null) {117 source = String(source);118 if (!this._sources.has(source)) {119 this._sources.add(source);120 }121 }122 123 if (name != null) {124 name = String(name);125 if (!this._names.has(name)) {126 this._names.add(name);127 }128 }129 130 this._mappings.add({131 generatedLine: generated.line,132 generatedColumn: generated.column,133 originalLine: original != null && original.line,134 originalColumn: original != null && original.column,135 source: source,136 name: name137 });138 };139 140/**141 * Set the source content for a source file.142 */143SourceMapGenerator.prototype.setSourceContent =144 function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {145 var source = aSourceFile;146 if (this._sourceRoot != null) {147 source = util.relative(this._sourceRoot, source);148 }149 150 if (aSourceContent != null) {151 // Add the source content to the _sourcesContents map.152 // Create a new _sourcesContents map if the property is null.153 if (!this._sourcesContents) {154 this._sourcesContents = Object.create(null);155 }156 this._sourcesContents[util.toSetString(source)] = aSourceContent;157 } else if (this._sourcesContents) {158 // Remove the source file from the _sourcesContents map.159 // If the _sourcesContents map is empty, set the property to null.160 delete this._sourcesContents[util.toSetString(source)];161 if (Object.keys(this._sourcesContents).length === 0) {162 this._sourcesContents = null;163 }164 }165 };166 167/**168 * Applies the mappings of a sub-source-map for a specific source file to the169 * source map being generated. Each mapping to the supplied source file is170 * rewritten using the supplied source map. Note: The resolution for the171 * resulting mappings is the minimium of this map and the supplied map.172 *173 * @param aSourceMapConsumer The source map to be applied.174 * @param aSourceFile Optional. The filename of the source file.175 * If omitted, SourceMapConsumer's file property will be used.176 * @param aSourceMapPath Optional. The dirname of the path to the source map177 * to be applied. If relative, it is relative to the SourceMapConsumer.178 * This parameter is needed when the two source maps aren't in the same179 * directory, and the source map to be applied contains relative source180 * paths. If so, those relative source paths need to be rewritten181 * relative to the SourceMapGenerator.182 */183SourceMapGenerator.prototype.applySourceMap =184 function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {185 var sourceFile = aSourceFile;186 // If aSourceFile is omitted, we will use the file property of the SourceMap187 if (aSourceFile == null) {188 if (aSourceMapConsumer.file == null) {189 throw new Error(190 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' +191 'or the source map\'s "file" property. Both were omitted.'192 );193 }194 sourceFile = aSourceMapConsumer.file;195 }196 var sourceRoot = this._sourceRoot;197 // Make "sourceFile" relative if an absolute Url is passed.198 if (sourceRoot != null) {199 sourceFile = util.relative(sourceRoot, sourceFile);200 }201 // Applying the SourceMap can add and remove items from the sources and202 // the names array.203 var newSources = new ArraySet();204 var newNames = new ArraySet();205 206 // Find mappings for the "sourceFile"207 this._mappings.unsortedForEach(function (mapping) {208 if (mapping.source === sourceFile && mapping.originalLine != null) {209 // Check if it can be mapped by the source map, then update the mapping.210 var original = aSourceMapConsumer.originalPositionFor({211 line: mapping.originalLine,212 column: mapping.originalColumn213 });214 if (original.source != null) {215 // Copy mapping216 mapping.source = original.source;217 if (aSourceMapPath != null) {218 mapping.source = util.join(aSourceMapPath, mapping.source)219 }220 if (sourceRoot != null) {221 mapping.source = util.relative(sourceRoot, mapping.source);222 }223 mapping.originalLine = original.line;224 mapping.originalColumn = original.column;225 if (original.name != null) {226 mapping.name = original.name;227 }228 }229 }230 231 var source = mapping.source;232 if (source != null && !newSources.has(source)) {233 newSources.add(source);234 }235 236 var name = mapping.name;237 if (name != null && !newNames.has(name)) {238 newNames.add(name);239 }240 241 }, this);242 this._sources = newSources;243 this._names = newNames;244 245 // Copy sourcesContents of applied map.246 aSourceMapConsumer.sources.forEach(function (sourceFile) {247 var content = aSourceMapConsumer.sourceContentFor(sourceFile);248 if (content != null) {249 if (aSourceMapPath != null) {250 sourceFile = util.join(aSourceMapPath, sourceFile);251 }252 if (sourceRoot != null) {253 sourceFile = util.relative(sourceRoot, sourceFile);254 }255 this.setSourceContent(sourceFile, content);256 }257 }, this);258 };259 260/**261 * A mapping can have one of the three levels of data:262 *263 * 1. Just the generated position.264 * 2. The Generated position, original position, and original source.265 * 3. Generated and original position, original source, as well as a name266 * token.267 *268 * To maintain consistency, we validate that any new mapping being added falls269 * in to one of these categories.270 */271SourceMapGenerator.prototype._validateMapping =272 function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,273 aName) {274 // When aOriginal is truthy but has empty values for .line and .column,275 // it is most likely a programmer error. In this case we throw a very276 // specific error message to try to guide them the right way.277 // For example: https://github.com/Polymer/polymer-bundler/pull/519278 if (aOriginal && typeof aOriginal.line !== 'number' && typeof aOriginal.column !== 'number') {279 var message = 'original.line and original.column are not numbers -- you probably meant to omit ' +280 'the original mapping entirely and only map the generated position. If so, pass ' +281 'null for the original mapping instead of an object with empty or null values.'282 283 if (this._ignoreInvalidMapping) {284 if (typeof console !== 'undefined' && console.warn) {285 console.warn(message);286 }287 return false;288 } else {289 throw new Error(message);290 }291 }292 293 if (aGenerated && 'line' in aGenerated && 'column' in aGenerated294 && aGenerated.line > 0 && aGenerated.column >= 0295 && !aOriginal && !aSource && !aName) {296 // Case 1.297 return;298 }299 else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated300 && aOriginal && 'line' in aOriginal && 'column' in aOriginal301 && aGenerated.line > 0 && aGenerated.column >= 0302 && aOriginal.line > 0 && aOriginal.column >= 0303 && aSource) {304 // Cases 2 and 3.305 return;306 }307 else {308 var message = 'Invalid mapping: ' + JSON.stringify({309 generated: aGenerated,310 source: aSource,311 original: aOriginal,312 name: aName313 });314 315 if (this._ignoreInvalidMapping) {316 if (typeof console !== 'undefined' && console.warn) {317 console.warn(message);318 }319 return false;320 } else {321 throw new Error(message)322 }323 }324 };325 326/**327 * Serialize the accumulated mappings in to the stream of base 64 VLQs328 * specified by the source map format.329 */330SourceMapGenerator.prototype._serializeMappings =331 function SourceMapGenerator_serializeMappings() {332 var previousGeneratedColumn = 0;333 var previousGeneratedLine = 1;334 var previousOriginalColumn = 0;335 var previousOriginalLine = 0;336 var previousName = 0;337 var previousSource = 0;338 var result = '';339 var next;340 var mapping;341 var nameIdx;342 var sourceIdx;343 344 var mappings = this._mappings.toArray();345 for (var i = 0, len = mappings.length; i < len; i++) {346 mapping = mappings[i];347 next = ''348 349 if (mapping.generatedLine !== previousGeneratedLine) {350 previousGeneratedColumn = 0;351 while (mapping.generatedLine !== previousGeneratedLine) {352 next += ';';353 previousGeneratedLine++;354 }355 }356 else {357 if (i > 0) {358 if (!util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])) {359 continue;360 }361 next += ',';362 }363 }364 365 next += base64VLQ.encode(mapping.generatedColumn366 - previousGeneratedColumn);367 previousGeneratedColumn = mapping.generatedColumn;368 369 if (mapping.source != null) {370 sourceIdx = this._sources.indexOf(mapping.source);371 next += base64VLQ.encode(sourceIdx - previousSource);372 previousSource = sourceIdx;373 374 // lines are stored 0-based in SourceMap spec version 3375 next += base64VLQ.encode(mapping.originalLine - 1376 - previousOriginalLine);377 previousOriginalLine = mapping.originalLine - 1;378 379 next += base64VLQ.encode(mapping.originalColumn380 - previousOriginalColumn);381 previousOriginalColumn = mapping.originalColumn;382 383 if (mapping.name != null) {384 nameIdx = this._names.indexOf(mapping.name);385 next += base64VLQ.encode(nameIdx - previousName);386 previousName = nameIdx;387 }388 }389 390 result += next;391 }392 393 return result;394 };395 396SourceMapGenerator.prototype._generateSourcesContent =397 function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {398 return aSources.map(function (source) {399 if (!this._sourcesContents) {400 return null;401 }402 if (aSourceRoot != null) {403 source = util.relative(aSourceRoot, source);404 }405 var key = util.toSetString(source);406 return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)407 ? this._sourcesContents[key]408 : null;409 }, this);410 };411 412/**413 * Externalize the source map.414 */415SourceMapGenerator.prototype.toJSON =416 function SourceMapGenerator_toJSON() {417 var map = {418 version: this._version,419 sources: this._sources.toArray(),420 names: this._names.toArray(),421 mappings: this._serializeMappings()422 };423 if (this._file != null) {424 map.file = this._file;425 }426 if (this._sourceRoot != null) {427 map.sourceRoot = this._sourceRoot;428 }429 if (this._sourcesContents) {430 map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);431 }432 433 return map;434 };435 436/**437 * Render the source map being generated to a string.438 */439SourceMapGenerator.prototype.toString =440 function SourceMapGenerator_toString() {441 return JSON.stringify(this.toJSON());442 };443 444exports.SourceMapGenerator = SourceMapGenerator;445 