Merge branch 'master' into harmony

This commit is contained in:
Richard van Velzen
2016-10-23 22:03:20 +02:00
17 changed files with 529 additions and 66 deletions

View File

@@ -62,6 +62,7 @@ The available options are:
--source-map-include-sources Pass this flag if you want to include the --source-map-include-sources Pass this flag if you want to include the
content of source files in the source map as content of source files in the source map as
sourcesContent property. sourcesContent property.
--source-map-inline Write base64-encoded source map to the end of js output.
--in-source-map Input source map, useful if you're compressing --in-source-map Input source map, useful if you're compressing
JS that was generated from some other original JS that was generated from some other original
code. code.
@@ -97,8 +98,8 @@ The available options are:
"@preserve". You can optionally pass one of the "@preserve". You can optionally pass one of the
following arguments to this flag: following arguments to this flag:
- "all" to keep all comments - "all" to keep all comments
- a valid JS regexp (needs to start with a - a valid JS RegExp like `/foo/` or `/^!/` to
slash) to keep only comments that match. keep only matching comments.
Note that currently not *all* comments can be Note that currently not *all* comments can be
kept when compression is on, because of dead kept when compression is on, because of dead
code removal or cascading statements into code removal or cascading statements into
@@ -348,6 +349,9 @@ to set `true`; it's effectively a shortcut for `foo=true`).
- `collapse_vars` -- default `false`. Collapse single-use `var` and `const` - `collapse_vars` -- default `false`. Collapse single-use `var` and `const`
definitions when possible. definitions when possible.
- `reduce_vars` -- default `false`. Improve optimization on variables assigned
with and used as constant values.
- `warnings` -- display warnings when dropping unreachable code or unused - `warnings` -- display warnings when dropping unreachable code or unused
declarations etc. declarations etc.
@@ -641,7 +645,9 @@ var result = UglifyJS.minify({"file1.js": "var a = function () {};"}, {
Note that the source map is not saved in a file, it's just returned in Note that the source map is not saved in a file, it's just returned in
`result.map`. The value passed for `outSourceMap` is only used to set the `result.map`. The value passed for `outSourceMap` is only used to set the
`file` attribute in the source map (see [the spec][sm-spec]). `file` attribute in the source map (see [the spec][sm-spec]). You can set
option `sourceMapInline` to be `true` and source map will be appended to
code.
You can also specify sourceRoot property to be included in source map: You can also specify sourceRoot property to be included in source map:
```javascript ```javascript
@@ -852,8 +858,11 @@ which we care about here are `source_map` and `comments`.
#### Keeping comments in the output #### Keeping comments in the output
In order to keep certain comments in the output you need to pass the In order to keep certain comments in the output you need to pass the
`comments` option. Pass a RegExp or a function. If you pass a RegExp, only `comments` option. Pass a RegExp (as string starting and closing with `/`
those comments whose body matches the regexp will be kept. Note that body or pass a RegExp object), a boolean or a function. Stringified options
`all` and `some` can be passed too, where `some` behaves like it's cli
equivalent `--comments` without passing a value. If you pass a RegExp,
only those comments whose body matches the RegExp will be kept. Note that body
means without the initial `//` or `/*`. If you pass a function, it will be means without the initial `//` or `/*`. If you pass a function, it will be
called for every comment in the tree and will receive two arguments: the called for every comment in the tree and will receive two arguments: the
node that the comment is attached to, and the comment token itself. node that the comment is attached to, and the comment token itself.

View File

@@ -23,6 +23,7 @@ mangling you need to use `-c` and `-m`.\
.describe("source-map", "Specify an output file where to generate source map.") .describe("source-map", "Specify an output file where to generate source map.")
.describe("source-map-root", "The path to the original source to be included in the source map.") .describe("source-map-root", "The path to the original source to be included in the source map.")
.describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.") .describe("source-map-url", "The path to the source map to be added in //# sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("source-map-inline", "Write base64-encoded source map to the end of js output. Disabled by default")
.describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.") .describe("source-map-include-sources", "Pass this flag if you want to include the content of source files in the source map as sourcesContent property.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.") .describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("screw-ie8", "Do not support Internet Explorer 6-8 quirks. This flag is enabled by default.") .describe("screw-ie8", "Do not support Internet Explorer 6-8 quirks. This flag is enabled by default.")
@@ -46,7 +47,7 @@ Use -c with no argument to use the default compression options.")
By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \ By default this works like Google Closure, keeping JSDoc-style comments that contain \"@license\" or \"@preserve\". \
You can optionally pass one of the following arguments to this flag:\n\ You can optionally pass one of the following arguments to this flag:\n\
- \"all\" to keep all comments\n\ - \"all\" to keep all comments\n\
- a valid JS regexp (needs to start with a slash) to keep only comments that match.\n\ - a valid JS RegExp like `/foo/`or `/^!/` to keep only matching comments.\n\
\ \
Note that currently not *all* comments can be kept when compression is on, \ Note that currently not *all* comments can be kept when compression is on, \
because of dead code removal or cascading statements into sequences.") because of dead code removal or cascading statements into sequences.")
@@ -76,6 +77,7 @@ You need to pass an argument to this option to specify the name that your module
.describe("name-cache", "File to hold mangled names mappings") .describe("name-cache", "File to hold mangled names mappings")
.describe("pure-funcs", "List of functions that can be safely removed if their return value is not used") .describe("pure-funcs", "List of functions that can be safely removed if their return value is not used")
.describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.") .describe("dump-spidermonkey-ast", "Dump SpiderMonkey AST to stdout.")
.describe("wrap-iife", "Wrap IIFEs in parenthesis. Note: this disables the negate_iife compression option")
.alias("p", "prefix") .alias("p", "prefix")
.alias("o", "output") .alias("o", "output")
@@ -112,6 +114,7 @@ You need to pass an argument to this option to specify the name that your module
.array("pure-funcs") .array("pure-funcs")
.boolean("expr") .boolean("expr")
.boolean("source-map-inline")
.boolean("source-map-include-sources") .boolean("source-map-include-sources")
.boolean("screw-ie8") .boolean("screw-ie8")
.boolean("support-ie8") .boolean("support-ie8")
@@ -130,6 +133,7 @@ You need to pass an argument to this option to specify the name that your module
.boolean("bare-returns") .boolean("bare-returns")
.boolean("keep-fnames") .boolean("keep-fnames")
.boolean("reserve-domprops") .boolean("reserve-domprops")
.boolean("wrap-iife")
.wrap(80) .wrap(80)
@@ -247,28 +251,18 @@ if (ARGS.keep_fnames) {
if (MANGLE) MANGLE.keep_fnames = true; if (MANGLE) MANGLE.keep_fnames = true;
} }
if (ARGS.wrap_iife) {
if (COMPRESS) COMPRESS.negate_iife = false;
OUTPUT_OPTIONS.wrap_iife = true;
}
if (BEAUTIFY) if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY); UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
if (ARGS.comments != null) { if (ARGS.comments === "") {
if (/^\/.*\/[a-zA-Z]*$/.test(ARGS.comments)) { OUTPUT_OPTIONS.comments = "some";
try {
OUTPUT_OPTIONS.comments = extractRegex(ARGS.comments);
} catch (e) {
print_error("ERROR: Invalid --comments: " + e.message);
}
} else if (ARGS.comments == "all") {
OUTPUT_OPTIONS.comments = true;
} else { } else {
OUTPUT_OPTIONS.comments = function(node, comment) { OUTPUT_OPTIONS.comments = ARGS.comments;
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
}
}
} }
var files = ARGS._.slice(); var files = ARGS._.slice();
@@ -317,7 +311,7 @@ var TOPLEVEL = null;
var P_RELATIVE = ARGS.p && ARGS.p == "relative"; var P_RELATIVE = ARGS.p && ARGS.p == "relative";
var SOURCES_CONTENT = {}; var SOURCES_CONTENT = {};
var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({ var SOURCE_MAP = (ARGS.source_map || ARGS.source_map_inline) ? UglifyJS.SourceMap({
file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE, file: P_RELATIVE ? path.relative(path.dirname(ARGS.source_map), OUTPUT_FILE) : OUTPUT_FILE,
root: ARGS.source_map_root, root: ARGS.source_map_root,
orig: ORIG_MAP, orig: ORIG_MAP,
@@ -482,6 +476,10 @@ async.eachLimit(files, 1, function (file, cb) {
output = output.get(); output = output.get();
if (SOURCE_MAP) { if (SOURCE_MAP) {
if (ARGS.source_map_inline) {
var base64_string = new Buffer(SOURCE_MAP.toString()).toString('base64');
output += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + base64_string;
} else {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8"); fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
var source_map_url = ARGS.source_map_url || ( var source_map_url = ARGS.source_map_url || (
P_RELATIVE P_RELATIVE
@@ -490,6 +488,7 @@ async.eachLimit(files, 1, function (file, cb) {
); );
output += "\n//# sourceMappingURL=" + source_map_url; output += "\n//# sourceMappingURL=" + source_map_url;
} }
}
if (OUTPUT_FILE) { if (OUTPUT_FILE) {
fs.writeFileSync(OUTPUT_FILE, output, "utf8"); fs.writeFileSync(OUTPUT_FILE, output, "utf8");

View File

@@ -67,6 +67,7 @@ function Compressor(options, false_by_default) {
if_return : !false_by_default, if_return : !false_by_default,
join_vars : !false_by_default, join_vars : !false_by_default,
collapse_vars : false, collapse_vars : false,
reduce_vars : false,
cascade : !false_by_default, cascade : !false_by_default,
side_effects : !false_by_default, side_effects : !false_by_default,
pure_getters : false, pure_getters : false,
@@ -1138,7 +1139,7 @@ merge(Compressor.prototype, {
this._evaluating = true; this._evaluating = true;
try { try {
var d = this.definition(); var d = this.definition();
if (d && d.constant && d.init) { if (d && (d.constant || compressor.option("reduce_vars") && !d.modified) && d.init) {
return ev(d.init, compressor); return ev(d.init, compressor);
} }
} finally { } finally {
@@ -2400,6 +2401,12 @@ merge(Compressor.prototype, {
// typeof always returns a non-empty string, thus it's // typeof always returns a non-empty string, thus it's
// always true in booleans // always true in booleans
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start); compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
if (self.expression.has_side_effects(compressor)) {
return make_node(AST_Seq, self, {
car: self.expression,
cdr: make_node(AST_True, self)
});
}
return make_node(AST_True, self); return make_node(AST_True, self);
} }
if (e instanceof AST_Binary && self.operator == "!") { if (e instanceof AST_Binary && self.operator == "!") {
@@ -2586,8 +2593,8 @@ merge(Compressor.prototype, {
case "+": case "+":
var ll = self.left.evaluate(compressor); var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor); var rr = self.right.evaluate(compressor);
if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1]) || if ((ll.length > 1 && ll[0] instanceof AST_String && ll[1] && !self.right.has_side_effects(compressor)) ||
(rr.length > 1 && rr[0] instanceof AST_String && rr[1])) { (rr.length > 1 && rr[0] instanceof AST_String && rr[1] && !self.left.has_side_effects(compressor))) {
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start); compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
return make_node(AST_True, self); return make_node(AST_True, self);
} }

View File

@@ -71,6 +71,7 @@ function OutputStream(options) {
keep_quoted_props: false, keep_quoted_props: false,
shorthand : undefined, shorthand : undefined,
ecma : 5, ecma : 5,
wrap_iife : false,
}, true); }, true);
if (typeof options.ascii_identifiers === 'undefined') if (typeof options.ascii_identifiers === 'undefined')
@@ -79,6 +80,50 @@ function OutputStream(options) {
if (options.shorthand === undefined) if (options.shorthand === undefined)
options.shorthand = options.ecma > 5; options.shorthand = options.ecma > 5;
// Convert comment option to RegExp if neccessary and set up comments filter
if (typeof options.comments === "string" && /^\/.*\/[a-zA-Z]*$/.test(options.comments)) {
var regex_pos = options.comments.lastIndexOf("/");
options.comments = new RegExp(
options.comments.substr(1, regex_pos - 1),
options.comments.substr(regex_pos + 1)
);
}
if (options.comments instanceof RegExp) {
options.comments = (function(f) {
return function(comment) {
return comment.type == "comment5" || f.test(comment.value);
}
})(options.comments);
}
else if (typeof options.comments === "function") {
options.comments = (function(f) {
return function(comment) {
return comment.type == "comment5" || f(this, comment);
}
})(options.comments);
}
else if (options.comments === "some") {
options.comments = function(comment) {
var text = comment.value;
var type = comment.type;
if (type == "comment2") {
// multiline comment
return /@preserve|@license|@cc_on/i.test(text);
}
return type == "comment5";
}
}
else if (options.comments){ // NOTE includes "all" option
options.comments = function() {
return true;
}
} else {
// Falsy case, so reject all comments, except shebangs
options.comments = function(comment) {
return comment.type == "comment5";
}
}
var indentation = 0; var indentation = 0;
var current_col = 0; var current_col = 0;
var current_line = 1; var current_line = 1;
@@ -484,7 +529,7 @@ function OutputStream(options) {
AST_Node.DEFMETHOD("add_comments", function(output){ AST_Node.DEFMETHOD("add_comments", function(output){
if (output._readonly) return; if (output._readonly) return;
var c = output.option("comments"), self = this; var self = this;
var start = self.start; var start = self.start;
if (start && !start._comments_dumped) { if (start && !start._comments_dumped) {
start._comments_dumped = true; start._comments_dumped = true;
@@ -507,19 +552,7 @@ function OutputStream(options) {
})); }));
} }
if (!c) { comments = comments.filter(output.option("comments"), self);
comments = comments.filter(function(comment) {
return comment.type == "comment5";
});
} else if (c.test) {
comments = comments.filter(function(comment){
return comment.type == "comment5" || c.test(comment.value);
});
} else if (typeof c == "function") {
comments = comments.filter(function(comment){
return comment.type == "comment5" || c(self, comment);
});
}
// Keep single line comments after nlb, after nlb // Keep single line comments after nlb, after nlb
if (!output.option("beautify") && comments.length > 0 && if (!output.option("beautify") && comments.length > 0 &&
@@ -570,7 +603,16 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably // a function expression needs parens around it when it's provably
// the first token to appear in a statement. // the first token to appear in a statement.
PARENS(AST_Function, function(output){ PARENS(AST_Function, function(output){
return first_in_statement(output); if (first_in_statement(output)) {
return true;
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
}
return false;
}); });
// same goes for an object literal, because otherwise it would be // same goes for an object literal, because otherwise it would be

View File

@@ -256,12 +256,16 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
} }
if (node instanceof AST_SymbolRef) { if (node instanceof AST_SymbolRef) {
var name = node.name; var name = node.name;
if (name == "eval" && tw.parent() instanceof AST_Call) { var parent = tw.parent();
if (name == "eval" && parent instanceof AST_Call) {
for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) {
s.uses_eval = true; s.uses_eval = true;
} }
} }
var sym = node.scope.find_variable(name); var sym = node.scope.find_variable(name);
if (node.scope instanceof AST_Lambda && name == "arguments") {
node.scope.uses_arguments = true;
}
if (!sym) { if (!sym) {
var g; var g;
if (globals.has(name)) { if (globals.has(name)) {
@@ -272,12 +276,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
g.global = true; g.global = true;
globals.set(name, g); globals.set(name, g);
} }
node.thedef = g; sym = g;
if (func && name == "arguments") {
func.uses_arguments = true;
} }
} else {
node.thedef = sym; node.thedef = sym;
if (parent instanceof AST_Unary && (parent.operator === '++' || parent.operator === '--')
|| parent instanceof AST_Assign && parent.left === node) {
sym.modified = true;
} }
node.reference(); node.reference();
return true; return true;

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)", "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause", "license": "BSD-2-Clause",
"version": "2.7.3", "version": "2.7.4",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -0,0 +1,49 @@
string_plus_optimization: {
options = {
side_effects : true,
evaluate : true,
conditionals : true,
comparisons : true,
dead_code : true,
booleans : true,
unused : true,
if_return : true,
join_vars : true,
cascade : true,
hoist_funs : true,
};
input: {
function foo(anything) {
function throwing_function() {
throw "nope";
}
try {
console.log('0' + throwing_function() ? "yes" : "no");
} catch (ex) {
console.log(ex);
}
console.log('0' + anything ? "yes" : "no");
console.log(anything + '0' ? "Yes" : "No");
console.log('' + anything);
console.log(anything + '');
}
foo();
}
expect: {
function foo(anything) {
function throwing_function() {
throw "nope";
}
try {
console.log('0' + throwing_function() ? "yes" : "no");
} catch (ex) {
console.log(ex);
}
console.log("yes");
console.log("Yes");
console.log('' + anything);
console.log(anything + '');
}
foo();
}
}

View File

@@ -0,0 +1,171 @@
reduce_vars: {
options = {
conditionals : true,
evaluate : true,
global_defs : {
C : 0
},
reduce_vars : true,
unused : true
}
input: {
var A = 1;
(function f0() {
var a = 2;
console.log(a - 5);
console.log(A - 5);
})();
(function f1() {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(a - 5);
eval("console.log(a);");
})(eval);
(function f3() {
var b = typeof C !== "undefined";
var c = 4;
if (b) {
return 'yes';
} else {
return 'no';
}
})();
console.log(A + 1);
}
expect: {
var A = 1;
(function() {
console.log(-3);
console.log(-4);
})();
(function f1() {
var a = 2;
console.log(-3);
eval("console.log(a);");
})();
(function f2(eval) {
var a = 2;
console.log(-3);
eval("console.log(a);");
})(eval);
(function() {
return "yes";
})();
console.log(2);
}
}
modified: {
options = {
conditionals : true,
evaluate : true,
reduce_vars : true,
unused : true
}
input: {
function f0() {
var a = 1, b = 2;
b++;
console.log(a + 1);
console.log(b + 1);
}
function f1() {
var a = 1, b = 2;
--b;
console.log(a + 1);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
} else {
c = b;
}
console.log(a + b);
console.log(b + c);
// TODO: as "modified" is determined in "figure_out_scope",
// even "passes" wouldn't improve this any further
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
expect: {
function f0() {
var b = 2;
b++;
console.log(2);
console.log(b + 1);
}
function f1() {
var b = 2;
--b;
console.log(2);
console.log(b + 1);
}
function f2() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(4);
console.log(a + b + c);
}
function f3() {
var a = 1, b = 2, c = 3;
b *= c;
console.log(a + b);
console.log(b + c);
console.log(4);
console.log(a + b + c);
}
function f4() {
var a = 1, b = 2, c = 3;
b = c;
console.log(a + b);
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
function f5(a) {
B = a;
console.log(A ? 'yes' : 'no');
console.log(B ? 'yes' : 'no');
}
}
}

View File

@@ -23,3 +23,25 @@ typeof_evaluation: {
h='undefined'; h='undefined';
} }
} }
typeof_in_boolean_context: {
options = {
booleans : true,
evaluate : true,
conditionals : true,
};
input: {
function f1(x) { return typeof x ? "yes" : "no"; }
function f2() { return typeof g()? "Yes" : "No"; }
typeof 0 ? foo() : bar();
!typeof console.log(1);
var a = !typeof console.log(2);
}
expect: {
function f1(x) { return "yes"; }
function f2() { return g(), "Yes"; }
foo();
!(console.log(1), !0);
var a = !(console.log(2), !0);
}
}

View File

@@ -0,0 +1,48 @@
wrap_iife: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return function() {
console.log('test')
};
})()();
}
expect_exact: '(function(){return function(){console.log("test")}})()();'
}
wrap_iife_in_expression: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
foo = (function () {
return bar();
})();
}
expect_exact: 'foo=(function(){return bar()})();'
}
wrap_iife_in_return_call: {
options = {
negate_iife: false,
}
beautify = {
wrap_iife: true,
}
input: {
(function() {
return (function() {
console.log('test')
})();
})()();
}
expect_exact: '(function(){return(function(){console.log("test")})()})()();'
}

View File

@@ -0,0 +1,3 @@
// foo
/*@preserve*/
// bar

View File

@@ -0,0 +1,7 @@
var bar = (function () {
function foo (bar) {
return bar;
}
return foo;
})();

View File

@@ -19,4 +19,12 @@ describe("arguments", function() {
value // Select function as scope value // Select function as scope
); );
}); });
it("Should recognize when a function uses arguments", function() {
var ast = UglifyJS.parse("function a(){function b(){function c(){}; return arguments[0];}}");
ast.figure_out_scope();
assert.strictEqual(ast.body[0].uses_arguments, false);
assert.strictEqual(ast.body[0].body[0].uses_arguments, true);
assert.strictEqual(ast.body[0].body[0].body[0].uses_arguments, false);
});
}); });

