toolkit/devtools/sourcemap/source-map.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 2 /*
michael@0 3 * Copyright 2011 Mozilla Foundation and contributors
michael@0 4 * Licensed under the New BSD license. See LICENSE or:
michael@0 5 * http://opensource.org/licenses/BSD-3-Clause
michael@0 6 */
michael@0 7
michael@0 8 /**
michael@0 9 * Define a module along with a payload.
michael@0 10 * @param {string} moduleName Name for the payload
michael@0 11 * @param {ignored} deps Ignored. For compatibility with CommonJS AMD Spec
michael@0 12 * @param {function} payload Function with (require, exports, module) params
michael@0 13 */
michael@0 14 function define(moduleName, deps, payload) {
michael@0 15 if (typeof moduleName != "string") {
michael@0 16 throw new TypeError('Expected string, got: ' + moduleName);
michael@0 17 }
michael@0 18
michael@0 19 if (arguments.length == 2) {
michael@0 20 payload = deps;
michael@0 21 }
michael@0 22
michael@0 23 if (moduleName in define.modules) {
michael@0 24 throw new Error("Module already defined: " + moduleName);
michael@0 25 }
michael@0 26 define.modules[moduleName] = payload;
michael@0 27 };
michael@0 28
michael@0 29 /**
michael@0 30 * The global store of un-instantiated modules
michael@0 31 */
michael@0 32 define.modules = {};
michael@0 33
michael@0 34
michael@0 35 /**
michael@0 36 * We invoke require() in the context of a Domain so we can have multiple
michael@0 37 * sets of modules running separate from each other.
michael@0 38 * This contrasts with JSMs which are singletons, Domains allows us to
michael@0 39 * optionally load a CommonJS module twice with separate data each time.
michael@0 40 * Perhaps you want 2 command lines with a different set of commands in each,
michael@0 41 * for example.
michael@0 42 */
michael@0 43 function Domain() {
michael@0 44 this.modules = {};
michael@0 45 this._currentModule = null;
michael@0 46 }
michael@0 47
michael@0 48 (function () {
michael@0 49
michael@0 50 /**
michael@0 51 * Lookup module names and resolve them by calling the definition function if
michael@0 52 * needed.
michael@0 53 * There are 2 ways to call this, either with an array of dependencies and a
michael@0 54 * callback to call when the dependencies are found (which can happen
michael@0 55 * asynchronously in an in-page context) or with a single string an no callback
michael@0 56 * where the dependency is resolved synchronously and returned.
michael@0 57 * The API is designed to be compatible with the CommonJS AMD spec and
michael@0 58 * RequireJS.
michael@0 59 * @param {string[]|string} deps A name, or names for the payload
michael@0 60 * @param {function|undefined} callback Function to call when the dependencies
michael@0 61 * are resolved
michael@0 62 * @return {undefined|object} The module required or undefined for
michael@0 63 * array/callback method
michael@0 64 */
michael@0 65 Domain.prototype.require = function(deps, callback) {
michael@0 66 if (Array.isArray(deps)) {
michael@0 67 var params = deps.map(function(dep) {
michael@0 68 return this.lookup(dep);
michael@0 69 }, this);
michael@0 70 if (callback) {
michael@0 71 callback.apply(null, params);
michael@0 72 }
michael@0 73 return undefined;
michael@0 74 }
michael@0 75 else {
michael@0 76 return this.lookup(deps);
michael@0 77 }
michael@0 78 };
michael@0 79
michael@0 80 function normalize(path) {
michael@0 81 var bits = path.split('/');
michael@0 82 var i = 1;
michael@0 83 while (i < bits.length) {
michael@0 84 if (bits[i] === '..') {
michael@0 85 bits.splice(i-1, 1);
michael@0 86 } else if (bits[i] === '.') {
michael@0 87 bits.splice(i, 1);
michael@0 88 } else {
michael@0 89 i++;
michael@0 90 }
michael@0 91 }
michael@0 92 return bits.join('/');
michael@0 93 }
michael@0 94
michael@0 95 function join(a, b) {
michael@0 96 a = a.trim();
michael@0 97 b = b.trim();
michael@0 98 if (/^\//.test(b)) {
michael@0 99 return b;
michael@0 100 } else {
michael@0 101 return a.replace(/\/*$/, '/') + b;
michael@0 102 }
michael@0 103 }
michael@0 104
michael@0 105 function dirname(path) {
michael@0 106 var bits = path.split('/');
michael@0 107 bits.pop();
michael@0 108 return bits.join('/');
michael@0 109 }
michael@0 110
michael@0 111 /**
michael@0 112 * Lookup module names and resolve them by calling the definition function if
michael@0 113 * needed.
michael@0 114 * @param {string} moduleName A name for the payload to lookup
michael@0 115 * @return {object} The module specified by aModuleName or null if not found.
michael@0 116 */
michael@0 117 Domain.prototype.lookup = function(moduleName) {
michael@0 118 if (/^\./.test(moduleName)) {
michael@0 119 moduleName = normalize(join(dirname(this._currentModule), moduleName));
michael@0 120 }
michael@0 121
michael@0 122 if (moduleName in this.modules) {
michael@0 123 var module = this.modules[moduleName];
michael@0 124 return module;
michael@0 125 }
michael@0 126
michael@0 127 if (!(moduleName in define.modules)) {
michael@0 128 throw new Error("Module not defined: " + moduleName);
michael@0 129 }
michael@0 130
michael@0 131 var module = define.modules[moduleName];
michael@0 132
michael@0 133 if (typeof module == "function") {
michael@0 134 var exports = {};
michael@0 135 var previousModule = this._currentModule;
michael@0 136 this._currentModule = moduleName;
michael@0 137 module(this.require.bind(this), exports, { id: moduleName, uri: "" });
michael@0 138 this._currentModule = previousModule;
michael@0 139 module = exports;
michael@0 140 }
michael@0 141
michael@0 142 // cache the resulting module object for next time
michael@0 143 this.modules[moduleName] = module;
michael@0 144
michael@0 145 return module;
michael@0 146 };
michael@0 147
michael@0 148 }());
michael@0 149
michael@0 150 define.Domain = Domain;
michael@0 151 define.globalDomain = new Domain();
michael@0 152 var require = define.globalDomain.require.bind(define.globalDomain);
michael@0 153 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 154 /*
michael@0 155 * Copyright 2011 Mozilla Foundation and contributors
michael@0 156 * Licensed under the New BSD license. See LICENSE or:
michael@0 157 * http://opensource.org/licenses/BSD-3-Clause
michael@0 158 */
michael@0 159 define('source-map/source-map-generator', ['require', 'exports', 'module' , 'source-map/base64-vlq', 'source-map/util', 'source-map/array-set'], function(require, exports, module) {
michael@0 160
michael@0 161 var base64VLQ = require('./base64-vlq');
michael@0 162 var util = require('./util');
michael@0 163 var ArraySet = require('./array-set').ArraySet;
michael@0 164
michael@0 165 /**
michael@0 166 * An instance of the SourceMapGenerator represents a source map which is
michael@0 167 * being built incrementally. To create a new one, you must pass an object
michael@0 168 * with the following properties:
michael@0 169 *
michael@0 170 * - file: The filename of the generated source.
michael@0 171 * - sourceRoot: An optional root for all URLs in this source map.
michael@0 172 */
michael@0 173 function SourceMapGenerator(aArgs) {
michael@0 174 this._file = util.getArg(aArgs, 'file');
michael@0 175 this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null);
michael@0 176 this._sources = new ArraySet();
michael@0 177 this._names = new ArraySet();
michael@0 178 this._mappings = [];
michael@0 179 this._sourcesContents = null;
michael@0 180 }
michael@0 181
michael@0 182 SourceMapGenerator.prototype._version = 3;
michael@0 183
michael@0 184 /**
michael@0 185 * Creates a new SourceMapGenerator based on a SourceMapConsumer
michael@0 186 *
michael@0 187 * @param aSourceMapConsumer The SourceMap.
michael@0 188 */
michael@0 189 SourceMapGenerator.fromSourceMap =
michael@0 190 function SourceMapGenerator_fromSourceMap(aSourceMapConsumer) {
michael@0 191 var sourceRoot = aSourceMapConsumer.sourceRoot;
michael@0 192 var generator = new SourceMapGenerator({
michael@0 193 file: aSourceMapConsumer.file,
michael@0 194 sourceRoot: sourceRoot
michael@0 195 });
michael@0 196 aSourceMapConsumer.eachMapping(function (mapping) {
michael@0 197 var newMapping = {
michael@0 198 generated: {
michael@0 199 line: mapping.generatedLine,
michael@0 200 column: mapping.generatedColumn
michael@0 201 }
michael@0 202 };
michael@0 203
michael@0 204 if (mapping.source) {
michael@0 205 newMapping.source = mapping.source;
michael@0 206 if (sourceRoot) {
michael@0 207 newMapping.source = util.relative(sourceRoot, newMapping.source);
michael@0 208 }
michael@0 209
michael@0 210 newMapping.original = {
michael@0 211 line: mapping.originalLine,
michael@0 212 column: mapping.originalColumn
michael@0 213 };
michael@0 214
michael@0 215 if (mapping.name) {
michael@0 216 newMapping.name = mapping.name;
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 generator.addMapping(newMapping);
michael@0 221 });
michael@0 222 aSourceMapConsumer.sources.forEach(function (sourceFile) {
michael@0 223 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
michael@0 224 if (content) {
michael@0 225 generator.setSourceContent(sourceFile, content);
michael@0 226 }
michael@0 227 });
michael@0 228 return generator;
michael@0 229 };
michael@0 230
michael@0 231 /**
michael@0 232 * Add a single mapping from original source line and column to the generated
michael@0 233 * source's line and column for this source map being created. The mapping
michael@0 234 * object should have the following properties:
michael@0 235 *
michael@0 236 * - generated: An object with the generated line and column positions.
michael@0 237 * - original: An object with the original line and column positions.
michael@0 238 * - source: The original source file (relative to the sourceRoot).
michael@0 239 * - name: An optional original token name for this mapping.
michael@0 240 */
michael@0 241 SourceMapGenerator.prototype.addMapping =
michael@0 242 function SourceMapGenerator_addMapping(aArgs) {
michael@0 243 var generated = util.getArg(aArgs, 'generated');
michael@0 244 var original = util.getArg(aArgs, 'original', null);
michael@0 245 var source = util.getArg(aArgs, 'source', null);
michael@0 246 var name = util.getArg(aArgs, 'name', null);
michael@0 247
michael@0 248 this._validateMapping(generated, original, source, name);
michael@0 249
michael@0 250 if (source && !this._sources.has(source)) {
michael@0 251 this._sources.add(source);
michael@0 252 }
michael@0 253
michael@0 254 if (name && !this._names.has(name)) {
michael@0 255 this._names.add(name);
michael@0 256 }
michael@0 257
michael@0 258 this._mappings.push({
michael@0 259 generatedLine: generated.line,
michael@0 260 generatedColumn: generated.column,
michael@0 261 originalLine: original != null && original.line,
michael@0 262 originalColumn: original != null && original.column,
michael@0 263 source: source,
michael@0 264 name: name
michael@0 265 });
michael@0 266 };
michael@0 267
michael@0 268 /**
michael@0 269 * Set the source content for a source file.
michael@0 270 */
michael@0 271 SourceMapGenerator.prototype.setSourceContent =
michael@0 272 function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) {
michael@0 273 var source = aSourceFile;
michael@0 274 if (this._sourceRoot) {
michael@0 275 source = util.relative(this._sourceRoot, source);
michael@0 276 }
michael@0 277
michael@0 278 if (aSourceContent !== null) {
michael@0 279 // Add the source content to the _sourcesContents map.
michael@0 280 // Create a new _sourcesContents map if the property is null.
michael@0 281 if (!this._sourcesContents) {
michael@0 282 this._sourcesContents = {};
michael@0 283 }
michael@0 284 this._sourcesContents[util.toSetString(source)] = aSourceContent;
michael@0 285 } else {
michael@0 286 // Remove the source file from the _sourcesContents map.
michael@0 287 // If the _sourcesContents map is empty, set the property to null.
michael@0 288 delete this._sourcesContents[util.toSetString(source)];
michael@0 289 if (Object.keys(this._sourcesContents).length === 0) {
michael@0 290 this._sourcesContents = null;
michael@0 291 }
michael@0 292 }
michael@0 293 };
michael@0 294
michael@0 295 /**
michael@0 296 * Applies the mappings of a sub-source-map for a specific source file to the
michael@0 297 * source map being generated. Each mapping to the supplied source file is
michael@0 298 * rewritten using the supplied source map. Note: The resolution for the
michael@0 299 * resulting mappings is the minimium of this map and the supplied map.
michael@0 300 *
michael@0 301 * @param aSourceMapConsumer The source map to be applied.
michael@0 302 * @param aSourceFile Optional. The filename of the source file.
michael@0 303 * If omitted, SourceMapConsumer's file property will be used.
michael@0 304 */
michael@0 305 SourceMapGenerator.prototype.applySourceMap =
michael@0 306 function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) {
michael@0 307 // If aSourceFile is omitted, we will use the file property of the SourceMap
michael@0 308 if (!aSourceFile) {
michael@0 309 aSourceFile = aSourceMapConsumer.file;
michael@0 310 }
michael@0 311 var sourceRoot = this._sourceRoot;
michael@0 312 // Make "aSourceFile" relative if an absolute Url is passed.
michael@0 313 if (sourceRoot) {
michael@0 314 aSourceFile = util.relative(sourceRoot, aSourceFile);
michael@0 315 }
michael@0 316 // Applying the SourceMap can add and remove items from the sources and
michael@0 317 // the names array.
michael@0 318 var newSources = new ArraySet();
michael@0 319 var newNames = new ArraySet();
michael@0 320
michael@0 321 // Find mappings for the "aSourceFile"
michael@0 322 this._mappings.forEach(function (mapping) {
michael@0 323 if (mapping.source === aSourceFile && mapping.originalLine) {
michael@0 324 // Check if it can be mapped by the source map, then update the mapping.
michael@0 325 var original = aSourceMapConsumer.originalPositionFor({
michael@0 326 line: mapping.originalLine,
michael@0 327 column: mapping.originalColumn
michael@0 328 });
michael@0 329 if (original.source !== null) {
michael@0 330 // Copy mapping
michael@0 331 if (sourceRoot) {
michael@0 332 mapping.source = util.relative(sourceRoot, original.source);
michael@0 333 } else {
michael@0 334 mapping.source = original.source;
michael@0 335 }
michael@0 336 mapping.originalLine = original.line;
michael@0 337 mapping.originalColumn = original.column;
michael@0 338 if (original.name !== null && mapping.name !== null) {
michael@0 339 // Only use the identifier name if it's an identifier
michael@0 340 // in both SourceMaps
michael@0 341 mapping.name = original.name;
michael@0 342 }
michael@0 343 }
michael@0 344 }
michael@0 345
michael@0 346 var source = mapping.source;
michael@0 347 if (source && !newSources.has(source)) {
michael@0 348 newSources.add(source);
michael@0 349 }
michael@0 350
michael@0 351 var name = mapping.name;
michael@0 352 if (name && !newNames.has(name)) {
michael@0 353 newNames.add(name);
michael@0 354 }
michael@0 355
michael@0 356 }, this);
michael@0 357 this._sources = newSources;
michael@0 358 this._names = newNames;
michael@0 359
michael@0 360 // Copy sourcesContents of applied map.
michael@0 361 aSourceMapConsumer.sources.forEach(function (sourceFile) {
michael@0 362 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
michael@0 363 if (content) {
michael@0 364 if (sourceRoot) {
michael@0 365 sourceFile = util.relative(sourceRoot, sourceFile);
michael@0 366 }
michael@0 367 this.setSourceContent(sourceFile, content);
michael@0 368 }
michael@0 369 }, this);
michael@0 370 };
michael@0 371
michael@0 372 /**
michael@0 373 * A mapping can have one of the three levels of data:
michael@0 374 *
michael@0 375 * 1. Just the generated position.
michael@0 376 * 2. The Generated position, original position, and original source.
michael@0 377 * 3. Generated and original position, original source, as well as a name
michael@0 378 * token.
michael@0 379 *
michael@0 380 * To maintain consistency, we validate that any new mapping being added falls
michael@0 381 * in to one of these categories.
michael@0 382 */
michael@0 383 SourceMapGenerator.prototype._validateMapping =
michael@0 384 function SourceMapGenerator_validateMapping(aGenerated, aOriginal, aSource,
michael@0 385 aName) {
michael@0 386 if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
michael@0 387 && aGenerated.line > 0 && aGenerated.column >= 0
michael@0 388 && !aOriginal && !aSource && !aName) {
michael@0 389 // Case 1.
michael@0 390 return;
michael@0 391 }
michael@0 392 else if (aGenerated && 'line' in aGenerated && 'column' in aGenerated
michael@0 393 && aOriginal && 'line' in aOriginal && 'column' in aOriginal
michael@0 394 && aGenerated.line > 0 && aGenerated.column >= 0
michael@0 395 && aOriginal.line > 0 && aOriginal.column >= 0
michael@0 396 && aSource) {
michael@0 397 // Cases 2 and 3.
michael@0 398 return;
michael@0 399 }
michael@0 400 else {
michael@0 401 throw new Error('Invalid mapping: ' + JSON.stringify({
michael@0 402 generated: aGenerated,
michael@0 403 source: aSource,
michael@0 404 orginal: aOriginal,
michael@0 405 name: aName
michael@0 406 }));
michael@0 407 }
michael@0 408 };
michael@0 409
michael@0 410 /**
michael@0 411 * Serialize the accumulated mappings in to the stream of base 64 VLQs
michael@0 412 * specified by the source map format.
michael@0 413 */
michael@0 414 SourceMapGenerator.prototype._serializeMappings =
michael@0 415 function SourceMapGenerator_serializeMappings() {
michael@0 416 var previousGeneratedColumn = 0;
michael@0 417 var previousGeneratedLine = 1;
michael@0 418 var previousOriginalColumn = 0;
michael@0 419 var previousOriginalLine = 0;
michael@0 420 var previousName = 0;
michael@0 421 var previousSource = 0;
michael@0 422 var result = '';
michael@0 423 var mapping;
michael@0 424
michael@0 425 // The mappings must be guaranteed to be in sorted order before we start
michael@0 426 // serializing them or else the generated line numbers (which are defined
michael@0 427 // via the ';' separators) will be all messed up. Note: it might be more
michael@0 428 // performant to maintain the sorting as we insert them, rather than as we
michael@0 429 // serialize them, but the big O is the same either way.
michael@0 430 this._mappings.sort(util.compareByGeneratedPositions);
michael@0 431
michael@0 432 for (var i = 0, len = this._mappings.length; i < len; i++) {
michael@0 433 mapping = this._mappings[i];
michael@0 434
michael@0 435 if (mapping.generatedLine !== previousGeneratedLine) {
michael@0 436 previousGeneratedColumn = 0;
michael@0 437 while (mapping.generatedLine !== previousGeneratedLine) {
michael@0 438 result += ';';
michael@0 439 previousGeneratedLine++;
michael@0 440 }
michael@0 441 }
michael@0 442 else {
michael@0 443 if (i > 0) {
michael@0 444 if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) {
michael@0 445 continue;
michael@0 446 }
michael@0 447 result += ',';
michael@0 448 }
michael@0 449 }
michael@0 450
michael@0 451 result += base64VLQ.encode(mapping.generatedColumn
michael@0 452 - previousGeneratedColumn);
michael@0 453 previousGeneratedColumn = mapping.generatedColumn;
michael@0 454
michael@0 455 if (mapping.source) {
michael@0 456 result += base64VLQ.encode(this._sources.indexOf(mapping.source)
michael@0 457 - previousSource);
michael@0 458 previousSource = this._sources.indexOf(mapping.source);
michael@0 459
michael@0 460 // lines are stored 0-based in SourceMap spec version 3
michael@0 461 result += base64VLQ.encode(mapping.originalLine - 1
michael@0 462 - previousOriginalLine);
michael@0 463 previousOriginalLine = mapping.originalLine - 1;
michael@0 464
michael@0 465 result += base64VLQ.encode(mapping.originalColumn
michael@0 466 - previousOriginalColumn);
michael@0 467 previousOriginalColumn = mapping.originalColumn;
michael@0 468
michael@0 469 if (mapping.name) {
michael@0 470 result += base64VLQ.encode(this._names.indexOf(mapping.name)
michael@0 471 - previousName);
michael@0 472 previousName = this._names.indexOf(mapping.name);
michael@0 473 }
michael@0 474 }
michael@0 475 }
michael@0 476
michael@0 477 return result;
michael@0 478 };
michael@0 479
michael@0 480 SourceMapGenerator.prototype._generateSourcesContent =
michael@0 481 function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
michael@0 482 return aSources.map(function (source) {
michael@0 483 if (!this._sourcesContents) {
michael@0 484 return null;
michael@0 485 }
michael@0 486 if (aSourceRoot) {
michael@0 487 source = util.relative(aSourceRoot, source);
michael@0 488 }
michael@0 489 var key = util.toSetString(source);
michael@0 490 return Object.prototype.hasOwnProperty.call(this._sourcesContents,
michael@0 491 key)
michael@0 492 ? this._sourcesContents[key]
michael@0 493 : null;
michael@0 494 }, this);
michael@0 495 };
michael@0 496
michael@0 497 /**
michael@0 498 * Externalize the source map.
michael@0 499 */
michael@0 500 SourceMapGenerator.prototype.toJSON =
michael@0 501 function SourceMapGenerator_toJSON() {
michael@0 502 var map = {
michael@0 503 version: this._version,
michael@0 504 file: this._file,
michael@0 505 sources: this._sources.toArray(),
michael@0 506 names: this._names.toArray(),
michael@0 507 mappings: this._serializeMappings()
michael@0 508 };
michael@0 509 if (this._sourceRoot) {
michael@0 510 map.sourceRoot = this._sourceRoot;
michael@0 511 }
michael@0 512 if (this._sourcesContents) {
michael@0 513 map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot);
michael@0 514 }
michael@0 515
michael@0 516 return map;
michael@0 517 };
michael@0 518
michael@0 519 /**
michael@0 520 * Render the source map being generated to a string.
michael@0 521 */
michael@0 522 SourceMapGenerator.prototype.toString =
michael@0 523 function SourceMapGenerator_toString() {
michael@0 524 return JSON.stringify(this);
michael@0 525 };
michael@0 526
michael@0 527 exports.SourceMapGenerator = SourceMapGenerator;
michael@0 528
michael@0 529 });
michael@0 530 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 531 /*
michael@0 532 * Copyright 2011 Mozilla Foundation and contributors
michael@0 533 * Licensed under the New BSD license. See LICENSE or:
michael@0 534 * http://opensource.org/licenses/BSD-3-Clause
michael@0 535 *
michael@0 536 * Based on the Base 64 VLQ implementation in Closure Compiler:
michael@0 537 * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
michael@0 538 *
michael@0 539 * Copyright 2011 The Closure Compiler Authors. All rights reserved.
michael@0 540 * Redistribution and use in source and binary forms, with or without
michael@0 541 * modification, are permitted provided that the following conditions are
michael@0 542 * met:
michael@0 543 *
michael@0 544 * * Redistributions of source code must retain the above copyright
michael@0 545 * notice, this list of conditions and the following disclaimer.
michael@0 546 * * Redistributions in binary form must reproduce the above
michael@0 547 * copyright notice, this list of conditions and the following
michael@0 548 * disclaimer in the documentation and/or other materials provided
michael@0 549 * with the distribution.
michael@0 550 * * Neither the name of Google Inc. nor the names of its
michael@0 551 * contributors may be used to endorse or promote products derived
michael@0 552 * from this software without specific prior written permission.
michael@0 553 *
michael@0 554 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 555 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 556 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 557 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 558 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 559 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 560 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 561 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 562 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 563 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 564 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 565 */
michael@0 566 define('source-map/base64-vlq', ['require', 'exports', 'module' , 'source-map/base64'], function(require, exports, module) {
michael@0 567
michael@0 568 var base64 = require('./base64');
michael@0 569
michael@0 570 // A single base 64 digit can contain 6 bits of data. For the base 64 variable
michael@0 571 // length quantities we use in the source map spec, the first bit is the sign,
michael@0 572 // the next four bits are the actual value, and the 6th bit is the
michael@0 573 // continuation bit. The continuation bit tells us whether there are more
michael@0 574 // digits in this value following this digit.
michael@0 575 //
michael@0 576 // Continuation
michael@0 577 // | Sign
michael@0 578 // | |
michael@0 579 // V V
michael@0 580 // 101011
michael@0 581
michael@0 582 var VLQ_BASE_SHIFT = 5;
michael@0 583
michael@0 584 // binary: 100000
michael@0 585 var VLQ_BASE = 1 << VLQ_BASE_SHIFT;
michael@0 586
michael@0 587 // binary: 011111
michael@0 588 var VLQ_BASE_MASK = VLQ_BASE - 1;
michael@0 589
michael@0 590 // binary: 100000
michael@0 591 var VLQ_CONTINUATION_BIT = VLQ_BASE;
michael@0 592
michael@0 593 /**
michael@0 594 * Converts from a two-complement value to a value where the sign bit is
michael@0 595 * is placed in the least significant bit. For example, as decimals:
michael@0 596 * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
michael@0 597 * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
michael@0 598 */
michael@0 599 function toVLQSigned(aValue) {
michael@0 600 return aValue < 0
michael@0 601 ? ((-aValue) << 1) + 1
michael@0 602 : (aValue << 1) + 0;
michael@0 603 }
michael@0 604
michael@0 605 /**
michael@0 606 * Converts to a two-complement value from a value where the sign bit is
michael@0 607 * is placed in the least significant bit. For example, as decimals:
michael@0 608 * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
michael@0 609 * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
michael@0 610 */
michael@0 611 function fromVLQSigned(aValue) {
michael@0 612 var isNegative = (aValue & 1) === 1;
michael@0 613 var shifted = aValue >> 1;
michael@0 614 return isNegative
michael@0 615 ? -shifted
michael@0 616 : shifted;
michael@0 617 }
michael@0 618
michael@0 619 /**
michael@0 620 * Returns the base 64 VLQ encoded value.
michael@0 621 */
michael@0 622 exports.encode = function base64VLQ_encode(aValue) {
michael@0 623 var encoded = "";
michael@0 624 var digit;
michael@0 625
michael@0 626 var vlq = toVLQSigned(aValue);
michael@0 627
michael@0 628 do {
michael@0 629 digit = vlq & VLQ_BASE_MASK;
michael@0 630 vlq >>>= VLQ_BASE_SHIFT;
michael@0 631 if (vlq > 0) {
michael@0 632 // There are still more digits in this value, so we must make sure the
michael@0 633 // continuation bit is marked.
michael@0 634 digit |= VLQ_CONTINUATION_BIT;
michael@0 635 }
michael@0 636 encoded += base64.encode(digit);
michael@0 637 } while (vlq > 0);
michael@0 638
michael@0 639 return encoded;
michael@0 640 };
michael@0 641
michael@0 642 /**
michael@0 643 * Decodes the next base 64 VLQ value from the given string and returns the
michael@0 644 * value and the rest of the string.
michael@0 645 */
michael@0 646 exports.decode = function base64VLQ_decode(aStr) {
michael@0 647 var i = 0;
michael@0 648 var strLen = aStr.length;
michael@0 649 var result = 0;
michael@0 650 var shift = 0;
michael@0 651 var continuation, digit;
michael@0 652
michael@0 653 do {
michael@0 654 if (i >= strLen) {
michael@0 655 throw new Error("Expected more digits in base 64 VLQ value.");
michael@0 656 }
michael@0 657 digit = base64.decode(aStr.charAt(i++));
michael@0 658 continuation = !!(digit & VLQ_CONTINUATION_BIT);
michael@0 659 digit &= VLQ_BASE_MASK;
michael@0 660 result = result + (digit << shift);
michael@0 661 shift += VLQ_BASE_SHIFT;
michael@0 662 } while (continuation);
michael@0 663
michael@0 664 return {
michael@0 665 value: fromVLQSigned(result),
michael@0 666 rest: aStr.slice(i)
michael@0 667 };
michael@0 668 };
michael@0 669
michael@0 670 });
michael@0 671 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 672 /*
michael@0 673 * Copyright 2011 Mozilla Foundation and contributors
michael@0 674 * Licensed under the New BSD license. See LICENSE or:
michael@0 675 * http://opensource.org/licenses/BSD-3-Clause
michael@0 676 */
michael@0 677 define('source-map/base64', ['require', 'exports', 'module' , ], function(require, exports, module) {
michael@0 678
michael@0 679 var charToIntMap = {};
michael@0 680 var intToCharMap = {};
michael@0 681
michael@0 682 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
michael@0 683 .split('')
michael@0 684 .forEach(function (ch, index) {
michael@0 685 charToIntMap[ch] = index;
michael@0 686 intToCharMap[index] = ch;
michael@0 687 });
michael@0 688
michael@0 689 /**
michael@0 690 * Encode an integer in the range of 0 to 63 to a single base 64 digit.
michael@0 691 */
michael@0 692 exports.encode = function base64_encode(aNumber) {
michael@0 693 if (aNumber in intToCharMap) {
michael@0 694 return intToCharMap[aNumber];
michael@0 695 }
michael@0 696 throw new TypeError("Must be between 0 and 63: " + aNumber);
michael@0 697 };
michael@0 698
michael@0 699 /**
michael@0 700 * Decode a single base 64 digit to an integer.
michael@0 701 */
michael@0 702 exports.decode = function base64_decode(aChar) {
michael@0 703 if (aChar in charToIntMap) {
michael@0 704 return charToIntMap[aChar];
michael@0 705 }
michael@0 706 throw new TypeError("Not a valid base 64 digit: " + aChar);
michael@0 707 };
michael@0 708
michael@0 709 });
michael@0 710 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 711 /*
michael@0 712 * Copyright 2011 Mozilla Foundation and contributors
michael@0 713 * Licensed under the New BSD license. See LICENSE or:
michael@0 714 * http://opensource.org/licenses/BSD-3-Clause
michael@0 715 */
michael@0 716 define('source-map/util', ['require', 'exports', 'module' , ], function(require, exports, module) {
michael@0 717
michael@0 718 /**
michael@0 719 * This is a helper function for getting values from parameter/options
michael@0 720 * objects.
michael@0 721 *
michael@0 722 * @param args The object we are extracting values from
michael@0 723 * @param name The name of the property we are getting.
michael@0 724 * @param defaultValue An optional value to return if the property is missing
michael@0 725 * from the object. If this is not specified and the property is missing, an
michael@0 726 * error will be thrown.
michael@0 727 */
michael@0 728 function getArg(aArgs, aName, aDefaultValue) {
michael@0 729 if (aName in aArgs) {
michael@0 730 return aArgs[aName];
michael@0 731 } else if (arguments.length === 3) {
michael@0 732 return aDefaultValue;
michael@0 733 } else {
michael@0 734 throw new Error('"' + aName + '" is a required argument.');
michael@0 735 }
michael@0 736 }
michael@0 737 exports.getArg = getArg;
michael@0 738
michael@0 739 var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/;
michael@0 740 var dataUrlRegexp = /^data:.+\,.+/;
michael@0 741
michael@0 742 function urlParse(aUrl) {
michael@0 743 var match = aUrl.match(urlRegexp);
michael@0 744 if (!match) {
michael@0 745 return null;
michael@0 746 }
michael@0 747 return {
michael@0 748 scheme: match[1],
michael@0 749 auth: match[3],
michael@0 750 host: match[4],
michael@0 751 port: match[6],
michael@0 752 path: match[7]
michael@0 753 };
michael@0 754 }
michael@0 755 exports.urlParse = urlParse;
michael@0 756
michael@0 757 function urlGenerate(aParsedUrl) {
michael@0 758 var url = aParsedUrl.scheme + "://";
michael@0 759 if (aParsedUrl.auth) {
michael@0 760 url += aParsedUrl.auth + "@"
michael@0 761 }
michael@0 762 if (aParsedUrl.host) {
michael@0 763 url += aParsedUrl.host;
michael@0 764 }
michael@0 765 if (aParsedUrl.port) {
michael@0 766 url += ":" + aParsedUrl.port
michael@0 767 }
michael@0 768 if (aParsedUrl.path) {
michael@0 769 url += aParsedUrl.path;
michael@0 770 }
michael@0 771 return url;
michael@0 772 }
michael@0 773 exports.urlGenerate = urlGenerate;
michael@0 774
michael@0 775 function join(aRoot, aPath) {
michael@0 776 var url;
michael@0 777
michael@0 778 if (aPath.match(urlRegexp) || aPath.match(dataUrlRegexp)) {
michael@0 779 return aPath;
michael@0 780 }
michael@0 781
michael@0 782 if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) {
michael@0 783 url.path = aPath;
michael@0 784 return urlGenerate(url);
michael@0 785 }
michael@0 786
michael@0 787 return aRoot.replace(/\/$/, '') + '/' + aPath;
michael@0 788 }
michael@0 789 exports.join = join;
michael@0 790
michael@0 791 /**
michael@0 792 * Because behavior goes wacky when you set `__proto__` on objects, we
michael@0 793 * have to prefix all the strings in our set with an arbitrary character.
michael@0 794 *
michael@0 795 * See https://github.com/mozilla/source-map/pull/31 and
michael@0 796 * https://github.com/mozilla/source-map/issues/30
michael@0 797 *
michael@0 798 * @param String aStr
michael@0 799 */
michael@0 800 function toSetString(aStr) {
michael@0 801 return '$' + aStr;
michael@0 802 }
michael@0 803 exports.toSetString = toSetString;
michael@0 804
michael@0 805 function fromSetString(aStr) {
michael@0 806 return aStr.substr(1);
michael@0 807 }
michael@0 808 exports.fromSetString = fromSetString;
michael@0 809
michael@0 810 function relative(aRoot, aPath) {
michael@0 811 aRoot = aRoot.replace(/\/$/, '');
michael@0 812
michael@0 813 var url = urlParse(aRoot);
michael@0 814 if (aPath.charAt(0) == "/" && url && url.path == "/") {
michael@0 815 return aPath.slice(1);
michael@0 816 }
michael@0 817
michael@0 818 return aPath.indexOf(aRoot + '/') === 0
michael@0 819 ? aPath.substr(aRoot.length + 1)
michael@0 820 : aPath;
michael@0 821 }
michael@0 822 exports.relative = relative;
michael@0 823
michael@0 824 function strcmp(aStr1, aStr2) {
michael@0 825 var s1 = aStr1 || "";
michael@0 826 var s2 = aStr2 || "";
michael@0 827 return (s1 > s2) - (s1 < s2);
michael@0 828 }
michael@0 829
michael@0 830 /**
michael@0 831 * Comparator between two mappings where the original positions are compared.
michael@0 832 *
michael@0 833 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
michael@0 834 * mappings with the same original source/line/column, but different generated
michael@0 835 * line and column the same. Useful when searching for a mapping with a
michael@0 836 * stubbed out mapping.
michael@0 837 */
michael@0 838 function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) {
michael@0 839 var cmp;
michael@0 840
michael@0 841 cmp = strcmp(mappingA.source, mappingB.source);
michael@0 842 if (cmp) {
michael@0 843 return cmp;
michael@0 844 }
michael@0 845
michael@0 846 cmp = mappingA.originalLine - mappingB.originalLine;
michael@0 847 if (cmp) {
michael@0 848 return cmp;
michael@0 849 }
michael@0 850
michael@0 851 cmp = mappingA.originalColumn - mappingB.originalColumn;
michael@0 852 if (cmp || onlyCompareOriginal) {
michael@0 853 return cmp;
michael@0 854 }
michael@0 855
michael@0 856 cmp = strcmp(mappingA.name, mappingB.name);
michael@0 857 if (cmp) {
michael@0 858 return cmp;
michael@0 859 }
michael@0 860
michael@0 861 cmp = mappingA.generatedLine - mappingB.generatedLine;
michael@0 862 if (cmp) {
michael@0 863 return cmp;
michael@0 864 }
michael@0 865
michael@0 866 return mappingA.generatedColumn - mappingB.generatedColumn;
michael@0 867 };
michael@0 868 exports.compareByOriginalPositions = compareByOriginalPositions;
michael@0 869
michael@0 870 /**
michael@0 871 * Comparator between two mappings where the generated positions are
michael@0 872 * compared.
michael@0 873 *
michael@0 874 * Optionally pass in `true` as `onlyCompareGenerated` to consider two
michael@0 875 * mappings with the same generated line and column, but different
michael@0 876 * source/name/original line and column the same. Useful when searching for a
michael@0 877 * mapping with a stubbed out mapping.
michael@0 878 */
michael@0 879 function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) {
michael@0 880 var cmp;
michael@0 881
michael@0 882 cmp = mappingA.generatedLine - mappingB.generatedLine;
michael@0 883 if (cmp) {
michael@0 884 return cmp;
michael@0 885 }
michael@0 886
michael@0 887 cmp = mappingA.generatedColumn - mappingB.generatedColumn;
michael@0 888 if (cmp || onlyCompareGenerated) {
michael@0 889 return cmp;
michael@0 890 }
michael@0 891
michael@0 892 cmp = strcmp(mappingA.source, mappingB.source);
michael@0 893 if (cmp) {
michael@0 894 return cmp;
michael@0 895 }
michael@0 896
michael@0 897 cmp = mappingA.originalLine - mappingB.originalLine;
michael@0 898 if (cmp) {
michael@0 899 return cmp;
michael@0 900 }
michael@0 901
michael@0 902 cmp = mappingA.originalColumn - mappingB.originalColumn;
michael@0 903 if (cmp) {
michael@0 904 return cmp;
michael@0 905 }
michael@0 906
michael@0 907 return strcmp(mappingA.name, mappingB.name);
michael@0 908 };
michael@0 909 exports.compareByGeneratedPositions = compareByGeneratedPositions;
michael@0 910
michael@0 911 });
michael@0 912 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 913 /*
michael@0 914 * Copyright 2011 Mozilla Foundation and contributors
michael@0 915 * Licensed under the New BSD license. See LICENSE or:
michael@0 916 * http://opensource.org/licenses/BSD-3-Clause
michael@0 917 */
michael@0 918 define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/util'], function(require, exports, module) {
michael@0 919
michael@0 920 var util = require('./util');
michael@0 921
michael@0 922 /**
michael@0 923 * A data structure which is a combination of an array and a set. Adding a new
michael@0 924 * member is O(1), testing for membership is O(1), and finding the index of an
michael@0 925 * element is O(1). Removing elements from the set is not supported. Only
michael@0 926 * strings are supported for membership.
michael@0 927 */
michael@0 928 function ArraySet() {
michael@0 929 this._array = [];
michael@0 930 this._set = {};
michael@0 931 }
michael@0 932
michael@0 933 /**
michael@0 934 * Static method for creating ArraySet instances from an existing array.
michael@0 935 */
michael@0 936 ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
michael@0 937 var set = new ArraySet();
michael@0 938 for (var i = 0, len = aArray.length; i < len; i++) {
michael@0 939 set.add(aArray[i], aAllowDuplicates);
michael@0 940 }
michael@0 941 return set;
michael@0 942 };
michael@0 943
michael@0 944 /**
michael@0 945 * Add the given string to this set.
michael@0 946 *
michael@0 947 * @param String aStr
michael@0 948 */
michael@0 949 ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
michael@0 950 var isDuplicate = this.has(aStr);
michael@0 951 var idx = this._array.length;
michael@0 952 if (!isDuplicate || aAllowDuplicates) {
michael@0 953 this._array.push(aStr);
michael@0 954 }
michael@0 955 if (!isDuplicate) {
michael@0 956 this._set[util.toSetString(aStr)] = idx;
michael@0 957 }
michael@0 958 };
michael@0 959
michael@0 960 /**
michael@0 961 * Is the given string a member of this set?
michael@0 962 *
michael@0 963 * @param String aStr
michael@0 964 */
michael@0 965 ArraySet.prototype.has = function ArraySet_has(aStr) {
michael@0 966 return Object.prototype.hasOwnProperty.call(this._set,
michael@0 967 util.toSetString(aStr));
michael@0 968 };
michael@0 969
michael@0 970 /**
michael@0 971 * What is the index of the given string in the array?
michael@0 972 *
michael@0 973 * @param String aStr
michael@0 974 */
michael@0 975 ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
michael@0 976 if (this.has(aStr)) {
michael@0 977 return this._set[util.toSetString(aStr)];
michael@0 978 }
michael@0 979 throw new Error('"' + aStr + '" is not in the set.');
michael@0 980 };
michael@0 981
michael@0 982 /**
michael@0 983 * What is the element at the given index?
michael@0 984 *
michael@0 985 * @param Number aIdx
michael@0 986 */
michael@0 987 ArraySet.prototype.at = function ArraySet_at(aIdx) {
michael@0 988 if (aIdx >= 0 && aIdx < this._array.length) {
michael@0 989 return this._array[aIdx];
michael@0 990 }
michael@0 991 throw new Error('No element indexed by ' + aIdx);
michael@0 992 };
michael@0 993
michael@0 994 /**
michael@0 995 * Returns the array representation of this set (which has the proper indices
michael@0 996 * indicated by indexOf). Note that this is a copy of the internal array used
michael@0 997 * for storing the members so that no one can mess with internal state.
michael@0 998 */
michael@0 999 ArraySet.prototype.toArray = function ArraySet_toArray() {
michael@0 1000 return this._array.slice();
michael@0 1001 };
michael@0 1002
michael@0 1003 exports.ArraySet = ArraySet;
michael@0 1004
michael@0 1005 });
michael@0 1006 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 1007 /*
michael@0 1008 * Copyright 2011 Mozilla Foundation and contributors
michael@0 1009 * Licensed under the New BSD license. See LICENSE or:
michael@0 1010 * http://opensource.org/licenses/BSD-3-Clause
michael@0 1011 */
michael@0 1012 define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'source-map/util', 'source-map/binary-search', 'source-map/array-set', 'source-map/base64-vlq'], function(require, exports, module) {
michael@0 1013
michael@0 1014 var util = require('./util');
michael@0 1015 var binarySearch = require('./binary-search');
michael@0 1016 var ArraySet = require('./array-set').ArraySet;
michael@0 1017 var base64VLQ = require('./base64-vlq');
michael@0 1018
michael@0 1019 /**
michael@0 1020 * A SourceMapConsumer instance represents a parsed source map which we can
michael@0 1021 * query for information about the original file positions by giving it a file
michael@0 1022 * position in the generated source.
michael@0 1023 *
michael@0 1024 * The only parameter is the raw source map (either as a JSON string, or
michael@0 1025 * already parsed to an object). According to the spec, source maps have the
michael@0 1026 * following attributes:
michael@0 1027 *
michael@0 1028 * - version: Which version of the source map spec this map is following.
michael@0 1029 * - sources: An array of URLs to the original source files.
michael@0 1030 * - names: An array of identifiers which can be referrenced by individual mappings.
michael@0 1031 * - sourceRoot: Optional. The URL root from which all sources are relative.
michael@0 1032 * - sourcesContent: Optional. An array of contents of the original source files.
michael@0 1033 * - mappings: A string of base64 VLQs which contain the actual mappings.
michael@0 1034 * - file: The generated file this source map is associated with.
michael@0 1035 *
michael@0 1036 * Here is an example source map, taken from the source map spec[0]:
michael@0 1037 *
michael@0 1038 * {
michael@0 1039 * version : 3,
michael@0 1040 * file: "out.js",
michael@0 1041 * sourceRoot : "",
michael@0 1042 * sources: ["foo.js", "bar.js"],
michael@0 1043 * names: ["src", "maps", "are", "fun"],
michael@0 1044 * mappings: "AA,AB;;ABCDE;"
michael@0 1045 * }
michael@0 1046 *
michael@0 1047 * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
michael@0 1048 */
michael@0 1049 function SourceMapConsumer(aSourceMap) {
michael@0 1050 var sourceMap = aSourceMap;
michael@0 1051 if (typeof aSourceMap === 'string') {
michael@0 1052 sourceMap = JSON.parse(aSourceMap.replace(/^\)\]\}'/, ''));
michael@0 1053 }
michael@0 1054
michael@0 1055 var version = util.getArg(sourceMap, 'version');
michael@0 1056 var sources = util.getArg(sourceMap, 'sources');
michael@0 1057 var names = util.getArg(sourceMap, 'names');
michael@0 1058 var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null);
michael@0 1059 var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null);
michael@0 1060 var mappings = util.getArg(sourceMap, 'mappings');
michael@0 1061 var file = util.getArg(sourceMap, 'file', null);
michael@0 1062
michael@0 1063 if (version !== this._version) {
michael@0 1064 throw new Error('Unsupported version: ' + version);
michael@0 1065 }
michael@0 1066
michael@0 1067 // Pass `true` below to allow duplicate names and sources. While source maps
michael@0 1068 // are intended to be compressed and deduplicated, the TypeScript compiler
michael@0 1069 // sometimes generates source maps with duplicates in them. See Github issue
michael@0 1070 // #72 and bugzil.la/889492.
michael@0 1071 this._names = ArraySet.fromArray(names, true);
michael@0 1072 this._sources = ArraySet.fromArray(sources, true);
michael@0 1073
michael@0 1074 this.sourceRoot = sourceRoot;
michael@0 1075 this.sourcesContent = sourcesContent;
michael@0 1076 this._mappings = mappings;
michael@0 1077 this.file = file;
michael@0 1078 }
michael@0 1079
michael@0 1080 /**
michael@0 1081 * Create a SourceMapConsumer from a SourceMapGenerator.
michael@0 1082 *
michael@0 1083 * @param SourceMapGenerator aSourceMap
michael@0 1084 * The source map that will be consumed.
michael@0 1085 * @returns SourceMapConsumer
michael@0 1086 */
michael@0 1087 SourceMapConsumer.fromSourceMap =
michael@0 1088 function SourceMapConsumer_fromSourceMap(aSourceMap) {
michael@0 1089 var smc = Object.create(SourceMapConsumer.prototype);
michael@0 1090
michael@0 1091 smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true);
michael@0 1092 smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true);
michael@0 1093 smc.sourceRoot = aSourceMap._sourceRoot;
michael@0 1094 smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(),
michael@0 1095 smc.sourceRoot);
michael@0 1096 smc.file = aSourceMap._file;
michael@0 1097
michael@0 1098 smc.__generatedMappings = aSourceMap._mappings.slice()
michael@0 1099 .sort(util.compareByGeneratedPositions);
michael@0 1100 smc.__originalMappings = aSourceMap._mappings.slice()
michael@0 1101 .sort(util.compareByOriginalPositions);
michael@0 1102
michael@0 1103 return smc;
michael@0 1104 };
michael@0 1105
michael@0 1106 /**
michael@0 1107 * The version of the source mapping spec that we are consuming.
michael@0 1108 */
michael@0 1109 SourceMapConsumer.prototype._version = 3;
michael@0 1110
michael@0 1111 /**
michael@0 1112 * The list of original sources.
michael@0 1113 */
michael@0 1114 Object.defineProperty(SourceMapConsumer.prototype, 'sources', {
michael@0 1115 get: function () {
michael@0 1116 return this._sources.toArray().map(function (s) {
michael@0 1117 return this.sourceRoot ? util.join(this.sourceRoot, s) : s;
michael@0 1118 }, this);
michael@0 1119 }
michael@0 1120 });
michael@0 1121
michael@0 1122 // `__generatedMappings` and `__originalMappings` are arrays that hold the
michael@0 1123 // parsed mapping coordinates from the source map's "mappings" attribute. They
michael@0 1124 // are lazily instantiated, accessed via the `_generatedMappings` and
michael@0 1125 // `_originalMappings` getters respectively, and we only parse the mappings
michael@0 1126 // and create these arrays once queried for a source location. We jump through
michael@0 1127 // these hoops because there can be many thousands of mappings, and parsing
michael@0 1128 // them is expensive, so we only want to do it if we must.
michael@0 1129 //
michael@0 1130 // Each object in the arrays is of the form:
michael@0 1131 //
michael@0 1132 // {
michael@0 1133 // generatedLine: The line number in the generated code,
michael@0 1134 // generatedColumn: The column number in the generated code,
michael@0 1135 // source: The path to the original source file that generated this
michael@0 1136 // chunk of code,
michael@0 1137 // originalLine: The line number in the original source that
michael@0 1138 // corresponds to this chunk of generated code,
michael@0 1139 // originalColumn: The column number in the original source that
michael@0 1140 // corresponds to this chunk of generated code,
michael@0 1141 // name: The name of the original symbol which generated this chunk of
michael@0 1142 // code.
michael@0 1143 // }
michael@0 1144 //
michael@0 1145 // All properties except for `generatedLine` and `generatedColumn` can be
michael@0 1146 // `null`.
michael@0 1147 //
michael@0 1148 // `_generatedMappings` is ordered by the generated positions.
michael@0 1149 //
michael@0 1150 // `_originalMappings` is ordered by the original positions.
michael@0 1151
michael@0 1152 SourceMapConsumer.prototype.__generatedMappings = null;
michael@0 1153 Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', {
michael@0 1154 get: function () {
michael@0 1155 if (!this.__generatedMappings) {
michael@0 1156 this.__generatedMappings = [];
michael@0 1157 this.__originalMappings = [];
michael@0 1158 this._parseMappings(this._mappings, this.sourceRoot);
michael@0 1159 }
michael@0 1160
michael@0 1161 return this.__generatedMappings;
michael@0 1162 }
michael@0 1163 });
michael@0 1164
michael@0 1165 SourceMapConsumer.prototype.__originalMappings = null;
michael@0 1166 Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', {
michael@0 1167 get: function () {
michael@0 1168 if (!this.__originalMappings) {
michael@0 1169 this.__generatedMappings = [];
michael@0 1170 this.__originalMappings = [];
michael@0 1171 this._parseMappings(this._mappings, this.sourceRoot);
michael@0 1172 }
michael@0 1173
michael@0 1174 return this.__originalMappings;
michael@0 1175 }
michael@0 1176 });
michael@0 1177
michael@0 1178 /**
michael@0 1179 * Parse the mappings in a string in to a data structure which we can easily
michael@0 1180 * query (the ordered arrays in the `this.__generatedMappings` and
michael@0 1181 * `this.__originalMappings` properties).
michael@0 1182 */
michael@0 1183 SourceMapConsumer.prototype._parseMappings =
michael@0 1184 function SourceMapConsumer_parseMappings(aStr, aSourceRoot) {
michael@0 1185 var generatedLine = 1;
michael@0 1186 var previousGeneratedColumn = 0;
michael@0 1187 var previousOriginalLine = 0;
michael@0 1188 var previousOriginalColumn = 0;
michael@0 1189 var previousSource = 0;
michael@0 1190 var previousName = 0;
michael@0 1191 var mappingSeparator = /^[,;]/;
michael@0 1192 var str = aStr;
michael@0 1193 var mapping;
michael@0 1194 var temp;
michael@0 1195
michael@0 1196 while (str.length > 0) {
michael@0 1197 if (str.charAt(0) === ';') {
michael@0 1198 generatedLine++;
michael@0 1199 str = str.slice(1);
michael@0 1200 previousGeneratedColumn = 0;
michael@0 1201 }
michael@0 1202 else if (str.charAt(0) === ',') {
michael@0 1203 str = str.slice(1);
michael@0 1204 }
michael@0 1205 else {
michael@0 1206 mapping = {};
michael@0 1207 mapping.generatedLine = generatedLine;
michael@0 1208
michael@0 1209 // Generated column.
michael@0 1210 temp = base64VLQ.decode(str);
michael@0 1211 mapping.generatedColumn = previousGeneratedColumn + temp.value;
michael@0 1212 previousGeneratedColumn = mapping.generatedColumn;
michael@0 1213 str = temp.rest;
michael@0 1214
michael@0 1215 if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
michael@0 1216 // Original source.
michael@0 1217 temp = base64VLQ.decode(str);
michael@0 1218 mapping.source = this._sources.at(previousSource + temp.value);
michael@0 1219 previousSource += temp.value;
michael@0 1220 str = temp.rest;
michael@0 1221 if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
michael@0 1222 throw new Error('Found a source, but no line and column');
michael@0 1223 }
michael@0 1224
michael@0 1225 // Original line.
michael@0 1226 temp = base64VLQ.decode(str);
michael@0 1227 mapping.originalLine = previousOriginalLine + temp.value;
michael@0 1228 previousOriginalLine = mapping.originalLine;
michael@0 1229 // Lines are stored 0-based
michael@0 1230 mapping.originalLine += 1;
michael@0 1231 str = temp.rest;
michael@0 1232 if (str.length === 0 || mappingSeparator.test(str.charAt(0))) {
michael@0 1233 throw new Error('Found a source and line, but no column');
michael@0 1234 }
michael@0 1235
michael@0 1236 // Original column.
michael@0 1237 temp = base64VLQ.decode(str);
michael@0 1238 mapping.originalColumn = previousOriginalColumn + temp.value;
michael@0 1239 previousOriginalColumn = mapping.originalColumn;
michael@0 1240 str = temp.rest;
michael@0 1241
michael@0 1242 if (str.length > 0 && !mappingSeparator.test(str.charAt(0))) {
michael@0 1243 // Original name.
michael@0 1244 temp = base64VLQ.decode(str);
michael@0 1245 mapping.name = this._names.at(previousName + temp.value);
michael@0 1246 previousName += temp.value;
michael@0 1247 str = temp.rest;
michael@0 1248 }
michael@0 1249 }
michael@0 1250
michael@0 1251 this.__generatedMappings.push(mapping);
michael@0 1252 if (typeof mapping.originalLine === 'number') {
michael@0 1253 this.__originalMappings.push(mapping);
michael@0 1254 }
michael@0 1255 }
michael@0 1256 }
michael@0 1257
michael@0 1258 this.__originalMappings.sort(util.compareByOriginalPositions);
michael@0 1259 };
michael@0 1260
michael@0 1261 /**
michael@0 1262 * Find the mapping that best matches the hypothetical "needle" mapping that
michael@0 1263 * we are searching for in the given "haystack" of mappings.
michael@0 1264 */
michael@0 1265 SourceMapConsumer.prototype._findMapping =
michael@0 1266 function SourceMapConsumer_findMapping(aNeedle, aMappings, aLineName,
michael@0 1267 aColumnName, aComparator) {
michael@0 1268 // To return the position we are searching for, we must first find the
michael@0 1269 // mapping for the given position and then return the opposite position it
michael@0 1270 // points to. Because the mappings are sorted, we can use binary search to
michael@0 1271 // find the best mapping.
michael@0 1272
michael@0 1273 if (aNeedle[aLineName] <= 0) {
michael@0 1274 throw new TypeError('Line must be greater than or equal to 1, got '
michael@0 1275 + aNeedle[aLineName]);
michael@0 1276 }
michael@0 1277 if (aNeedle[aColumnName] < 0) {
michael@0 1278 throw new TypeError('Column must be greater than or equal to 0, got '
michael@0 1279 + aNeedle[aColumnName]);
michael@0 1280 }
michael@0 1281
michael@0 1282 return binarySearch.search(aNeedle, aMappings, aComparator);
michael@0 1283 };
michael@0 1284
michael@0 1285 /**
michael@0 1286 * Returns the original source, line, and column information for the generated
michael@0 1287 * source's line and column positions provided. The only argument is an object
michael@0 1288 * with the following properties:
michael@0 1289 *
michael@0 1290 * - line: The line number in the generated source.
michael@0 1291 * - column: The column number in the generated source.
michael@0 1292 *
michael@0 1293 * and an object is returned with the following properties:
michael@0 1294 *
michael@0 1295 * - source: The original source file, or null.
michael@0 1296 * - line: The line number in the original source, or null.
michael@0 1297 * - column: The column number in the original source, or null.
michael@0 1298 * - name: The original identifier, or null.
michael@0 1299 */
michael@0 1300 SourceMapConsumer.prototype.originalPositionFor =
michael@0 1301 function SourceMapConsumer_originalPositionFor(aArgs) {
michael@0 1302 var needle = {
michael@0 1303 generatedLine: util.getArg(aArgs, 'line'),
michael@0 1304 generatedColumn: util.getArg(aArgs, 'column')
michael@0 1305 };
michael@0 1306
michael@0 1307 var mapping = this._findMapping(needle,
michael@0 1308 this._generatedMappings,
michael@0 1309 "generatedLine",
michael@0 1310 "generatedColumn",
michael@0 1311 util.compareByGeneratedPositions);
michael@0 1312
michael@0 1313 if (mapping) {
michael@0 1314 var source = util.getArg(mapping, 'source', null);
michael@0 1315 if (source && this.sourceRoot) {
michael@0 1316 source = util.join(this.sourceRoot, source);
michael@0 1317 }
michael@0 1318 return {
michael@0 1319 source: source,
michael@0 1320 line: util.getArg(mapping, 'originalLine', null),
michael@0 1321 column: util.getArg(mapping, 'originalColumn', null),
michael@0 1322 name: util.getArg(mapping, 'name', null)
michael@0 1323 };
michael@0 1324 }
michael@0 1325
michael@0 1326 return {
michael@0 1327 source: null,
michael@0 1328 line: null,
michael@0 1329 column: null,
michael@0 1330 name: null
michael@0 1331 };
michael@0 1332 };
michael@0 1333
michael@0 1334 /**
michael@0 1335 * Returns the original source content. The only argument is the url of the
michael@0 1336 * original source file. Returns null if no original source content is
michael@0 1337 * availible.
michael@0 1338 */
michael@0 1339 SourceMapConsumer.prototype.sourceContentFor =
michael@0 1340 function SourceMapConsumer_sourceContentFor(aSource) {
michael@0 1341 if (!this.sourcesContent) {
michael@0 1342 return null;
michael@0 1343 }
michael@0 1344
michael@0 1345 if (this.sourceRoot) {
michael@0 1346 aSource = util.relative(this.sourceRoot, aSource);
michael@0 1347 }
michael@0 1348
michael@0 1349 if (this._sources.has(aSource)) {
michael@0 1350 return this.sourcesContent[this._sources.indexOf(aSource)];
michael@0 1351 }
michael@0 1352
michael@0 1353 var url;
michael@0 1354 if (this.sourceRoot
michael@0 1355 && (url = util.urlParse(this.sourceRoot))) {
michael@0 1356 // XXX: file:// URIs and absolute paths lead to unexpected behavior for
michael@0 1357 // many users. We can help them out when they expect file:// URIs to
michael@0 1358 // behave like it would if they were running a local HTTP server. See
michael@0 1359 // https://bugzilla.mozilla.org/show_bug.cgi?id=885597.
michael@0 1360 var fileUriAbsPath = aSource.replace(/^file:\/\//, "");
michael@0 1361 if (url.scheme == "file"
michael@0 1362 && this._sources.has(fileUriAbsPath)) {
michael@0 1363 return this.sourcesContent[this._sources.indexOf(fileUriAbsPath)]
michael@0 1364 }
michael@0 1365
michael@0 1366 if ((!url.path || url.path == "/")
michael@0 1367 && this._sources.has("/" + aSource)) {
michael@0 1368 return this.sourcesContent[this._sources.indexOf("/" + aSource)];
michael@0 1369 }
michael@0 1370 }
michael@0 1371
michael@0 1372 throw new Error('"' + aSource + '" is not in the SourceMap.');
michael@0 1373 };
michael@0 1374
michael@0 1375 /**
michael@0 1376 * Returns the generated line and column information for the original source,
michael@0 1377 * line, and column positions provided. The only argument is an object with
michael@0 1378 * the following properties:
michael@0 1379 *
michael@0 1380 * - source: The filename of the original source.
michael@0 1381 * - line: The line number in the original source.
michael@0 1382 * - column: The column number in the original source.
michael@0 1383 *
michael@0 1384 * and an object is returned with the following properties:
michael@0 1385 *
michael@0 1386 * - line: The line number in the generated source, or null.
michael@0 1387 * - column: The column number in the generated source, or null.
michael@0 1388 */
michael@0 1389 SourceMapConsumer.prototype.generatedPositionFor =
michael@0 1390 function SourceMapConsumer_generatedPositionFor(aArgs) {
michael@0 1391 var needle = {
michael@0 1392 source: util.getArg(aArgs, 'source'),
michael@0 1393 originalLine: util.getArg(aArgs, 'line'),
michael@0 1394 originalColumn: util.getArg(aArgs, 'column')
michael@0 1395 };
michael@0 1396
michael@0 1397 if (this.sourceRoot) {
michael@0 1398 needle.source = util.relative(this.sourceRoot, needle.source);
michael@0 1399 }
michael@0 1400
michael@0 1401 var mapping = this._findMapping(needle,
michael@0 1402 this._originalMappings,
michael@0 1403 "originalLine",
michael@0 1404 "originalColumn",
michael@0 1405 util.compareByOriginalPositions);
michael@0 1406
michael@0 1407 if (mapping) {
michael@0 1408 return {
michael@0 1409 line: util.getArg(mapping, 'generatedLine', null),
michael@0 1410 column: util.getArg(mapping, 'generatedColumn', null)
michael@0 1411 };
michael@0 1412 }
michael@0 1413
michael@0 1414 return {
michael@0 1415 line: null,
michael@0 1416 column: null
michael@0 1417 };
michael@0 1418 };
michael@0 1419
michael@0 1420 SourceMapConsumer.GENERATED_ORDER = 1;
michael@0 1421 SourceMapConsumer.ORIGINAL_ORDER = 2;
michael@0 1422
michael@0 1423 /**
michael@0 1424 * Iterate over each mapping between an original source/line/column and a
michael@0 1425 * generated line/column in this source map.
michael@0 1426 *
michael@0 1427 * @param Function aCallback
michael@0 1428 * The function that is called with each mapping.
michael@0 1429 * @param Object aContext
michael@0 1430 * Optional. If specified, this object will be the value of `this` every
michael@0 1431 * time that `aCallback` is called.
michael@0 1432 * @param aOrder
michael@0 1433 * Either `SourceMapConsumer.GENERATED_ORDER` or
michael@0 1434 * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
michael@0 1435 * iterate over the mappings sorted by the generated file's line/column
michael@0 1436 * order or the original's source/line/column order, respectively. Defaults to
michael@0 1437 * `SourceMapConsumer.GENERATED_ORDER`.
michael@0 1438 */
michael@0 1439 SourceMapConsumer.prototype.eachMapping =
michael@0 1440 function SourceMapConsumer_eachMapping(aCallback, aContext, aOrder) {
michael@0 1441 var context = aContext || null;
michael@0 1442 var order = aOrder || SourceMapConsumer.GENERATED_ORDER;
michael@0 1443
michael@0 1444 var mappings;
michael@0 1445 switch (order) {
michael@0 1446 case SourceMapConsumer.GENERATED_ORDER:
michael@0 1447 mappings = this._generatedMappings;
michael@0 1448 break;
michael@0 1449 case SourceMapConsumer.ORIGINAL_ORDER:
michael@0 1450 mappings = this._originalMappings;
michael@0 1451 break;
michael@0 1452 default:
michael@0 1453 throw new Error("Unknown order of iteration.");
michael@0 1454 }
michael@0 1455
michael@0 1456 var sourceRoot = this.sourceRoot;
michael@0 1457 mappings.map(function (mapping) {
michael@0 1458 var source = mapping.source;
michael@0 1459 if (source && sourceRoot) {
michael@0 1460 source = util.join(sourceRoot, source);
michael@0 1461 }
michael@0 1462 return {
michael@0 1463 source: source,
michael@0 1464 generatedLine: mapping.generatedLine,
michael@0 1465 generatedColumn: mapping.generatedColumn,
michael@0 1466 originalLine: mapping.originalLine,
michael@0 1467 originalColumn: mapping.originalColumn,
michael@0 1468 name: mapping.name
michael@0 1469 };
michael@0 1470 }).forEach(aCallback, context);
michael@0 1471 };
michael@0 1472
michael@0 1473 exports.SourceMapConsumer = SourceMapConsumer;
michael@0 1474
michael@0 1475 });
michael@0 1476 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 1477 /*
michael@0 1478 * Copyright 2011 Mozilla Foundation and contributors
michael@0 1479 * Licensed under the New BSD license. See LICENSE or:
michael@0 1480 * http://opensource.org/licenses/BSD-3-Clause
michael@0 1481 */
michael@0 1482 define('source-map/binary-search', ['require', 'exports', 'module' , ], function(require, exports, module) {
michael@0 1483
michael@0 1484 /**
michael@0 1485 * Recursive implementation of binary search.
michael@0 1486 *
michael@0 1487 * @param aLow Indices here and lower do not contain the needle.
michael@0 1488 * @param aHigh Indices here and higher do not contain the needle.
michael@0 1489 * @param aNeedle The element being searched for.
michael@0 1490 * @param aHaystack The non-empty array being searched.
michael@0 1491 * @param aCompare Function which takes two elements and returns -1, 0, or 1.
michael@0 1492 */
michael@0 1493 function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare) {
michael@0 1494 // This function terminates when one of the following is true:
michael@0 1495 //
michael@0 1496 // 1. We find the exact element we are looking for.
michael@0 1497 //
michael@0 1498 // 2. We did not find the exact element, but we can return the next
michael@0 1499 // closest element that is less than that element.
michael@0 1500 //
michael@0 1501 // 3. We did not find the exact element, and there is no next-closest
michael@0 1502 // element which is less than the one we are searching for, so we
michael@0 1503 // return null.
michael@0 1504 var mid = Math.floor((aHigh - aLow) / 2) + aLow;
michael@0 1505 var cmp = aCompare(aNeedle, aHaystack[mid], true);
michael@0 1506 if (cmp === 0) {
michael@0 1507 // Found the element we are looking for.
michael@0 1508 return aHaystack[mid];
michael@0 1509 }
michael@0 1510 else if (cmp > 0) {
michael@0 1511 // aHaystack[mid] is greater than our needle.
michael@0 1512 if (aHigh - mid > 1) {
michael@0 1513 // The element is in the upper half.
michael@0 1514 return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare);
michael@0 1515 }
michael@0 1516 // We did not find an exact match, return the next closest one
michael@0 1517 // (termination case 2).
michael@0 1518 return aHaystack[mid];
michael@0 1519 }
michael@0 1520 else {
michael@0 1521 // aHaystack[mid] is less than our needle.
michael@0 1522 if (mid - aLow > 1) {
michael@0 1523 // The element is in the lower half.
michael@0 1524 return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare);
michael@0 1525 }
michael@0 1526 // The exact needle element was not found in this haystack. Determine if
michael@0 1527 // we are in termination case (2) or (3) and return the appropriate thing.
michael@0 1528 return aLow < 0
michael@0 1529 ? null
michael@0 1530 : aHaystack[aLow];
michael@0 1531 }
michael@0 1532 }
michael@0 1533
michael@0 1534 /**
michael@0 1535 * This is an implementation of binary search which will always try and return
michael@0 1536 * the next lowest value checked if there is no exact hit. This is because
michael@0 1537 * mappings between original and generated line/col pairs are single points,
michael@0 1538 * and there is an implicit region between each of them, so a miss just means
michael@0 1539 * that you aren't on the very start of a region.
michael@0 1540 *
michael@0 1541 * @param aNeedle The element you are looking for.
michael@0 1542 * @param aHaystack The array that is being searched.
michael@0 1543 * @param aCompare A function which takes the needle and an element in the
michael@0 1544 * array and returns -1, 0, or 1 depending on whether the needle is less
michael@0 1545 * than, equal to, or greater than the element, respectively.
michael@0 1546 */
michael@0 1547 exports.search = function search(aNeedle, aHaystack, aCompare) {
michael@0 1548 return aHaystack.length > 0
michael@0 1549 ? recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack, aCompare)
michael@0 1550 : null;
michael@0 1551 };
michael@0 1552
michael@0 1553 });
michael@0 1554 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 1555 /*
michael@0 1556 * Copyright 2011 Mozilla Foundation and contributors
michael@0 1557 * Licensed under the New BSD license. See LICENSE or:
michael@0 1558 * http://opensource.org/licenses/BSD-3-Clause
michael@0 1559 */
michael@0 1560 define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/source-map-generator', 'source-map/util'], function(require, exports, module) {
michael@0 1561
michael@0 1562 var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator;
michael@0 1563 var util = require('./util');
michael@0 1564
michael@0 1565 /**
michael@0 1566 * SourceNodes provide a way to abstract over interpolating/concatenating
michael@0 1567 * snippets of generated JavaScript source code while maintaining the line and
michael@0 1568 * column information associated with the original source code.
michael@0 1569 *
michael@0 1570 * @param aLine The original line number.
michael@0 1571 * @param aColumn The original column number.
michael@0 1572 * @param aSource The original source's filename.
michael@0 1573 * @param aChunks Optional. An array of strings which are snippets of
michael@0 1574 * generated JS, or other SourceNodes.
michael@0 1575 * @param aName The original identifier.
michael@0 1576 */
michael@0 1577 function SourceNode(aLine, aColumn, aSource, aChunks, aName) {
michael@0 1578 this.children = [];
michael@0 1579 this.sourceContents = {};
michael@0 1580 this.line = aLine === undefined ? null : aLine;
michael@0 1581 this.column = aColumn === undefined ? null : aColumn;
michael@0 1582 this.source = aSource === undefined ? null : aSource;
michael@0 1583 this.name = aName === undefined ? null : aName;
michael@0 1584 if (aChunks != null) this.add(aChunks);
michael@0 1585 }
michael@0 1586
michael@0 1587 /**
michael@0 1588 * Creates a SourceNode from generated code and a SourceMapConsumer.
michael@0 1589 *
michael@0 1590 * @param aGeneratedCode The generated code
michael@0 1591 * @param aSourceMapConsumer The SourceMap for the generated code
michael@0 1592 */
michael@0 1593 SourceNode.fromStringWithSourceMap =
michael@0 1594 function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) {
michael@0 1595 // The SourceNode we want to fill with the generated code
michael@0 1596 // and the SourceMap
michael@0 1597 var node = new SourceNode();
michael@0 1598
michael@0 1599 // The generated code
michael@0 1600 // Processed fragments are removed from this array.
michael@0 1601 var remainingLines = aGeneratedCode.split('\n');
michael@0 1602
michael@0 1603 // We need to remember the position of "remainingLines"
michael@0 1604 var lastGeneratedLine = 1, lastGeneratedColumn = 0;
michael@0 1605
michael@0 1606 // The generate SourceNodes we need a code range.
michael@0 1607 // To extract it current and last mapping is used.
michael@0 1608 // Here we store the last mapping.
michael@0 1609 var lastMapping = null;
michael@0 1610
michael@0 1611 aSourceMapConsumer.eachMapping(function (mapping) {
michael@0 1612 if (lastMapping === null) {
michael@0 1613 // We add the generated code until the first mapping
michael@0 1614 // to the SourceNode without any mapping.
michael@0 1615 // Each line is added as separate string.
michael@0 1616 while (lastGeneratedLine < mapping.generatedLine) {
michael@0 1617 node.add(remainingLines.shift() + "\n");
michael@0 1618 lastGeneratedLine++;
michael@0 1619 }
michael@0 1620 if (lastGeneratedColumn < mapping.generatedColumn) {
michael@0 1621 var nextLine = remainingLines[0];
michael@0 1622 node.add(nextLine.substr(0, mapping.generatedColumn));
michael@0 1623 remainingLines[0] = nextLine.substr(mapping.generatedColumn);
michael@0 1624 lastGeneratedColumn = mapping.generatedColumn;
michael@0 1625 }
michael@0 1626 } else {
michael@0 1627 // We add the code from "lastMapping" to "mapping":
michael@0 1628 // First check if there is a new line in between.
michael@0 1629 if (lastGeneratedLine < mapping.generatedLine) {
michael@0 1630 var code = "";
michael@0 1631 // Associate full lines with "lastMapping"
michael@0 1632 do {
michael@0 1633 code += remainingLines.shift() + "\n";
michael@0 1634 lastGeneratedLine++;
michael@0 1635 lastGeneratedColumn = 0;
michael@0 1636 } while (lastGeneratedLine < mapping.generatedLine);
michael@0 1637 // When we reached the correct line, we add code until we
michael@0 1638 // reach the correct column too.
michael@0 1639 if (lastGeneratedColumn < mapping.generatedColumn) {
michael@0 1640 var nextLine = remainingLines[0];
michael@0 1641 code += nextLine.substr(0, mapping.generatedColumn);
michael@0 1642 remainingLines[0] = nextLine.substr(mapping.generatedColumn);
michael@0 1643 lastGeneratedColumn = mapping.generatedColumn;
michael@0 1644 }
michael@0 1645 // Create the SourceNode.
michael@0 1646 addMappingWithCode(lastMapping, code);
michael@0 1647 } else {
michael@0 1648 // There is no new line in between.
michael@0 1649 // Associate the code between "lastGeneratedColumn" and
michael@0 1650 // "mapping.generatedColumn" with "lastMapping"
michael@0 1651 var nextLine = remainingLines[0];
michael@0 1652 var code = nextLine.substr(0, mapping.generatedColumn -
michael@0 1653 lastGeneratedColumn);
michael@0 1654 remainingLines[0] = nextLine.substr(mapping.generatedColumn -
michael@0 1655 lastGeneratedColumn);
michael@0 1656 lastGeneratedColumn = mapping.generatedColumn;
michael@0 1657 addMappingWithCode(lastMapping, code);
michael@0 1658 }
michael@0 1659 }
michael@0 1660 lastMapping = mapping;
michael@0 1661 }, this);
michael@0 1662 // We have processed all mappings.
michael@0 1663 // Associate the remaining code in the current line with "lastMapping"
michael@0 1664 // and add the remaining lines without any mapping
michael@0 1665 addMappingWithCode(lastMapping, remainingLines.join("\n"));
michael@0 1666
michael@0 1667 // Copy sourcesContent into SourceNode
michael@0 1668 aSourceMapConsumer.sources.forEach(function (sourceFile) {
michael@0 1669 var content = aSourceMapConsumer.sourceContentFor(sourceFile);
michael@0 1670 if (content) {
michael@0 1671 node.setSourceContent(sourceFile, content);
michael@0 1672 }
michael@0 1673 });
michael@0 1674
michael@0 1675 return node;
michael@0 1676
michael@0 1677 function addMappingWithCode(mapping, code) {
michael@0 1678 if (mapping === null || mapping.source === undefined) {
michael@0 1679 node.add(code);
michael@0 1680 } else {
michael@0 1681 node.add(new SourceNode(mapping.originalLine,
michael@0 1682 mapping.originalColumn,
michael@0 1683 mapping.source,
michael@0 1684 code,
michael@0 1685 mapping.name));
michael@0 1686 }
michael@0 1687 }
michael@0 1688 };
michael@0 1689
michael@0 1690 /**
michael@0 1691 * Add a chunk of generated JS to this source node.
michael@0 1692 *
michael@0 1693 * @param aChunk A string snippet of generated JS code, another instance of
michael@0 1694 * SourceNode, or an array where each member is one of those things.
michael@0 1695 */
michael@0 1696 SourceNode.prototype.add = function SourceNode_add(aChunk) {
michael@0 1697 if (Array.isArray(aChunk)) {
michael@0 1698 aChunk.forEach(function (chunk) {
michael@0 1699 this.add(chunk);
michael@0 1700 }, this);
michael@0 1701 }
michael@0 1702 else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
michael@0 1703 if (aChunk) {
michael@0 1704 this.children.push(aChunk);
michael@0 1705 }
michael@0 1706 }
michael@0 1707 else {
michael@0 1708 throw new TypeError(
michael@0 1709 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
michael@0 1710 );
michael@0 1711 }
michael@0 1712 return this;
michael@0 1713 };
michael@0 1714
michael@0 1715 /**
michael@0 1716 * Add a chunk of generated JS to the beginning of this source node.
michael@0 1717 *
michael@0 1718 * @param aChunk A string snippet of generated JS code, another instance of
michael@0 1719 * SourceNode, or an array where each member is one of those things.
michael@0 1720 */
michael@0 1721 SourceNode.prototype.prepend = function SourceNode_prepend(aChunk) {
michael@0 1722 if (Array.isArray(aChunk)) {
michael@0 1723 for (var i = aChunk.length-1; i >= 0; i--) {
michael@0 1724 this.prepend(aChunk[i]);
michael@0 1725 }
michael@0 1726 }
michael@0 1727 else if (aChunk instanceof SourceNode || typeof aChunk === "string") {
michael@0 1728 this.children.unshift(aChunk);
michael@0 1729 }
michael@0 1730 else {
michael@0 1731 throw new TypeError(
michael@0 1732 "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " + aChunk
michael@0 1733 );
michael@0 1734 }
michael@0 1735 return this;
michael@0 1736 };
michael@0 1737
michael@0 1738 /**
michael@0 1739 * Walk over the tree of JS snippets in this node and its children. The
michael@0 1740 * walking function is called once for each snippet of JS and is passed that
michael@0 1741 * snippet and the its original associated source's line/column location.
michael@0 1742 *
michael@0 1743 * @param aFn The traversal function.
michael@0 1744 */
michael@0 1745 SourceNode.prototype.walk = function SourceNode_walk(aFn) {
michael@0 1746 var chunk;
michael@0 1747 for (var i = 0, len = this.children.length; i < len; i++) {
michael@0 1748 chunk = this.children[i];
michael@0 1749 if (chunk instanceof SourceNode) {
michael@0 1750 chunk.walk(aFn);
michael@0 1751 }
michael@0 1752 else {
michael@0 1753 if (chunk !== '') {
michael@0 1754 aFn(chunk, { source: this.source,
michael@0 1755 line: this.line,
michael@0 1756 column: this.column,
michael@0 1757 name: this.name });
michael@0 1758 }
michael@0 1759 }
michael@0 1760 }
michael@0 1761 };
michael@0 1762
michael@0 1763 /**
michael@0 1764 * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
michael@0 1765 * each of `this.children`.
michael@0 1766 *
michael@0 1767 * @param aSep The separator.
michael@0 1768 */
michael@0 1769 SourceNode.prototype.join = function SourceNode_join(aSep) {
michael@0 1770 var newChildren;
michael@0 1771 var i;
michael@0 1772 var len = this.children.length;
michael@0 1773 if (len > 0) {
michael@0 1774 newChildren = [];
michael@0 1775 for (i = 0; i < len-1; i++) {
michael@0 1776 newChildren.push(this.children[i]);
michael@0 1777 newChildren.push(aSep);
michael@0 1778 }
michael@0 1779 newChildren.push(this.children[i]);
michael@0 1780 this.children = newChildren;
michael@0 1781 }
michael@0 1782 return this;
michael@0 1783 };
michael@0 1784
michael@0 1785 /**
michael@0 1786 * Call String.prototype.replace on the very right-most source snippet. Useful
michael@0 1787 * for trimming whitespace from the end of a source node, etc.
michael@0 1788 *
michael@0 1789 * @param aPattern The pattern to replace.
michael@0 1790 * @param aReplacement The thing to replace the pattern with.
michael@0 1791 */
michael@0 1792 SourceNode.prototype.replaceRight = function SourceNode_replaceRight(aPattern, aReplacement) {
michael@0 1793 var lastChild = this.children[this.children.length - 1];
michael@0 1794 if (lastChild instanceof SourceNode) {
michael@0 1795 lastChild.replaceRight(aPattern, aReplacement);
michael@0 1796 }
michael@0 1797 else if (typeof lastChild === 'string') {
michael@0 1798 this.children[this.children.length - 1] = lastChild.replace(aPattern, aReplacement);
michael@0 1799 }
michael@0 1800 else {
michael@0 1801 this.children.push(''.replace(aPattern, aReplacement));
michael@0 1802 }
michael@0 1803 return this;
michael@0 1804 };
michael@0 1805
michael@0 1806 /**
michael@0 1807 * Set the source content for a source file. This will be added to the SourceMapGenerator
michael@0 1808 * in the sourcesContent field.
michael@0 1809 *
michael@0 1810 * @param aSourceFile The filename of the source file
michael@0 1811 * @param aSourceContent The content of the source file
michael@0 1812 */
michael@0 1813 SourceNode.prototype.setSourceContent =
michael@0 1814 function SourceNode_setSourceContent(aSourceFile, aSourceContent) {
michael@0 1815 this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
michael@0 1816 };
michael@0 1817
michael@0 1818 /**
michael@0 1819 * Walk over the tree of SourceNodes. The walking function is called for each
michael@0 1820 * source file content and is passed the filename and source content.
michael@0 1821 *
michael@0 1822 * @param aFn The traversal function.
michael@0 1823 */
michael@0 1824 SourceNode.prototype.walkSourceContents =
michael@0 1825 function SourceNode_walkSourceContents(aFn) {
michael@0 1826 for (var i = 0, len = this.children.length; i < len; i++) {
michael@0 1827 if (this.children[i] instanceof SourceNode) {
michael@0 1828 this.children[i].walkSourceContents(aFn);
michael@0 1829 }
michael@0 1830 }
michael@0 1831
michael@0 1832 var sources = Object.keys(this.sourceContents);
michael@0 1833 for (var i = 0, len = sources.length; i < len; i++) {
michael@0 1834 aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
michael@0 1835 }
michael@0 1836 };
michael@0 1837
michael@0 1838 /**
michael@0 1839 * Return the string representation of this source node. Walks over the tree
michael@0 1840 * and concatenates all the various snippets together to one string.
michael@0 1841 */
michael@0 1842 SourceNode.prototype.toString = function SourceNode_toString() {
michael@0 1843 var str = "";
michael@0 1844 this.walk(function (chunk) {
michael@0 1845 str += chunk;
michael@0 1846 });
michael@0 1847 return str;
michael@0 1848 };
michael@0 1849
michael@0 1850 /**
michael@0 1851 * Returns the string representation of this source node along with a source
michael@0 1852 * map.
michael@0 1853 */
michael@0 1854 SourceNode.prototype.toStringWithSourceMap = function SourceNode_toStringWithSourceMap(aArgs) {
michael@0 1855 var generated = {
michael@0 1856 code: "",
michael@0 1857 line: 1,
michael@0 1858 column: 0
michael@0 1859 };
michael@0 1860 var map = new SourceMapGenerator(aArgs);
michael@0 1861 var sourceMappingActive = false;
michael@0 1862 var lastOriginalSource = null;
michael@0 1863 var lastOriginalLine = null;
michael@0 1864 var lastOriginalColumn = null;
michael@0 1865 var lastOriginalName = null;
michael@0 1866 this.walk(function (chunk, original) {
michael@0 1867 generated.code += chunk;
michael@0 1868 if (original.source !== null
michael@0 1869 && original.line !== null
michael@0 1870 && original.column !== null) {
michael@0 1871 if(lastOriginalSource !== original.source
michael@0 1872 || lastOriginalLine !== original.line
michael@0 1873 || lastOriginalColumn !== original.column
michael@0 1874 || lastOriginalName !== original.name) {
michael@0 1875 map.addMapping({
michael@0 1876 source: original.source,
michael@0 1877 original: {
michael@0 1878 line: original.line,
michael@0 1879 column: original.column
michael@0 1880 },
michael@0 1881 generated: {
michael@0 1882 line: generated.line,
michael@0 1883 column: generated.column
michael@0 1884 },
michael@0 1885 name: original.name
michael@0 1886 });
michael@0 1887 }
michael@0 1888 lastOriginalSource = original.source;
michael@0 1889 lastOriginalLine = original.line;
michael@0 1890 lastOriginalColumn = original.column;
michael@0 1891 lastOriginalName = original.name;
michael@0 1892 sourceMappingActive = true;
michael@0 1893 } else if (sourceMappingActive) {
michael@0 1894 map.addMapping({
michael@0 1895 generated: {
michael@0 1896 line: generated.line,
michael@0 1897 column: generated.column
michael@0 1898 }
michael@0 1899 });
michael@0 1900 lastOriginalSource = null;
michael@0 1901 sourceMappingActive = false;
michael@0 1902 }
michael@0 1903 chunk.split('').forEach(function (ch) {
michael@0 1904 if (ch === '\n') {
michael@0 1905 generated.line++;
michael@0 1906 generated.column = 0;
michael@0 1907 } else {
michael@0 1908 generated.column++;
michael@0 1909 }
michael@0 1910 });
michael@0 1911 });
michael@0 1912 this.walkSourceContents(function (sourceFile, sourceContent) {
michael@0 1913 map.setSourceContent(sourceFile, sourceContent);
michael@0 1914 });
michael@0 1915
michael@0 1916 return { code: generated.code, map: map };
michael@0 1917 };
michael@0 1918
michael@0 1919 exports.SourceNode = SourceNode;
michael@0 1920
michael@0 1921 });
michael@0 1922 /* -*- Mode: js; js-indent-level: 2; -*- */
michael@0 1923 ///////////////////////////////////////////////////////////////////////////////
michael@0 1924
michael@0 1925 this.sourceMap = {
michael@0 1926 SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer,
michael@0 1927 SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator,
michael@0 1928 SourceNode: require('source-map/source-node').SourceNode
michael@0 1929 };

mercurial