web-dev-qa-db-ja.com

Uglify-jsは変数名をマングルしません

私のjsライブラリ用の良いビルド環境を準備しようとしています。ウェブ上のレビューによると glifyJS は、NodeJSの下で動作する、最も優れた圧縮モジュールの1つであるようです。だからここにコードを縮小する最良の方法があります:

_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エキスパート、私が間違っていることのヒントはありますか?

[〜#〜]更新[〜#〜]

実際のコードはここで参照するには大きすぎますが、次のようなスニペットでさえも破損しません。

_;(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)
_
17
jayarjo

Uglify JSの最新バージョンでは、mangleオプションを明示的にtrueとして渡す必要があるようです。それ以外の場合は、何もマングルしません。このような:

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
16
jayarjo

デフォルトでは、uglifyはトップレベルの名前を壊しません。

試してください:-mtまたは--mangle-toplevel —トップレベルスコープ内の名前もマングルします(デフォルトではこれを行いません)。

10
axkibe

Uglify2を使用している場合は、TopLevel.figure_out_scope()を使用できます。 http://lisperator.net/uglifyjs/scope

Uglify1を使用している場合は、少し複雑になります。これが glifyの_squeeze_more.js_ファイル のコードを変更してまとめたコードです。

_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's the important part
        callback(fnName, args, scope);
      }
    }
  }, function() {
    return walk(uglify.uglify.ast_add_scope(ast));
  });
}
_

上記の1つはグローバル関数呼び出しでのみ機能しますが、ウォーカーが未知の(グローバル)メソッドへの呼び出しを見つけると実行されるコールバックを提供します。

たとえば、次の入力があるとします。

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

これはbar(1)という呼び出しを検出しますが、notbar(2)またはbar(3)を検出します。

1
nickf

グローバルスコープの変数は他のスクリプトで使用できるので、本当に表示する必要がある場合に備えて、特別なスイッチなしでUglifyが変数を変更することはありません。 -mt/toplevelスイッチ/設定、または、さらに良いことに、グローバルスコープの汚染を停止し、これらの変数が外部に見られることを意図せず、コードを匿名の自己呼び出し関数にフレーム化することを明確に示すプライベートスコープとして機能します。

0
Oleg V. Volkov