View File

@@ -2,11 +2,11 @@ var assert = require("assert");
var exec = require("child_process").exec; var exec = require("child_process").exec;
describe("bin/uglifyjs", function () { describe("bin/uglifyjs", function () {
var uglifyjscmd = '"' + process.argv[0] + '" bin/uglifyjs';
it("should produce a functional build when using --self", function (done) { it("should produce a functional build when using --self", function (done) {
this.timeout(15000); this.timeout(15000);
var uglifyjs = '"' + process.argv[0] + '" bin/uglifyjs'; var command = uglifyjscmd + ' --self -cm --wrap WrappedUglifyJS';
var command = uglifyjs + ' --self -cm --wrap WrappedUglifyJS';
exec(command, function (err, stdout) { exec(command, function (err, stdout) {
if (err) throw err; if (err) throw err;
@@ -19,4 +19,55 @@ describe("bin/uglifyjs", function () {
done(); done();
}); });
}); });
it("Should be able to filter comments correctly with `--comment all`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments all';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "// foo\n/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with `--comment <RegExp>`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments /r/';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n// bar\n\n");
done();
});
});
it("Should be able to filter comments correctly with just `--comment`", function (done) {
var command = uglifyjscmd + ' test/input/comments/filter.js --comments';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "/*@preserve*/\n\n");
done();
});
});
it("Should append source map to output when using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js --source-map-inline';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvaW5wdXQvaXNzdWUtMTMyMy9zYW1wbGUuanMiXSwibmFtZXMiOlsiYmFyIiwiZm9vIl0sIm1hcHBpbmdzIjoiQUFBQSxHQUFJQSxLQUFPLFdBQ1AsUUFBU0MsS0FBS0QsS0FDVixNQUFPQSxLQUdYLE1BQU9DIn0=\n");
done();
});
});
it("should not append source map to output when not using --source-map-inline", function (done) {
var command = uglifyjscmd + ' test/input/issue-1323/sample.js';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, "var bar=function(){function foo(bar){return bar}return foo}();\n");
done();
});
});
}); });

