485d9c11cb299c4b82dbc6bd9c28f08e7ec1a579
[framework/web/web-ui-fw.git] / build-tools / bin / uglifyjs
1 #!/usr/bin/env node
2 // -*- js -*-
3
4 global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
5 var fs = require("fs");
6
7 // Add ../lib to require path
8 // by Youmin Ha <youmin.ha@samsung.com>
9 var path = require("path");
10
11 try {
12         require.paths.unshift(path.join(__dirname, '..', 'lib'));
13 } catch (ex) {
14 }
15
16 var uglify = require("uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
17     jsp = uglify.parser,
18     pro = uglify.uglify;
19
20 var options = {
21         ast: false,
22         mangle: true,
23         mangle_toplevel: false,
24         no_mangle_functions: false,
25         squeeze: true,
26         make_seqs: true,
27         dead_code: true,
28         verbose: false,
29         show_copyright: true,
30         out_same_file: false,
31         max_line_length: 32 * 1024,
32         unsafe: false,
33         reserved_names: null,
34         defines: { },
35         lift_vars: false,
36         codegen_options: {
37                 ascii_only: false,
38                 beautify: false,
39                 indent_level: 4,
40                 indent_start: 0,
41                 quote_keys: false,
42                 space_colon: false,
43                 inline_script: false
44         },
45         make: false,
46         output: true            // stdout
47 };
48
49 var args = jsp.slice(process.argv, 2);
50 var filename;
51
52 out: while (args.length > 0) {
53         var v = args.shift();
54         switch (v) {
55             case "-b":
56             case "--beautify":
57                 options.codegen_options.beautify = true;
58                 break;
59             case "-i":
60             case "--indent":
61                 options.codegen_options.indent_level = args.shift();
62                 break;
63             case "-q":
64             case "--quote-keys":
65                 options.codegen_options.quote_keys = true;
66                 break;
67             case "-mt":
68             case "--mangle-toplevel":
69                 options.mangle_toplevel = true;
70                 break;
71             case "-nmf":
72             case "--no-mangle-functions":
73                 options.no_mangle_functions = true;
74                 break;
75             case "--no-mangle":
76             case "-nm":
77                 options.mangle = false;
78                 break;
79             case "--no-squeeze":
80             case "-ns":
81                 options.squeeze = false;
82                 break;
83             case "--no-seqs":
84                 options.make_seqs = false;
85                 break;
86             case "--no-dead-code":
87                 options.dead_code = false;
88                 break;
89             case "--no-copyright":
90             case "-nc":
91                 options.show_copyright = false;
92                 break;
93             case "-o":
94             case "--output":
95                 options.output = args.shift();
96                 break;
97             case "--overwrite":
98                 options.out_same_file = true;
99                 break;
100             case "-v":
101             case "--verbose":
102                 options.verbose = true;
103                 break;
104             case "--ast":
105                 options.ast = true;
106                 break;
107             case "--unsafe":
108                 options.unsafe = true;
109                 break;
110             case "--max-line-len":
111                 options.max_line_length = parseInt(args.shift(), 10);
112                 break;
113             case "--reserved-names":
114                 options.reserved_names = args.shift().split(",");
115                 break;
116             case "--lift-vars":
117                 options.lift_vars = true;
118                 break;
119             case "-d":
120             case "--define":
121                  var defarg = args.shift();
122                  try {
123                      var defsym = function(sym) {
124                              // KEYWORDS_ATOM doesn't include NaN or Infinity - should we check
125                              // for them too ?? We don't check reserved words and the like as the
126                              // define values are only substituted AFTER parsing
127                              if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) {
128                                  throw "Don't define values for inbuilt constant '"+sym+"'";
129                              }
130                              return sym;
131                          },
132                          defval = function(v) {
133                              if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) {
134                                  return [ "string", RegExp.$1 ];
135                              }
136                              else if (!isNaN(parseFloat(v))) {
137                                  return [ "num", parseFloat(v) ];
138                              }
139                              else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) {
140                                  return [ "name", v ];
141                              }
142                              else if (!v.match(/"/)) {
143                                  return [ "string", v ];
144                              }
145                              else if (!v.match(/'/)) {
146                                  return [ "string", v ];
147                              }
148                              throw "Can't understand the specified value: "+v;
149                          };
150                      if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) {
151                          var sym = defsym(RegExp.$1),
152                              val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ];
153                          options.defines[sym] = val;
154                      }
155                      else {
156                          throw "The --define option expects SYMBOL[=value]";
157                      }
158                  } catch(ex) {
159                      sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n");
160                      process.exit(1);
161                  }
162                  break;
163             case "--define-from-module":
164                 var defmodarg = args.shift(),
165                     defmodule = require(defmodarg),
166                     sym,
167                     val;
168                 for (sym in defmodule) {
169                     if (defmodule.hasOwnProperty(sym)) {
170                         options.defines[sym] = function(val) {
171                             if (typeof val == "string")
172                                 return [ "string", val ];
173                             if (typeof val == "number")
174                                 return [ "num", val ];
175                             if (val === true)
176                                 return [ 'name', 'true' ];
177                             if (val === false)
178                                 return [ 'name', 'false' ];
179                             if (val === null)
180                                 return [ 'name', 'null' ];
181                             if (val === undefined)
182                                 return [ 'name', 'undefined' ];
183                             sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
184                             sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
185                             process.exit(1);
186                             return null;
187                         }(defmodule[sym]);
188                     }
189                 }
190                 break;
191             case "--ascii":
192                 options.codegen_options.ascii_only = true;
193                 break;
194             case "--make":
195                 options.make = true;
196                 break;
197             case "--inline-script":
198                 options.codegen_options.inline_script = true;
199                 break;
200             default:
201                 filename = v;
202                 break out;
203         }
204 }
205
206 if (options.verbose) {
207         pro.set_logger(function(msg){
208                 sys.debug(msg);
209         });
210 }
211
212 jsp.set_logger(function(msg){
213         sys.debug(msg);
214 });
215
216 if (options.make) {
217         options.out_same_file = false; // doesn't make sense in this case
218         var makefile = JSON.parse(fs.readFileSync(filename || "Makefile.uglify.js").toString());
219         output(makefile.files.map(function(file){
220                 var code = fs.readFileSync(file.name);
221                 if (file.module) {
222                         code = "!function(exports, global){global = this;\n" + code + "\n;this." + file.module + " = exports;}({})";
223                 }
224                 else if (file.hide) {
225                         code = "(function(){" + code + "}());";
226                 }
227                 return squeeze_it(code);
228         }).join("\n"));
229 }
230 else if (filename) {
231         fs.readFile(filename, "utf8", function(err, text){
232                 if (err) throw err;
233                 output(squeeze_it(text));
234         });
235 }
236 else {
237         var stdin = process.openStdin();
238         stdin.setEncoding("utf8");
239         var text = "";
240         stdin.on("data", function(chunk){
241                 text += chunk;
242         });
243         stdin.on("end", function() {
244                 output(squeeze_it(text));
245         });
246 }
247
248 function output(text) {
249         var out;
250         if (options.out_same_file && filename)
251                 options.output = filename;
252         if (options.output === true) {
253                 out = process.stdout;
254         } else {
255                 out = fs.createWriteStream(options.output, {
256                         flags: "w",
257                         encoding: "utf8",
258                         mode: 0644
259                 });
260         }
261         out.write(text.replace(/;*$/, ";"));
262         if (options.output !== true) {
263                 out.end();
264         }
265 };
266
267 // --------- main ends here.
268
269 function show_copyright(comments) {
270         var ret = "";
271         for (var i = 0; i < comments.length; ++i) {
272                 var c = comments[i];
273                 if (c.type == "comment1") {
274                         ret += "//" + c.value + "\n";
275                 } else {
276                         ret += "/*" + c.value + "*/";
277                 }
278         }
279         return ret;
280 };
281
282 function squeeze_it(code) {
283         var result = "";
284         if (options.show_copyright) {
285                 var tok = jsp.tokenizer(code), c;
286                 c = tok();
287                 result += show_copyright(c.comments_before);
288         }
289         try {
290                 var ast = time_it("parse", function(){ return jsp.parse(code); });
291                 if (options.lift_vars) {
292                         ast = time_it("lift", function(){ return pro.ast_lift_variables(ast); });
293                 }
294                 if (options.mangle) ast = time_it("mangle", function(){
295                         return pro.ast_mangle(ast, {
296                                 toplevel     : options.mangle_toplevel,
297                                 defines      : options.defines,
298                                 except       : options.reserved_names,
299                                 no_functions : options.no_mangle_functions
300                         });
301                 });
302                 if (options.squeeze) ast = time_it("squeeze", function(){
303                         ast = pro.ast_squeeze(ast, {
304                                 make_seqs  : options.make_seqs,
305                                 dead_code  : options.dead_code,
306                                 keep_comps : !options.unsafe
307                         });
308                         if (options.unsafe)
309                                 ast = pro.ast_squeeze_more(ast);
310                         return ast;
311                 });
312                 if (options.ast)
313                         return sys.inspect(ast, null, null);
314                 result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
315                 if (!options.codegen_options.beautify && options.max_line_length) {
316                         result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
317                 }
318                 return result;
319         } catch(ex) {
320                 sys.debug(ex.stack);
321                 sys.debug(sys.inspect(ex));
322                 sys.debug(JSON.stringify(ex));
323                 process.exit(1);
324         }
325 };
326
327 function time_it(name, cont) {
328         if (!options.verbose)
329                 return cont();
330         var t1 = new Date().getTime();
331         try { return cont(); }
332         finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
333 };