support inline source map from multiple files (#3058)
authorAlex Lam S.L <alexlamsl@gmail.com>
Fri, 6 Apr 2018 08:04:15 +0000 (16:04 +0800)
committerGitHub <noreply@github.com>
Fri, 6 Apr 2018 08:04:15 +0000 (16:04 +0800)
fixes #145

lib/minify.js
lib/sourcemap.js
test/mocha/cli.js
test/mocha/sourcemaps.js

index 0e117ed..399b861 100644 (file)
@@ -7,15 +7,23 @@ var to_base64 = typeof btoa == "undefined" ? function(str) {
     return new Buffer(str).toString("base64");
 } : btoa;
 
-function read_source_map(code) {
+function read_source_map(name, code) {
     var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
     if (!match) {
-        AST_Node.warn("inline source map not found");
+        AST_Node.warn("inline source map not found: " + name);
         return null;
     }
     return to_ascii(match[2]);
 }
 
+function parse_source_map(content) {
+    try {
+        return JSON.parse(content);
+    } catch (ex) {
+        throw new Error("invalid input source map: " + content);
+    }
+}
+
 function set_shorthand(name, options, keys) {
     if (options[name]) {
         keys.forEach(function(key) {
@@ -113,7 +121,7 @@ function minify(files, options) {
             };
         }
         if (timings) timings.parse = Date.now();
-        var toplevel;
+        var source_maps, toplevel;
         if (files instanceof AST_Toplevel) {
             toplevel = files;
         } else {
@@ -122,13 +130,23 @@ function minify(files, options) {
             }
             options.parse = options.parse || {};
             options.parse.toplevel = null;
+            var source_map_content = options.sourceMap && options.sourceMap.content;
+            if (typeof source_map_content == "string" && source_map_content != "inline") {
+                source_map_content = parse_source_map(source_map_content);
+            }
+            source_maps = source_map_content && Object.create(null);
             for (var name in files) if (HOP(files, name)) {
                 options.parse.filename = name;
                 options.parse.toplevel = parse(files[name], options.parse);
-                if (options.sourceMap && options.sourceMap.content == "inline") {
-                    if (Object.keys(files).length > 1)
-                        throw new Error("inline source map only works with singular input");
-                    options.sourceMap.content = read_source_map(files[name]);
+                if (source_maps) {
+                    if (source_map_content == "inline") {
+                        var inlined_content = read_source_map(name, files[name]);
+                        if (inlined_content) {
+                            source_maps[name] = parse_source_map(inlined_content);
+                        }
+                    } else {
+                        source_maps[name] = source_map_content;
+                    }
                 }
             }
             toplevel = options.parse.toplevel;
@@ -164,16 +182,9 @@ function minify(files, options) {
         }
         if (!HOP(options.output, "code") || options.output.code) {
             if (options.sourceMap) {
-                if (typeof options.sourceMap.content == "string") {
-                    try {
-                        options.sourceMap.content = JSON.parse(options.sourceMap.content);
-                    } catch (ex) {
-                        throw new Error("invalid input source map: " + options.sourceMap.content);
-                    }
-                }
                 options.output.source_map = SourceMap({
                     file: options.sourceMap.filename,
-                    orig: options.sourceMap.content,
+                    orig: source_maps,
                     root: options.sourceMap.root
                 });
                 if (options.sourceMap.includeSources) {
index 0be16bf..dcb8e47 100644 (file)
@@ -57,26 +57,26 @@ function SourceMap(options) {
         file       : options.file,
         sourceRoot : options.root
     });
-    var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig);
-
-    if (orig_map && Array.isArray(options.orig.sources)) {
-        orig_map._sources.toArray().forEach(function(source) {
-            var sourceContent = orig_map.sourceContentFor(source, true);
-            if (sourceContent) {
-                generator.setSourceContent(source, sourceContent);
-            }
-        });
+    var maps = options.orig && Object.create(null);
+    if (maps) for (var source in options.orig) {
+        var map = new MOZ_SourceMap.SourceMapConsumer(options.orig[source]);
+        if (Array.isArray(options.orig[source].sources)) {
+            map._sources.toArray().forEach(function(source) {
+                var sourceContent = map.sourceContentFor(source, true);
+                if (sourceContent) generator.setSourceContent(source, sourceContent);
+            });
+        }
+        maps[source] = map;
     }
 
     function add(source, gen_line, gen_col, orig_line, orig_col, name) {
-        if (orig_map) {
-            var info = orig_map.originalPositionFor({
+        var map = maps && maps[source];
+        if (map) {
+            var info = map.originalPositionFor({
                 line: orig_line,
                 column: orig_col
             });
-            if (info.source === null) {
-                return;
-            }
+            if (info.source === null) return;
             source = info.source;
             orig_line = info.line;
             orig_col = info.column;
index a64cb21..8c858bd 100644 (file)
@@ -244,16 +244,27 @@ describe("bin/uglifyjs", function () {
                 "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFJQSxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
                 "",
             ].join("\n"));
-            assert.strictEqual(stderr, "WARN: inline source map not found\n");
+            assert.strictEqual(stderr, "WARN: inline source map not found: test/input/issue-1323/sample.js\n");
             done();
         });
     });
-    it("Should fail with multiple input and inline source map", function(done) {
-        var command = uglifyjscmd + " test/input/issue-520/input.js test/input/issue-520/output.js --source-map content=inline,url=inline";
+    it("Should handle multiple input and inline source map", function(done) {
+        var command = [
+            uglifyjscmd,
+            "test/input/issue-520/input.js",
+            "test/input/issue-1323/sample.js",
+            "--source-map", "content=inline,url=inline",
+        ].join(" ");
 
         exec(command, function (err, stdout, stderr) {
-            assert.ok(err);
-            assert.strictEqual(stderr.split(/\n/)[0], "ERROR: inline source map only works with singular input");
+            if (err) throw err;
+
+            assert.strictEqual(stdout, [
+                "var Foo=function Foo(){console.log(1+2)};new Foo;var bar=function(){function foo(bar){return bar}return foo}();",
+                "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwidGVzdC9pbnB1dC9pc3N1ZS0xMzIzL3NhbXBsZS5qcyJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFNBQUFBLE1BQWdCQyxRQUFRQyxJQUFJLEVBQUUsSUFBTyxJQUFJRixJQ0FuRCxJQUFJRyxJQUFNLFdBQ04sU0FBU0MsSUFBS0QsS0FDVixPQUFPQSxJQUdYLE9BQU9DLElBTEQifQ==",
+                "",
+            ].join("\n"));
+            assert.strictEqual(stderr, "WARN: inline source map not found: test/input/issue-1323/sample.js\n");
             done();
         });
     });
index 8868c5a..2f178b2 100644 (file)
@@ -91,24 +91,37 @@ describe("sourcemaps", function() {
                 });
                 assert.strictEqual(result.code, "var bar=function(bar){return bar};");
                 assert.strictEqual(warnings.length, 1);
-                assert.strictEqual(warnings[0], "inline source map not found");
+                assert.strictEqual(warnings[0], "inline source map not found: 0");
             } finally {
                 Uglify.AST_Node.warn_function = warn_function;
             }
         });
-        it("Should fail with multiple input and inline source map", function() {
-            var result = Uglify.minify([
-                read("./test/input/issue-520/input.js"),
-                read("./test/input/issue-520/output.js")
-            ], {
-                sourceMap: {
-                    content: "inline",
-                    url: "inline"
-                }
-            });
-            var err = result.error;
-            assert.ok(err instanceof Error);
-            assert.strictEqual(err.stack.split(/\n/)[0], "Error: inline source map only works with singular input");
+        it("Should handle multiple input and inline source map", function() {
+            var warn_function = Uglify.AST_Node.warn_function;
+            var warnings = [];
+            Uglify.AST_Node.warn_function = function(txt) {
+                warnings.push(txt);
+            };
+            try {
+                var result = Uglify.minify([
+                    read("./test/input/issue-520/input.js"),
+                    read("./test/input/issue-1323/sample.js"),
+                ], {
+                    sourceMap: {
+                        content: "inline",
+                        url: "inline",
+                    }
+                });
+                if (result.error) throw result.error;
+                assert.strictEqual(result.code, [
+                    "var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};",
+                    "//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=",
+                ].join("\n"));
+                assert.strictEqual(warnings.length, 1);
+                assert.strictEqual(warnings[0], "inline source map not found: 1");
+            } finally {
+                Uglify.AST_Node.warn_function = warn_function;
+            }
         });
         it("Should drop source contents for includeSources=false", function() {
             var result = Uglify.minify(read("./test/input/issue-520/input.js"), {