Compare commits

..

35 Commits

Author SHA1 Message Date
Mihai Bazon
db66eca958 v2.2.5 2013-02-14 12:51:13 +02:00
Mihai Bazon
916faf0a48 Force space after literal regexp when used in "instanceof" or "in"
Close #118
2013-02-06 11:57:59 +02:00
Mihai Bazon
f36e4e9a78 Give up evaluating (unary-prefix '-' 0)
Close #117

------

    JS, WHY YOU SUCK SO BADLY? ;-(
2013-02-06 11:51:09 +02:00
Mihai Bazon
fdf8b5eb71 Fix parens for NaN
Close #116
2013-02-06 11:38:29 +02:00
Mihai Bazon
de7ec7f1b7 Fix parens for negative numbers
Close #115
2013-02-06 11:36:04 +02:00
Mihai Bazon
3c8a0bdff4 Fix parens for AST_New
Close #114
2013-02-06 11:28:49 +02:00
Mihai Bazon
9e8ba27dcd Fix handling of constants
Close #113
2013-02-06 11:15:31 +02:00
Mihai Bazon
719a8fd102 Ugly hack to print comments before return/throw statements
Close #112
2013-02-05 19:11:39 +02:00
Mihai Bazon
3a22e917de Merge pull request #111 from mattrobenolt/safer-sourcemap
Wraps sourceMappingURL in a multiline comment. Fixes #108
2013-02-03 23:44:31 -08:00
Matt Robenolt
a9af2c9e62 Wraps sourceMappingURL in a multiline comment. Fixes #108 2013-02-03 16:01:01 -08:00
Mihai Bazon
31e99cebe7 v2.2.4 2013-02-01 13:32:15 +02:00
Mihai Bazon
a5b209470c Fix end token for Assign nodes 2013-02-01 13:32:15 +02:00
Mihai Bazon
e9a571b2a1 Merge pull request #94 from paulmillr/patch-1
Add better fromstring docs.
2013-01-31 23:50:59 -08:00
Mihai Bazon
8bf83f42ea Merge pull request #106 from gibson042/105
Fix #105: property comparison to undefined is not always safe
2013-01-24 05:51:33 -08:00
Richard Gibson
522566ea80 Fix #105: property comparison to undefined is not always safe 2013-01-23 23:52:04 -05:00
Mihai Bazon
297af47c89 Add --source-map-url option
Fix #100
Fix #47
2013-01-20 12:32:07 +02:00
Mihai Bazon
faa354f5ca [AST_Hole] the print function can be a no-op. 2013-01-17 11:36:10 +02:00
David Glasser
1529ab965a Fix output for arrays containing undefined values.
1b6bcca7 was a first attempt at this. That commit made Uglify stop replacing
holes with undefined, but instead it started replacing undefined with
holes. This is slightly problematic, because there is a difference between a
hole and an undefined value. More problematically, it changed [1,undefined] to
[1,] which generally doesn't even parse as a hole (just as a trailing comma), so
it didn't even preserve the length of the array!

Instead, parse holes as their own special AST node which prints invisibly.
2013-01-17 11:36:10 +02:00
Mihai Bazon
605f330e69 Merge pull request #98 from ForbesLindesay/patch-1
Update installation instructions
2013-01-17 01:08:59 -08:00
Mihai Bazon
f0909bdc8f Handle String() with no arguments.
Fix #91
2013-01-17 11:01:38 +02:00
Forbes Lindesay
c13e7e621d Update installation instructions re #4 2013-01-17 00:13:42 +00:00
Paul Miller
ad071f8017 Add better fromstring docs. 2013-01-13 18:45:43 +02:00
Mihai Bazon
c058d8b9cd Merge pull request #90 from jakearchibald/patch-1
Compressor options use underscores rather than hyphens
2013-01-08 14:21:25 -08:00
Jake Archibald
1d8871a092 Compressor options use underscores rather than hyphens 2013-01-08 12:33:58 -08:00
Mihai Bazon
16953c2064 v2.2.3 2013-01-04 22:50:53 +02:00
Mihai Bazon
6b14f7c224 Fix handling of labels in nested scopes 2013-01-04 14:17:33 +02:00
Mihai Bazon
130c623be7 Support output, mangle and compress options to UglifyJS.minify.
Close #57
Close #86
Close #33
2013-01-04 11:25:13 +02:00
Mihai Bazon
47c9895d59 Merge pull request #87 from BenoitZugmeyer/master
Add a --version option
2013-01-03 02:28:35 -08:00
Benoît Zugmeyer
ba403331c5 Set --version as a boolean #87 2013-01-03 11:22:37 +01:00
Benoît Zugmeyer
e82e89d1b0 --version option 2013-01-03 11:07:53 +01:00
Mihai Bazon
83a4ebfedc Implement -m sort=true
close #83
2013-01-02 12:39:00 +02:00
Mihai Bazon
9916d0e547 Accept string or number as name of an accessor.
[not sure I'm happy about this fix]

Reference mishoo/UglifyJS#478
2012-12-22 01:24:04 +02:00
Mihai Bazon
31c4a37e37 Optimize new Array(1, 2, 3) → [1, 2, 3]
Close #74
2012-12-21 21:04:35 +02:00
Mihai Bazon
08219f0cee Fix output when semicolons is off.
(need to force a semicolon for the empty body of an `if`)

Close #72
2012-12-21 11:57:08 +02:00
Mihai Bazon
c4993e1e5c Small cleanup 2012-12-12 11:51:55 +02:00
11 changed files with 182 additions and 60 deletions

View File

@@ -12,7 +12,14 @@ Chrome and probably Safari).
Install
-------
From NPM:
First make sure you have installed the latest version of [node.js](http://nodejs.org/)
(You may need to restart your computer after this step).
From NPM for use as a command line app:
npm install uglify-js -g
From NPM for programmatic use:
npm install uglify-js
@@ -42,6 +49,9 @@ The available options are:
[string]
--source-map-root The path to the original source to be included in the
source map. [string]
--source-map-url The path to the source map to be added in //@
sourceMappingURL. Defaults to the value passed with
--source-map. [string]
--in-source-map Input source map, useful if you're compressing JS that was
generated from some other original code.
-p, --prefix Skip prefix for original filenames that appear in source
@@ -78,7 +88,9 @@ The available options are:
[string]
--export-all Only used when --wrap, this tells UglifyJS to add code to
automatically export all globals. [boolean]
--lint Display some scope warnings [boolean]
-v, --verbose Verbose [boolean]
-V, --version Print version number and exit. [boolean]
Specify `--output` (`-o`) to declare the output file. Otherwise the output
goes to STDOUT.
@@ -130,7 +142,7 @@ input files from the command line.
## Mangler options
To enable the mangler you need to pass `--mangle` (`-m`). Optionally you
can pass `-m sort` (we'll possibly have other flags in the future) in order
can pass `-m sort=true` (we'll possibly have other flags in the future) in order
to assign shorter names to most frequently used variables. This saves a few
hundred bytes on jQuery before gzip, but the output is _bigger_ after gzip
(and seems to happen for other libraries I tried it on) therefore it's not
@@ -157,8 +169,8 @@ the available options (all are `true` by default, except `hoist_vars`):
- `sequences` -- join consecutive simple statements using the comma operator
- `properties` -- rewrite property access using the dot notation, for
example `foo["bar"] → foo.bar`
- `dead-code` -- remove unreachable code
- `drop-debugger` -- remove `debugger;` statements
- `dead_code` -- remove unreachable code
- `drop_debugger` -- remove `debugger;` statements
- `unsafe` -- apply "unsafe" transformations (discussion below)
- `conditionals` -- apply optimizations for `if`-s and conditional
expressions
@@ -171,11 +183,11 @@ the available options (all are `true` by default, except `hoist_vars`):
- `loops` -- optimizations for `do`, `while` and `for` loops when we can
statically determine the condition
- `unused` -- drop unreferenced functions and variables
- `hoist-funs` -- hoist function declarations
- `hoist-vars` -- hoist `var` declarations (this is `false` by default
- `hoist_funs` -- hoist function declarations
- `hoist_vars` -- hoist `var` declarations (this is `false` by default
because it seems to increase the size of the output in general)
- `if-return` -- optimizations for if/return and if/continue
- `join-vars` -- join consecutive `var` statements
- `if_return` -- optimizations for if/return and if/continue
- `join_vars` -- join consecutive `var` statements
- `cascade` -- small optimization for sequences, transform `x, x` into `x`
and `x = something(), x` into `x = something()`
- `warnings` -- display warnings when dropping unreachable code or unused
@@ -213,6 +225,7 @@ will evaluate references to them to the value itself and drop unreachable
code as usual. The possible downside of this approach is that the build
will contain the `const` declarations.
<a name="codegen-options"></a>
## Beautifier options
The code generator tries to output shortest code possible by default. In
@@ -322,9 +335,10 @@ There's a single toplevel function which combines all the steps. If you
don't need additional customization, you might want to go with `minify`.
Example:
// see "fromString" below if you need to pass code instead of file name
var result = UglifyJS.minify("/path/to/file.js");
console.log(result.code); // minified output
// if you need to pass code instead of file name
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
You can also compress multiple files:
@@ -366,9 +380,19 @@ no sense otherwise).
Other options:
- `warnings` (default `false`) — pass `true` to display compressor warnings.
- `fromString` (default `false`) — if you pass `true` then you can pass
JavaScript source code, rather than file names.
- `mangle` — pass `false` to skip mangling names.
- `output` (default `null`) — pass an object if you wish to specify
additional [output options][codegen]. The defaults are optimized
for best compression.
- `compress` (default `{}`) — pass `false` to skip compressing entirely.
Pass an object to specify custom [compressor options][compressor].
We could add more options to `UglifyJS.minify` — if you need additional
functionality please suggest!
@@ -516,3 +540,5 @@ The `source_map_options` (optional) can contain the following properties:
[acorn]: https://github.com/marijnh/acorn
[source-map]: https://github.com/mozilla/source-map
[sm-spec]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
[codegen]: http://lisperator.net/uglifyjs/codegen
[compressor]: http://lisperator.net/uglifyjs/compress

View File

@@ -19,6 +19,7 @@ mangling you need to use `-c` and `-m`.\
")
.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-url", "The path to the source map to be added in //@ sourceMappingURL. Defaults to the value passed with --source-map.")
.describe("in-source-map", "Input source map, useful if you're compressing JS that was generated from some other original code.")
.describe("p", "Skip prefix for original filenames that appear in source maps. \
For example -p 3 will drop 3 directories from file names and ensure they are relative paths.")
@@ -49,6 +50,7 @@ You need to pass an argument to this option to specify the name that your module
.describe("export-all", "Only used when --wrap, this tells UglifyJS to add code to automatically export all globals.")
.describe("lint", "Display some scope warnings")
.describe("v", "Verbose")
.describe("V", "Print version number and exit.")
.alias("p", "prefix")
.alias("o", "output")
@@ -58,9 +60,11 @@ You need to pass an argument to this option to specify the name that your module
.alias("c", "compress")
.alias("d", "define")
.alias("r", "reserved")
.alias("V", "version")
.string("source-map")
.string("source-map-root")
.string("source-map-url")
.string("b")
.string("m")
.string("c")
@@ -74,6 +78,7 @@ You need to pass an argument to this option to specify the name that your module
.boolean("acorn")
.boolean("spidermonkey")
.boolean("lint")
.boolean("V")
.wrap(80)
@@ -82,6 +87,12 @@ You need to pass an argument to this option to specify the name that your module
normalize(ARGS);
if (ARGS.version || ARGS.V) {
var json = require("../package.json");
sys.puts(json.name + ' ' + json.version);
process.exit(0);
}
if (ARGS.ast_help) {
var desc = UglifyJS.describe_ast();
sys.puts(typeof desc == "string" ? desc : JSON.stringify(desc, null, 2));
@@ -268,7 +279,7 @@ output = output.get();
if (SOURCE_MAP) {
fs.writeFileSync(ARGS.source_map, SOURCE_MAP, "utf8");
output += "\n//@ sourceMappingURL=" + ARGS.source_map;
output += "\n/*\n//@ sourceMappingURL=" + (ARGS.source_map_url || ARGS.source_map) + "\n*/";
}
if (OUTPUT_FILE) {

View File

@@ -863,6 +863,11 @@ var AST_Undefined = DEFNODE("Undefined", null, {
value: (function(){}())
}, AST_Atom);
var AST_Hole = DEFNODE("Hole", null, {
$documentation: "A hole in an array",
value: (function(){}())
}, AST_Atom);
var AST_Infinity = DEFNODE("Infinity", null, {
$documentation: "The `Infinity` value",
value: 1/0

View File

@@ -628,7 +628,10 @@ merge(Compressor.prototype, {
case "typeof": return typeof ev(e);
case "void": return void ev(e);
case "~": return ~ev(e);
case "-": return -ev(e);
case "-":
e = ev(e);
if (e === 0) throw def;
return -e;
case "+": return +ev(e);
}
throw def;
@@ -669,12 +672,7 @@ merge(Compressor.prototype, {
});
def(AST_SymbolRef, function(){
var d = this.definition();
if (d && d.constant) {
var orig = d.orig[0];
if (orig) orig = orig.init[0];
orig = orig && orig.value;
if (orig) return ev(orig);
}
if (d && d.constant && d.init) return ev(d.init);
throw def;
});
})(function(node, func){
@@ -1566,6 +1564,9 @@ merge(Compressor.prototype, {
}
break;
case "String":
if (self.args.length == 0) return make_node(AST_String, self, {
value: ""
});
return make_node(AST_Binary, self, {
left: self.args[0],
operator: "+",
@@ -1601,7 +1602,7 @@ merge(Compressor.prototype, {
case "Function":
case "Error":
case "Array":
return make_node(AST_Call, self, self);
return make_node(AST_Call, self, self).transform(compressor);
}
}
}
@@ -1736,7 +1737,8 @@ merge(Compressor.prototype, {
if (self.left instanceof AST_String
&& self.left.value == "undefined"
&& self.right instanceof AST_UnaryPrefix
&& self.right.operator == "typeof") {
&& self.right.operator == "typeof"
&& compressor.option("unsafe")) {
if (!(self.right.expression instanceof AST_SymbolRef)
|| !self.right.expression.undeclared()) {
self.left = self.right.expression;

View File

@@ -340,23 +340,25 @@ function OutputStream(options) {
/* -----[ utils ]----- */
function DEFPRINT(nodetype, generator) {
nodetype.DEFMETHOD("print", function(stream, force_parens){
var self = this;
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(function(){
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
});
} else {
nodetype.DEFMETHOD("_codegen", generator);
};
AST_Node.DEFMETHOD("print", function(stream, force_parens){
var self = this, generator = self._codegen;
stream.push_node(self);
if (force_parens || self.needs_parens(stream)) {
stream.with_parens(function(){
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.pop_node();
});
};
});
} else {
self.add_comments(stream);
self.add_source_map(stream);
generator(self, stream);
}
stream.pop_node();
});
AST_Node.DEFMETHOD("print_to_string", function(options){
var s = OutputStream(options);
@@ -373,6 +375,16 @@ function OutputStream(options) {
if (start && !start._comments_dumped) {
start._comments_dumped = true;
var comments = start.comments_before;
// XXX: ugly fix for https://github.com/mishoo/UglifyJS2/issues/112
// if this node is `return` or `throw`, we cannot allow comments before
// the returned or thrown value.
if (self instanceof AST_Exit &&
self.value && self.value.start.comments_before.length > 0) {
comments = (comments || []).concat(self.value.start.comments_before);
self.value.start.comments_before = [];
}
if (c.test) {
comments = comments.filter(function(comment){
return c.test(comment.value);
@@ -497,11 +509,23 @@ function OutputStream(options) {
PARENS(AST_New, function(output){
var p = output.parent();
if (no_constructor_parens(this, output)
&& (p instanceof AST_Dot // (new Date).getTime()
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
return true;
});
PARENS(AST_Number, function(output){
var p = output.parent();
if (this.getValue() < 0 && p instanceof AST_PropAccess && p.expression === this)
return true;
});
PARENS(AST_NaN, function(output){
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this)
return true;
});
function assign_and_conditional_paren_rules(output) {
var p = output.parent();
// !(a = false) → true
@@ -726,7 +750,7 @@ function OutputStream(options) {
// to the inner IF). This function checks for this case and
// adds the block brackets if needed.
if (!self.body)
return output.semicolon();
return output.force_semicolon();
if (self.body instanceof AST_Do
&& output.option("ie_proof")) {
// https://github.com/mishoo/UglifyJS/issues/#issue/57 IE
@@ -750,7 +774,7 @@ function OutputStream(options) {
}
else break;
}
self.body.print(output);
force_statement(self.body, output);
};
DEFPRINT(AST_If, function(self, output){
output.print("if");
@@ -902,7 +926,7 @@ function OutputStream(options) {
DEFPRINT(AST_New, function(self, output){
output.print("new");
output.space();
AST_Call.prototype.print.call(self, output);
AST_Call.prototype._codegen(self, output);
});
AST_Seq.DEFMETHOD("_do_print", function(output){
@@ -930,7 +954,7 @@ function OutputStream(options) {
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
if (expr instanceof AST_Number) {
if (expr instanceof AST_Number && expr.getValue() >= 0) {
if (!/[xa-f.]/i.test(output.last())) {
output.print(".");
}
@@ -982,8 +1006,7 @@ function OutputStream(options) {
if (len > 0) output.space();
a.forEach(function(exp, i){
if (i) output.comma();
if (!(exp instanceof AST_Undefined))
exp.print(output);
exp.print(output);
});
if (len > 0) output.space();
});
@@ -1034,6 +1057,7 @@ function OutputStream(options) {
DEFPRINT(AST_Undefined, function(self, output){
output.print("void 0");
});
DEFPRINT(AST_Hole, noop);
DEFPRINT(AST_Infinity, function(self, output){
output.print("1/0");
});
@@ -1057,6 +1081,9 @@ function OutputStream(options) {
if (output.option("ascii_only"))
str = output.to_ascii(str);
output.print(str);
var p = output.parent();
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
output.print(" ");
});
function force_statement(stat, output) {

View File

@@ -881,11 +881,14 @@ function parse($TEXT, options) {
};
var function_ = function(in_statement, ctor) {
var name = is("name") ? as_symbol(in_statement
? AST_SymbolDefun
: ctor === AST_Accessor
? AST_SymbolAccessor
: AST_SymbolLambda) : null;
var is_accessor = ctor === AST_Accessor;
var name = (is("name") ? as_symbol(in_statement
? AST_SymbolDefun
: is_accessor
? AST_SymbolAccessor
: AST_SymbolLambda)
: is_accessor && (is("string") || is("num")) ? as_atom_node()
: null);
if (in_statement && !name)
unexpected();
expect("(");
@@ -1128,7 +1131,7 @@ function parse($TEXT, options) {
if (first) first = false; else expect(",");
if (allow_trailing_comma && is("punc", closing)) break;
if (is("punc", ",") && allow_empty) {
a.push(new AST_Undefined({ start: S.token, end: S.token }));
a.push(new AST_Hole({ start: S.token, end: S.token }));
} else {
a.push(expression(false));
}
@@ -1355,7 +1358,7 @@ function parse($TEXT, options) {
left : left,
operator : val,
right : maybe_assign(no_in),
end : peek()
end : prev()
});
}
croak("Invalid assignment");

View File

@@ -84,9 +84,12 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
if (node instanceof AST_Scope) {
node.init_scope_vars(nesting);
var save_scope = node.parent_scope = scope;
var save_labels = labels;
++nesting;
scope = node;
labels = new Dictionary();
descend();
labels = save_labels;
scope = save_scope;
--nesting;
return true; // don't descend again in TreeWalker
@@ -138,7 +141,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(){
|| node instanceof AST_SymbolConst) {
var def = scope.def_variable(node);
def.constant = node instanceof AST_SymbolConst;
def = tw.parent();
def.init = tw.parent().value;
}
else if (node instanceof AST_SymbolCatch) {
// XXX: this is wrong according to ECMA-262 (12.4). the
@@ -340,6 +343,7 @@ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],
eval : false,
sort : false
});
});
@@ -360,12 +364,16 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options){
return true; // don't descend again in TreeWalker
}
if (node instanceof AST_Scope) {
var p = tw.parent();
var p = tw.parent(), a = [];
node.variables.each(function(symbol){
if (options.except.indexOf(symbol.name) < 0) {
to_mangle.push(symbol);
a.push(symbol);
}
});
if (options.sort) a.sort(function(a, b){
return b.references.length - a.references.length;
});
to_mangle.push.apply(to_mangle, a);
return;
}
if (node instanceof AST_Label) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"homepage": "http://lisperator.net/uglifyjs",
"main": "tools/node.js",
"version": "2.2.2",
"version": "2.2.5",
"engines": { "node" : ">=0.4.0" },
"maintainers": [{
"name": "Mihai Bazon",

12
test/compress/arrays.js Normal file
View File

@@ -0,0 +1,12 @@
holes_and_undefined: {
input: {
x = [1, 2, undefined];
y = [1, , 2, ];
z = [1, undefined, 3];
}
expect: {
x=[1,2,void 0];
y=[1,,2];
z=[1,void 0,3];
}
}

View File

@@ -0,0 +1,17 @@
typeof_eq_undefined: {
options = {
comparisons: true,
unsafe: false
};
input: { a = typeof b.c != "undefined" }
expect: { a = "undefined" != typeof b.c }
}
typeof_eq_undefined_unsafe: {
options = {
comparisons: true,
unsafe: true
};
input: { a = typeof b.c != "undefined" }
expect: { a = b.c !== void 0 }
}

View File

@@ -56,6 +56,9 @@ exports.minify = function(files, options) {
inSourceMap : null,
fromString : false,
warnings : false,
mangle : {},
output : null,
compress : {}
});
if (typeof files == "string")
files = [ files ];
@@ -73,16 +76,20 @@ exports.minify = function(files, options) {
});
// 2. compress
toplevel.figure_out_scope();
var sq = UglifyJS.Compressor({
warnings: options.warnings,
});
toplevel = toplevel.transform(sq);
if (options.compress) {
var compress = { warnings: options.warnings };
UglifyJS.merge(compress, options.compress);
toplevel.figure_out_scope();
var sq = UglifyJS.Compressor(compress);
toplevel = toplevel.transform(sq);
}
// 3. mangle
toplevel.figure_out_scope();
toplevel.compute_char_frequency();
toplevel.mangle_names();
if (options.mangle) {
toplevel.figure_out_scope();
toplevel.compute_char_frequency();
toplevel.mangle_names(options.mangle);
}
// 4. output
var map = null;
@@ -95,7 +102,11 @@ exports.minify = function(files, options) {
orig: inMap,
root: options.sourceRoot
});
var stream = UglifyJS.OutputStream({ source_map: map });
var output = { source_map: map };
if (options.output) {
UglifyJS.merge(output, options.output);
}
var stream = UglifyJS.OutputStream(output);
toplevel.print(stream);
return {
code : stream + "",