uglifyjs 10 KB

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