Skip to content

Commit

Permalink
Fix compressed source-maps have non-terminated segments (#342)
Browse files Browse the repository at this point in the history
Currently when `terser` compresses a given input file that comes
with a source-map, but the source-map does not have mappings for
all code parts in the input file. e.g. the input file has been generated
and some specific code-parts have been generated which are not
originating from any source-file.

In that case if mappings before the "generated code" are collected, and
the original file location for the "generated code" comes after previous
segments for the same line, Terser just ignores the fact that there is no
original location and the previous segment is not terminated. This causes
the "generated code" incorrectly to be mapped to the previous segment/
original source location.
  • Loading branch information
devversion authored and fabiosantoscode committed May 24, 2019
1 parent 1a6e1d7 commit 7d47254
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
11 changes: 10 additions & 1 deletion lib/sourcemap.js
Expand Up @@ -74,12 +74,21 @@ function SourceMap(options) {
}

function add(source, gen_line, gen_col, orig_line, orig_col, name) {
var generatedPos = { line: gen_line + options.dest_line_diff, column: gen_col };
if (orig_map) {
var info = orig_map.originalPositionFor({
line: orig_line,
column: orig_col
});
if (info.source === null) {
if (generatedPos.column !== 0) {
generator.addMapping({
generated: generatedPos,
original: null,
source: null,
name: null
});
}
return;
}
source = info.source;
Expand All @@ -88,7 +97,7 @@ function SourceMap(options) {
name = info.name || name;
}
generator.addMapping({
generated : { line: gen_line + options.dest_line_diff, column: gen_col },
generated : generatedPos,
original : { line: orig_line + options.orig_line_diff, column: orig_col },
source : source,
name : name
Expand Down
44 changes: 44 additions & 0 deletions test/mocha/input-sourcemaps.js
@@ -1,6 +1,7 @@
var Uglify = require('../../');
var assert = require("assert");
var SourceMapConsumer = require("source-map").SourceMapConsumer;
var SourceMapGenerator = require("source-map").SourceMapGenerator;

describe("input sourcemaps", function() {
var transpilemap, map;
Expand Down Expand Up @@ -63,4 +64,47 @@ describe("input sourcemaps", function() {
assert.ok(pos.line <= 2, msg);
})
});

it("Should preserve unmapped segments in output source map", function() {
var generator = new SourceMapGenerator();

generator.addMapping({
source: "source.ts",
generated: {line: 1, column: 0},
original: {line: 1, column: 0},
});

generator.addMapping({
source: null,
original: null,
generated: {line: 1, column: 38}
});

generator.addMapping({
source: "source.ts",
generated: {line: 1, column: 51},
original: {line: 2, column: 0},
});

// Everything except the "say('hello');" part is mapped to "source.ts". The "say"
// function call is not mapped to any original source location. e.g. this can
// happen when a code transformer inserts generated code in between existing code.
var inputFile = "function say(msg) {console.log(msg)};say('hello');process.exit(1);";
var result = Uglify.minify(inputFile, {
sourceMap: {
content: JSON.parse(generator.toString())
}
});

var transformedMap = new SourceMapConsumer(result.map);
var hasMappedSource = true;

for (let i = 0; i < result.code.length; i++) {
var info = transformedMap.originalPositionFor({line: 1, column: i});
hasMappedSource = hasMappedSource && !!info.source;
}

assert.equal(hasMappedSource, false, "Expected transformed source map to preserve the " +
"mapping without original source location");
});
});

0 comments on commit 7d47254

Please sign in to comment.