Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41be8632d3 | ||
|
|
bee01dc1be | ||
|
|
12f71e01d0 | ||
|
|
3a72deacab | ||
|
|
fc8314e810 | ||
|
|
11dffe950e | ||
|
|
6f45928a73 | ||
|
|
afb7faa6fa | ||
|
|
6aa56f92fe | ||
|
|
4fe4257c69 | ||
|
|
a5e75c5a21 |
15
README.md
15
README.md
@@ -322,6 +322,7 @@ 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
|
||||
|
||||
@@ -342,6 +343,14 @@ 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
|
||||
`file` attribute in the source map (see [the spec][sm-spec]).
|
||||
|
||||
You can also specify sourceRoot property to be included in source map:
|
||||
|
||||
var result = UglifyJS.minify([ "file1.js", "file2.js", "file3.js" ], {
|
||||
outSourceMap: "out.js.map",
|
||||
sourceRoot: "http://example.com/src"
|
||||
});
|
||||
|
||||
|
||||
If you're compressing compiled JavaScript and have a source map for it, you
|
||||
can use the `inSourceMap` argument:
|
||||
|
||||
@@ -354,6 +363,12 @@ can use the `inSourceMap` argument:
|
||||
The `inSourceMap` is only used if you also request `outSourceMap` (it makes
|
||||
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.
|
||||
|
||||
We could add more options to `UglifyJS.minify` — if you need additional
|
||||
functionality please suggest!
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ if (ARGS.comments) {
|
||||
var type = comment.type;
|
||||
if (type == "comment2") {
|
||||
// multiline comment
|
||||
return /@preserve|@license|@cc_on/i.test(test);
|
||||
return /@preserve|@license|@cc_on/i.test(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,10 +929,10 @@ TreeWalker.prototype = {
|
||||
} else {
|
||||
for (var i = stack.length; --i >= 0;) {
|
||||
var x = stack[i];
|
||||
if (x instanceof AST_Switch) return x;
|
||||
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
||||
return (x.body instanceof AST_BlockStatement ? x.body : x);
|
||||
}
|
||||
if (x instanceof AST_Switch
|
||||
|| x instanceof AST_For
|
||||
|| x instanceof AST_ForIn
|
||||
|| x instanceof AST_DWLoop) return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,14 @@ merge(Compressor.prototype, {
|
||||
return false;
|
||||
};
|
||||
|
||||
function loop_body(x) {
|
||||
if (x instanceof AST_Switch) return x;
|
||||
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
|
||||
return (x.body instanceof AST_BlockStatement ? x.body : x);
|
||||
}
|
||||
return x;
|
||||
};
|
||||
|
||||
function tighten_body(statements, compressor) {
|
||||
var CHANGED;
|
||||
do {
|
||||
@@ -303,8 +311,13 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
var ab = aborts(stat.body);
|
||||
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
|
||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||
if (ab.label) {
|
||||
remove(ab.label.thedef.references, ab.label);
|
||||
}
|
||||
CHANGED = true;
|
||||
var body = as_statement_array(stat.body).slice(0, -1);
|
||||
stat = stat.clone();
|
||||
@@ -320,8 +333,13 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
var ab = aborts(stat.alternative);
|
||||
var lct = ab instanceof AST_LoopControl ? compressor.loopcontrol_target(ab.label) : null;
|
||||
if (ab && ((ab instanceof AST_Return && !ab.value && in_lambda)
|
||||
|| (ab instanceof AST_Continue && self === compressor.loopcontrol_target(ab.label)))) {
|
||||
|| (ab instanceof AST_Continue && self === loop_body(lct))
|
||||
|| (ab instanceof AST_Break && lct instanceof AST_BlockStatement && self === lct))) {
|
||||
if (ab.label) {
|
||||
remove(ab.label.thedef.references, ab.label);
|
||||
}
|
||||
CHANGED = true;
|
||||
stat = stat.clone();
|
||||
stat.body = make_node(AST_BlockStatement, stat.body, {
|
||||
@@ -347,11 +365,26 @@ merge(Compressor.prototype, {
|
||||
function eliminate_dead_code(statements, compressor) {
|
||||
var has_quit = false;
|
||||
var orig = statements.length;
|
||||
var self = compressor.self();
|
||||
statements = statements.reduce(function(a, stat){
|
||||
if (has_quit) {
|
||||
extract_declarations_from_unreachable_code(compressor, stat, a);
|
||||
} else {
|
||||
a.push(stat);
|
||||
if (stat instanceof AST_LoopControl) {
|
||||
var lct = compressor.loopcontrol_target(stat.label);
|
||||
if ((stat instanceof AST_Break
|
||||
&& lct instanceof AST_BlockStatement
|
||||
&& loop_body(lct) === self) || (stat instanceof AST_Continue
|
||||
&& loop_body(lct) === self)) {
|
||||
if (stat.label) {
|
||||
remove(stat.label.thedef.references, stat.label);
|
||||
}
|
||||
} else {
|
||||
a.push(stat);
|
||||
}
|
||||
} else {
|
||||
a.push(stat);
|
||||
}
|
||||
if (aborts(stat)) has_quit = true;
|
||||
}
|
||||
return a;
|
||||
@@ -708,9 +741,10 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
def(AST_SimpleStatement, function(){
|
||||
if (this.body instanceof AST_Function) return false;
|
||||
return this.body.has_side_effects();
|
||||
});
|
||||
def(AST_Defun, function(){ return true });
|
||||
def(AST_Function, function(){ return false });
|
||||
def(AST_Binary, function(){
|
||||
return this.left.has_side_effects()
|
||||
|| this.right.has_side_effects();
|
||||
@@ -795,6 +829,10 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
OPT(AST_LabeledStatement, function(self, compressor){
|
||||
if (self.body instanceof AST_Break
|
||||
&& compressor.loopcontrol_target(self.body.label) === self.body) {
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
return self.label.references.length == 0 ? self.body : self;
|
||||
});
|
||||
|
||||
@@ -1227,7 +1265,7 @@ merge(Compressor.prototype, {
|
||||
var last_branch = self.body[self.body.length - 1];
|
||||
if (last_branch) {
|
||||
var stat = last_branch.body[last_branch.body.length - 1]; // last statement
|
||||
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat.label) === self)
|
||||
if (stat instanceof AST_Break && loop_body(compressor.loopcontrol_target(stat.label)) === self)
|
||||
last_branch.body.pop();
|
||||
}
|
||||
return self;
|
||||
@@ -1317,7 +1355,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("side_effects")) {
|
||||
if (self.expression instanceof AST_Function
|
||||
&& self.args.length == 0
|
||||
&& !self.expression.has_side_effects()) {
|
||||
&& !AST_Block.prototype.has_side_effects.call(self.expression)) {
|
||||
return make_node(AST_Undefined, self).transform(compressor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,12 @@ function string_template(text, props) {
|
||||
});
|
||||
};
|
||||
|
||||
function remove(array, el) {
|
||||
for (var i = array.length; --i >= 0;) {
|
||||
if (array[i] === el) array.splice(i, 1);
|
||||
}
|
||||
};
|
||||
|
||||
function mergeSort(array, cmp) {
|
||||
if (array.length < 2) return array.slice();
|
||||
function merge(a, b) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"homepage": "http://lisperator.net/uglifyjs",
|
||||
"main": "tools/node.js",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.2",
|
||||
"engines": { "node" : ">=0.4.0" },
|
||||
"maintainers": [{
|
||||
"name": "Mihai Bazon",
|
||||
|
||||
163
test/compress/labels.js
Normal file
163
test/compress/labels.js
Normal file
@@ -0,0 +1,163 @@
|
||||
labels_1: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
out: {
|
||||
if (foo) break out;
|
||||
console.log("bar");
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
foo || console.log("bar");
|
||||
}
|
||||
}
|
||||
|
||||
labels_2: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
out: {
|
||||
if (foo) print("stuff");
|
||||
else break out;
|
||||
console.log("here");
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
if (foo) {
|
||||
print("stuff");
|
||||
console.log("here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels_3: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
for (var i = 0; i < 5; ++i) {
|
||||
if (i < 3) continue;
|
||||
console.log(i);
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
for (var i = 0; i < 5; ++i)
|
||||
i < 3 || console.log(i);
|
||||
}
|
||||
}
|
||||
|
||||
labels_4: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
out: for (var i = 0; i < 5; ++i) {
|
||||
if (i < 3) continue out;
|
||||
console.log(i);
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
for (var i = 0; i < 5; ++i)
|
||||
i < 3 || console.log(i);
|
||||
}
|
||||
}
|
||||
|
||||
labels_5: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
// should keep the break-s in the following
|
||||
input: {
|
||||
while (foo) {
|
||||
if (bar) break;
|
||||
console.log("foo");
|
||||
}
|
||||
out: while (foo) {
|
||||
if (bar) break out;
|
||||
console.log("foo");
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
while (foo) {
|
||||
if (bar) break;
|
||||
console.log("foo");
|
||||
}
|
||||
out: while (foo) {
|
||||
if (bar) break out;
|
||||
console.log("foo");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels_6: {
|
||||
input: {
|
||||
out: break out;
|
||||
};
|
||||
expect: {}
|
||||
}
|
||||
|
||||
labels_7: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
while (foo) {
|
||||
x();
|
||||
y();
|
||||
continue;
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
while (foo) {
|
||||
x();
|
||||
y();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels_8: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
while (foo) {
|
||||
x();
|
||||
y();
|
||||
break;
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
while (foo) {
|
||||
x();
|
||||
y();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels_9: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
out: while (foo) {
|
||||
x();
|
||||
y();
|
||||
continue out;
|
||||
z();
|
||||
k();
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
while (foo) {
|
||||
x();
|
||||
y();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels_10: {
|
||||
options = { if_return: true, conditionals: true, dead_code: true };
|
||||
input: {
|
||||
out: while (foo) {
|
||||
x();
|
||||
y();
|
||||
break out;
|
||||
z();
|
||||
k();
|
||||
}
|
||||
};
|
||||
expect: {
|
||||
out: while (foo) {
|
||||
x();
|
||||
y();
|
||||
break out;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,12 +73,13 @@ function run_compress_tests() {
|
||||
var cmp = new U.Compressor(options, true);
|
||||
var expect = make_code(as_toplevel(test.expect), false);
|
||||
var input = as_toplevel(test.input);
|
||||
var input_code = make_code(test.input);
|
||||
var output = input.transform(cmp);
|
||||
output.figure_out_scope();
|
||||
output = make_code(output, false);
|
||||
if (expect != output) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||
input: make_code(test.input),
|
||||
input: input_code,
|
||||
output: output,
|
||||
expected: expect
|
||||
});
|
||||
|
||||
@@ -1,27 +1,25 @@
|
||||
var save_stderr = process.stderr;
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
|
||||
// discard annoying NodeJS warning ("path.existsSync is now called `fs.existsSync`.")
|
||||
var devnull = fs.createWriteStream("/dev/null");
|
||||
process.__defineGetter__("stderr", function(){
|
||||
return devnull;
|
||||
});
|
||||
// Avoid NodeJS warning.
|
||||
//
|
||||
// There's a --no-deprecation command line argument supported by
|
||||
// NodeJS, but that's tricky to use, so I'd like to set it from the
|
||||
// program itself. Turns out you need to set `process.noDeprecation`,
|
||||
// but by the time you can set that the `path` module is already
|
||||
// loaded and `path.existsSync` is already changed to display that
|
||||
// warning, therefore here's the poor solution:
|
||||
path.existsSync = fs.existsSync;
|
||||
|
||||
var vm = require("vm");
|
||||
var sys = require("util");
|
||||
var path = require("path");
|
||||
|
||||
var UglifyJS = vm.createContext({
|
||||
sys : sys,
|
||||
console : console,
|
||||
|
||||
MOZ_SourceMap : require("source-map")
|
||||
});
|
||||
|
||||
process.__defineGetter__("stderr", function(){
|
||||
return save_stderr;
|
||||
});
|
||||
|
||||
function load_global(file) {
|
||||
file = path.resolve(path.dirname(module.filename), file);
|
||||
try {
|
||||
@@ -65,7 +63,9 @@ for (var i in UglifyJS) {
|
||||
exports.minify = function(files, options) {
|
||||
options = UglifyJS.defaults(options, {
|
||||
outSourceMap : null,
|
||||
sourceRoot : null,
|
||||
inSourceMap : null,
|
||||
fromString : false,
|
||||
warnings : false,
|
||||
});
|
||||
if (typeof files == "string")
|
||||
@@ -74,9 +74,11 @@ exports.minify = function(files, options) {
|
||||
// 1. parse
|
||||
var toplevel = null;
|
||||
files.forEach(function(file){
|
||||
var code = fs.readFileSync(file, "utf8");
|
||||
var code = options.fromString
|
||||
? file
|
||||
: fs.readFileSync(file, "utf8");
|
||||
toplevel = UglifyJS.parse(code, {
|
||||
filename: file,
|
||||
filename: options.fromString ? "?" : file,
|
||||
toplevel: toplevel
|
||||
});
|
||||
});
|
||||
@@ -101,7 +103,8 @@ exports.minify = function(files, options) {
|
||||
}
|
||||
if (options.outSourceMap) map = UglifyJS.SourceMap({
|
||||
file: options.outSourceMap,
|
||||
orig: inMap
|
||||
orig: inMap,
|
||||
root: options.sourceRoot
|
||||
});
|
||||
var stream = UglifyJS.OutputStream({ source_map: map });
|
||||
toplevel.print(stream);
|
||||
|
||||
Reference in New Issue
Block a user