View File

@@ -7,6 +7,16 @@ describe("comment filters", function() {
assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n"); assert.strictEqual(ast.print_to_string({comments: /^!/}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
}); });
it("Should be able to filter comments with the 'all' option", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "all"}), "/*!test1*/\n/*test2*/\n//!test3\n//test4\n//test5\n//!test6\n//test7\n//!test8\n");
});
it("Should be able to filter commments with the 'some' option", function() {
var ast = UglifyJS.parse("// foo\n/*@preserve*/\n// bar\n/*@license*/\n//@license with the wrong comment type\n/*@cc_on something*/");
assert.strictEqual(ast.print_to_string({comments: "some"}), "/*@preserve*/\n/*@license*/\n/*@cc_on something*/\n");
});
it("Should be able to filter comments by passing a function", function() { it("Should be able to filter comments by passing a function", function() {
var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars."); var ast = UglifyJS.parse("/*TEST 123*/\n//An other comment\n//8 chars.");
var f = function(node, comment) { var f = function(node, comment) {
@@ -16,6 +26,11 @@ describe("comment filters", function() {
assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n"); assert.strictEqual(ast.print_to_string({comments: f}), "/*TEST 123*/\n//8 chars.\n");
}); });
it("Should be able to filter comments by passing regex in string format", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
assert.strictEqual(ast.print_to_string({comments: "/^!/"}), "/*!test1*/\n//!test3\n//!test6\n//!test8\n");
});
it("Should be able to get the comment and comment type when using a function", function() { it("Should be able to get the comment and comment type when using a function", function() {
var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8"); var ast = UglifyJS.parse("/*!test1*/\n/*test2*/\n//!test3\n//test4\n<!--test5\n<!--!test6\n-->test7\n-->!test8");
var f = function(node, comment) { var f = function(node, comment) {
@@ -42,4 +57,9 @@ describe("comment filters", function() {
assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n"); assert.strictEqual(ast.print_to_string({comments: f}), "#!Random comment\n//test1\n/*test2*/\n");
}); });
it("Should never be able to filter comment5 when using 'some' as filter", function() {
var ast = UglifyJS.parse("#!foo\n//foo\n/*@preserve*/\n/* please hide me */");
assert.strictEqual(ast.print_to_string({comments: "some"}), "#!foo\n/*@preserve*/\n");
});
}); });

View File

@@ -75,4 +75,23 @@ describe("minify", function() {
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));'); 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
}); });
}); });
describe("sourceMapInline", function() {
it("should append source map to output js when sourceMapInline is enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
fromString: true,
sourceMapInline: true
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};\n" +
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIj8iXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsR0FBSUEsR0FBSSxTQUFTQyxHQUFPLE1BQU9BIn0=");
});
it("should not append source map to output js when sourceMapInline is not enabled", function() {
var result = Uglify.minify('var a = function(foo) { return foo; };', {
fromString: true
});
var code = result.code;
assert.strictEqual(code, "var a=function(n){return n};");
});
});
}); });

