From 923deeff35b708e49c4d8bcfa64dc53832cd4b5a Mon Sep 17 00:00:00 2001 From: "Alex Lam S.L" Date: Fri, 6 Apr 2018 16:04:15 +0800 Subject: support inline source map from multiple files (#3058) fixes #145 --- lib/minify.js | 41 ++++++++++++++++++++++++++--------------- lib/sourcemap.js | 28 ++++++++++++++-------------- test/mocha/cli.js | 21 ++++++++++++++++----- test/mocha/sourcemaps.js | 41 +++++++++++++++++++++++++++-------------- 4 files changed, 83 insertions(+), 48 deletions(-) diff --git a/lib/minify.js b/lib/minify.js index 0e117ed1..399b8615 100644 --- a/lib/minify.js +++ b/lib/minify.js @@ -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) { diff --git a/lib/sourcemap.js b/lib/sourcemap.js index 0be16bfc..dcb8e476 100644 --- a/lib/sourcemap.js +++ b/lib/sourcemap.js @@ -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; diff --git a/test/mocha/cli.js b/test/mocha/cli.js index a64cb216..8c858bd9 100644 --- a/test/mocha/cli.js +++ b/test/mocha/cli.js @@ -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(); }); }); diff --git a/test/mocha/sourcemaps.js b/test/mocha/sourcemaps.js index 8868c5ae..2f178b27 100644 --- a/test/mocha/sourcemaps.js +++ b/test/mocha/sourcemaps.js @@ -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"), { -- cgit v1.2.3