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);
7 var path = require("path");
8 var fs = require("fs");
10 var FILES = exports.FILES = [
14 "../lib/transform.js",
18 "../lib/sourcemap.js",
19 "../lib/mozilla-ast.js",
20 "../lib/propmangle.js",
23 return fs.realpathSync(path.join(path.dirname(__filename), file));
26 var UglifyJS = exports;
28 new Function("MOZ_SourceMap", "exports", "DEBUG", FILES.map(function(file){
29 return fs.readFileSync(file, "utf8");
31 require("source-map"),
36 UglifyJS.AST_Node.warn_function = function(txt) {
37 console.error("WARN: %s", txt);
40 function read_source_map(code) {
41 var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
43 UglifyJS.AST_Node.warn("inline source map not found");
46 return JSON.parse(new Buffer(match[2], "base64"));
49 exports.minify = function(files, options) {
50 options = UglifyJS.defaults(options, {
57 sourceMapInline : false,
61 mangleProperties : false,
67 UglifyJS.base54.reset();
69 var inMap = options.inSourceMap;
70 if (typeof inMap == "string" && inMap != "inline") {
71 inMap = JSON.parse(fs.readFileSync(inMap, "utf8"));
78 if (options.spidermonkey) {
79 if (inMap == "inline") {
80 throw new Error("inline source map only works with built-in parser");
82 toplevel = UglifyJS.AST_Node.from_mozilla_ast(files);
84 function addFile(file, fileUrl) {
85 var code = options.fromString
87 : fs.readFileSync(file, "utf8");
88 if (inMap == "inline") {
89 inMap = read_source_map(code);
91 sourcesContent[fileUrl] = code;
92 toplevel = UglifyJS.parse(code, {
95 bare_returns: options.parse ? options.parse.bare_returns : undefined
98 if (!options.fromString) {
99 files = UglifyJS.simple_glob(files);
100 if (inMap == "inline" && files.length > 1) {
101 throw new Error("inline source map only works with singular input");
104 [].concat(files).forEach(function (files, i) {
105 if (typeof files === 'string') {
106 addFile(files, options.fromString ? i : files);
108 for (var fileUrl in files) {
109 addFile(files[fileUrl], fileUrl);
115 toplevel = toplevel.wrap_commonjs(options.wrap, options.exportAll);
119 if (options.compress) {
120 var compress = { warnings: options.warnings };
121 UglifyJS.merge(compress, options.compress);
122 toplevel.figure_out_scope(options.mangle);
123 var sq = UglifyJS.Compressor(compress);
124 toplevel = sq.compress(toplevel);
127 // 3. mangle properties
128 if (options.mangleProperties || options.nameCache) {
129 options.mangleProperties.cache = UglifyJS.readNameCache(options.nameCache, "props");
130 toplevel = UglifyJS.mangle_properties(toplevel, options.mangleProperties);
131 UglifyJS.writeNameCache(options.nameCache, "props", options.mangleProperties.cache);
135 if (options.mangle) {
136 toplevel.figure_out_scope(options.mangle);
137 toplevel.compute_char_frequency(options.mangle);
138 toplevel.mangle_names(options.mangle);
142 var output = { max_line_len: 32000 };
143 if (options.outSourceMap || options.sourceMapInline) {
144 output.source_map = UglifyJS.SourceMap({
145 // prefer outFileName, otherwise use outSourceMap without .map suffix
146 file: options.outFileName || (typeof options.outSourceMap === 'string' ? options.outSourceMap.replace(/\.map$/i, '') : null),
148 root: options.sourceRoot
150 if (options.sourceMapIncludeSources) {
151 for (var file in sourcesContent) {
152 if (sourcesContent.hasOwnProperty(file)) {
153 output.source_map.get().setSourceContent(file, sourcesContent[file]);
159 if (options.output) {
160 UglifyJS.merge(output, options.output);
162 var stream = UglifyJS.OutputStream(output);
163 toplevel.print(stream);
166 var source_map = output.source_map;
168 source_map = source_map + "";
171 var mappingUrlPrefix = "\n//# sourceMappingURL=";
172 if (options.sourceMapInline) {
173 stream += mappingUrlPrefix + "data:application/json;charset=utf-8;base64," + new Buffer(source_map).toString("base64");
174 } else if (options.outSourceMap && typeof options.outSourceMap === "string" && options.sourceMapUrl !== false) {
175 stream += mappingUrlPrefix + (typeof options.sourceMapUrl === "string" ? options.sourceMapUrl : options.outSourceMap);
184 // exports.describe_ast = function() {
185 // function doitem(ctor) {
187 // ctor.SUBCLASSES.forEach(function(ctor){
188 // sub[ctor.TYPE] = doitem(ctor);
191 // if (ctor.SELF_PROPS.length > 0) ret.props = ctor.SELF_PROPS;
192 // if (ctor.SUBCLASSES.length > 0) ret.sub = sub;
195 // return doitem(UglifyJS.AST_Node).sub;
198 exports.describe_ast = function() {
199 var out = UglifyJS.OutputStream({ beautify: true });
200 function doitem(ctor) {
201 out.print("AST_" + ctor.TYPE);
202 var props = ctor.SELF_PROPS.filter(function(prop){
203 return !/^\$/.test(prop);
205 if (props.length > 0) {
207 out.with_parens(function(){
208 props.forEach(function(prop, i){
214 if (ctor.documentation) {
216 out.print_string(ctor.documentation);
218 if (ctor.SUBCLASSES.length > 0) {
220 out.with_block(function(){
221 ctor.SUBCLASSES.forEach(function(ctor, i){
229 doitem(UglifyJS.AST_Node);
233 function readReservedFile(filename, reserved) {
235 reserved = { vars: [], props: [] };
237 var data = fs.readFileSync(filename, "utf8");
238 data = JSON.parse(data);
240 data.vars.forEach(function(name){
241 UglifyJS.push_uniq(reserved.vars, name);
245 data.props.forEach(function(name){
246 UglifyJS.push_uniq(reserved.props, name);
252 exports.readReservedFile = readReservedFile;
254 exports.readDefaultReservedFile = function(reserved) {
255 return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
258 exports.readNameCache = function(filename, key) {
262 var cache = fs.readFileSync(filename, "utf8");
263 cache = JSON.parse(cache)[key];
264 if (!cache) throw "init";
265 cache.props = UglifyJS.Dictionary.fromObject(cache.props);
269 props: new UglifyJS.Dictionary()
276 exports.writeNameCache = function(filename, key, cache) {
280 data = fs.readFileSync(filename, "utf8");
281 data = JSON.parse(data);
287 props: cache.props.toObject()
289 fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
293 // A file glob function that only supports "*" and "?" wildcards in the basename.
294 // Example: "foo/bar/*baz??.*.js"
295 // Argument `glob` may be a string or an array of strings.
296 // Returns an array of strings. Garbage in, garbage out.
297 exports.simple_glob = function simple_glob(glob) {
299 if (Array.isArray(glob)) {
300 glob.forEach(function(elem) {
301 results = results.concat(simple_glob(elem));
305 if (glob.match(/\*|\?/)) {
306 var dir = path.dirname(glob);
308 var entries = fs.readdirSync(dir);
311 var pattern = "^" + (path.basename(glob)
312 .replace(/\(/g, "\\(")
313 .replace(/\)/g, "\\)")
314 .replace(/\{/g, "\\{")
315 .replace(/\}/g, "\\}")
316 .replace(/\[/g, "\\[")
317 .replace(/\]/g, "\\]")
318 .replace(/\+/g, "\\+")
319 .replace(/\^/g, "\\^")
320 .replace(/\$/g, "\\$")
321 .replace(/\*/g, "[^/\\\\]*")
322 .replace(/\./g, "\\.")
323 .replace(/\?/g, ".")) + "$";
324 var mod = process.platform === "win32" ? "i" : "";
325 var rx = new RegExp(pattern, mod);
326 for (var i in entries) {
327 if (rx.test(entries[i]))
328 results.push(dir + "/" + entries[i]);
332 if (results.length === 0)