Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add `SourceMapConsumer.fromSourceMap` #78

Merged
merged 4 commits into from

1 participant

@fitzgen fitzgen merged commit 4f5b353 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 21, 2013
  1. @fitzgen
  2. @fitzgen

    Refactor SourceMapConsumer and SourceMapGenerator to share the same i…

    fitzgen authored
    …nternal
    
    representation of a single mapping, and to share comparators of those mappings.
Commits on Sep 23, 2013
  1. @fitzgen

    Add SourceMapConsumer.fromSourceMap to create SourceMapConsumer insta…

    fitzgen authored
    …nces from
    
    SourceMapGenerator instances.
Commits on Sep 24, 2013
  1. @fitzgen
This page is out of date. Refresh to see the latest.
View
2  lib/source-map/binary-search.js
@@ -30,7 +30,7 @@ define(function (require, exports, module) {
// 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];
View
62 lib/source-map/source-map-consumer.js
@@ -101,6 +101,32 @@ define(function (require, exports, module) {
}
/**
+ * 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.
*/
SourceMapConsumer.prototype._version = 3;
@@ -195,37 +221,7 @@ define(function (require, exports, module) {
}
}
- 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._originalMappings.sort(util.compareByOriginalPositions);
};
/**
@@ -278,7 +274,7 @@ define(function (require, exports, module) {
this._generatedMappings,
"generatedLine",
"generatedColumn",
- this._compareGeneratedPositions);
+ util.compareByGeneratedPositions);
if (mapping) {
var source = util.getArg(mapping, 'source', null);
@@ -372,7 +368,7 @@ define(function (require, exports, module) {
this._originalMappings,
"originalLine",
"originalColumn",
- this._compareOriginalPositions);
+ util.compareByOriginalPositions);
if (mapping) {
return {
View
81 lib/source-map/source-map-generator.js
@@ -107,8 +107,10 @@ define(function (require, exports, module) {
}
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
});
@@ -169,11 +171,11 @@ define(function (require, exports, module) {
// Find mappings for the "aSourceFile"
this._mappings.forEach(function (mapping) {
- if (mapping.source === aSourceFile && mapping.original) {
+ if (mapping.source === aSourceFile && mapping.originalLine) {
// 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) {
// Copy mapping
@@ -182,8 +184,8 @@ define(function (require, exports, module) {
} else {
mapping.source = original.source;
}
- mapping.original.line = original.line;
- mapping.original.column = original.column;
+ 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
@@ -251,24 +253,6 @@ define(function (require, exports, module) {
}
};
- 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.
@@ -289,44 +273,44 @@ define(function (require, exports, module) {
// 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) {
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) {
result += base64VLQ.encode(this._names.indexOf(mapping.name)
@@ -339,6 +323,20 @@ define(function (require, exports, module) {
return result;
};
+ SourceMapGenerator.prototype._generateSourcesContent =
+ function SourceMapGenerator_generateSourcesContent(aSources, aSourceRoot) {
+ return aSources.map(function (source) {
+ if (aSourceRoot) {
+ 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.
*/
@@ -355,16 +353,9 @@ define(function (require, exports, module) {
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;
};
View
22 lib/source-map/source-node.js
@@ -193,7 +193,9 @@ define(function (require, exports, module) {
* @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);
}
@@ -205,7 +207,7 @@ define(function (require, exports, module) {
name: this.name });
}
}
- }, this);
+ }
};
/**
@@ -271,14 +273,16 @@ define(function (require, exports, module) {
*/
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]]);
+ }
};
/**
View
87 lib/source-map/util.js
@@ -115,4 +115,91 @@ define(function (require, exports, module) {
}
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;
+
});
View
24 test/run-tests.js
@@ -22,27 +22,20 @@ function run(tests) {
try {
tests[i].testCase[k](assert, util);
passed++;
- process.stdout.write('.');
}
catch (e) {
- failures.push({
- name: tests[i].name + ': ' + k,
- error: e
- });
- process.stdout.write('E');
+ console.log('FAILED ' + tests[i].name + ': ' + k + '!');
+ console.log(e.stack);
}
}
}
}
- process.stdout.write('\n');
+ console.log("");
console.log(passed + ' / ' + total + ' tests passed.');
+ console.log("");
failures.forEach(function (f) {
- console.log('================================================================================');
- console.log(f.name);
- console.log('--------------------------------------------------------------------------------');
- console.log(f.error.stack);
});
return failures.length;
@@ -55,14 +48,19 @@ process.stdout.on('close', function () {
});
function isTestFile(f) {
- return /^test\-.*?\.js/.test(f);
+ var testToRun = process.argv[2];
+ return testToRun
+ ? path.basename(testToRun) === f
+ : /^test\-.*?\.js/.test(f);
}
function toModule(f) {
return './source-map/' + f.replace(/\.js$/, '');
}
-var requires = fs.readdirSync(path.join(__dirname, 'source-map')).filter(isTestFile).map(toModule);
+var requires = fs.readdirSync(path.join(__dirname, 'source-map'))
+ .filter(isTestFile)
+ .map(toModule);
code = run(requires.map(require).map(function (mod, i) {
return {
View
60 test/source-map/test-source-map-consumer.js
@@ -388,4 +388,64 @@ define(function (require, exports, module) {
assert.equal(pos.column, 5);
};
+ exports['test SourceMapConsumer.fromSourceMap'] = function (assert, util) {
+ var smg = new SourceMapGenerator({
+ sourceRoot: 'http://example.com/',
+ file: 'foo.js'
+ });
+ smg.addMapping({
+ original: { line: 1, column: 1 },
+ generated: { line: 2, column: 2 },
+ source: 'bar.js'
+ });
+ smg.addMapping({
+ original: { line: 2, column: 2 },
+ generated: { line: 4, column: 4 },
+ source: 'baz.js',
+ name: 'dirtMcGirt'
+ });
+ smg.setSourceContent('baz.js', 'baz.js content');
+
+ var smc = SourceMapConsumer.fromSourceMap(smg);
+ assert.equal(smc.file, 'foo.js');
+ assert.equal(smc.sourceRoot, 'http://example.com/');
+ assert.equal(smc.sources.length, 2);
+ assert.equal(smc.sources[0], 'http://example.com/bar.js');
+ assert.equal(smc.sources[1], 'http://example.com/baz.js');
+ assert.equal(smc.sourceContentFor('baz.js'), 'baz.js content');
+
+ var pos = smc.originalPositionFor({
+ line: 2,
+ column: 2
+ });
+ assert.equal(pos.line, 1);
+ assert.equal(pos.column, 1);
+ assert.equal(pos.source, 'http://example.com/bar.js');
+ assert.equal(pos.name, null);
+
+ pos = smc.generatedPositionFor({
+ line: 1,
+ column: 1,
+ source: 'http://example.com/bar.js'
+ });
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+
+ pos = smc.originalPositionFor({
+ line: 4,
+ column: 4
+ });
+ assert.equal(pos.line, 2);
+ assert.equal(pos.column, 2);
+ assert.equal(pos.source, 'http://example.com/baz.js');
+ assert.equal(pos.name, 'dirtMcGirt');
+
+ pos = smc.generatedPositionFor({
+ line: 2,
+ column: 2,
+ source: 'http://example.com/baz.js'
+ });
+ assert.equal(pos.line, 4);
+ assert.equal(pos.column, 4);
+ };
});
View
16 test/source-map/test-source-node.js
@@ -257,11 +257,17 @@ define(function (require, exports, module) {
exports['test .fromStringWithSourceMap() merging duplicate mappings'] = function (assert, util) {
var input = new SourceNode(null, null, null, [
- new SourceNode(1, 0, "a.js", "(function"), new SourceNode(1, 0, "a.js", "() {\n"),
- " ", new SourceNode(1, 0, "a.js", "var Test = "), new SourceNode(1, 0, "b.js", "{};\n"),
- new SourceNode(2, 0, "b.js", "Test"), new SourceNode(2, 0, "b.js", ".A", "A"), new SourceNode(2, 20, "b.js", " = { value: 1234 };\n", "A"),
- "}());\n",
- "/* Generated Source */"]);
+ new SourceNode(1, 0, "a.js", "(function"),
+ new SourceNode(1, 0, "a.js", "() {\n"),
+ " ",
+ new SourceNode(1, 0, "a.js", "var Test = "),
+ new SourceNode(1, 0, "b.js", "{};\n"),
+ new SourceNode(2, 0, "b.js", "Test"),
+ new SourceNode(2, 0, "b.js", ".A", "A"),
+ new SourceNode(2, 20, "b.js", " = { value: 1234 };\n", "A"),
+ "}());\n",
+ "/* Generated Source */"
+ ]);
input = input.toStringWithSourceMap({
file: 'foo.js'
});
View
3  test/source-map/util.js
@@ -143,7 +143,8 @@ define(function (require, exports, module) {
expectedMap.sourceRoot,
"sourceRoot mismatch: " +
actualMap.sourceRoot + " != " + expectedMap.sourceRoot);
- assert.equal(actualMap.mappings, expectedMap.mappings, "mappings mismatch");
+ assert.equal(actualMap.mappings, expectedMap.mappings,
+ "mappings mismatch:\nActual: " + actualMap.mappings + "\nExpected: " + expectedMap.mappings);
if (actualMap.sourcesContent) {
assert.equal(actualMap.sourcesContent.length,
expectedMap.sourcesContent.length,
Something went wrong with that request. Please try again.