Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73d77f4f64 | ||
|
|
ccf0e2ef4f | ||
|
|
20ca0f5906 | ||
|
|
b29d435bb5 | ||
|
|
90585e29c2 | ||
|
|
d8fc281915 | ||
|
|
188c39e8d5 | ||
|
|
5429234138 | ||
|
|
b9f72a4a81 | ||
|
|
fc6ebd04a5 |
11
README.md
11
README.md
@@ -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.
|
||||
|
||||
22
lib/ast.js
22
lib/ast.js
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
254
lib/compress.js
254
lib/compress.js
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
32
package.json
32
package.json
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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 = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]",
|
||||
]
|
||||
|
||||
21
test/compress/issue-2989.js
Normal file
21
test/compress/issue-2989.js
Normal 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>"
|
||||
}
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ if_else_empty: {
|
||||
if ({} ? a : b); else {}
|
||||
}
|
||||
expect: {
|
||||
!{} ? b : a;
|
||||
({}), a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 = [
|
||||
[
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -11,7 +11,7 @@ function try_beautify(code) {
|
||||
mangle: false,
|
||||
output: {
|
||||
beautify: true,
|
||||
bracketize: true
|
||||
braces: true
|
||||
}
|
||||
});
|
||||
if (beautified.error) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -975,7 +975,7 @@ function try_beautify(code, result, printfn) {
|
||||
mangle: false,
|
||||
output: {
|
||||
beautify: true,
|
||||
bracketize: true,
|
||||
braces: true,
|
||||
},
|
||||
});
|
||||
if (beautified.error) {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"mangle": false,
|
||||
"output": {
|
||||
"beautify": true,
|
||||
"bracketize": true
|
||||
"braces": true
|
||||
},
|
||||
"rename": true
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user