diff --git a/README.md b/README.md index f4d82ace..d2ec432f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ UglifyJS 2 ========== -[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.png)](https://travis-ci.org/mishoo/UglifyJS2) +[![Build Status](https://travis-ci.org/mishoo/UglifyJS2.svg)](https://travis-ci.org/mishoo/UglifyJS2) UglifyJS is a JavaScript parser, minifier, compressor or beautifier toolkit. @@ -44,6 +44,11 @@ variable/function declared in another file will be matched properly. If you want to read from STDIN instead, pass a single dash instead of input files. +If you wish to pass your options before the input files, separate the two with +a double dash to prevent input files being used as option arguments: + + uglifyjs --compress --mangle -- input.js + The available options are: ``` @@ -612,7 +617,7 @@ or, for a shortcut you can do: var code = compressed_ast.print_to_string(options); ``` -As usual, `options` is optional. The output stream accepts a lot of otions, +As usual, `options` is optional. The output stream accepts a lot of options, most of them documented above in section “Beautifier options”. The two which we care about here are `source_map` and `comments`. diff --git a/bin/uglifyjs b/bin/uglifyjs index 3a3318b2..11c3a01a 100755 --- a/bin/uglifyjs +++ b/bin/uglifyjs @@ -5,12 +5,12 @@ var UglifyJS = require("../tools/node"); var sys = require("util"); -var optimist = require("optimist"); +var yargs = require("yargs"); var fs = require("fs"); var path = require("path"); var async = require("async"); var acorn; -var ARGS = optimist +var ARGS = yargs .usage("$0 input1.js [input2.js ...] [options]\n\ Use a single dash to read input from the standard input.\ \n\n\ @@ -64,6 +64,8 @@ You need to pass an argument to this option to specify the name that your module .describe("v", "Verbose") .describe("V", "Print version number and exit.") .describe("noerr", "Don't throw an error for unknown options in -c, -b or -m.") + .describe("bare-returns", "Allow return outside of functions. Useful when minifying CommonJS modules.") + .describe("keep-fnames", "Do not mangle/drop function names. Useful for code relying on Function.prototype.name.") .alias("p", "prefix") .alias("o", "output") @@ -100,6 +102,8 @@ You need to pass an argument to this option to specify the name that your module .boolean("lint") .boolean("V") .boolean("noerr") + .boolean("bare-returns") + .boolean("keep-fnames") .wrap(80) @@ -127,7 +131,7 @@ if (ARGS.ast_help) { } if (ARGS.h || ARGS.help) { - sys.puts(optimist.help()); + sys.puts(yargs.help()); process.exit(0); } @@ -158,6 +162,11 @@ if (ARGS.screw_ie8) { OUTPUT_OPTIONS.screw_ie8 = true; } +if (ARGS.keep_fnames) { + if (COMPRESS) COMPRESS.keep_fnames = true; + if (MANGLE) MANGLE.keep_fnames = true; +} + if (BEAUTIFY) UglifyJS.merge(OUTPUT_OPTIONS, BEAUTIFY); @@ -250,7 +259,7 @@ async.eachLimit(files, 1, function (file, cb) { } if (ARGS.p != null) { if (P_RELATIVE) { - file = path.relative(path.dirname(ARGS.source_map), file); + file = path.relative(path.dirname(ARGS.source_map), file).replace(/\\/g, '/'); } else { var p = parseInt(ARGS.p, 10); if (!isNaN(p)) { @@ -275,9 +284,10 @@ async.eachLimit(files, 1, function (file, cb) { else { try { TOPLEVEL = UglifyJS.parse(code, { - filename : file, - toplevel : TOPLEVEL, - expression : ARGS.expr, + filename : file, + toplevel : TOPLEVEL, + expression : ARGS.expr, + bare_returns : ARGS.bare_returns, }); } catch(ex) { if (ex instanceof UglifyJS.JS_Parse_Error) { diff --git a/lib/ast.js b/lib/ast.js index 051cd2fb..5aa1be30 100644 --- a/lib/ast.js +++ b/lib/ast.js @@ -84,7 +84,7 @@ function DEFNODE(type, props, methods, base) { return ctor; }; -var AST_Token = DEFNODE("Token", "type value line col pos endpos nlb comments_before file", { +var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before file", { }, null); var AST_Node = DEFNODE("Node", "start end", { @@ -205,21 +205,27 @@ var AST_DWLoop = DEFNODE("DWLoop", "condition", { $documentation: "Base class for do/while statements", $propdoc: { condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" - }, + } +}, AST_IterationStatement); + +var AST_Do = DEFNODE("Do", null, { + $documentation: "A `do` statement", + _walk: function(visitor) { + return visitor._visit(this, function(){ + this.body._walk(visitor); + this.condition._walk(visitor); + }); + } +}, AST_DWLoop); + +var AST_While = DEFNODE("While", null, { + $documentation: "A `while` statement", _walk: function(visitor) { return visitor._visit(this, function(){ this.condition._walk(visitor); this.body._walk(visitor); }); } -}, AST_IterationStatement); - -var AST_Do = DEFNODE("Do", null, { - $documentation: "A `do` statement", -}, AST_DWLoop); - -var AST_While = DEFNODE("While", null, { - $documentation: "A `while` statement", }, AST_DWLoop); var AST_For = DEFNODE("For", "init condition step", { diff --git a/lib/compress.js b/lib/compress.js index 5328e05d..28c2f6dd 100644 --- a/lib/compress.js +++ b/lib/compress.js @@ -62,6 +62,7 @@ function Compressor(options, false_by_default) { unused : !false_by_default, hoist_funs : !false_by_default, keep_fargs : false, + keep_fnames : false, hoist_vars : false, if_return : !false_by_default, join_vars : !false_by_default, @@ -162,10 +163,10 @@ merge(Compressor.prototype, { return make_node(AST_Undefined, orig).optimize(compressor); default: if (val === null) { - return make_node(AST_Null, orig).optimize(compressor); + return make_node(AST_Null, orig, { value: null }).optimize(compressor); } if (val instanceof RegExp) { - return make_node(AST_RegExp, orig).optimize(compressor); + return make_node(AST_RegExp, orig, { value: val }).optimize(compressor); } throw new Error(string_template("Can't handle constant of type: {type}", { type: typeof val @@ -225,6 +226,17 @@ merge(Compressor.prototype, { return statements; function process_for_angular(statements) { + function has_inject(comment) { + return /@ngInject/.test(comment.value); + } + function make_arguments_names_list(func) { + return func.argnames.map(function(sym){ + return make_node(AST_String, sym, { value: sym.name }); + }); + } + function make_array(orig, elements) { + return make_node(AST_Array, orig, { elements: elements }); + } function make_injector(func, name) { return make_node(AST_SimpleStatement, func, { body: make_node(AST_Assign, func, { @@ -233,37 +245,56 @@ merge(Compressor.prototype, { expression: make_node(AST_SymbolRef, name, name), property: "$inject" }), - right: make_node(AST_Array, func, { - elements: func.argnames.map(function(sym){ - return make_node(AST_String, sym, { value: sym.name }); - }) - }) + right: make_array(func, make_arguments_names_list(func)) }) }); } + function check_expression(body) { + if (body && body.args) { + // if this is a function call check all of arguments passed + body.args.forEach(function(argument, index, array) { + var comments = argument.start.comments_before; + // if the argument is function preceded by @ngInject + if (argument instanceof AST_Lambda && comments.length && has_inject(comments[0])) { + // replace the function with an array of names of its parameters and function at the end + array[index] = make_array(argument, make_arguments_names_list(argument).concat(argument)); + } + }); + // if this is chained call check previous one recursively + if (body.expression && body.expression.expression) { + check_expression(body.expression.expression); + } + } + } return statements.reduce(function(a, stat){ a.push(stat); - var token = stat.start; - var comments = token.comments_before; - if (comments && comments.length > 0) { - var last = comments.pop(); - if (/@ngInject/.test(last.value)) { - // case 1: defun - if (stat instanceof AST_Defun) { - a.push(make_injector(stat, stat.name)); - } - else if (stat instanceof AST_Definitions) { - stat.definitions.forEach(function(def){ - if (def.value && def.value instanceof AST_Lambda) { - a.push(make_injector(def.value, def.name)); - } - }); - } - else { - compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token); + + if (stat.body && stat.body.args) { + check_expression(stat.body); + } else { + var token = stat.start; + var comments = token.comments_before; + if (comments && comments.length > 0) { + var last = comments.pop(); + if (has_inject(last)) { + // case 1: defun + if (stat instanceof AST_Defun) { + a.push(make_injector(stat, stat.name)); + } + else if (stat instanceof AST_Definitions) { + stat.definitions.forEach(function(def) { + if (def.value && def.value instanceof AST_Lambda) { + a.push(make_injector(def.value, def.name)); + } + }); + } + else { + compressor.warn("Unknown statement marked with @ngInject [{file}:{line},{col}]", token); + } } } } + return a; }, []); } @@ -949,7 +980,7 @@ merge(Compressor.prototype, { def(AST_BlockStatement, block_aborts); def(AST_SwitchBranch, block_aborts); def(AST_If, function(){ - return this.alternative && aborts(this.body) && aborts(this.alternative); + return this.alternative && aborts(this.body) && aborts(this.alternative) && this; }); })(function(node, func){ node.DEFMETHOD("aborts", func); @@ -1666,7 +1697,7 @@ merge(Compressor.prototype, { OPT(AST_Function, function(self, compressor){ self = AST_Lambda.prototype.optimize.call(self, compressor); - if (compressor.option("unused")) { + if (compressor.option("unused") && !compressor.option("keep_fnames")) { if (self.name && self.name.unreferenced()) { self.name = null; } @@ -1722,6 +1753,11 @@ merge(Compressor.prototype, { }).transform(compressor); break; case "Function": + // new Function() => function(){} + if (self.args.length == 0) return make_node(AST_Function, self, { + argnames: [], + body: [] + }); if (all(self.args, function(x){ return x instanceof AST_String })) { // quite a corner-case, but we can handle it: // https://github.com/mishoo/UglifyJS2/issues/203 @@ -1901,7 +1937,7 @@ merge(Compressor.prototype, { if (self.cdr instanceof AST_UnaryPrefix && self.cdr.operator == "void" && !self.cdr.expression.has_side_effects(compressor)) { - self.cdr.operator = self.car; + self.cdr.expression = self.car; return self.cdr; } if (self.cdr instanceof AST_Undefined) { @@ -2203,14 +2239,30 @@ merge(Compressor.prototype, { case "undefined": return make_node(AST_Undefined, self); case "NaN": - return make_node(AST_NaN, self); + return make_node(AST_NaN, self).transform(compressor); case "Infinity": - return make_node(AST_Infinity, self); + return make_node(AST_Infinity, self).transform(compressor); } } return self; }); + OPT(AST_Infinity, function (self, compressor) { + return make_node(AST_Binary, self, { + operator : '/', + left : make_node(AST_Number, null, {value: 1}), + right : make_node(AST_Number, null, {value: 0}) + }); + }); + + OPT(AST_NaN, function (self, compressor) { + return make_node(AST_Binary, self, { + operator : '/', + left : make_node(AST_Number, null, {value: 0}), + right : make_node(AST_Number, null, {value: 0}) + }); + }); + OPT(AST_Undefined, function(self, compressor){ if (compressor.option("unsafe")) { var scope = compressor.find_parent(AST_Scope); diff --git a/lib/mozilla-ast.js b/lib/mozilla-ast.js index 602ef0e6..1deb18e2 100644 --- a/lib/mozilla-ast.js +++ b/lib/mozilla-ast.js @@ -370,26 +370,30 @@ /* -----[ tools ]----- */ function my_start_token(moznode) { - var loc = moznode.loc; + var loc = moznode.loc, start = loc && loc.start; var range = moznode.range; return new AST_Token({ - file : loc && loc.source, - line : loc && loc.start.line, - col : loc && loc.start.column, - pos : range ? range[0] : moznode.start, - endpos : range ? range[0] : moznode.start + file : loc && loc.source, + line : start && start.line, + col : start && start.column, + pos : range ? range[0] : moznode.start, + endline : start && start.line, + endcol : start && start.column, + endpos : range ? range[0] : moznode.start }); }; function my_end_token(moznode) { - var loc = moznode.loc; + var loc = moznode.loc, end = loc && loc.end; var range = moznode.range; return new AST_Token({ - file : loc && loc.source, - line : loc && loc.end.line, - col : loc && loc.end.column, - pos : range ? range[1] : moznode.end, - endpos : range ? range[1] : moznode.end + file : loc && loc.source, + line : end && end.line, + col : end && end.column, + pos : range ? range[1] : moznode.end, + endline : end && end.line, + endcol : end && end.column, + endpos : range ? range[1] : moznode.end }); }; @@ -465,23 +469,16 @@ return ast; }; - function moz_sub_loc(token) { - return token.line ? { - line: token.line, - column: token.col - } : null; - }; - - function set_moz_loc(mynode, moznode) { + function set_moz_loc(mynode, moznode, myparent) { var start = mynode.start; var end = mynode.end; - if (start.pos != null && end.pos != null) { - moznode.range = [start.pos, end.pos]; + if (start.pos != null && end.endpos != null) { + moznode.range = [start.pos, end.endpos]; } if (start.line) { moznode.loc = { - start: moz_sub_loc(start), - end: moz_sub_loc(end) + start: {line: start.line, column: start.col}, + end: end.endline ? {line: end.endline, column: end.endcol} : null }; if (start.file) { moznode.loc.source = start.file; diff --git a/lib/output.js b/lib/output.js index 7fe61af3..72bcdd5e 100644 --- a/lib/output.js +++ b/lib/output.js @@ -86,7 +86,7 @@ function OutputStream(options) { function make_string(str) { var dq = 0, sq = 0; - str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){ + str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s){ switch (s) { case "\\": return "\\\\"; case "\b": return "\\b"; @@ -98,6 +98,7 @@ function OutputStream(options) { case '"': ++dq; return '"'; case "'": ++sq; return "'"; case "\0": return "\\x00"; + case "\ufeff": return "\\ufeff"; } return s; }); @@ -221,7 +222,7 @@ function OutputStream(options) { var newline = options.beautify ? function() { print("\n"); - } : noop; + } : maybe_newline; var semicolon = options.beautify ? function() { print(";"); @@ -549,12 +550,6 @@ function OutputStream(options) { return true; }); - PARENS(AST_NaN, function(output){ - var p = output.parent(); - if (p instanceof AST_PropAccess && p.expression === this) - return true; - }); - PARENS([ AST_Assign, AST_Conditional ], function (output){ var p = output.parent(); // !(a = false) → true @@ -1109,10 +1104,10 @@ function OutputStream(options) { }); DEFPRINT(AST_Hole, noop); DEFPRINT(AST_Infinity, function(self, output){ - output.print("1/0"); + output.print("Infinity"); }); DEFPRINT(AST_NaN, function(self, output){ - output.print("0/0"); + output.print("NaN"); }); DEFPRINT(AST_This, function(self, output){ output.print("this"); diff --git a/lib/parse.js b/lib/parse.js index de982b1e..f463526f 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -206,7 +206,7 @@ var EX_EOF = {}; function tokenizer($TEXT, filename, html5_comments) { var S = { - text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/\uFEFF/g, ''), + text : $TEXT.replace(/\uFEFF/g, ''), filename : filename, pos : 0, tokpos : 0, @@ -225,10 +225,15 @@ function tokenizer($TEXT, filename, html5_comments) { var ch = S.text.charAt(S.pos++); if (signal_eof && !ch) throw EX_EOF; - if (ch == "\n") { + if ("\r\n\u2028\u2029".indexOf(ch) >= 0) { S.newline_before = S.newline_before || !in_string; ++S.line; S.col = 0; + if (!in_string && ch == "\r" && peek() == "\n") { + // treat a \r\n sequence as a single \n + ++S.pos; + ch = "\n"; + } } else { ++S.col; } @@ -262,14 +267,16 @@ function tokenizer($TEXT, filename, html5_comments) { (type == "punc" && PUNC_BEFORE_EXPRESSION(value))); prev_was_dot = (type == "punc" && value == "."); var ret = { - type : type, - value : value, - line : S.tokline, - col : S.tokcol, - pos : S.tokpos, - endpos : S.pos, - nlb : S.newline_before, - file : filename + type : type, + value : value, + line : S.tokline, + col : S.tokcol, + pos : S.tokpos, + endline : S.line, + endcol : S.col, + endpos : S.pos, + nlb : S.newline_before, + file : filename }; if (!is_comment) { ret.comments_before = S.comments_before; @@ -392,6 +399,7 @@ function tokenizer($TEXT, filename, html5_comments) { ret = S.text.substring(S.pos, i); S.pos = i; } + S.col = S.tokcol + (S.pos - S.tokpos); S.comments_before.push(token(type, ret, true)); S.regex_allowed = regex_allowed; return next_token(); @@ -609,6 +617,7 @@ function parse($TEXT, options) { toplevel : null, expression : false, html5_comments : true, + bare_returns : false, }); var S = { @@ -788,7 +797,7 @@ function parse($TEXT, options) { return if_(); case "return": - if (S.in_function == 0) + if (S.in_function == 0 && !options.bare_returns) croak("'return' outside of function"); return new AST_Return({ value: ( is("punc", ";") diff --git a/lib/scope.js b/lib/scope.js index 6f29921f..73442a30 100644 --- a/lib/scope.js +++ b/lib/scope.js @@ -57,9 +57,14 @@ function SymbolDef(scope, index, orig) { SymbolDef.prototype = { unmangleable: function(options) { - return (this.global && !(options && options.toplevel)) + if (!options) options = {}; + + return (this.global && !options.toplevel) || this.undeclared - || (!(options && options.eval) && (this.scope.uses_eval || this.scope.uses_with)); + || (!options.eval && (this.scope.uses_eval || this.scope.uses_with)) + || (options.keep_fnames + && (this.orig[0] instanceof AST_SymbolLambda + || this.orig[0] instanceof AST_SymbolDefun)); }, mangle: function(options) { if (!this.mangled_name && !this.unmangleable(options)) { @@ -322,11 +327,12 @@ AST_Symbol.DEFMETHOD("global", function(){ AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){ return defaults(options, { - except : [], - eval : false, - sort : false, - toplevel : false, - screw_ie8 : false + except : [], + eval : false, + sort : false, + toplevel : false, + screw_ie8 : false, + keep_fnames : false }); }); @@ -471,7 +477,9 @@ var base54 = (function() { base54.freq = function(){ return frequency }; function base54(num) { var ret = "", base = 54; + num++; do { + num--; ret += String.fromCharCode(chars[num % base]); num = Math.floor(num / base); base = 64; diff --git a/lib/sourcemap.js b/lib/sourcemap.js index 663ef12e..3998e405 100644 --- a/lib/sourcemap.js +++ b/lib/sourcemap.js @@ -53,11 +53,16 @@ function SourceMap(options) { orig_line_diff : 0, dest_line_diff : 0, }); - var generator = new MOZ_SourceMap.SourceMapGenerator({ - file : options.file, - sourceRoot : options.root - }); var orig_map = options.orig && new MOZ_SourceMap.SourceMapConsumer(options.orig); + var generator; + if (orig_map) { + generator = MOZ_SourceMap.SourceMapGenerator.fromSourceMap(orig_map); + } else { + generator = new MOZ_SourceMap.SourceMapGenerator({ + file : options.file, + sourceRoot : options.root + }); + } function add(source, gen_line, gen_col, orig_line, orig_col, name) { if (orig_map) { var info = orig_map.originalPositionFor({ @@ -70,7 +75,7 @@ function SourceMap(options) { source = info.source; orig_line = info.line; orig_col = info.column; - name = info.name; + name = info.name || name; } generator.addMapping({ generated : { line: gen_line + options.dest_line_diff, column: gen_col }, @@ -78,7 +83,7 @@ function SourceMap(options) { source : source, name : name }); - }; + } return { add : add, get : function() { return generator }, diff --git a/package.json b/package.json index f3e3c533..8e066602 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "JavaScript parser, mangler/compressor and beautifier toolkit", "homepage": "http://lisperator.net/uglifyjs", "main": "tools/node.js", - "version": "2.4.15", + "version": "2.4.16", "engines": { "node" : ">=0.4.0" }, "maintainers": [{ "name": "Mihai Bazon", @@ -17,7 +17,7 @@ "dependencies": { "async" : "~0.2.6", "source-map" : "0.1.34", - "optimist": "~0.3.5", + "yargs": "~1.3.3", "uglify-to-browserify": "~1.0.0" }, "devDependencies": { diff --git a/test/compress/angular-inject.js b/test/compress/angular-inject.js new file mode 100644 index 00000000..8b24c846 --- /dev/null +++ b/test/compress/angular-inject.js @@ -0,0 +1,67 @@ +ng_inject_defun: { + options = { + angular: true + }; + input: { + /*@ngInject*/ + function Controller(dependency) { + return dependency; + } + } + expect: { + function Controller(dependency) { + return dependency; + } + Controller.$inject=['dependency'] + } +} + +ng_inject_assignment: { + options = { + angular: true + }; + input: { + /*@ngInject*/ + var Controller = function(dependency) { + return dependency; + } + } + expect: { + var Controller = function(dependency) { + return dependency; + } + Controller.$inject=['dependency'] + } +} + +ng_inject_inline: { + options = { + angular: true + }; + input: { + angular.module('a'). + factory('b', + /*@ngInject*/ + function(dependency) { + return dependency; + }). + directive('c', + /*@ngInject*/ + function(anotherDependency) { + return anotherDependency; + }) + } + expect: { + angular.module('a'). + factory('b',[ + 'dependency', + function(dependency) { + return dependency; + }]). + directive('c',[ + 'anotherDependency', + function(anotherDependency) { + return anotherDependency; + }]) + } +} diff --git a/test/compress/drop-unused.js b/test/compress/drop-unused.js index 89bf0088..de4b2220 100644 --- a/test/compress/drop-unused.js +++ b/test/compress/drop-unused.js @@ -163,3 +163,17 @@ used_var_in_catch: { } } } + +keep_fnames: { + options = { unused: true, keep_fnames: true }; + input: { + function foo() { + return function bar(baz) {}; + } + } + expect: { + function foo() { + return function bar() {}; + } + } +} \ No newline at end of file diff --git a/test/compress/issue-597.js b/test/compress/issue-597.js new file mode 100644 index 00000000..3c651a4c --- /dev/null +++ b/test/compress/issue-597.js @@ -0,0 +1,25 @@ +NaN_and_Infinity_must_have_parens: { + options = {}; + input: { + Infinity.toString(); + NaN.toString(); + } + expect: { + (1/0).toString(); + (0/0).toString(); + } +} + +NaN_and_Infinity_should_not_be_replaced_when_they_are_redefined: { + options = {}; + input: { + var Infinity, NaN; + Infinity.toString(); + NaN.toString(); + } + expect: { + var Infinity, NaN; + Infinity.toString(); + NaN.toString(); + } +} diff --git a/test/compress/issue-611.js b/test/compress/issue-611.js new file mode 100644 index 00000000..6f2c65d2 --- /dev/null +++ b/test/compress/issue-611.js @@ -0,0 +1,21 @@ +issue_611: { + options = { + sequences: true, + side_effects: true + }; + input: { + define(function() { + function fn() {} + if (fn()) { + fn(); + return void 0; + } + }); + } + expect: { + define(function() { + function fn(){} + if (fn()) return void fn(); + }); + } +} diff --git a/tools/node.js b/tools/node.js index 084998da..4bc8517b 100644 --- a/tools/node.js +++ b/tools/node.js @@ -6,6 +6,8 @@ var sys = require("util"); var UglifyJS = vm.createContext({ sys : sys, console : console, + process : process, + Buffer : Buffer, MOZ_SourceMap : require("source-map") }); @@ -33,7 +35,7 @@ var FILES = exports.FILES = [ "../lib/sourcemap.js", "../lib/mozilla-ast.js" ].map(function(file){ - return path.join(path.dirname(fs.realpathSync(__filename)), file); + return fs.realpathSync(path.join(path.dirname(__filename), file)); }); FILES.forEach(load_global); @@ -95,8 +97,8 @@ exports.minify = function(files, options) { // 3. mangle if (options.mangle) { - toplevel.figure_out_scope(); - toplevel.compute_char_frequency(); + toplevel.figure_out_scope(options.mangle); + toplevel.compute_char_frequency(options.mangle); toplevel.mangle_names(options.mangle); }