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 fs = require("fs");
var ARGS = optimist var ARGS = optimist
.usage("$0 [options] input1.js [input2.js ...]\n\ .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.\ 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", "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-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("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("p", "Skip prefix for original filenames that appear in source maps. \
.describe("o", "Output file (default STDOUT)") For example -p 3 will drop 3 directories from file names and ensure they are relative paths.")
.describe("b", "Beautify output") .describe("o", "Output file (default STDOUT).")
.describe("m", "Don't mangle names") .describe("b", "Beautify output/specify output options.")
.describe("c", "Disable compressor, or pass compressor 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. \ 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") .describe("v", "Verbose")
.alias("p", "prefix") .alias("p", "prefix")
.alias("o", "output") .alias("o", "output")
.alias("v", "verbose") .alias("v", "verbose")
.alias("b", "beautify") .alias("b", "beautify")
.alias("c", "options") .alias("m", "mangle")
.alias("m", "no-mangle") .alias("c", "compress")
.boolean("b") .string("b")
.string("m")
.string("c")
.boolean("v") .boolean("v")
.boolean("stats") .boolean("stats")
.boolean("m")
.string("c")
.wrap(80) .wrap(80)
@@ -45,6 +45,7 @@ Use -c with no argument if you want to disable the squeezer entirely")
function normalize(o) { function normalize(o) {
for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) { for (var i in o) if (o.hasOwnProperty(i) && /-/.test(i)) {
o[i.replace(/-/g, "_")] = o[i]; o[i.replace(/-/g, "_")] = o[i];
delete o[i];
} }
} }
@@ -55,15 +56,30 @@ if (ARGS.h || ARGS.help) {
process.exit(0); process.exit(0);
} }
var COMPRESSOR_OPTIONS = {}; function getOptions(x) {
if (ARGS.c && ARGS.c !== true) { x = ARGS[x];
ARGS.c.replace(/^\s+|\s+$/g).split(/\s*,+\s*/).forEach(function(opt){ if (!x) return null;
var a = opt.split(/\s*=\s*/); var ret = {};
COMPRESSOR_OPTIONS[a[0]] = new Function("return(" + a[1] + ")")(); 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(COMPRESSOR_OPTIONS); 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 files = ARGS._.slice();
var ORIG_MAP = ARGS.in_source_map; var ORIG_MAP = ARGS.in_source_map;
@@ -103,10 +119,19 @@ var SOURCE_MAP = ARGS.source_map ? UglifyJS.SourceMap({
orig: ORIG_MAP, orig: ORIG_MAP,
}) : null; }) : null;
var output = UglifyJS.OutputStream({ OUTPUT_OPTIONS.source_map = SOURCE_MAP;
beautify: ARGS.b,
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) { files.forEach(function(file) {
var code = read_whole_file(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) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
@@ -129,9 +154,8 @@ if (SCOPE_IS_NEEDED) {
}); });
} }
if (ARGS.c !== true) { if (COMPRESS) {
time_it("squeeze", function(){ time_it("squeeze", function(){
var compressor = UglifyJS.Compressor(COMPRESSOR_OPTIONS);
TOPLEVEL = TOPLEVEL.transform(compressor); TOPLEVEL = TOPLEVEL.transform(compressor);
}); });
} }
@@ -139,15 +163,15 @@ if (ARGS.c !== true) {
if (SCOPE_IS_NEEDED) { if (SCOPE_IS_NEEDED) {
time_it("scope", function(){ time_it("scope", function(){
TOPLEVEL.figure_out_scope(); TOPLEVEL.figure_out_scope();
if (!ARGS.m) { if (MANGLE) {
TOPLEVEL.compute_char_frequency(); TOPLEVEL.compute_char_frequency();
UglifyJS.base54.sort(); UglifyJS.base54.sort();
} }
}); });
} }
if (!ARGS.m) time_it("mangle", function(){ if (MANGLE) time_it("mangle", function(){
TOPLEVEL.mangle_names(); TOPLEVEL.mangle_names(MANGLE);
}); });
time_it("generate", function(){ time_it("generate", function(){
TOPLEVEL.print(output); TOPLEVEL.print(output);

View File

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

View File

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

View File

@@ -339,7 +339,10 @@ AST_LoopControl.DEFMETHOD("target", function(){
return this.loopcontrol_target; 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 // We only need to mangle declaration nodes. Special logic wired
// into the code generator will display the mangled name if it's // 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 // 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); 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; 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(){ AST_Toplevel.DEFMETHOD("compute_char_frequency", function(){

View File

@@ -96,10 +96,17 @@ function repeat_string(str, i) {
return d; return d;
}; };
function defaults(args, defs) { function DefaultsError(msg, defs) {
this.msg = msg;
this.defs = defs;
};
function defaults(args, defs, croak) {
if (args === true) if (args === true)
args = {}; args = {};
var ret = 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)) { for (var i in defs) if (HOP(defs, i)) {
ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
} }