node.js 10.0 KB

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