From 614f120f2ba620e5b2faa87bb6351b093beb2f09 Mon Sep 17 00:00:00 2001 From: Ville Lautanala Date: Sun, 15 Mar 2015 09:29:17 +0200 Subject: [PATCH] Update source-map to 0.1.36 --- lib/es5.js | 2 +- lib/uglify.js | 794 +++++++++++++++++++++++++++------------- spec/source_map_spec.rb | 2 +- vendor/source-map | 2 +- 4 files changed, 550 insertions(+), 250 deletions(-) diff --git a/lib/es5.js b/lib/es5.js index b3bd4af..b5712b8 100644 --- a/lib/es5.js +++ b/lib/es5.js @@ -307,7 +307,7 @@ if(!String.prototype.trim) { function definePropertyWorks() { try { Object.defineProperty({}, "property", {}); - return "property" in object; + return true; } catch (exception) { return false; } diff --git a/lib/uglify.js b/lib/uglify.js index 560a2a5..511bd14 100644 --- a/lib/uglify.js +++ b/lib/uglify.js @@ -164,14 +164,17 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so /** * An instance of the SourceMapGenerator represents a source map which is - * being built incrementally. To create a new one, you must pass an object - * with the following properties: + * being built incrementally. You may pass an object with the following + * properties: * * - file: The filename of the generated source. - * - sourceRoot: An optional root for all URLs in this source map. + * - sourceRoot: A root for all relative URLs in this source map. */ function SourceMapGenerator(aArgs) { - this._file = util.getArg(aArgs, 'file'); + if (!aArgs) { + aArgs = {}; + } + this._file = util.getArg(aArgs, 'file', null); this._sourceRoot = util.getArg(aArgs, 'sourceRoot', null); this._sources = new ArraySet(); this._names = new ArraySet(); @@ -201,9 +204,9 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so } }; - if (mapping.source) { + if (mapping.source != null) { newMapping.source = mapping.source; - if (sourceRoot) { + if (sourceRoot != null) { newMapping.source = util.relative(sourceRoot, newMapping.source); } @@ -212,7 +215,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so column: mapping.originalColumn }; - if (mapping.name) { + if (mapping.name != null) { newMapping.name = mapping.name; } } @@ -221,7 +224,7 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so }); aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { + if (content != null) { generator.setSourceContent(sourceFile, content); } }); @@ -247,17 +250,19 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so this._validateMapping(generated, original, source, name); - if (source && !this._sources.has(source)) { + if (source != null && !this._sources.has(source)) { this._sources.add(source); } - if (name && !this._names.has(name)) { + if (name != null && !this._names.has(name)) { this._names.add(name); } this._mappings.push({ - generated: generated, - original: original, + generatedLine: generated.line, + generatedColumn: generated.column, + originalLine: original != null && original.line, + originalColumn: original != null && original.column, source: source, name: name }); @@ -269,11 +274,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so SourceMapGenerator.prototype.setSourceContent = function SourceMapGenerator_setSourceContent(aSourceFile, aSourceContent) { var source = aSourceFile; - if (this._sourceRoot) { + if (this._sourceRoot != null) { source = util.relative(this._sourceRoot, source); } - if (aSourceContent !== null) { + if (aSourceContent != null) { // Add the source content to the _sourcesContents map. // Create a new _sourcesContents map if the property is null. if (!this._sourcesContents) { @@ -299,41 +304,56 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so * @param aSourceMapConsumer The source map to be applied. * @param aSourceFile Optional. The filename of the source file. * If omitted, SourceMapConsumer's file property will be used. + * @param aSourceMapPath Optional. The dirname of the path to the source map + * to be applied. If relative, it is relative to the SourceMapConsumer. + * This parameter is needed when the two source maps aren't in the same + * directory, and the source map to be applied contains relative source + * paths. If so, those relative source paths need to be rewritten + * relative to the SourceMapGenerator. */ SourceMapGenerator.prototype.applySourceMap = - function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile) { + function SourceMapGenerator_applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) { + var sourceFile = aSourceFile; // If aSourceFile is omitted, we will use the file property of the SourceMap - if (!aSourceFile) { - aSourceFile = aSourceMapConsumer.file; + if (aSourceFile == null) { + if (aSourceMapConsumer.file == null) { + throw new Error( + 'SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, ' + + 'or the source map\'s "file" property. Both were omitted.' + ); + } + sourceFile = aSourceMapConsumer.file; } var sourceRoot = this._sourceRoot; - // Make "aSourceFile" relative if an absolute Url is passed. - if (sourceRoot) { - aSourceFile = util.relative(sourceRoot, aSourceFile); + // Make "sourceFile" relative if an absolute Url is passed. + if (sourceRoot != null) { + sourceFile = util.relative(sourceRoot, sourceFile); } // Applying the SourceMap can add and remove items from the sources and // the names array. var newSources = new ArraySet(); var newNames = new ArraySet(); - // Find mappings for the "aSourceFile" + // Find mappings for the "sourceFile" this._mappings.forEach(function (mapping) { - if (mapping.source === aSourceFile && mapping.original) { + if (mapping.source === sourceFile && mapping.originalLine != null) { // Check if it can be mapped by the source map, then update the mapping. var original = aSourceMapConsumer.originalPositionFor({ - line: mapping.original.line, - column: mapping.original.column + line: mapping.originalLine, + column: mapping.originalColumn }); - if (original.source !== null) { + if (original.source != null) { // Copy mapping - if (sourceRoot) { - mapping.source = util.relative(sourceRoot, original.source); - } else { - mapping.source = original.source; + mapping.source = original.source; + if (aSourceMapPath != null) { + mapping.source = util.join(aSourceMapPath, mapping.source) + } + if (sourceRoot != null) { + mapping.source = util.relative(sourceRoot, mapping.source); } - mapping.original.line = original.line; - mapping.original.column = original.column; - if (original.name !== null && mapping.name !== null) { + mapping.originalLine = original.line; + mapping.originalColumn = original.column; + if (original.name != null && mapping.name != null) { // Only use the identifier name if it's an identifier // in both SourceMaps mapping.name = original.name; @@ -342,12 +362,12 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so } var source = mapping.source; - if (source && !newSources.has(source)) { + if (source != null && !newSources.has(source)) { newSources.add(source); } var name = mapping.name; - if (name && !newNames.has(name)) { + if (name != null && !newNames.has(name)) { newNames.add(name); } @@ -358,8 +378,11 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so // Copy sourcesContents of applied map. aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { - if (sourceRoot) { + if (content != null) { + if (aSourceMapPath != null) { + sourceFile = util.join(aSourceMapPath, sourceFile); + } + if (sourceRoot != null) { sourceFile = util.relative(sourceRoot, sourceFile); } this.setSourceContent(sourceFile, content); @@ -396,28 +419,15 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so return; } else { - throw new Error('Invalid mapping.'); + throw new Error('Invalid mapping: ' + JSON.stringify({ + generated: aGenerated, + source: aSource, + original: aOriginal, + name: aName + })); } }; - function cmpLocation(loc1, loc2) { - var cmp = (loc1 && loc1.line) - (loc2 && loc2.line); - return cmp ? cmp : (loc1 && loc1.column) - (loc2 && loc2.column); - } - - function strcmp(str1, str2) { - str1 = str1 || ''; - str2 = str2 || ''; - return (str1 > str2) - (str1 < str2); - } - - function cmpMapping(mappingA, mappingB) { - return cmpLocation(mappingA.generated, mappingB.generated) || - cmpLocation(mappingA.original, mappingB.original) || - strcmp(mappingA.source, mappingB.source) || - strcmp(mappingA.name, mappingB.name); - } - /** * Serialize the accumulated mappings in to the stream of base 64 VLQs * specified by the source map format. @@ -438,46 +448,46 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so // via the ';' separators) will be all messed up. Note: it might be more // performant to maintain the sorting as we insert them, rather than as we // serialize them, but the big O is the same either way. - this._mappings.sort(cmpMapping); + this._mappings.sort(util.compareByGeneratedPositions); for (var i = 0, len = this._mappings.length; i < len; i++) { mapping = this._mappings[i]; - if (mapping.generated.line !== previousGeneratedLine) { + if (mapping.generatedLine !== previousGeneratedLine) { previousGeneratedColumn = 0; - while (mapping.generated.line !== previousGeneratedLine) { + while (mapping.generatedLine !== previousGeneratedLine) { result += ';'; previousGeneratedLine++; } } else { if (i > 0) { - if (!cmpMapping(mapping, this._mappings[i - 1])) { + if (!util.compareByGeneratedPositions(mapping, this._mappings[i - 1])) { continue; } result += ','; } } - result += base64VLQ.encode(mapping.generated.column + result += base64VLQ.encode(mapping.generatedColumn - previousGeneratedColumn); - previousGeneratedColumn = mapping.generated.column; + previousGeneratedColumn = mapping.generatedColumn; - if (mapping.source && mapping.original) { + if (mapping.source != null) { result += base64VLQ.encode(this._sources.indexOf(mapping.source) - previousSource); previousSource = this._sources.indexOf(mapping.source); // lines are stored 0-based in SourceMap spec version 3 - result += base64VLQ.encode(mapping.original.line - 1 + result += base64VLQ.encode(mapping.originalLine - 1 - previousOriginalLine); - previousOriginalLine = mapping.original.line - 1; + previousOriginalLine = mapping.originalLine - 1; - result += base64VLQ.encode(mapping.original.column + result += base64VLQ.encode(mapping.originalColumn - previousOriginalColumn); - previousOriginalColumn = mapping.original.column; + previousOriginalColumn = mapping.originalColumn; - if (mapping.name) { + if (mapping.name != null) { result += base64VLQ.encode(this._names.indexOf(mapping.name) - previousName); previousName = this._names.indexOf(mapping.name); @@ -488,6 +498,23 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so return result; }; + SourceMapGenerator.prototype._generateSourcesContent = + function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) { + return aSources.map(function (source) { + if (!this._sourcesContents) { + return null; + } + if (aSourceRoot != null) { + source = util.relative(aSourceRoot, source); + } + var key = util.toSetString(source); + return Object.prototype.hasOwnProperty.call(this._sourcesContents, + key) + ? this._sourcesContents[key] + : null; + }, this); + }; + /** * Externalize the source map. */ @@ -495,25 +522,20 @@ define('source-map/source-map-generator', ['require', 'exports', 'module' , 'so function SourceMapGenerator_toJSON() { var map = { version: this._version, - file: this._file, sources: this._sources.toArray(), names: this._names.toArray(), mappings: this._serializeMappings() }; - if (this._sourceRoot) { + if (this._file != null) { + map.file = this._file; + } + if (this._sourceRoot != null) { map.sourceRoot = this._sourceRoot; } if (this._sourcesContents) { - map.sourcesContent = map.sources.map(function (source) { - if (map.sourceRoot) { - source = util.relative(map.sourceRoot, source); - } - return Object.prototype.hasOwnProperty.call( - this._sourcesContents, util.toSetString(source)) - ? this._sourcesContents[util.toSetString(source)] - : null; - }, this); + map.sourcesContent = this._generateSourcesContent(map.sources, map.sourceRoot); } + return map; }; @@ -737,7 +759,8 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require, } exports.getArg = getArg; - var urlRegexp = /([\w+\-.]+):\/\/((\w+:\w+)@)?([\w.]+)?(:(\d+))?(\S+)?/; + var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.]*)(?::(\d+))?(\S*)$/; + var dataUrlRegexp = /^data:.+\,.+$/; function urlParse(aUrl) { var match = aUrl.match(urlRegexp); @@ -746,18 +769,22 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require, } return { scheme: match[1], - auth: match[3], - host: match[4], - port: match[6], - path: match[7] + auth: match[2], + host: match[3], + port: match[4], + path: match[5] }; } exports.urlParse = urlParse; function urlGenerate(aParsedUrl) { - var url = aParsedUrl.scheme + "://"; + var url = ''; + if (aParsedUrl.scheme) { + url += aParsedUrl.scheme + ':'; + } + url += '//'; if (aParsedUrl.auth) { - url += aParsedUrl.auth + "@" + url += aParsedUrl.auth + '@'; } if (aParsedUrl.host) { url += aParsedUrl.host; @@ -772,19 +799,112 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require, } exports.urlGenerate = urlGenerate; + /** + * Normalizes a path, or the path portion of a URL: + * + * - Replaces consequtive slashes with one slash. + * - Removes unnecessary '.' parts. + * - Removes unnecessary '/..' parts. + * + * Based on code in the Node.js 'path' core module. + * + * @param aPath The path or url to normalize. + */ + function normalize(aPath) { + var path = aPath; + var url = urlParse(aPath); + if (url) { + if (!url.path) { + return aPath; + } + path = url.path; + } + var isAbsolute = (path.charAt(0) === '/'); + + var parts = path.split(/\/+/); + for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { + part = parts[i]; + if (part === '.') { + parts.splice(i, 1); + } else if (part === '..') { + up++; + } else if (up > 0) { + if (part === '') { + // The first part is blank if the path is absolute. Trying to go + // above the root is a no-op. Therefore we can remove all '..' parts + // directly after the root. + parts.splice(i + 1, up); + up = 0; + } else { + parts.splice(i, 2); + up--; + } + } + } + path = parts.join('/'); + + if (path === '') { + path = isAbsolute ? '/' : '.'; + } + + if (url) { + url.path = path; + return urlGenerate(url); + } + return path; + } + exports.normalize = normalize; + + /** + * Joins two paths/URLs. + * + * @param aRoot The root path or URL. + * @param aPath The path or URL to be joined with the root. + * + * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a + * scheme-relative URL: Then the scheme of aRoot, if any, is prepended + * first. + * - Otherwise aPath is a path. If aRoot is a URL, then its path portion + * is updated with the result and aRoot is returned. Otherwise the result + * is returned. + * - If aPath is absolute, the result is aPath. + * - Otherwise the two paths are joined with a slash. + * - Joining for example 'http://' and 'www.example.com' is also supported. + */ function join(aRoot, aPath) { - var url; + var aPathUrl = urlParse(aPath); + var aRootUrl = urlParse(aRoot); + if (aRootUrl) { + aRoot = aRootUrl.path || '/'; + } - if (aPath.match(urlRegexp)) { + // `join(foo, '//www.example.org')` + if (aPathUrl && !aPathUrl.scheme) { + if (aRootUrl) { + aPathUrl.scheme = aRootUrl.scheme; + } + return urlGenerate(aPathUrl); + } + + if (aPathUrl || aPath.match(dataUrlRegexp)) { return aPath; } - if (aPath.charAt(0) === '/' && (url = urlParse(aRoot))) { - url.path = aPath; - return urlGenerate(url); + // `join('http://', 'www.example.com')` + if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { + aRootUrl.host = aPath; + return urlGenerate(aRootUrl); } - return aRoot.replace(/\/$/, '') + '/' + aPath; + var joined = aPath.charAt(0) === '/' + ? aPath + : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); + + if (aRootUrl) { + aRootUrl.path = joined; + return urlGenerate(aRootUrl); + } + return joined; } exports.join = join; @@ -821,6 +941,93 @@ define('source-map/util', ['require', 'exports', 'module' , ], function(require, } exports.relative = relative; + function strcmp(aStr1, aStr2) { + var s1 = aStr1 || ""; + var s2 = aStr2 || ""; + return (s1 > s2) - (s1 < s2); + } + + /** + * Comparator between two mappings where the original positions are compared. + * + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same original source/line/column, but different generated + * line and column the same. Useful when searching for a mapping with a + * stubbed out mapping. + */ + function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { + var cmp; + + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp) { + return cmp; + } + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp) { + return cmp; + } + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp || onlyCompareOriginal) { + return cmp; + } + + cmp = strcmp(mappingA.name, mappingB.name); + if (cmp) { + return cmp; + } + + cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp) { + return cmp; + } + + return mappingA.generatedColumn - mappingB.generatedColumn; + }; + exports.compareByOriginalPositions = compareByOriginalPositions; + + /** + * Comparator between two mappings where the generated positions are + * compared. + * + * Optionally pass in `true` as `onlyCompareGenerated` to consider two + * mappings with the same generated line and column, but different + * source/name/original line and column the same. Useful when searching for a + * mapping with a stubbed out mapping. + */ + function compareByGeneratedPositions(mappingA, mappingB, onlyCompareGenerated) { + var cmp; + + cmp = mappingA.generatedLine - mappingB.generatedLine; + if (cmp) { + return cmp; + } + + cmp = mappingA.generatedColumn - mappingB.generatedColumn; + if (cmp || onlyCompareGenerated) { + return cmp; + } + + cmp = strcmp(mappingA.source, mappingB.source); + if (cmp) { + return cmp; + } + + cmp = mappingA.originalLine - mappingB.originalLine; + if (cmp) { + return cmp; + } + + cmp = mappingA.originalColumn - mappingB.originalColumn; + if (cmp) { + return cmp; + } + + return strcmp(mappingA.name, mappingB.name); + }; + exports.compareByGeneratedPositions = compareByGeneratedPositions; + }); /* -*- Mode: js; js-indent-level: 2; -*- */ /* @@ -846,10 +1053,10 @@ define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/ut /** * Static method for creating ArraySet instances from an existing array. */ - ArraySet.fromArray = function ArraySet_fromArray(aArray) { + ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) { var set = new ArraySet(); for (var i = 0, len = aArray.length; i < len; i++) { - set.add(aArray[i]); + set.add(aArray[i], aAllowDuplicates); } return set; }; @@ -859,14 +1066,15 @@ define('source-map/array-set', ['require', 'exports', 'module' , 'source-map/ut * * @param String aStr */ - ArraySet.prototype.add = function ArraySet_add(aStr) { - if (this.has(aStr)) { - // Already a member; nothing to do. - return; - } + ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) { + var isDuplicate = this.has(aStr); var idx = this._array.length; - this._array.push(aStr); - this._set[util.toSetString(aStr)] = idx; + if (!isDuplicate || aAllowDuplicates) { + this._array.push(aStr); + } + if (!isDuplicate) { + this._set[util.toSetString(aStr)] = idx; + } }; /** @@ -943,7 +1151,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou * - sourceRoot: Optional. The URL root from which all sources are relative. * - sourcesContent: Optional. An array of contents of the original source files. * - mappings: A string of base64 VLQs which contain the actual mappings. - * - file: The generated file this source map is associated with. + * - file: Optional. The generated file this source map is associated with. * * Here is an example source map, taken from the source map spec[0]: * @@ -966,50 +1174,59 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou var version = util.getArg(sourceMap, 'version'); var sources = util.getArg(sourceMap, 'sources'); - var names = util.getArg(sourceMap, 'names'); + // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which + // requires the array) to play nice here. + var names = util.getArg(sourceMap, 'names', []); var sourceRoot = util.getArg(sourceMap, 'sourceRoot', null); var sourcesContent = util.getArg(sourceMap, 'sourcesContent', null); var mappings = util.getArg(sourceMap, 'mappings'); - var file = util.getArg(sourceMap, 'file'); + var file = util.getArg(sourceMap, 'file', null); - if (version !== this._version) { + // Once again, Sass deviates from the spec and supplies the version as a + // string rather than a number, so we use loose equality checking here. + if (version != this._version) { throw new Error('Unsupported version: ' + version); } - this._names = ArraySet.fromArray(names); - this._sources = ArraySet.fromArray(sources); + // Pass `true` below to allow duplicate names and sources. While source maps + // are intended to be compressed and deduplicated, the TypeScript compiler + // sometimes generates source maps with duplicates in them. See Github issue + // #72 and bugzil.la/889492. + this._names = ArraySet.fromArray(names, true); + this._sources = ArraySet.fromArray(sources, true); + this.sourceRoot = sourceRoot; this.sourcesContent = sourcesContent; + this._mappings = mappings; this.file = file; - - // `this._generatedMappings` and `this._originalMappings` hold the parsed - // mapping coordinates from the source map's "mappings" attribute. Each - // object in the array is of the form - // - // { - // generatedLine: The line number in the generated code, - // generatedColumn: The column number in the generated code, - // source: The path to the original source file that generated this - // chunk of code, - // originalLine: The line number in the original source that - // corresponds to this chunk of generated code, - // originalColumn: The column number in the original source that - // corresponds to this chunk of generated code, - // name: The name of the original symbol which generated this chunk of - // code. - // } - // - // All properties except for `generatedLine` and `generatedColumn` can be - // `null`. - // - // `this._generatedMappings` is ordered by the generated positions. - // - // `this._originalMappings` is ordered by the original positions. - this._generatedMappings = []; - this._originalMappings = []; - this._parseMappings(mappings, sourceRoot); } + /** + * Create a SourceMapConsumer from a SourceMapGenerator. + * + * @param SourceMapGenerator aSourceMap + * The source map that will be consumed. + * @returns SourceMapConsumer + */ + SourceMapConsumer.fromSourceMap = + function SourceMapConsumer_fromSourceMap(aSourceMap) { + var smc = Object.create(SourceMapConsumer.prototype); + + smc._names = ArraySet.fromArray(aSourceMap._names.toArray(), true); + smc._sources = ArraySet.fromArray(aSourceMap._sources.toArray(), true); + smc.sourceRoot = aSourceMap._sourceRoot; + smc.sourcesContent = aSourceMap._generateSourcesContent(smc._sources.toArray(), + smc.sourceRoot); + smc.file = aSourceMap._file; + + smc.__generatedMappings = aSourceMap._mappings.slice() + .sort(util.compareByGeneratedPositions); + smc.__originalMappings = aSourceMap._mappings.slice() + .sort(util.compareByOriginalPositions); + + return smc; + }; + /** * The version of the source mapping spec that we are consuming. */ @@ -1021,14 +1238,71 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou Object.defineProperty(SourceMapConsumer.prototype, 'sources', { get: function () { return this._sources.toArray().map(function (s) { - return this.sourceRoot ? util.join(this.sourceRoot, s) : s; + return this.sourceRoot != null ? util.join(this.sourceRoot, s) : s; }, this); } }); + // `__generatedMappings` and `__originalMappings` are arrays that hold the + // parsed mapping coordinates from the source map's "mappings" attribute. They + // are lazily instantiated, accessed via the `_generatedMappings` and + // `_originalMappings` getters respectively, and we only parse the mappings + // and create these arrays once queried for a source location. We jump through + // these hoops because there can be many thousands of mappings, and parsing + // them is expensive, so we only want to do it if we must. + // + // Each object in the arrays is of the form: + // + // { + // generatedLine: The line number in the generated code, + // generatedColumn: The column number in the generated code, + // source: The path to the original source file that generated this + // chunk of code, + // originalLine: The line number in the original source that + // corresponds to this chunk of generated code, + // originalColumn: The column number in the original source that + // corresponds to this chunk of generated code, + // name: The name of the original symbol which generated this chunk of + // code. + // } + // + // All properties except for `generatedLine` and `generatedColumn` can be + // `null`. + // + // `_generatedMappings` is ordered by the generated positions. + // + // `_originalMappings` is ordered by the original positions. + + SourceMapConsumer.prototype.__generatedMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_generatedMappings', { + get: function () { + if (!this.__generatedMappings) { + this.__generatedMappings = []; + this.__originalMappings = []; + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__generatedMappings; + } + }); + + SourceMapConsumer.prototype.__originalMappings = null; + Object.defineProperty(SourceMapConsumer.prototype, '_originalMappings', { + get: function () { + if (!this.__originalMappings) { + this.__generatedMappings = []; + this.__originalMappings = []; + this._parseMappings(this._mappings, this.sourceRoot); + } + + return this.__originalMappings; + } + }); + /** * Parse the mappings in a string in to a data structure which we can easily - * query (an ordered list in this._generatedMappings). + * query (the ordered arrays in the `this.__generatedMappings` and + * `this.__originalMappings` properties). */ SourceMapConsumer.prototype._parseMappings = function SourceMapConsumer_parseMappings(aStr, aSourceRoot) { @@ -1098,44 +1372,15 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou } } - this._generatedMappings.push(mapping); + this.__generatedMappings.push(mapping); if (typeof mapping.originalLine === 'number') { - this._originalMappings.push(mapping); + this.__originalMappings.push(mapping); } } } - this._originalMappings.sort(this._compareOriginalPositions); - }; - - /** - * Comparator between two mappings where the original positions are compared. - */ - SourceMapConsumer.prototype._compareOriginalPositions = - function SourceMapConsumer_compareOriginalPositions(mappingA, mappingB) { - if (mappingA.source > mappingB.source) { - return 1; - } - else if (mappingA.source < mappingB.source) { - return -1; - } - else { - var cmp = mappingA.originalLine - mappingB.originalLine; - return cmp === 0 - ? mappingA.originalColumn - mappingB.originalColumn - : cmp; - } - }; - - /** - * Comparator between two mappings where the generated positions are compared. - */ - SourceMapConsumer.prototype._compareGeneratedPositions = - function SourceMapConsumer_compareGeneratedPositions(mappingA, mappingB) { - var cmp = mappingA.generatedLine - mappingB.generatedLine; - return cmp === 0 - ? mappingA.generatedColumn - mappingB.generatedColumn - : cmp; + this.__generatedMappings.sort(util.compareByGeneratedPositions); + this.__originalMappings.sort(util.compareByOriginalPositions); }; /** @@ -1188,11 +1433,11 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou this._generatedMappings, "generatedLine", "generatedColumn", - this._compareGeneratedPositions); + util.compareByGeneratedPositions); - if (mapping) { + if (mapping && mapping.generatedLine === needle.generatedLine) { var source = util.getArg(mapping, 'source', null); - if (source && this.sourceRoot) { + if (source != null && this.sourceRoot != null) { source = util.join(this.sourceRoot, source); } return { @@ -1222,7 +1467,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou return null; } - if (this.sourceRoot) { + if (this.sourceRoot != null) { aSource = util.relative(this.sourceRoot, aSource); } @@ -1231,7 +1476,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou } var url; - if (this.sourceRoot + if (this.sourceRoot != null && (url = util.urlParse(this.sourceRoot))) { // XXX: file:// URIs and absolute paths lead to unexpected behavior for // many users. We can help them out when they expect file:// URIs to @@ -1274,7 +1519,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou originalColumn: util.getArg(aArgs, 'column') }; - if (this.sourceRoot) { + if (this.sourceRoot != null) { needle.source = util.relative(this.sourceRoot, needle.source); } @@ -1282,7 +1527,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou this._originalMappings, "originalLine", "originalColumn", - this._compareOriginalPositions); + util.compareByOriginalPositions); if (mapping) { return { @@ -1336,7 +1581,7 @@ define('source-map/source-map-consumer', ['require', 'exports', 'module' , 'sou var sourceRoot = this.sourceRoot; mappings.map(function (mapping) { var source = mapping.source; - if (source && sourceRoot) { + if (source != null && sourceRoot != null) { source = util.join(sourceRoot, source); } return { @@ -1382,7 +1627,7 @@ define('source-map/binary-search', ['require', 'exports', 'module' , ], function // element which is less than the one we are searching for, so we // return null. var mid = Math.floor((aHigh - aLow) / 2) + aLow; - var cmp = aCompare(aNeedle, aHaystack[mid]); + var cmp = aCompare(aNeedle, aHaystack[mid], true); if (cmp === 0) { // Found the element we are looking for. return aHaystack[mid]; @@ -1442,6 +1687,13 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ var SourceMapGenerator = require('./source-map-generator').SourceMapGenerator; var util = require('./util'); + // Matches a Windows-style `\r\n` newline or a `\n` newline used by all other + // operating systems these days (capturing the result). + var REGEX_NEWLINE = /(\r?\n)/; + + // Matches a Windows-style newline, or any character. + var REGEX_CHARACTER = /\r\n|[\s\S]/g; + /** * SourceNodes provide a way to abstract over interpolating/concatenating * snippets of generated JavaScript source code while maintaining the line and @@ -1457,10 +1709,10 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ function SourceNode(aLine, aColumn, aSource, aChunks, aName) { this.children = []; this.sourceContents = {}; - this.line = aLine === undefined ? null : aLine; - this.column = aColumn === undefined ? null : aColumn; - this.source = aSource === undefined ? null : aSource; - this.name = aName === undefined ? null : aName; + this.line = aLine == null ? null : aLine; + this.column = aColumn == null ? null : aColumn; + this.source = aSource == null ? null : aSource; + this.name = aName == null ? null : aName; if (aChunks != null) this.add(aChunks); } @@ -1469,16 +1721,26 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ * * @param aGeneratedCode The generated code * @param aSourceMapConsumer The SourceMap for the generated code + * @param aRelativePath Optional. The path that relative sources in the + * SourceMapConsumer should be relative to. */ SourceNode.fromStringWithSourceMap = - function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer) { + function SourceNode_fromStringWithSourceMap(aGeneratedCode, aSourceMapConsumer, aRelativePath) { // The SourceNode we want to fill with the generated code // and the SourceMap var node = new SourceNode(); - // The generated code - // Processed fragments are removed from this array. - var remainingLines = aGeneratedCode.split('\n'); + // All even indices of this array are one line of the generated code, + // while all odd indices are the newlines between two adjacent lines + // (since `REGEX_NEWLINE` captures its match). + // Processed fragments are removed from this array, by calling `shiftNextLine`. + var remainingLines = aGeneratedCode.split(REGEX_NEWLINE); + var shiftNextLine = function() { + var lineContents = remainingLines.shift(); + // The last line of a file might not have a newline. + var newLine = remainingLines.shift() || ""; + return lineContents + newLine; + }; // We need to remember the position of "remainingLines" var lastGeneratedLine = 1, lastGeneratedColumn = 0; @@ -1489,41 +1751,16 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ var lastMapping = null; aSourceMapConsumer.eachMapping(function (mapping) { - if (lastMapping === null) { - // We add the generated code until the first mapping - // to the SourceNode without any mapping. - // Each line is added as separate string. - while (lastGeneratedLine < mapping.generatedLine) { - node.add(remainingLines.shift() + "\n"); - lastGeneratedLine++; - } - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - node.add(nextLine.substr(0, mapping.generatedColumn)); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - } else { + if (lastMapping !== null) { // We add the code from "lastMapping" to "mapping": // First check if there is a new line in between. if (lastGeneratedLine < mapping.generatedLine) { var code = ""; - // Associate full lines with "lastMapping" - do { - code += remainingLines.shift() + "\n"; - lastGeneratedLine++; - lastGeneratedColumn = 0; - } while (lastGeneratedLine < mapping.generatedLine); - // When we reached the correct line, we add code until we - // reach the correct column too. - if (lastGeneratedColumn < mapping.generatedColumn) { - var nextLine = remainingLines[0]; - code += nextLine.substr(0, mapping.generatedColumn); - remainingLines[0] = nextLine.substr(mapping.generatedColumn); - lastGeneratedColumn = mapping.generatedColumn; - } - // Create the SourceNode. - addMappingWithCode(lastMapping, code); + // Associate first line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + lastGeneratedLine++; + lastGeneratedColumn = 0; + // The remaining code is added without mapping } else { // There is no new line in between. // Associate the code between "lastGeneratedColumn" and @@ -1535,19 +1772,43 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ lastGeneratedColumn); lastGeneratedColumn = mapping.generatedColumn; addMappingWithCode(lastMapping, code); + // No more remaining code, continue + lastMapping = mapping; + return; } } + // We add the generated code until the first mapping + // to the SourceNode without any mapping. + // Each line is added as separate string. + while (lastGeneratedLine < mapping.generatedLine) { + node.add(shiftNextLine()); + lastGeneratedLine++; + } + if (lastGeneratedColumn < mapping.generatedColumn) { + var nextLine = remainingLines[0]; + node.add(nextLine.substr(0, mapping.generatedColumn)); + remainingLines[0] = nextLine.substr(mapping.generatedColumn); + lastGeneratedColumn = mapping.generatedColumn; + } lastMapping = mapping; }, this); // We have processed all mappings. - // Associate the remaining code in the current line with "lastMapping" - // and add the remaining lines without any mapping - addMappingWithCode(lastMapping, remainingLines.join("\n")); + if (remainingLines.length > 0) { + if (lastMapping) { + // Associate the remaining code in the current line with "lastMapping" + addMappingWithCode(lastMapping, shiftNextLine()); + } + // and add the remaining lines without any mapping + node.add(remainingLines.join("")); + } // Copy sourcesContent into SourceNode aSourceMapConsumer.sources.forEach(function (sourceFile) { var content = aSourceMapConsumer.sourceContentFor(sourceFile); - if (content) { + if (content != null) { + if (aRelativePath != null) { + sourceFile = util.join(aRelativePath, sourceFile); + } node.setSourceContent(sourceFile, content); } }); @@ -1555,12 +1816,15 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ return node; function addMappingWithCode(mapping, code) { - if (mapping.source === undefined) { + if (mapping === null || mapping.source === undefined) { node.add(code); } else { + var source = aRelativePath + ? util.join(aRelativePath, mapping.source) + : mapping.source; node.add(new SourceNode(mapping.originalLine, mapping.originalColumn, - mapping.source, + source, code, mapping.name)); } @@ -1623,7 +1887,9 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ * @param aFn The traversal function. */ SourceNode.prototype.walk = function SourceNode_walk(aFn) { - this.children.forEach(function (chunk) { + var chunk; + for (var i = 0, len = this.children.length; i < len; i++) { + chunk = this.children[i]; if (chunk instanceof SourceNode) { chunk.walk(aFn); } @@ -1635,7 +1901,7 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ name: this.name }); } } - }, this); + } }; /** @@ -1701,14 +1967,16 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ */ SourceNode.prototype.walkSourceContents = function SourceNode_walkSourceContents(aFn) { - this.children.forEach(function (chunk) { - if (chunk instanceof SourceNode) { - chunk.walkSourceContents(aFn); + for (var i = 0, len = this.children.length; i < len; i++) { + if (this.children[i] instanceof SourceNode) { + this.children[i].walkSourceContents(aFn); } - }, this); - Object.keys(this.sourceContents).forEach(function (sourceFileKey) { - aFn(util.fromSetString(sourceFileKey), this.sourceContents[sourceFileKey]); - }, this); + } + + var sources = Object.keys(this.sourceContents); + for (var i = 0, len = sources.length; i < len; i++) { + aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]); + } }; /** @@ -1735,23 +2003,36 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ }; var map = new SourceMapGenerator(aArgs); var sourceMappingActive = false; + var lastOriginalSource = null; + var lastOriginalLine = null; + var lastOriginalColumn = null; + var lastOriginalName = null; this.walk(function (chunk, original) { generated.code += chunk; if (original.source !== null && original.line !== null && original.column !== null) { - map.addMapping({ - source: original.source, - original: { - line: original.line, - column: original.column - }, - generated: { - line: generated.line, - column: generated.column - }, - name: original.name - }); + if(lastOriginalSource !== original.source + || lastOriginalLine !== original.line + || lastOriginalColumn !== original.column + || lastOriginalName !== original.name) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } + lastOriginalSource = original.source; + lastOriginalLine = original.line; + lastOriginalColumn = original.column; + lastOriginalName = original.name; sourceMappingActive = true; } else if (sourceMappingActive) { map.addMapping({ @@ -1760,14 +2041,33 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ column: generated.column } }); + lastOriginalSource = null; sourceMappingActive = false; } - chunk.split('').forEach(function (ch) { - if (ch === '\n') { + chunk.match(REGEX_CHARACTER).forEach(function (ch, idx, array) { + if (REGEX_NEWLINE.test(ch)) { generated.line++; generated.column = 0; + // Mappings end at eol + if (idx + 1 === array.length) { + lastOriginalSource = null; + sourceMappingActive = false; + } else if (sourceMappingActive) { + map.addMapping({ + source: original.source, + original: { + line: original.line, + column: original.column + }, + generated: { + line: generated.line, + column: generated.column + }, + name: original.name + }); + } } else { - generated.column++; + generated.column += ch.length; } }); }); @@ -1784,7 +2084,7 @@ define('source-map/source-node', ['require', 'exports', 'module' , 'source-map/ /* -*- Mode: js; js-indent-level: 2; -*- */ /////////////////////////////////////////////////////////////////////////////// -window.sourceMap = { +this.sourceMap = { SourceMapConsumer: require('source-map/source-map-consumer').SourceMapConsumer, SourceMapGenerator: require('source-map/source-map-generator').SourceMapGenerator, SourceNode: require('source-map/source-node').SourceNode diff --git a/spec/source_map_spec.rb b/spec/source_map_spec.rb index 369a364..bf2aebf 100644 --- a/spec/source_map_spec.rb +++ b/spec/source_map_spec.rb @@ -81,7 +81,7 @@ expect(minified1.lines.to_a.length).to eq(1) map = SourceMap.from_s(map2) - expect(map.sources).to eq(["http://localhost/ahoy.js"]) + expect(map.sources).to eq(["ahoy.js", "http://localhost/ahoy.js"]) expect(map.mappings.first[:source_line]).to eq(1) expect(map.mappings.last[:source_line]).to eq(6) end diff --git a/vendor/source-map b/vendor/source-map index 9949093..2b8eca6 160000 --- a/vendor/source-map +++ b/vendor/source-map @@ -1 +1 @@ -Subproject commit 99490930b0e47a5ccac3ff8cce46164e2336dff2 +Subproject commit 2b8eca6bbfe42dc4234a28442f18ac40e0e2e22c