Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d13aa3954d | ||
|
|
f64539fb76 | ||
|
|
d56ebd7d7b | ||
|
|
3edfe7d0ee | ||
|
|
7f77edadb3 | ||
|
|
a9511dfbe5 | ||
|
|
064e7aa1bb | ||
|
|
46814f88d9 | ||
|
|
4a19802d0c | ||
|
|
1e9f98aa51 | ||
|
|
11e24d53a1 | ||
|
|
0f509f8336 | ||
|
|
a6ed2c84ac | ||
|
|
a1958aad56 | ||
|
|
672699613e | ||
|
|
645d5bdbc5 | ||
|
|
9af2bbffde | ||
|
|
fcd544cc10 | ||
|
|
1e3bc0caa0 | ||
|
|
8227e8795b | ||
|
|
790b3bcdc6 | ||
|
|
d6e6458f68 | ||
|
|
a54b6703c0 |
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.4"
|
||||||
|
- "0.6"
|
||||||
|
- "0.8"
|
||||||
|
- "0.10"
|
||||||
|
- "0.11"
|
||||||
55
README.md
55
README.md
@@ -1,5 +1,6 @@
|
|||||||
UglifyJS 2
|
UglifyJS 2
|
||||||
==========
|
==========
|
||||||
|
[](https://travis-ci.org/mishoo/UglifyJS2)
|
||||||
|
|
||||||
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit.
|
||||||
|
|
||||||
@@ -80,7 +81,7 @@ The available options are:
|
|||||||
cascading statements into sequences. [string]
|
cascading statements into sequences. [string]
|
||||||
--stats Display operations run time on STDERR. [boolean]
|
--stats Display operations run time on STDERR. [boolean]
|
||||||
--acorn Use Acorn for parsing. [boolean]
|
--acorn Use Acorn for parsing. [boolean]
|
||||||
--spidermonkey Assume input fles are SpiderMonkey AST format (as JSON).
|
--spidermonkey Assume input files are SpiderMonkey AST format (as JSON).
|
||||||
[boolean]
|
[boolean]
|
||||||
--self Build itself (UglifyJS2) as a library (implies
|
--self Build itself (UglifyJS2) as a library (implies
|
||||||
--wrap=UglifyJS --export-all) [boolean]
|
--wrap=UglifyJS --export-all) [boolean]
|
||||||
@@ -222,10 +223,11 @@ You can use the `--define` (`-d`) switch in order to declare global
|
|||||||
variables that UglifyJS will assume to be constants (unless defined in
|
variables that UglifyJS will assume to be constants (unless defined in
|
||||||
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
scope). For example if you pass `--define DEBUG=false` then, coupled with
|
||||||
dead code removal UglifyJS will discard the following from the output:
|
dead code removal UglifyJS will discard the following from the output:
|
||||||
|
```javascript
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
console.log("debug stuff");
|
console.log("debug stuff");
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
UglifyJS will warn about the condition being always false and about dropping
|
UglifyJS will warn about the condition being always false and about dropping
|
||||||
unreachable code; for now there is no option to turn off only this specific
|
unreachable code; for now there is no option to turn off only this specific
|
||||||
@@ -234,10 +236,11 @@ warning, you can pass `warnings=false` to turn off *all* warnings.
|
|||||||
Another way of doing that is to declare your globals as constants in a
|
Another way of doing that is to declare your globals as constants in a
|
||||||
separate file and include it into the build. For example you can have a
|
separate file and include it into the build. For example you can have a
|
||||||
`build/defines.js` file with the following:
|
`build/defines.js` file with the following:
|
||||||
|
```javascript
|
||||||
const DEBUG = false;
|
const DEBUG = false;
|
||||||
const PRODUCTION = true;
|
const PRODUCTION = true;
|
||||||
// etc.
|
// etc.
|
||||||
|
```
|
||||||
|
|
||||||
and build your code like this:
|
and build your code like this:
|
||||||
|
|
||||||
@@ -296,7 +299,7 @@ keep only comments that match this regexp. For example `--comments
|
|||||||
|
|
||||||
Note, however, that there might be situations where comments are lost. For
|
Note, however, that there might be situations where comments are lost. For
|
||||||
example:
|
example:
|
||||||
|
```javascript
|
||||||
function f() {
|
function f() {
|
||||||
/** @preserve Foo Bar */
|
/** @preserve Foo Bar */
|
||||||
function g() {
|
function g() {
|
||||||
@@ -304,6 +307,7 @@ example:
|
|||||||
}
|
}
|
||||||
return something();
|
return something();
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Even though it has "@preserve", the comment will be lost because the inner
|
Even though it has "@preserve", the comment will be lost because the inner
|
||||||
function `g` (which is the AST node to which the comment is attached to) is
|
function `g` (which is the AST node to which the comment is attached to) is
|
||||||
@@ -345,8 +349,9 @@ API Reference
|
|||||||
|
|
||||||
Assuming installation via NPM, you can load UglifyJS in your application
|
Assuming installation via NPM, you can load UglifyJS in your application
|
||||||
like this:
|
like this:
|
||||||
|
```javascript
|
||||||
var UglifyJS = require("uglify-js");
|
var UglifyJS = require("uglify-js");
|
||||||
|
```
|
||||||
|
|
||||||
It exports a lot of names, but I'll discuss here the basics that are needed
|
It exports a lot of names, but I'll discuss here the basics that are needed
|
||||||
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
for parsing, mangling and compressing a piece of code. The sequence is (1)
|
||||||
@@ -357,45 +362,49 @@ parse, (2) compress, (3) mangle, (4) generate output code.
|
|||||||
There's a single toplevel function which combines all the steps. If you
|
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`.
|
don't need additional customization, you might want to go with `minify`.
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify("/path/to/file.js");
|
var result = UglifyJS.minify("/path/to/file.js");
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
// if you need to pass code instead of file name
|
// if you need to pass code instead of file name
|
||||||
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
var result = UglifyJS.minify("var b = function () {};", {fromString: true});
|
||||||
|
```
|
||||||
|
|
||||||
You can also compress multiple files:
|
You can also compress multiple files:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ]);
|
||||||
console.log(result.code);
|
console.log(result.code);
|
||||||
|
```
|
||||||
|
|
||||||
To generate a source map:
|
To generate a source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map"
|
outSourceMap: "out.js.map"
|
||||||
});
|
});
|
||||||
console.log(result.code); // minified output
|
console.log(result.code); // minified output
|
||||||
console.log(result.map);
|
console.log(result.map);
|
||||||
|
```
|
||||||
|
|
||||||
Note that the source map is not saved in a file, it's just returned in
|
Note that the source map is not saved in a file, it's just returned in
|
||||||
`result.map`. The value passed for `outSourceMap` is only used to set the
|
`result.map`. The value passed for `outSourceMap` is only used to set the
|
||||||
`file` attribute in the source map (see [the spec][sm-spec]).
|
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||||
|
|
||||||
You can also specify sourceRoot property to be included in source map:
|
You can also specify sourceRoot property to be included in source map:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||||
outSourceMap: "out.js.map",
|
outSourceMap: "out.js.map",
|
||||||
sourceRoot: "http://example.com/src"
|
sourceRoot: "http://example.com/src"
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
If you're compressing compiled JavaScript and have a source map for it, you
|
If you're compressing compiled JavaScript and have a source map for it, you
|
||||||
can use the `inSourceMap` argument:
|
can use the `inSourceMap` argument:
|
||||||
|
```javascript
|
||||||
var result = UglifyJS.minify("compiled.js", {
|
var result = UglifyJS.minify("compiled.js", {
|
||||||
inSourceMap: "compiled.js.map",
|
inSourceMap: "compiled.js.map",
|
||||||
outSourceMap: "minified.js.map"
|
outSourceMap: "minified.js.map"
|
||||||
});
|
});
|
||||||
// same as before, it returns `code` and `map`
|
// same as before, it returns `code` and `map`
|
||||||
|
```
|
||||||
|
|
||||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||||
no sense otherwise).
|
no sense otherwise).
|
||||||
@@ -425,8 +434,9 @@ Following there's more detailed API info, in case the `minify` function is
|
|||||||
too simple for your needs.
|
too simple for your needs.
|
||||||
|
|
||||||
#### The parser
|
#### The parser
|
||||||
|
```javascript
|
||||||
var toplevel_ast = UglifyJS.parse(code, options);
|
var toplevel_ast = UglifyJS.parse(code, options);
|
||||||
|
```
|
||||||
|
|
||||||
`options` is optional and if present it must be an object. The following
|
`options` is optional and if present it must be an object. The following
|
||||||
properties are available:
|
properties are available:
|
||||||
@@ -440,7 +450,7 @@ properties are available:
|
|||||||
The last two options are useful when you'd like to minify multiple files and
|
The last two options are useful when you'd like to minify multiple files and
|
||||||
get a single file as the output and a proper source map. Our CLI tool does
|
get a single file as the output and a proper source map. Our CLI tool does
|
||||||
something like this:
|
something like this:
|
||||||
|
```javascript
|
||||||
var toplevel = null;
|
var toplevel = null;
|
||||||
files.forEach(function(file){
|
files.forEach(function(file){
|
||||||
var code = fs.readFileSync(file);
|
var code = fs.readFileSync(file);
|
||||||
@@ -449,6 +459,7 @@ something like this:
|
|||||||
toplevel: toplevel
|
toplevel: toplevel
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
```
|
||||||
|
|
||||||
After this, we have in `toplevel` a big AST containing all our files, with
|
After this, we have in `toplevel` a big AST containing all our files, with
|
||||||
each token having proper information about where it came from.
|
each token having proper information about where it came from.
|
||||||
@@ -462,15 +473,17 @@ referenced, if it is a global or not, if a function is using `eval` or the
|
|||||||
`with` statement etc. I will discuss this some place else, for now what's
|
`with` statement etc. I will discuss this some place else, for now what's
|
||||||
important to know is that you need to call the following before doing
|
important to know is that you need to call the following before doing
|
||||||
anything with the tree:
|
anything with the tree:
|
||||||
|
```javascript
|
||||||
toplevel.figure_out_scope()
|
toplevel.figure_out_scope()
|
||||||
|
```
|
||||||
|
|
||||||
#### Compression
|
#### Compression
|
||||||
|
|
||||||
Like this:
|
Like this:
|
||||||
|
```javascript
|
||||||
var compressor = UglifyJS.Compressor(options);
|
var compressor = UglifyJS.Compressor(options);
|
||||||
var compressed_ast = toplevel.transform(compressor);
|
var compressed_ast = toplevel.transform(compressor);
|
||||||
|
```
|
||||||
|
|
||||||
The `options` can be missing. Available options are discussed above in
|
The `options` can be missing. Available options are discussed above in
|
||||||
“Compressor options”. Defaults should lead to best compression in most
|
“Compressor options”. Defaults should lead to best compression in most
|
||||||
@@ -486,23 +499,26 @@ the compressor might drop unused variables / unreachable code and this might
|
|||||||
change the number of identifiers or their position). Optionally, you can
|
change the number of identifiers or their position). Optionally, you can
|
||||||
call a trick that helps after Gzip (counting character frequency in
|
call a trick that helps after Gzip (counting character frequency in
|
||||||
non-mangleable words). Example:
|
non-mangleable words). Example:
|
||||||
|
```javascript
|
||||||
compressed_ast.figure_out_scope();
|
compressed_ast.figure_out_scope();
|
||||||
compressed_ast.compute_char_frequency();
|
compressed_ast.compute_char_frequency();
|
||||||
compressed_ast.mangle_names();
|
compressed_ast.mangle_names();
|
||||||
|
```
|
||||||
|
|
||||||
#### Generating output
|
#### Generating output
|
||||||
|
|
||||||
AST nodes have a `print` method that takes an output stream. Essentially,
|
AST nodes have a `print` method that takes an output stream. Essentially,
|
||||||
to generate code you do this:
|
to generate code you do this:
|
||||||
|
```javascript
|
||||||
var stream = UglifyJS.OutputStream(options);
|
var stream = UglifyJS.OutputStream(options);
|
||||||
compressed_ast.print(stream);
|
compressed_ast.print(stream);
|
||||||
var code = stream.toString(); // this is your minified code
|
var code = stream.toString(); // this is your minified code
|
||||||
|
```
|
||||||
|
|
||||||
or, for a shortcut you can do:
|
or, for a shortcut you can do:
|
||||||
|
```javascript
|
||||||
var code = compressed_ast.print_to_string(options);
|
var code = compressed_ast.print_to_string(options);
|
||||||
|
```
|
||||||
|
|
||||||
As usual, `options` is optional. The output stream accepts a lot of otions,
|
As usual, `options` is optional. The output stream accepts a lot of otions,
|
||||||
most of them documented above in section “Beautifier options”. The two
|
most of them documented above in section “Beautifier options”. The two
|
||||||
@@ -540,7 +556,7 @@ to be a `SourceMap` object (which is a thin wrapper on top of the
|
|||||||
[source-map][source-map] library).
|
[source-map][source-map] library).
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
```javascript
|
||||||
var source_map = UglifyJS.SourceMap(source_map_options);
|
var source_map = UglifyJS.SourceMap(source_map_options);
|
||||||
var stream = UglifyJS.OutputStream({
|
var stream = UglifyJS.OutputStream({
|
||||||
...
|
...
|
||||||
@@ -550,6 +566,7 @@ Example:
|
|||||||
|
|
||||||
var code = stream.toString();
|
var code = stream.toString();
|
||||||
var map = source_map.toString(); // json output for your source map
|
var map = source_map.toString(); // json output for your source map
|
||||||
|
```
|
||||||
|
|
||||||
The `source_map_options` (optional) can contain the following properties:
|
The `source_map_options` (optional) can contain the following properties:
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ because of dead code removal or cascading statements into sequences.")
|
|||||||
|
|
||||||
.describe("stats", "Display operations run time on STDERR.")
|
.describe("stats", "Display operations run time on STDERR.")
|
||||||
.describe("acorn", "Use Acorn for parsing.")
|
.describe("acorn", "Use Acorn for parsing.")
|
||||||
.describe("spidermonkey", "Assume input fles are SpiderMonkey AST format (as JSON).")
|
.describe("spidermonkey", "Assume input files are SpiderMonkey AST format (as JSON).")
|
||||||
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
.describe("self", "Build itself (UglifyJS2) as a library (implies --wrap=UglifyJS --export-all)")
|
||||||
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
.describe("wrap", "Embed everything in a big function, making the “exports” and “global” variables available. \
|
||||||
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
You need to pass an argument to this option to specify the name that your module will take when included in, say, a browser.")
|
||||||
|
|||||||
@@ -1589,6 +1589,45 @@ merge(Compressor.prototype, {
|
|||||||
operator: "+",
|
operator: "+",
|
||||||
right: make_node(AST_String, self, { value: "" })
|
right: make_node(AST_String, self, { value: "" })
|
||||||
});
|
});
|
||||||
|
case "Function":
|
||||||
|
if (self.args[self.args.length - 1] instanceof AST_String) {
|
||||||
|
// quite a corner-case, but we can handle it:
|
||||||
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
||||||
|
// if the code argument is a constant, then we can minify it.
|
||||||
|
try {
|
||||||
|
var code = "(function(" + self.args.slice(0, -1).map(function(arg){
|
||||||
|
return arg.value;
|
||||||
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})()";
|
||||||
|
var ast = parse(code);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
var comp = new Compressor(compressor.options);
|
||||||
|
ast = ast.transform(comp);
|
||||||
|
ast.figure_out_scope();
|
||||||
|
ast.mangle_names();
|
||||||
|
var fun = ast.body[0].body.expression;
|
||||||
|
var args = fun.argnames.map(function(arg, i){
|
||||||
|
return make_node(AST_String, self.args[i], {
|
||||||
|
value: arg.print_to_string()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var code = OutputStream();
|
||||||
|
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
|
||||||
|
code = code.toString().replace(/^\{|\}$/g, "");
|
||||||
|
args.push(make_node(AST_String, self.args[self.args.length - 1], {
|
||||||
|
value: code
|
||||||
|
}));
|
||||||
|
self.args = args;
|
||||||
|
return self;
|
||||||
|
} catch(ex) {
|
||||||
|
if (ex instanceof JS_Parse_Error) {
|
||||||
|
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
|
||||||
|
compressor.warn(ex.toString());
|
||||||
|
} else {
|
||||||
|
console.log(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
else if (exp instanceof AST_Dot && exp.property == "toString" && self.args.length == 0) {
|
||||||
@@ -1726,8 +1765,8 @@ merge(Compressor.prototype, {
|
|||||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||||
|
|
||||||
OPT(AST_Binary, function(self, compressor){
|
OPT(AST_Binary, function(self, compressor){
|
||||||
function reverse(op) {
|
function reverse(op, force) {
|
||||||
if (!(self.left.has_side_effects() || self.right.has_side_effects())) {
|
if (force || !(self.left.has_side_effects() || self.right.has_side_effects())) {
|
||||||
if (op) self.operator = op;
|
if (op) self.operator = op;
|
||||||
var tmp = self.left;
|
var tmp = self.left;
|
||||||
self.left = self.right;
|
self.left = self.right;
|
||||||
@@ -1737,7 +1776,10 @@ merge(Compressor.prototype, {
|
|||||||
if (commutativeOperators(self.operator)) {
|
if (commutativeOperators(self.operator)) {
|
||||||
if (self.right instanceof AST_Constant
|
if (self.right instanceof AST_Constant
|
||||||
&& !(self.left instanceof AST_Constant)) {
|
&& !(self.left instanceof AST_Constant)) {
|
||||||
reverse();
|
// if right is a constant, whatever side effects the
|
||||||
|
// left side might have could not influence the
|
||||||
|
// result. hence, force switch.
|
||||||
|
reverse(null, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self = self.lift_sequences(compressor);
|
self = self.lift_sequences(compressor);
|
||||||
@@ -1758,8 +1800,8 @@ merge(Compressor.prototype, {
|
|||||||
&& compressor.option("unsafe")) {
|
&& compressor.option("unsafe")) {
|
||||||
if (!(self.right.expression instanceof AST_SymbolRef)
|
if (!(self.right.expression instanceof AST_SymbolRef)
|
||||||
|| !self.right.expression.undeclared()) {
|
|| !self.right.expression.undeclared()) {
|
||||||
self.left = self.right.expression;
|
self.right = self.right.expression;
|
||||||
self.right = make_node(AST_Undefined, self.left).optimize(compressor);
|
self.left = make_node(AST_Undefined, self.left).optimize(compressor);
|
||||||
if (self.operator.length == 2) self.operator += "=";
|
if (self.operator.length == 2) self.operator += "=";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1962,7 +2004,8 @@ merge(Compressor.prototype, {
|
|||||||
var prop = self.property;
|
var prop = self.property;
|
||||||
if (prop instanceof AST_String && compressor.option("properties")) {
|
if (prop instanceof AST_String && compressor.option("properties")) {
|
||||||
prop = prop.getValue();
|
prop = prop.getValue();
|
||||||
if (is_identifier(prop) || compressor.option("screw_ie8")) {
|
if ((compressor.option("screw_ie8") && RESERVED_WORDS(prop))
|
||||||
|
|| (!(RESERVED_WORDS(prop)) && is_identifier_string(prop))) {
|
||||||
return make_node(AST_Dot, self, {
|
return make_node(AST_Dot, self, {
|
||||||
expression : self.expression,
|
expression : self.expression,
|
||||||
property : prop
|
property : prop
|
||||||
|
|||||||
@@ -351,7 +351,9 @@ function OutputStream(options) {
|
|||||||
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
AST_Node.DEFMETHOD("print", function(stream, force_parens){
|
||||||
var self = this, generator = self._codegen;
|
var self = this, generator = self._codegen;
|
||||||
stream.push_node(self);
|
stream.push_node(self);
|
||||||
if (force_parens || self.needs_parens(stream)) {
|
var needs_parens = self.needs_parens(stream);
|
||||||
|
var fc = self instanceof AST_Function && !stream.option("beautify");
|
||||||
|
if (force_parens || (needs_parens && !fc)) {
|
||||||
stream.with_parens(function(){
|
stream.with_parens(function(){
|
||||||
self.add_comments(stream);
|
self.add_comments(stream);
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
@@ -359,6 +361,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
self.add_comments(stream);
|
self.add_comments(stream);
|
||||||
|
if (needs_parens && fc) stream.print("!");
|
||||||
self.add_source_map(stream);
|
self.add_source_map(stream);
|
||||||
generator(self, stream);
|
generator(self, stream);
|
||||||
}
|
}
|
||||||
|
|||||||
22
lib/parse.js
22
lib/parse.js
@@ -167,6 +167,17 @@ function is_identifier_char(ch) {
|
|||||||
;
|
;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function is_identifier_string(str){
|
||||||
|
var i = str.length;
|
||||||
|
if (i == 0) return false;
|
||||||
|
if (is_digit(str.charCodeAt(0))) return false;
|
||||||
|
while (--i >= 0) {
|
||||||
|
if (!is_identifier_char(str.charAt(i)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
if (RE_HEX_NUMBER.test(num)) {
|
if (RE_HEX_NUMBER.test(num)) {
|
||||||
return parseInt(num.substr(2), 16);
|
return parseInt(num.substr(2), 16);
|
||||||
@@ -1330,15 +1341,8 @@ function parse($TEXT, options) {
|
|||||||
|
|
||||||
function is_assignable(expr) {
|
function is_assignable(expr) {
|
||||||
if (!options.strict) return true;
|
if (!options.strict) return true;
|
||||||
switch (expr[0]+"") {
|
if (expr instanceof AST_This) return false;
|
||||||
case "dot":
|
return (expr instanceof AST_PropAccess || expr instanceof AST_Symbol);
|
||||||
case "sub":
|
|
||||||
case "new":
|
|
||||||
case "call":
|
|
||||||
return true;
|
|
||||||
case "name":
|
|
||||||
return expr[1] != "this";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var maybe_assign = function(no_in) {
|
var maybe_assign = function(no_in) {
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Tree transformer helpers.
|
// Tree transformer helpers.
|
||||||
// XXX: eventually I should refactor the compressor to use this infrastructure.
|
|
||||||
|
|
||||||
function TreeTransformer(before, after) {
|
function TreeTransformer(before, after) {
|
||||||
TreeWalker.call(this);
|
TreeWalker.call(this);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"homepage": "http://lisperator.net/uglifyjs",
|
"homepage": "http://lisperator.net/uglifyjs",
|
||||||
"main": "tools/node.js",
|
"main": "tools/node.js",
|
||||||
"version": "2.3.0",
|
"version": "2.3.3",
|
||||||
"engines": { "node" : ">=0.4.0" },
|
"engines": { "node" : ">=0.4.0" },
|
||||||
"maintainers": [{
|
"maintainers": [{
|
||||||
"name": "Mihai Bazon",
|
"name": "Mihai Bazon",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
typeof_eq_undefined: {
|
typeof_eq_undefined: {
|
||||||
options = {
|
options = {
|
||||||
comparisons: true,
|
comparisons: true
|
||||||
unsafe: false
|
|
||||||
};
|
};
|
||||||
input: { a = typeof b.c != "undefined" }
|
input: { a = typeof b.c != "undefined" }
|
||||||
expect: { a = "undefined" != typeof b.c }
|
expect: { a = "undefined" != typeof b.c }
|
||||||
@@ -13,5 +12,14 @@ typeof_eq_undefined_unsafe: {
|
|||||||
unsafe: true
|
unsafe: true
|
||||||
};
|
};
|
||||||
input: { a = typeof b.c != "undefined" }
|
input: { a = typeof b.c != "undefined" }
|
||||||
expect: { a = b.c !== void 0 }
|
expect: { a = void 0 !== b.c }
|
||||||
|
}
|
||||||
|
|
||||||
|
typeof_eq_undefined_unsafe2: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
unsafe: true
|
||||||
|
};
|
||||||
|
input: { a = "undefined" != typeof b.c }
|
||||||
|
expect: { a = void 0 !== b.c }
|
||||||
}
|
}
|
||||||
|
|||||||
48
test/compress/issue-143.js
Normal file
48
test/compress/issue-143.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* There was an incorrect sort behaviour documented in issue #143:
|
||||||
|
* (x = f(…)) <= x → x >= (x = f(…))
|
||||||
|
*
|
||||||
|
* For example, let the equation be:
|
||||||
|
* (a = parseInt('100')) <= a
|
||||||
|
*
|
||||||
|
* If a was an integer and has the value of 99,
|
||||||
|
* (a = parseInt('100')) <= a → 100 <= 100 → true
|
||||||
|
*
|
||||||
|
* When transformed incorrectly:
|
||||||
|
* a >= (a = parseInt('100')) → 99 >= 100 → false
|
||||||
|
*/
|
||||||
|
|
||||||
|
tranformation_sort_order_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) == a }
|
||||||
|
expect: { (a = parseInt('100')) == a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_unequal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) != a }
|
||||||
|
expect: { (a = parseInt('100')) != a }
|
||||||
|
}
|
||||||
|
|
||||||
|
tranformation_sort_order_lesser_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) <= a }
|
||||||
|
expect: { (a = parseInt('100')) <= a }
|
||||||
|
}
|
||||||
|
tranformation_sort_order_greater_or_equal: {
|
||||||
|
options = {
|
||||||
|
comparisons: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
input: { (a = parseInt('100')) >= a }
|
||||||
|
expect: { (a = parseInt('100')) >= a }
|
||||||
|
}
|
||||||
@@ -17,10 +17,18 @@ dot_properties: {
|
|||||||
input: {
|
input: {
|
||||||
a["foo"] = "bar";
|
a["foo"] = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
|
a["1_1"] = "foo";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,9 +40,15 @@ dot_properties_es5: {
|
|||||||
input: {
|
input: {
|
||||||
a["foo"] = "bar";
|
a["foo"] = "bar";
|
||||||
a["if"] = "if";
|
a["if"] = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a["\u0EB3"] = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
a.foo = "bar";
|
a.foo = "bar";
|
||||||
a.if = "if";
|
a.if = "if";
|
||||||
|
a["*"] = "asterisk";
|
||||||
|
a.\u0EB3 = "unicode";
|
||||||
|
a[""] = "whitespace";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user