node.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. outFileName : null,
  39. sourceRoot : null,
  40. inSourceMap : null,
  41. sourceMapUrl : null,
  42. sourceMapInline : false,
  43. fromString : false,
  44. warnings : false,
  45. mangle : {},
  46. mangleProperties : false,
  47. nameCache : null,
  48. output : null,
  49. compress : {},
  50. parse : {}
  51. });
  52. UglifyJS.base54.reset();
  53. // 1. parse
  54. var toplevel = null,
  55. sourcesContent = {};
  56. if (options.spidermonkey) {
  57. toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
  58. } else {
  59. function addFile(file, fileUrl) {
  60. var code = options.fromString
  61. ? file
  62. : fs.readFileSync(file, "utf8");
  63. sourcesContent[fileUrl] = code;
  64. toplevel = UglifyJS.parse(code, {
  65. filename: fileUrl,
  66. toplevel: toplevel,
  67. bare_returns: options.parse ? options.parse.bare_returns : undefined
  68. });
  69. }
  70. if (!options.fromString) files = UglifyJS.simple_glob(files);
  71. [].concat(files).forEach(function (files, i) {
  72. if (typeof files === 'string') {
  73. addFile(files, options.fromString ? i : files);
  74. } else {
  75. for (var fileUrl in files) {
  76. addFile(files[fileUrl], fileUrl);
  77. }
  78. }
  79. });
  80. }
  81. if (options.wrap) {
  82. toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
  83. }
  84. // 2. compress
  85. if (options.compress) {
  86. var compress = { warnings: options.warnings };
  87. UglifyJS.merge(compress, options.compress);
  88. toplevel.figure_out_scope();
  89. var sq = UglifyJS.Compressor(compress);
  90. toplevel = sq.compress(toplevel);
  91. }
  92. // 3. mangle properties
  93. if (options.mangleProperties || options.nameCache) {
  94. options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
  95. toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
  96. UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
  97. }
  98. // 4. mangle
  99. if (options.mangle) {
  100. toplevel.figure_out_scope(options.mangle);
  101. toplevel.compute_char_frequency(options.mangle);
  102. toplevel.mangle_names(options.mangle);
  103. }
  104. // 5. output
  105. var inMap = options.inSourceMap;
  106. var output = {};
  107. if (typeof options.inSourceMap == "string") {
  108. inMap = JSON.parse(fs.readFileSync(options.inSourceMap, "utf8"));
  109. }
  110. if (options.outSourceMap || options.sourceMapInline) {
  111. output.source_map = UglifyJS.SourceMap({
  112. // prefer outFileName, otherwise use outSourceMap without .map suffix
  113. file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null),
  114. orig: inMap,
  115. root: options.sourceRoot
  116. });
  117. if (options.sourceMapIncludeSources) {
  118. for (var file in sourcesContent) {
  119. if (sourcesContent.hasOwnProperty(file)) {
  120. output.source_map.get().setSourceContent(file, sourcesContent[file]);
  121. }
  122. }
  123. }
  124. }
  125. if (options.output) {
  126. UglifyJS.merge(output, options.output);
  127. }
  128. var stream = UglifyJS.OutputStream(output);
  129. toplevel.print(stream);
  130. var source_map = output.source_map;
  131. if (source_map) {
  132. source_map = source_map + "";
  133. }
  134. var mappingUrlPrefix = "\n//# sourceMappingURL=";
  135. if (options.sourceMapInline) {
  136. stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64");
  137. } else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
  138. stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
  139. }
  140. return {
  141. code : stream + "",
  142. map : source_map
  143. };
  144. };
  145. // exports.describe_ast = function() {
  146. // function doitem(ctor) {
  147. // var sub = {};
  148. // ctor.SUBCLASSES.forEach(function(ctor){
  149. // sub[ctor.TYPE] = doitem(ctor);
  150. // });
  151. // var ret = {};
  152. // if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
  153. // if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
  154. // return ret;
  155. // }
  156. // return doitem(UglifyJS.AST_Node).sub;
  157. // }
  158. exports.describe_ast = function() {
  159. var out = UglifyJS.OutputStream({ beautify: true });
  160. function doitem(ctor) {
  161. out.print("AST_" + ctor.TYPE);
  162. var props = ctor.SELF_PROPS.filter(function(prop){
  163. return !/^\$/.test(prop);
  164. });
  165. if (props.length > 0) {
  166. out.space();
  167. out.with_parens(function(){
  168. props.forEach(function(prop, i){
  169. if (i) out.space();
  170. out.print(prop);
  171. });
  172. });
  173. }
  174. if (ctor.documentation) {
  175. out.space();
  176. out.print_string(ctor.documentation);
  177. }
  178. if (ctor.SUBCLASSES.length > 0) {
  179. out.space();
  180. out.with_block(function(){
  181. ctor.SUBCLASSES.forEach(function(ctor, i){
  182. out.indent();
  183. doitem(ctor);
  184. out.newline();
  185. });
  186. });
  187. }
  188. };
  189. doitem(UglifyJS.AST_Node);
  190. return out + "";
  191. };
  192. function readReservedFile(filename, reserved) {
  193. if (!reserved) {
  194. reserved = { vars: [], props: [] };
  195. }
  196. var data = fs.readFileSync(filename, "utf8");
  197. data = JSON.parse(data);
  198. if (data.vars) {
  199. data.vars.forEach(function(name){
  200. UglifyJS.push_uniq(reserved.vars, name);
  201. });
  202. }
  203. if (data.props) {
  204. data.props.forEach(function(name){
  205. UglifyJS.push_uniq(reserved.props, name);
  206. });
  207. }
  208. return reserved;
  209. }
  210. exports.readReservedFile = readReservedFile;
  211. exports.readDefaultReservedFile = function(reserved) {
  212. return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
  213. };
  214. exports.readNameCache = function(filename, key) {
  215. var cache = null;
  216. if (filename) {
  217. try {
  218. var cache = fs.readFileSync(filename, "utf8");
  219. cache = JSON.parse(cache)[key];
  220. if (!cache) throw "init";
  221. cache.props = UglifyJS.Dictionary.fromObject(cache.props);
  222. } catch(ex) {
  223. cache = {
  224. cname: -1,
  225. props: new UglifyJS.Dictionary()
  226. };
  227. }
  228. }
  229. return cache;
  230. };
  231. exports.writeNameCache = function(filename, key, cache) {
  232. if (filename) {
  233. var data;
  234. try {
  235. data = fs.readFileSync(filename, "utf8");
  236. data = JSON.parse(data);
  237. } catch(ex) {
  238. data = {};
  239. }
  240. data[key] = {
  241. cname: cache.cname,
  242. props: cache.props.toObject()
  243. };
  244. fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
  245. }
  246. };
  247. // A file glob function that only supports "*" and "?" wildcards in the basename.
  248. // Example: "foo/bar/*baz??.*.js"
  249. // Argument `glob` may be a string or an array of strings.
  250. // Returns an array of strings. Garbage in, garbage out.
  251. exports.simple_glob = function simple_glob(glob) {
  252. var results = [];
  253. if (Array.isArray(glob)) {
  254. glob.forEach(function(elem) {
  255. results = results.concat(simple_glob(elem));
  256. });
  257. return results;
  258. }
  259. if (glob.match(/\*|\?/)) {
  260. var dir = path.dirname(glob);
  261. try {
  262. var entries = fs.readdirSync(dir);
  263. } catch (ex) {}
  264. if (entries) {
  265. var pattern = "^" + (path.basename(glob)
  266. .replace(/\(/g, "\\(")
  267. .replace(/\)/g, "\\)")
  268. .replace(/\{/g, "\\{")
  269. .replace(/\}/g, "\\}")
  270. .replace(/\[/g, "\\[")
  271. .replace(/\]/g, "\\]")
  272. .replace(/\+/g, "\\+")
  273. .replace(/\^/g, "\\^")
  274. .replace(/\$/g, "\\$")
  275. .replace(/\*/g, "[^/\\\\]*")
  276. .replace(/\./g, "\\.")
  277. .replace(/\?/g, ".")) + "$";
  278. var mod = process.platform === "win32" ? "i" : "";
  279. var rx = new RegExp(pattern, mod);
  280. for (var i in entries) {
  281. if (rx.test(entries[i]))
  282. results.push(dir + "/" + entries[i]);
  283. }
  284. }
  285. }
  286. if (results.length === 0)
  287. results = [ glob ];
  288. return results;
  289. };