View File

@@ -44,6 +44,7 @@ exports.minify = function(files, options) {
sourceRoot : null, sourceRoot : null,
inSourceMap : null, inSourceMap : null,
sourceMapUrl : null, sourceMapUrl : null,
sourceMapInline : false,
fromString : false, fromString : false,
warnings : false, warnings : false,
mangle : {}, mangle : {},
@@ -117,7 +118,7 @@ exports.minify = function(files, options) {
if (typeof options.inSourceMap == "string") { if (typeof options.inSourceMap == "string") {
inMap = JSON.parse(fs.readFileSync(options.inSourceMap, "utf8")); inMap = JSON.parse(fs.readFileSync(options.inSourceMap, "utf8"));
} }
if (options.outSourceMap) { if (options.outSourceMap || options.sourceMapInline) {
output.source_map = UglifyJS.SourceMap({ output.source_map = UglifyJS.SourceMap({
file: options.outSourceMap, file: options.outSourceMap,
orig: inMap, orig: inMap,
@@ -138,16 +139,19 @@ exports.minify = function(files, options) {
var stream = UglifyJS.OutputStream(output); var stream = UglifyJS.OutputStream(output);
toplevel.print(stream); toplevel.print(stream);
var mappingUrlPrefix = "\n//# sourceMappingURL=";
if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
}
var source_map = output.source_map; var source_map = output.source_map;
if (source_map) { if (source_map) {
source_map = source_map + ""; source_map = source_map + "";
} }
var mappingUrlPrefix = "\n//# sourceMappingURL=";
if (options.sourceMapInline) {
stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64");
} else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
}
return { return {
code : stream + "", code : stream + "",
map : source_map map : source_map