uglifyjs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. #!/usr/bin/env node
  2. // -*- js -*-
  3. global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
  4. var fs = require("fs");
  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. defmodule = require(defmodarg),
  158. sym,
  159. val;
  160. for (sym in defmodule) {
  161. if (defmodule.hasOwnProperty(sym)) {
  162. options.defines[sym] = function(val) {
  163. if (typeof val == "string")
  164. return [ "string", val ];
  165. if (typeof val == "number")
  166. return [ "num", val ];
  167. if (val === true)
  168. return [ 'name', 'true' ];
  169. if (val === false)
  170. return [ 'name', 'false' ];
  171. if (val === null)
  172. return [ 'name', 'null' ];
  173. if (val === undefined)
  174. return [ 'name', 'undefined' ];
  175. sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
  176. sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
  177. process.exit(1);
  178. return null;
  179. }(defmodule[sym]);
  180. }
  181. }
  182. break;
  183. case "--ascii":
  184. options.codegen_options.ascii_only = true;
  185. break;
  186. case "--make":
  187. options.make = true;
  188. break;
  189. case "--inline-script":
  190. options.codegen_options.inline_script = true;
  191. break;
  192. default:
  193. filename = v;
  194. break out;
  195. }
  196. }
  197. if (options.verbose) {
  198. pro.set_logger(function(msg){
  199. sys.debug(msg);
  200. });
  201. }
  202. jsp.set_logger(function(msg){
  203. sys.debug(msg);
  204. });
  205. if (options.make) {
  206. options.out_same_file = false; // doesn't make sense in this case
  207. var makefile = JSON.parse(fs.readFileSync(filename || "Makefile.uglify.js").toString());
  208. output(makefile.files.map(function(file){
  209. var code = fs.readFileSync(file.name);
  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. if (options.mangle) ast = time_it("mangle", function(){
  283. return pro.ast_mangle(ast, {
  284. toplevel : options.mangle_toplevel,
  285. defines : options.defines,
  286. except : options.reserved_names,
  287. no_functions : options.no_mangle_functions
  288. });
  289. });
  290. if (options.squeeze) ast = time_it("squeeze", function(){
  291. ast = pro.ast_squeeze(ast, {
  292. make_seqs : options.make_seqs,
  293. dead_code : options.dead_code,
  294. keep_comps : !options.unsafe
  295. });
  296. if (options.unsafe)
  297. ast = pro.ast_squeeze_more(ast);
  298. return ast;
  299. });
  300. if (options.ast)
  301. return sys.inspect(ast, null, null);
  302. result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
  303. if (!options.codegen_options.beautify && options.max_line_length) {
  304. result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
  305. }
  306. return result;
  307. } catch(ex) {
  308. sys.debug(ex.stack);
  309. sys.debug(sys.inspect(ex));
  310. sys.debug(JSON.stringify(ex));
  311. process.exit(1);
  312. }
  313. };
  314. function time_it(name, cont) {
  315. if (!options.verbose)
  316. return cont();
  317. var t1 = new Date().getTime();
  318. try { return cont(); }
  319. finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
  320. };