Compare commits

..

10 Commits

Author SHA1 Message Date
Alex Lam S.L
73d77f4f64 v3.3.16 2018-03-19 06:53:51 +00:00
Alex Lam S.L
ccf0e2ef4f extend fuzzy RHS folding (#3006)
- `a = []; if (1) x();` => `if (a = []) x();`
2018-03-17 03:10:21 +08:00
Alex Lam S.L
20ca0f5906 improve truthy compression (#3009) 2018-03-16 06:12:59 +08:00
Alex Lam S.L
b29d435bb5 refactor brackets to braces (#3005) 2018-03-15 15:46:45 +08:00
Alex Lam S.L
90585e29c2 v3.3.15 2018-03-14 16:45:38 +00:00
Alex Lam S.L
d8fc281915 update dependencies (#3002)
acorn 5.5.3
commander 2.15.0

Miscellaneous
- drop unmaintained package from README
2018-03-14 15:54:41 +08:00
Alex Lam S.L
188c39e8d5 retain comments within brackets (#2999)
fixes #2998
2018-03-13 18:44:21 +08:00
Alex Lam S.L
5429234138 preserve non-constant value assignments with modifications (#2997)
fixes #2995
2018-03-13 17:35:34 +08:00
Alex Lam S.L
b9f72a4a81 handle case correctly under reduce_vars (#2993)
fixes #2992
2018-03-11 15:54:43 +08:00
Alex Lam S.L
fc6ebd04a5 preserve case when inline_script (#2991)
fixes #2989
2018-03-11 05:11:12 +08:00
23 changed files with 474 additions and 163 deletions

View File

@@ -6,9 +6,8 @@ UglifyJS is a JavaScript parser, minifier, compressor and beautifier toolkit.
#### Note:
- **`uglify-js@3` has a simplified [API](#api-reference) and [CLI](#command-line-usage) that is not backwards compatible with [`uglify-js@2`](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- **Documentation for UglifyJS `2.x` releases can be found [here](https://github.com/mishoo/UglifyJS2/tree/v2.x)**.
- `uglify-js` only supports ECMAScript 5 (ES5).
- Those wishing to minify
ES2015+ (ES6+) should use the `npm` package [**uglify-es**](https://github.com/mishoo/UglifyJS2/tree/harmony).
- `uglify-js` only supports JavaScript (ECMAScript 5).
- To minify ECMAScript 2015 or above, transpile using tools like [Babel](https://babeljs.io/).
Install
-------
@@ -825,7 +824,7 @@ can pass additional arguments that control the code output:
when you want to generate minified code, in order to specify additional
arguments, so you can use `-b beautify=false` to override it.
- `bracketize` (default `false`) -- always insert brackets in `if`, `for`,
- `braces` (default `false`) -- always insert braces in `if`, `for`,
`do`, `while` or `with` statements, even if their body is a single
statement.
@@ -837,8 +836,8 @@ can pass additional arguments that control the code output:
- `indent_start` (default `0`) -- prefix all lines by that many spaces
- `inline_script` (default `false`) -- escape the slash in occurrences of
`</script` in strings
- `inline_script` (default `true`) -- escape HTML comments and the slash in
occurrences of `</script>` in strings
- `keep_quoted_props` (default `false`) -- when turned on, prevents stripping
quotes from property names in object literals.

View File

@@ -165,7 +165,7 @@ function walk_body(node, visitor) {
};
var AST_Block = DEFNODE("Block", "body", {
$documentation: "A body of statements (usually bracketed)",
$documentation: "A body of statements (usually braced)",
$propdoc: {
body: "[AST_Statement*] an array of statements"
},
@@ -916,5 +916,25 @@ TreeWalker.prototype = {
|| node instanceof AST_Break && x instanceof AST_Switch)
return x;
}
},
in_boolean_context: function() {
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
}
};

View File

@@ -147,27 +147,6 @@ merge(Compressor.prototype, {
return true;
return false;
},
in_boolean_context: function() {
if (!this.option("booleans")) return false;
var self = this.self();
for (var i = 0, p; p = this.parent(i); i++) {
if (p instanceof AST_SimpleStatement
|| p instanceof AST_Conditional && p.condition === self
|| p instanceof AST_DWLoop && p.condition === self
|| p instanceof AST_For && p.condition === self
|| p instanceof AST_If && p.condition === self
|| p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) {
return true;
}
if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||")
|| p instanceof AST_Conditional
|| p.tail_node() === self) {
self = p;
} else {
return false;
}
}
},
compress: function(node) {
if (this.option("expression")) {
node.process_expression(true);
@@ -517,6 +496,15 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_Case, function(tw) {
push(tw);
this.expression.walk(tw);
pop(tw);
push(tw);
walk_body(this, tw);
pop(tw);
return true;
});
def(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
@@ -527,6 +515,12 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_Default, function(tw, descend) {
push(tw);
descend();
pop(tw);
return true;
});
def(AST_Defun, function(tw, descend, compressor) {
this.inlined = false;
var save_ids = tw.safe_ids;
@@ -624,12 +618,6 @@ merge(Compressor.prototype, {
pop(tw);
return true;
});
def(AST_SwitchBranch, function(tw, descend) {
push(tw);
descend();
pop(tw);
return true;
});
def(AST_SymbolCatch, function() {
this.definition().fixed = false;
});
@@ -971,7 +959,7 @@ merge(Compressor.prototype, {
var args;
var candidates = [];
var stat_index = statements.length;
var scanner = new TreeTransformer(function(node, descend) {
var scanner = new TreeTransformer(function(node) {
if (abort) return node;
// Skip nodes before `candidate` as quickly as possible
if (!hit) {
@@ -1010,7 +998,7 @@ merge(Compressor.prototype, {
if (can_replace
&& !(node instanceof AST_SymbolDeclaration)
&& (scan_lhs && (hit_lhs = lhs.equivalent_to(node))
|| scan_rhs && (hit_rhs = rhs.equivalent_to(node)))) {
|| scan_rhs && (hit_rhs = scan_rhs(node, this)))) {
if (stop_if_hit && (hit_rhs || !lhs_local || !replace_all)) {
abort = true;
return node;
@@ -1379,12 +1367,16 @@ merge(Compressor.prototype, {
}
function foldable(expr) {
if (expr.is_constant()) return true;
if (expr instanceof AST_Array) return false;
if (expr instanceof AST_Function) return false;
if (expr instanceof AST_Object) return false;
if (expr instanceof AST_RegExp) return false;
if (expr instanceof AST_Symbol) return true;
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
if (value === expr) return rhs_exact_match;
return rhs_fuzzy_match(value, rhs_exact_match);
}
if (expr instanceof AST_This) return rhs_exact_match;
if (expr.is_truthy()) return rhs_fuzzy_match(true, return_false);
if (expr.is_constant()) {
return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
}
if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false;
var circular;
@@ -1395,7 +1387,25 @@ merge(Compressor.prototype, {
circular = true;
}
}));
return !circular;
return !circular && rhs_exact_match;
}
function rhs_exact_match(node) {
return rhs.equivalent_to(node);
}
function rhs_fuzzy_match(value, fallback) {
return function(node, tw) {
if (tw.in_boolean_context()) {
if (value && node.is_truthy() && !node.has_side_effects(compressor)) {
return true;
}
if (node.is_constant()) {
return !node.evaluate(compressor) == !value;
}
}
return fallback(node);
};
}
function get_lvalues(expr) {
@@ -2032,6 +2042,28 @@ merge(Compressor.prototype, {
&& !node.expression.has_side_effects(compressor);
}
// is_truthy()
// return true if `!!node === true`
(function(def) {
def(AST_Node, return_false);
def(AST_Array, return_true);
def(AST_Assign, function() {
return this.operator == "=" && this.right.is_truthy();
});
def(AST_Lambda, return_true);
def(AST_Object, return_true);
def(AST_RegExp, return_true);
def(AST_Sequence, function() {
return this.tail_node().is_truthy();
});
def(AST_SymbolRef, function() {
var fixed = this.fixed_value();
return fixed && fixed.is_truthy();
});
})(function(node, func) {
node.DEFMETHOD("is_truthy", func);
});
// may_throw_on_access()
// returns true if this node may be null, undefined or contain `AST_Accessor`
(function(def) {
@@ -3105,6 +3137,8 @@ merge(Compressor.prototype, {
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
var fixed_ids = Object.create(null);
var value_read = Object.create(null);
var value_modified = Object.create(null);
if (self instanceof AST_Toplevel && compressor.top_retain) {
self.variables.each(function(def) {
if (compressor.top_retain(def) && !(def.id in in_use_ids)) {
@@ -3372,6 +3406,17 @@ merge(Compressor.prototype, {
);
self.transform(tt);
function verify_safe_usage(def, read, modified) {
if (def.id in in_use_ids) return;
if (read && modified) {
in_use_ids[def.id] = true;
in_use.push(def);
} else {
value_read[def.id] = read;
value_modified[def.id] = modified;
}
}
function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef
@@ -3381,8 +3426,18 @@ merge(Compressor.prototype, {
});
if (node instanceof AST_Assign) {
node.right.walk(tw);
if (node.left === sym && !node_def.chained && sym.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
if (node.left === sym) {
if (!node_def.chained && sym.fixed_value() === node.right) {
fixed_ids[node_def.id] = node;
}
if (!node.write_only) {
verify_safe_usage(node_def, true, value_modified[node_def.id]);
}
} else {
var fixed = sym.fixed_value();
if (!fixed || !fixed.is_constant()) {
verify_safe_usage(node_def, value_read[node_def.id], true);
}
}
}
return true;
@@ -3789,7 +3844,7 @@ merge(Compressor.prototype, {
OPT(AST_Do, function(self, compressor){
if (!compressor.option("loops")) return self;
var cond = self.condition.tail_node().evaluate(compressor);
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!(cond instanceof AST_Node)) {
if (cond) return make_node(AST_For, self, {
body: make_node(AST_BlockStatement, self.body, {
@@ -3911,9 +3966,11 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
}
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (!cond) {
if (cond instanceof AST_Node) {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
}
if (!cond) {
if (compressor.option("dead_code")) {
var body = [];
extract_declarations_from_unreachable_code(compressor, self.body, body);
if (self.init instanceof AST_Statement) {
@@ -3928,6 +3985,16 @@ merge(Compressor.prototype, {
}));
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
} else if (self.condition && !(cond instanceof AST_Node)) {
self.body = make_node(AST_BlockStatement, self.body, {
body: [
make_node(AST_SimpleStatement, self.condition, {
body: self.condition
}),
self.body
]
});
self.condition = null;
}
}
return if_break_in_loop(self, compressor);
@@ -3948,7 +4015,9 @@ merge(Compressor.prototype, {
self.condition = best_of_expression(self.condition.transform(compressor), orig);
}
if (compressor.option("dead_code")) {
if (cond instanceof AST_Node) cond = self.condition.tail_node().evaluate(compressor);
if (cond instanceof AST_Node) {
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
}
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
var body = [];
@@ -4832,8 +4901,10 @@ merge(Compressor.prototype, {
return make_node(AST_Undefined, self).optimize(compressor);
}
}
if (compressor.in_boolean_context()) {
switch (self.operator) {
if (compressor.option("booleans")) {
if (self.operator == "!" && e.is_truthy()) {
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
} else if (compressor.in_boolean_context()) switch (self.operator) {
case "!":
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
// !!foo ==> foo, if we're in boolean context
@@ -5019,7 +5090,7 @@ merge(Compressor.prototype, {
}
break;
}
if (self.operator == "+" && compressor.in_boolean_context()) {
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
var ll = self.left.evaluate(compressor);
var rr = self.right.evaluate(compressor);
if (ll && typeof ll == "string") {
@@ -5074,7 +5145,7 @@ merge(Compressor.prototype, {
if (compressor.option("evaluate")) {
switch (self.operator) {
case "&&":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
@@ -5084,7 +5155,7 @@ merge(Compressor.prototype, {
}
var rr = self.right.evaluate(compressor);
if (!rr) {
if (compressor.in_boolean_context()) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5093,7 +5164,8 @@ merge(Compressor.prototype, {
} else self.falsy = true;
} else if (!(rr instanceof AST_Node)) {
var parent = compressor.parent();
if (parent.operator == "&&" && parent.left === compressor.self() || compressor.in_boolean_context()) {
if (parent.operator == "&&" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
@@ -5109,7 +5181,7 @@ merge(Compressor.prototype, {
}
break;
case "||":
var ll = self.left.truthy ? true : self.left.falsy ? false : self.left.evaluate(compressor);
var ll = fuzzy_eval(self.left);
if (!ll) {
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
@@ -5120,12 +5192,13 @@ merge(Compressor.prototype, {
var rr = self.right.evaluate(compressor);
if (!rr) {
var parent = compressor.parent();
if (parent.operator == "||" && parent.left === compressor.self() || compressor.in_boolean_context()) {
if (parent.operator == "||" && parent.left === compressor.self()
|| compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
return self.left.optimize(compressor);
}
} else if (!(rr instanceof AST_Node)) {
if (compressor.in_boolean_context()) {
if (compressor.option("booleans") && compressor.in_boolean_context()) {
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [
self.left,
@@ -5347,6 +5420,13 @@ merge(Compressor.prototype, {
return best_of(compressor, ev, self);
}
return self;
function fuzzy_eval(node) {
if (node.truthy) return true;
if (node.falsy) return false;
if (node.is_truthy()) return true;
return node.evaluate(compressor);
}
});
function recursive_ref(compressor, def) {
@@ -5642,15 +5722,13 @@ merge(Compressor.prototype, {
expressions.push(self);
return make_sequence(self, expressions);
}
var cond = self.condition.evaluate(compressor);
if (cond !== self.condition) {
if (cond) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.consequent);
} else {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return maintain_this_binding(compressor.parent(), compressor.self(), self.alternative);
}
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
if (!cond) {
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
} else if (!(cond instanceof AST_Node)) {
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
}
var negated = cond.negate(compressor, first_in_statement(compressor));
if (best_of(compressor, cond, negated) === negated) {
@@ -5758,7 +5836,7 @@ merge(Compressor.prototype, {
right: alternative
}).optimize(compressor);
}
var in_bool = compressor.in_boolean_context();
var in_bool = compressor.option("booleans") && compressor.in_boolean_context();
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
// c ? true : false ---> !!c
@@ -5856,32 +5934,29 @@ merge(Compressor.prototype, {
});
OPT(AST_Boolean, function(self, compressor){
if (!compressor.option("booleans")) return self;
if (compressor.in_boolean_context()) return make_node(AST_Number, self, {
value: +self.value
});
if (compressor.option("booleans")) {
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "=="
|| p.operator == "!=")) {
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
operator : p.operator,
value : self.value,
file : p.start.file,
line : p.start.line,
col : p.start.col,
});
return make_node(AST_Number, self, {
value: +self.value
});
}
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
var p = compressor.parent();
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
operator : p.operator,
value : self.value,
file : p.start.file,
line : p.start.line,
col : p.start.col,
});
return make_node(AST_Number, self, {
value: +self.value
});
}
return self;
return make_node(AST_UnaryPrefix, self, {
operator: "!",
expression: make_node(AST_Number, self, {
value: 1 - self.value
})
});
});
OPT(AST_Sub, function(self, compressor){
@@ -6090,19 +6165,6 @@ merge(Compressor.prototype, {
return self;
});
function literals_in_boolean_context(self, compressor) {
if (compressor.in_boolean_context()) {
return best_of(compressor, self, make_sequence(self, [
self,
make_node(AST_True, self)
]).optimize(compressor));
}
return self;
};
OPT(AST_Array, literals_in_boolean_context);
OPT(AST_Object, literals_in_boolean_context);
OPT(AST_RegExp, literals_in_boolean_context);
OPT(AST_Return, function(self, compressor){
if (self.value && is_undefined(self.value, compressor)) {
self.value = null;

View File

@@ -56,7 +56,7 @@ function OutputStream(options) {
options = defaults(options, {
ascii_only : false,
beautify : false,
bracketize : false,
braces : false,
comments : false,
ie8 : false,
indent_level : 4,
@@ -178,7 +178,7 @@ function OutputStream(options) {
function encode_string(str, quote) {
var ret = make_string(str, quote);
if (options.inline_script) {
ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
ret = ret.replace(/<\x2f(script)([>\/\t\n\f\r ])/gi, "<\\/$1$2");
ret = ret.replace(/\x3c!--/g, "\\x3c!--");
ret = ret.replace(/--\x3e/g, "--\\x3e");
}
@@ -886,21 +886,22 @@ function OutputStream(options) {
self.body.print(output);
output.semicolon();
});
function print_bracketed(self, output, allow_directives) {
function print_braced_empty(self, output) {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.append_comments(self, true);
});
output.print("}");
}
function print_braced(self, output, allow_directives) {
if (self.body.length > 0) {
output.with_block(function() {
display_body(self.body, false, output, allow_directives);
});
} else {
output.print("{");
output.with_indent(output.next_indent(), function() {
output.append_comments(self, true);
});
output.print("}");
}
} else print_braced_empty(self, output);
};
DEFPRINT(AST_BlockStatement, function(self, output){
print_bracketed(self, output);
print_braced(self, output);
});
DEFPRINT(AST_EmptyStatement, function(self, output){
output.semicolon();
@@ -995,7 +996,7 @@ function OutputStream(options) {
});
});
output.space();
print_bracketed(self, output, true);
print_braced(self, output, true);
});
DEFPRINT(AST_Lambda, function(self, output){
self._do_print(output);
@@ -1036,7 +1037,7 @@ function OutputStream(options) {
/* -----[ if ]----- */
function make_then(self, output) {
var b = self.body;
if (output.option("bracketize")
if (output.option("braces")
|| output.option("ie8") && b instanceof AST_Do)
return make_block(b, output);
// The squeezer replaces "block"-s that contain only a single
@@ -1045,7 +1046,7 @@ function OutputStream(options) {
// IF having an ELSE clause where the THEN clause ends in an
// IF *without* an ELSE block (then the outer ELSE would refer
// to the inner IF). This function checks for this case and
// adds the block brackets if needed.
// adds the block braces if needed.
if (!b) return output.force_semicolon();
while (true) {
if (b instanceof AST_If) {
@@ -1092,7 +1093,7 @@ function OutputStream(options) {
});
output.space();
var last = self.body.length - 1;
if (last < 0) output.print("{}");
if (last < 0) print_braced_empty(self, output);
else output.with_block(function(){
self.body.forEach(function(branch, i){
output.indent(true);
@@ -1126,7 +1127,7 @@ function OutputStream(options) {
DEFPRINT(AST_Try, function(self, output){
output.print("try");
output.space();
print_bracketed(self, output);
print_braced(self, output);
if (self.bcatch) {
output.space();
self.bcatch.print(output);
@@ -1143,12 +1144,12 @@ function OutputStream(options) {
self.argname.print(output);
});
output.space();
print_bracketed(self, output);
print_braced(self, output);
});
DEFPRINT(AST_Finally, function(self, output){
output.print("finally");
output.space();
print_bracketed(self, output);
print_braced(self, output);
});
/* -----[ var/const ]----- */
@@ -1347,7 +1348,7 @@ function OutputStream(options) {
});
output.newline();
});
else output.print("{}");
else print_braced_empty(self, output);
});
function print_property_name(key, quote, output) {
@@ -1419,7 +1420,7 @@ function OutputStream(options) {
});
function force_statement(stat, output) {
if (output.option("bracketize")) {
if (output.option("braces")) {
make_block(stat, output);
} else {
if (!stat || stat instanceof AST_EmptyStatement)

View File

@@ -1,10 +1,9 @@
{
"name": "uglify-js",
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.3.14",
"version": "3.3.16",
"engines": {
"node": ">=0.8.0"
},
@@ -24,16 +23,39 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.14.1",
"commander": "~2.15.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~5.4.1",
"acorn": "~5.5.3",
"mocha": "~3.5.1",
"semver": "~5.5.0"
},
"scripts": {
"test": "node test/run-tests.js"
},
"keywords": ["uglify", "uglify-js", "minify", "minifier", "es5"]
"keywords": [
"cli",
"compress",
"compressor",
"ecma",
"ecmascript",
"es",
"es5",
"javascript",
"js",
"jsmin",
"min",
"minification",
"minifier",
"minify",
"optimize",
"optimizer",
"pack",
"packer",
"parse",
"parser",
"uglifier",
"uglify"
]
}

View File

@@ -4976,7 +4976,7 @@ collapse_rhs_array: {
expect_stdout: "false false false"
}
collapse_rhs_boolean: {
collapse_rhs_boolean_1: {
options = {
collapse_vars: true,
}
@@ -5001,6 +5001,66 @@ collapse_rhs_boolean: {
expect_stdout: "true true true"
}
collapse_rhs_boolean_2: {
options = {
collapse_vars: true,
}
input: {
var a;
(function f1() {
a = function() {};
if (/foo/)
console.log(typeof a);
})();
console.log(function f2() {
a = [];
return !1;
}());
}
expect: {
var a;
(function f1() {
if (a = function() {})
console.log(typeof a);
})();
console.log(function f2() {
return !(a = []);
}());
}
expect_stdout: [
"function",
"false",
]
}
collapse_rhs_boolean_3: {
options = {
booleans: true,
collapse_vars: true,
conditionals: true,
}
input: {
var a, f, g, h, i, n, s, t, x, y;
if (x()) {
n = a;
} else if (y()) {
n = f();
} else if (s) {
i = false;
n = g(true);
} else if (t) {
i = false;
n = h(true);
} else {
n = [];
}
}
expect: {
var a, f, g, h, i, n, s, t, x, y;
n = x() ? a : y() ? f() : s ? g(!(i = !1)) : t ? h(!(i = !1)) : [];
}
}
collapse_rhs_function: {
options = {
collapse_vars: true,

View File

@@ -703,10 +703,11 @@ ternary_boolean_alternative: {
trivial_boolean_ternary_expressions : {
options = {
booleans: true,
conditionals: true,
evaluate : true,
booleans : true
};
evaluate: true,
side_effects: true,
}
input: {
f('foo' in m ? true : false);
f('foo' in m ? false : true);

View File

@@ -1785,3 +1785,32 @@ issue_805_2: {
"bar",
]
}
issue_2995: {
options = {
pure_getters: "strict",
reduce_vars: true,
unused: true,
}
input: {
function f(a) {
var b;
a.b = b = function() {};
b.c = "PASS";
}
var o = {};
f(o);
console.log(o.b.c);
}
expect: {
function f(a) {
var b;
a.b = b = function() {};
b.c = "PASS";
}
var o = {};
f(o);
console.log(o.b.c);
}
expect_stdout: "PASS"
}

View File

@@ -745,7 +745,7 @@ in_boolean_context: {
!b("foo"),
!b([1, 2]),
!b(/foo/),
![1, foo()],
(foo(), !1),
(foo(), !1)
);
}
@@ -1566,3 +1566,43 @@ issue_2968: {
}
expect_stdout: "PASS"
}
truthy_conditionals: {
options = {
conditionals: true,
evaluate: true,
}
input: {
if (a = {}) x();
(b = /foo/) && y();
(c = function() {}) || z();
}
expect: {
a = {}, x();
b = /foo/, y();
c = function() {};
}
}
truthy_loops: {
options = {
evaluate: true,
loops: true,
}
input: {
while ([]) x();
do {
y();
} while(a = {});
}
expect: {
for (;;) {
[];
x();
}
for (;;) {
y();
a = {};
}
}
}

View File

@@ -175,8 +175,8 @@ should_warn: {
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:141,31]",
"WARN: Condition always true [test/compress/issue-1261.js:141,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:142,23]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Condition always true [test/compress/issue-1261.js:143,8]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:143,24]",
"WARN: Dropping __PURE__ call [test/compress/issue-1261.js:144,31]",
"WARN: Condition always false [test/compress/issue-1261.js:144,8]",
]

View File

@@ -0,0 +1,21 @@
inline_script_off: {
beautify = {
inline_script: false,
}
input: {
console.log("</sCrIpT>");
}
expect_exact: 'console.log("</sCrIpT>");'
expect_stdout: "</sCrIpT>"
}
inline_script_on: {
beautify = {
inline_script: true,
}
input: {
console.log("</sCrIpT>");
}
expect_exact: 'console.log("<\\/sCrIpT>");'
expect_stdout: "</sCrIpT>"
}

View File

@@ -294,10 +294,10 @@ issue_186_beautify_ie8: {
]
}
issue_186_bracketize: {
issue_186_braces: {
beautify = {
beautify: false,
bracketize: true,
braces: true,
ie8: false,
}
input: {
@@ -314,10 +314,10 @@ issue_186_bracketize: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_bracketize_ie8: {
issue_186_braces_ie8: {
beautify = {
beautify: false,
bracketize: true,
braces: true,
ie8: true,
}
input: {
@@ -334,10 +334,10 @@ issue_186_bracketize_ie8: {
expect_exact: 'var x=3;if(foo()){do{do{alert(x)}while(--x)}while(x)}else{bar()}'
}
issue_186_beautify_bracketize: {
issue_186_beautify_braces: {
beautify = {
beautify: true,
bracketize: true,
braces: true,
ie8: false,
}
input: {
@@ -366,10 +366,10 @@ issue_186_beautify_bracketize: {
]
}
issue_186_beautify_bracketize_ie8: {
issue_186_beautify_braces_ie8: {
beautify = {
beautify: true,
bracketize: true,
braces: true,
ie8: true,
}
input: {

View File

@@ -67,7 +67,7 @@ negate_iife_3_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
true, console.log(true);
}
expect_stdout: true
}
@@ -110,7 +110,7 @@ negate_iife_3_off_evaluate: {
(function(){ return true })() ? console.log(true) : console.log(false);
}
expect: {
console.log(true);
true, console.log(true);
}
expect_stdout: true
}

View File

@@ -55,7 +55,7 @@ reduce_vars: {
console.log(a - 5);
eval("console.log(a);");
})(eval);
"yes";
true, "yes";
console.log(A + 1);
}
expect_stdout: true
@@ -147,7 +147,7 @@ modified: {
}
function f4() {
var b = 2, c = 3;
b = c;
1, b = c;
console.log(1 + b);
console.log(b + c);
console.log(1 + c);
@@ -715,10 +715,12 @@ passes: {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
unused: true,
}
input: {
function f() {
(function() {
var a = 1, b = 2, c = 3;
if (a) {
b = c;
@@ -729,17 +731,22 @@ passes: {
console.log(b + c);
console.log(a + c);
console.log(a + b + c);
}
})();
}
expect: {
function f() {
3;
console.log(4);
console.log(6);
console.log(4);
(function() {
console.log(4),
console.log(6),
console.log(4),
console.log(7);
}
})();
}
expect_stdout: [
"4",
"6",
"4",
"7",
]
}
iife: {
@@ -5545,3 +5552,33 @@ issue_2919: {
}
expect_stdout: "function"
}
issue_2992: {
options = {
evaluate: true,
reduce_vars: true,
}
input: {
var c = "PASS";
(function f(b) {
switch (0) {
case 0:
case b = 1:
b && (c = "FAIL");
}
})();
console.log(c);
}
expect: {
var c = "PASS";
(function f(b) {
switch (0) {
case 0:
case b = 1:
b && (c = "FAIL");
}
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -59,7 +59,7 @@ if_else_empty: {
if ({} ? a : b); else {}
}
expect: {
!{} ? b : a;
({}), a;
}
}

View File

@@ -164,13 +164,13 @@ describe("bin/uglifyjs", function () {
done();
});
});
it("Should work with `--beautify bracketize`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b bracketize';
it("Should work with `--beautify braces`", function (done) {
var command = uglifyjscmd + ' test/input/issue-1482/input.js -b braces';
exec(command, function (err, stdout) {
if (err) throw err;
assert.strictEqual(stdout, read("test/input/issue-1482/bracketize.js"));
assert.strictEqual(stdout, read("test/input/issue-1482/braces.js"));
done();
});
});

View File

@@ -139,6 +139,26 @@ describe("Comment", function() {
assert.strictEqual(result.code, code);
});
it("Should retain comments within braces", function() {
var code = [
"{/* foo */}",
"a({/* foo */});",
"while (a) {/* foo */}",
"switch (a) {/* foo */}",
"if (a) {/* foo */} else {/* bar */}",
].join("\n\n");
var result = uglify.minify(code, {
compress: false,
mangle: false,
output: {
beautify: true,
comments: "all",
},
});
if (result.error) throw result.error;
assert.strictEqual(result.code, code);
});
it("Should correctly preserve new lines around comments", function() {
var tests = [
[

View File

@@ -17,7 +17,7 @@ describe("test/benchmark.js", function() {
this.timeout(10 * 60 * 1000);
[
"-b",
"-b bracketize",
"-b braces",
"-m",
"-mc passes=3",
"-mc passes=3,toplevel",

View File

@@ -11,7 +11,7 @@ function try_beautify(code) {
mangle: false,
output: {
beautify: true,
bracketize: true
braces: true
}
});
if (beautified.error) {

View File

@@ -343,7 +343,6 @@ function parse_test(file) {
}
function make_code(ast, options) {
options.inline_script = true;
var stream = U.OutputStream(options);
ast.print(stream);
return stream.get();

View File

@@ -975,7 +975,7 @@ function try_beautify(code, result, printfn) {
mangle: false,
output: {
beautify: true,
bracketize: true,
braces: true,
},
});
if (beautified.error) {

View File

@@ -4,7 +4,7 @@
"mangle": false,
"output": {
"beautify": true,
"bracketize": true
"braces": true
},
"rename": true
},