a shy attempt to obey width in the beautifier; added bracketize option to always print brackets around if/do/while/for statements; export more options via the CLI

This commit is contained in:
Mihai Bazon
2012-10-02 11:00:47 +03:00
parent 896444482a
commit 9e5dd81f1e
5 changed files with 117 additions and 49 deletions

View File

@@ -7,35 +7,35 @@ var optimist = require("optimist");
var fs = require("fs");
var ARGS = optimist
.usage("$0 [options] input1.js [input2.js ...]\n\
Maximum compression settings are on by default.\n\
Use a single dash to read input from the standard input.\
")
.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("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.")
.describe("o", "Output file (default STDOUT)")
.describe("b", "Beautify output")
.describe("m", "Don't mangle names")
.describe("c", "Disable compressor, or pass compressor options. \
.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.")
.describe("o", "Output file (default STDOUT).")
.describe("b", "Beautify output/specify output options.")
.describe("m", "Mangle names/pass mangler options.")
.describe("c", "Enable compressor/pass compressor options. \
Pass options like -c hoist_vars=false,if_return=false. \
Use -c with no argument if you want to disable the squeezer entirely")
Use -c with no argument if you want to disable the squeezer entirely.")
.describe("stats", "Display operations run time on STDERR")
.describe("stats", "Display operations run time on STDERR.")
.describe("v", "Verbose")
.alias("p", "prefix")
.alias("o", "output")
.alias("v", "verbose")
.alias("b", "beautify")
.alias("c", "options")
.alias("m", "no-mangle")
.alias("m", "mangle")
.alias("c", "compress")
.boolean("b")
.string("b")
.string("m")
.string("c")
.boolean("v")
.boolean("stats")
.boolean("m")
.string("c")
.wrap(80)
@@ -45,6 +45,7 @@ Use -c with no argument if you want to disable the squeezer entirely")
function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i];
delete o[i];
}
}
@@ -55,15 +56,30 @@ if (ARGS.h || ARGS.help) {
process.exit(0);
}
var COMPRESSOR_OPTIONS = {};
if (ARGS.c && ARGS.c !== true) {
ARGS.c.replace(/^\s+|\s+$/g).split(/\s*,+\s*/).forEach(function(opt){
var a = opt.split(/\s*=\s*/);
COMPRESSOR_OPTIONS[a[0]] = new Function("return(" + a[1] + ")")();
});
normalize(COMPRESSOR_OPTIONS);
function getOptions(x) {
x = ARGS[x];
if (!x) return null;
var ret = {};
if (x !== true) {
x.replace(/^\s+|\s+$/g).split(/\s*,+\s*/).forEach(function(opt){
var a = opt.split(/\s*[=:]\s*/);
ret[a[0]] = a.length > 1 ? new Function("return(" + a[1] + ")")() : true;
});
normalize(ret)
}
return ret;
}
var COMPRESS = getOptions("c");
var MANGLE = getOptions("m");
var BEAUTIFY = getOptions("b");
var OUTPUT_OPTIONS = {
beautify: BEAUTIFY ? true : false
};
if (BEAUTIFY)
UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY);
var files = ARGS._.slice();
var ORIG_MAP = ARGS.in_source_map;
@@ -103,10 +119,19 @@ var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
orig: ORIG_MAP,
}) : null;
var output = UglifyJS.OutputStream({
beautify: ARGS.b,
source_map: SOURCE_MAP
});
OUTPUT_OPTIONS.source_map = SOURCE_MAP;
try {
var output = UglifyJS.OutputStream(OUTPUT_OPTIONS);
var compressor = COMPRESS && UglifyJS.Compressor(COMPRESS);
} catch(ex) {
if (ex instanceof UglifyJS.DefaultsError) {
sys.error(ex.msg);
sys.error("Supported options:");
sys.error(sys.inspect(ex.defs));
process.exit(1);
}
}
files.forEach(function(file) {
var code = read_whole_file(file);
@@ -121,7 +146,7 @@ files.forEach(function(file) {
});
});
var SCOPE_IS_NEEDED = ARGS.c !== true || !ARGS.m;
var SCOPE_IS_NEEDED = COMPRESS || MANGLE;
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
@@ -129,9 +154,8 @@ if (SCOPE_IS_NEEDED) {
});
}
if (ARGS.c !== true) {
if (COMPRESS) {
time_it("squeeze", function(){
var compressor = UglifyJS.Compressor(COMPRESSOR_OPTIONS);
TOPLEVEL = TOPLEVEL.transform(compressor);
});
}
@@ -139,15 +163,15 @@ if (ARGS.c !== true) {
if (SCOPE_IS_NEEDED) {
time_it("scope", function(){
TOPLEVEL.figure_out_scope();
if (!ARGS.m) {
if (MANGLE) {
TOPLEVEL.compute_char_frequency();
UglifyJS.base54.sort();
}
});
}
if (!ARGS.m) time_it("mangle", function(){
TOPLEVEL.mangle_names();
if (MANGLE) time_it("mangle", function(){
TOPLEVEL.mangle_names(MANGLE);
});
time_it("generate", function(){
TOPLEVEL.print(output);

View File

@@ -64,7 +64,7 @@ function Compressor(options, false_by_default) {
cascade : !false_by_default,
warnings : true
});
}, true);
};
Compressor.prototype = new TreeTransformer;

View File

@@ -53,9 +53,10 @@ function OutputStream(options) {
width : 80,
max_line_len : 32000,
ie_proof : true,
beautify : true,
beautify : false,
source_map : null,
});
bracketize : false,
}, true);
var indentation = 0;
var current_col = 0;
@@ -261,6 +262,9 @@ function OutputStream(options) {
get : get,
toString : get,
indent : indent,
indentation : function() { return indentation },
current_width : function() { return current_col - indentation },
should_break : function() { return options.width && this.current_width() >= options.width },
newline : newline,
print : print,
space : space,
@@ -271,6 +275,7 @@ function OutputStream(options) {
force_semicolon : force_semicolon,
print_name : function(name) { print(make_name(name)) },
print_string : function(str) { print(encode_string(str)) },
next_indent : next_indent,
with_indent : with_indent,
with_block : with_block,
with_parens : with_parens,
@@ -314,9 +319,6 @@ function OutputStream(options) {
};
AST_Node.DEFMETHOD("print_to_string", function(options){
options = defaults(options, {
beautify: false
});
var s = OutputStream(options);
this.print(s);
return s.get();
@@ -610,6 +612,10 @@ function OutputStream(options) {
/* -----[ if ]----- */
function make_then(self, output) {
if (output.option("bracketize")) {
make_block(self.body, output);
return;
}
// The squeezer replaces "block"-s that contain only a single
// statement with the statement itself; technically, the AST
// is correct, but this can create problems when we output an
@@ -780,13 +786,29 @@ function OutputStream(options) {
output.space();
AST_Call.prototype.print.call(self, output);
});
DEFPRINT(AST_Seq, function(self, output){
self.car.print(output);
if (self.cdr) {
AST_Seq.DEFMETHOD("_do_print", function(output){
this.car.print(output);
if (this.cdr) {
output.comma();
self.cdr.print(output);
if (output.should_break()) {
output.newline();
output.indent();
}
this.cdr.print(output);
}
});
DEFPRINT(AST_Seq, function(self, output){
self._do_print(output);
// var p = output.parent();
// if (p instanceof AST_Statement) {
// output.with_indent(output.next_indent(), function(){
// self._do_print(output);
// });
// } else {
// self._do_print(output);
// }
});
DEFPRINT(AST_Dot, function(self, output){
var expr = self.expression;
expr.print(output);
@@ -919,10 +941,22 @@ function OutputStream(options) {
});
function force_statement(stat, output) {
if (stat instanceof AST_EmptyStatement)
output.force_semicolon();
else
stat.print(output);
if (output.option("bracketize")) {
if (!stat || stat instanceof AST_EmptyStatement)
output.print("{}");
else if (stat instanceof AST_BlockStatement)
stat.print(output);
else output.with_block(function(){
output.indent();
stat.print(output);
output.newline();
});
} else {
if (!stat || stat instanceof AST_EmptyStatement)
output.force_semicolon();
else
stat.print(output);
}
};
// return true if the node at the top of the stack (that means the
@@ -938,7 +972,7 @@ function OutputStream(options) {
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;

View File

@@ -339,7 +339,10 @@ AST_LoopControl.DEFMETHOD("target", function(){
return this.loopcontrol_target;
});
AST_Toplevel.DEFMETHOD("mangle_names", function(sort){
AST_Toplevel.DEFMETHOD("mangle_names", function(options){
options = defaults(options, {
sort: false
});
// We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's
// present (and for AST_SymbolRef-s it'll use the mangled name of
@@ -374,11 +377,11 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(sort){
});
this.walk(tw);
if (sort) to_mangle = mergeSort(to_mangle, function(a, b){
if (options.sort) to_mangle = mergeSort(to_mangle, function(a, b){
return b.references.length - a.references.length;
});
to_mangle.forEach(function(def){ def.mangle() });
to_mangle.forEach(function(def){ def.mangle(options) });
});
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){

View File

@@ -96,10 +96,17 @@ function repeat_string(str, i) {
return d;
};
function defaults(args, defs) {
function DefaultsError(msg, defs) {
this.msg = msg;
this.defs = defs;
};
function defaults(args, defs, croak) {
if (args === true)
args = {};
var ret = args || {};
if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i))
throw new DefaultsError("`" + i + "` is not a supported option", defs);
for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
}