From 9e5dd81f1e98b5c77084e19345191d630576cc44 Mon Sep 17 00:00:00 2001 From: Mihai Bazon Date: Tue, 2 Oct 2012 11:00:47 +0300 Subject: [PATCH] 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 --- bin/uglifyjs2 | 84 +++++++++++++++++++++++++++++++------------------ lib/compress.js | 2 +- lib/output.js | 62 +++++++++++++++++++++++++++--------- lib/scope.js | 9 ++++-- lib/utils.js | 9 +++++- 5 files changed, 117 insertions(+), 49 deletions(-) diff --git a/bin/uglifyjs2 b/bin/uglifyjs2 index daf17573..dd8a758a 100755 --- a/bin/uglifyjs2 +++ b/bin/uglifyjs2 @@ -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); diff --git a/lib/compress.js b/lib/compress.js index 3054e1fe..14e722d3 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -64,7 +64,7 @@ function Compressor(options, false_by_default) { cascade : !false_by_default, warnings : true - }); + }, true); }; Compressor.prototype = new TreeTransformer; diff --git a/lib/output.js b/lib/output.js index 69401d0d..615f7110 100644 --- a/lib/output.js +++ b/lib/output.js @@ -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; diff --git a/lib/scope.js b/lib/scope.js index 1f564431..9f8981f9 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -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(){ diff --git a/lib/utils.js b/lib/utils.js index 5de83fbd..3b8aa00c 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -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]; } -- 2.34.1