a shy attempt to obey `width` in the beautifier; added `bracketize` option to always...
authorMihai Bazon <mihai@bazon.net>
Tue, 2 Oct 2012 08:00:47 +0000 (11:00 +0300)
committerMihai Bazon <mihai@bazon.net>
Tue, 2 Oct 2012 08:22:38 +0000 (11:22 +0300)
bin/uglifyjs2
lib/compress.js
lib/output.js
lib/scope.js
lib/utils.js

index daf1757..dd8a758 100755 (executable)
@@ -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);
index 3054e1f..14e722d 100644 (file)
@@ -64,7 +64,7 @@ function Compressor(options, false_by_default) {
         cascade       : !false_by_default,
 
         warnings      : true
-    });
+    }, true);
 };
 
 Compressor.prototype = new TreeTransformer;
index 69401d0..615f711 100644 (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;
index 1f56443..9f8981f 100644 (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(){
index 5de83fb..3b8aa00 100644 (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];
     }