node.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. // workaround for tty output truncation upon process.exit()
  2. [process.stdout, process.stderr].forEach(function(stream){
  3. if (stream._handle && stream._handle.setBlocking)
  4. stream._handle.setBlocking(true);
  5. });
  6. var path = require("path");
  7. var fs = require("fs");
  8. var FILES = exports.FILES = [
  9. "../lib/utils.js",
  10. "../lib/ast.js",
  11. "../lib/parse.js",
  12. "../lib/transform.js",
  13. "../lib/scope.js",
  14. "../lib/output.js",
  15. "../lib/compress.js",
  16. "../lib/sourcemap.js",
  17. "../lib/mozilla-ast.js",
  18. "../lib/propmangle.js",
  19. "./exports.js",
  20. ].map(function(file){
  21. return fs.realpathSync(path.join(path.dirname(__filename), file));
  22. });
  23. var UglifyJS = exports;
  24. new Function("MOZ_SourceMap", "exports", "DEBUG", FILES.map(function(file){
  25. return fs.readFileSync(file, "utf8");
  26. }).join("\n\n"))(
  27. require("source-map"),
  28. UglifyJS,
  29. !!global.UGLIFY_DEBUG
  30. );
  31. UglifyJS.AST_Node.warn_function = function(txt) {
  32. console.error("WARN: %s", txt);
  33. };
  34. exports.minify = function(files, options) {
  35. options = UglifyJS.defaults(options, {
  36. spidermonkey : false,
  37. outSourceMap : null,
  38. sourceRoot : null,
  39. inSourceMap : null,
  40. fromString : false,
  41. warnings : false,
  42. mangle : {},
  43. mangleProperties : false,
  44. nameCache : null,
  45. output : null,
  46. compress : {},
  47. parse : {}
  48. });
  49. UglifyJS.base54.reset();
  50. // 1. parse
  51. var toplevel = null,
  52. sourcesContent = {};
  53. if (options.spidermonkey) {
  54. toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
  55. } else {
  56. if (typeof files == "string")
  57. files = [ files ];
  58. files.forEach(function(file, i){
  59. var code = options.fromString
  60. ? file
  61. : fs.readFileSync(file, "utf8");
  62. sourcesContent[file] = code;
  63. toplevel = UglifyJS.parse(code, {
  64. filename: options.fromString ? i : file,
  65. toplevel: toplevel,
  66. bare_returns: options.parse ? options.parse.bare_returns : undefined
  67. });
  68. });
  69. }
  70. if (options.wrap) {
  71. toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
  72. }
  73. // 2. compress
  74. if (options.compress) {
  75. var compress = { warnings: options.warnings };
  76. UglifyJS.merge(compress, options.compress);
  77. toplevel.figure_out_scope();
  78. var sq = UglifyJS.Compressor(compress);
  79. toplevel = sq.compress(toplevel);
  80. }
  81. // 3. mangle properties
  82. if (options.mangleProperties || options.nameCache) {
  83. options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
  84. toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
  85. UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
  86. }
  87. // 4. mangle
  88. if (options.mangle) {
  89. toplevel.figure_out_scope(options.mangle);
  90. toplevel.compute_char_frequency(options.mangle);
  91. toplevel.mangle_names(options.mangle);
  92. }
  93. // 5. output
  94. var inMap = options.inSourceMap;
  95. var output = {};
  96. if (typeof options.inSourceMap == "string") {
  97. inMap = fs.readFileSync(options.inSourceMap, "utf8");
  98. }
  99. if (options.outSourceMap) {
  100. output.source_map = UglifyJS.SourceMap({
  101. file: options.outSourceMap,
  102. orig: inMap,
  103. root: options.sourceRoot
  104. });
  105. if (options.sourceMapIncludeSources) {
  106. for (var file in sourcesContent) {
  107. if (sourcesContent.hasOwnProperty(file)) {
  108. output.source_map.get().setSourceContent(file, sourcesContent[file]);
  109. }
  110. }
  111. }
  112. }
  113. if (options.output) {
  114. UglifyJS.merge(output, options.output);
  115. }
  116. var stream = UglifyJS.OutputStream(output);
  117. toplevel.print(stream);
  118. if (options.outSourceMap && "string" === typeof options.outSourceMap) {
  119. stream += "\n//# sourceMappingURL=" + options.outSourceMap;
  120. }
  121. var source_map = output.source_map;
  122. if (source_map) {
  123. source_map = source_map + "";
  124. }
  125. return {
  126. code : stream + "",
  127. map : source_map
  128. };
  129. };
  130. // exports.describe_ast = function() {
  131. // function doitem(ctor) {
  132. // var sub = {};
  133. // ctor.SUBCLASSES.forEach(function(ctor){
  134. // sub[ctor.TYPE] = doitem(ctor);
  135. // });
  136. // var ret = {};
  137. // if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
  138. // if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
  139. // return ret;
  140. // }
  141. // return doitem(UglifyJS.AST_Node).sub;
  142. // }
  143. exports.describe_ast = function() {
  144. var out = UglifyJS.OutputStream({ beautify: true });
  145. function doitem(ctor) {
  146. out.print("AST_" + ctor.TYPE);
  147. var props = ctor.SELF_PROPS.filter(function(prop){
  148. return !/^\$/.test(prop);
  149. });
  150. if (props.length > 0) {
  151. out.space();
  152. out.with_parens(function(){
  153. props.forEach(function(prop, i){
  154. if (i) out.space();
  155. out.print(prop);
  156. });
  157. });
  158. }
  159. if (ctor.documentation) {
  160. out.space();
  161. out.print_string(ctor.documentation);
  162. }
  163. if (ctor.SUBCLASSES.length > 0) {
  164. out.space();
  165. out.with_block(function(){
  166. ctor.SUBCLASSES.forEach(function(ctor, i){
  167. out.indent();
  168. doitem(ctor);
  169. out.newline();
  170. });
  171. });
  172. }
  173. };
  174. doitem(UglifyJS.AST_Node);
  175. return out + "";
  176. };
  177. function readReservedFile(filename, reserved) {
  178. if (!reserved) {
  179. reserved = { vars: [], props: [] };
  180. }
  181. var data = fs.readFileSync(filename, "utf8");
  182. data = JSON.parse(data);
  183. if (data.vars) {
  184. data.vars.forEach(function(name){
  185. UglifyJS.push_uniq(reserved.vars, name);
  186. });
  187. }
  188. if (data.props) {
  189. data.props.forEach(function(name){
  190. UglifyJS.push_uniq(reserved.props, name);
  191. });
  192. }
  193. return reserved;
  194. }
  195. exports.readReservedFile = readReservedFile;
  196. exports.readDefaultReservedFile = function(reserved) {
  197. return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
  198. };
  199. exports.readNameCache = function(filename, key) {
  200. var cache = null;
  201. if (filename) {
  202. try {
  203. var cache = fs.readFileSync(filename, "utf8");
  204. cache = JSON.parse(cache)[key];
  205. if (!cache) throw "init";
  206. cache.props = UglifyJS.Dictionary.fromObject(cache.props);
  207. } catch(ex) {
  208. cache = {
  209. cname: -1,
  210. props: new UglifyJS.Dictionary()
  211. };
  212. }
  213. }
  214. return cache;
  215. };
  216. exports.writeNameCache = function(filename, key, cache) {
  217. if (filename) {
  218. var data;
  219. try {
  220. data = fs.readFileSync(filename, "utf8");
  221. data = JSON.parse(data);
  222. } catch(ex) {
  223. data = {};
  224. }
  225. data[key] = {
  226. cname: cache.cname,
  227. props: cache.props.toObject()
  228. };
  229. fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
  230. }
  231. };