Uglify-js не изменяет имена переменных

Попытка подготовить хорошую среду сборки для моей js-библиотеки. Согласно отзывам в Интернете UglifyJS кажется одним из лучших сжимающих модулей, работающих под NodeJS. Итак, лучше всего рекомендуется использовать код для сокращения:

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;

var orig_code = "... JS code here";
var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here

Как видно здесь, pro.ast_mangle(ast) должен калечить имена переменных, но это не так. Все, что я выхожу из этого канала, - это код javascript, без пробелов. Сначала я думал, что мой код не был оптимизирован для сжатия, но затем я попробовал его с Google Closure и получил довольно сжатие (с измененными именами переменных и все).

Эксперты UglifyJS, любой намек на то, что я делаю неправильно?

UPDATE

Фактический код слишком велик, чтобы ссылаться на него, но даже такой фрагмент не выглядит искаженным:

;(function(window, document, undefined) {

    function o(id) {
        if (typeof id !== 'string') {
            return id;  
        }
        return document.getElementById(id);
    }   

    // ...

    /** @namespace */
    window.mOxie = o;

}(window, document));

Это то, что я получаю (только пробелы убираются, я думаю):

(function(window,document,undefined){function o(id){return typeof id!="string"?id:document.getElementById(id)}window.mOxie=window.o=o})(window,document)

Ответы

Ответ 1

Хорошо, похоже, что последняя версия Uglify JS требует, чтобы параметр mangle явно передавался как истинный, иначе он ничего не будет калечить. Вот так:

var jsp = require("uglify-js").parser;
var pro = require("uglify-js").uglify;

var orig_code = "... JS code here";
var options = {
    mangle: true
};

var ast = jsp.parse(orig_code); // parse code and get the initial AST
ast = pro.ast_mangle(ast, options); // get a new AST with mangled names
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
var final_code = pro.gen_code(ast); // compressed code here

Ответ 2

По умолчанию uglify не будет искажать имена верхнего уровня, может быть, это то, что вы видели?

Try: -mt или --mangle-toplevel - также манит имена в области верхнего уровня (по умолчанию мы этого не делаем).

Ответ 3

Если вы используете Uglify2, вы можете использовать TopLevel.figure_out_scope(). http://lisperator.net/uglifyjs/scope

Если вы используете Uglify1, это немного сложнее. Вот код, который я собрал, изменив код из Uglify squeeze_more.js file:

function eachGlobalFunctionCall(ast, callback) {
  var w = uglify.uglify.ast_walker(),
      walk = w.walk,
      MAP = uglify.uglify.MAP,
      scope;

  function with_scope(s, cont) {
    var save = scope, ret;
    scope = s;
    ret = cont();
    scope = save;
    return ret;
  }

  function _lambda(name, args, body) {
    return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
  }

  w.with_walkers({
    "function": _lambda,
    "defun": _lambda,
    "toplevel": function(body) {
      return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
    },
    "call": function(expr, args) {
      var fnName = expr[1];

      if (!scope.has(fnName)) {    // <--- here the important part
        callback(fnName, args, scope);
      }
    }
  }, function() {
    return walk(uglify.uglify.ast_add_scope(ast));
  });
}

Этот выше описан только для глобальных вызовов функций, но он дает вам обратный вызов, который выполняется, когда ходок выполняет вызов неизвестного (глобального) метода.

Например, учитывая следующий ввод:

function foo () {
  bar(1);
  (function () {
    function bar() { }
    bar(2);
    (function () {
      bar(3);
    }());
  }());
}

Он найдет вызов bar(1), но не bar(2) или bar(3).

Ответ 4

Переменные в глобальной области доступны для любого другого script, поэтому Uglify не будет изменять их без специального переключателя, если вам действительно нужно, чтобы они были видимыми. Вы можете либо использовать параметр -mt/toplevel switch/setting, либо, тем не менее, прекратить загрязнение глобальной области и четко указать, что вы не собираетесь, чтобы эти переменные были видны снаружи, но обрамление вашего кода в анонимный self- вызывающей функции, которая будет служить частной областью.