「JavaScript ファイルの縮小(Minify)」が なぜ難しいか?

シェアする

JavaScript を縮小・圧縮したい!

PHP の勉強も兼ねて、

「JavaScript ファイルを自動的に縮小(Minify)して、(gzip)圧縮する」

というスクリプトを作っています──と何度も書いて来ましたが、

結論: \(^o^)/ 無理

という、残念な結果に終わりそうです(8 割方 予想が付いていたけど)。

今回は、それがなぜ難しいのか という話と、/packer/ は やっぱり凄い! という話です。

目標

やりたいこと:

  • PHP4 で作る
  • 自動で処理する
  • JavaScript ファイルから
    • コメントを削除
    • 余分な空白・改行を削除

たったこれだけのことですが、じつは ものすごく難しいです。ちなみに、「PHP4」で、というのは、ただ単に ここのサーバで動いているのが PHP4 だから、というだけの理由です。PHP5 だと、類似のスクリプトはいくつかあるのですが……。

「そんなの、2 分でできるよ」というひと向けに、下記の(わざとらしい)"sample.js" をどうぞ。

/***/
/**
*  comment
*  // comment
*/
/*@cc_on
//  http://d.hatena.ne.jp/amachang/20071010/1192012056
var doc = document;          // comment
eval('var document = doc');  // comment
@*/
/*@cc_on document.write('"IE!"\n'); @*/     // comment
var hoge = "/* hoge */\n";
var fuga = '"/* fuga */"\n';
var foo  = "//* foo *//\n";
var bar  = '"\'//* bar *//\'"\n';
document.write(hoge + fuga + foo + bar);

上記のコードを Minify すると、こうなるはずです。──そんなスクリプト、書けますか?

/*@cc_on var doc=document;eval('var document = doc');@*//*@cc_on document.write('"IE!"\n');@*/var hoge="/* hoge */\n";var fuga='"/* fuga */"\n';var foo="//* foo *//\n";var bar='"\'//* bar *//\'"\n';document.write(hoge+fuga+foo+bar);

難しいところ

"sample.js" は、正規表現の初心者には、じつは一行目からコメントにマッチさせるのが難しいです。

Effective Perl』の p.77-78 に、「C のコメントにマッチする正規表現」として、詳しい説明が載っています。

/***/ にもマッチさせるためには、

#/\*([^*]|\*[^/])*\*/#g;

ではダメで、

#/\*[^*]*\*+([^/*][^*]*\*+)*/#g;

と書く必要があります(「欲張らない」繰り返し演算子を使わない場合)。

その他に、クォーテーションの中はコメントを無視したり、いろいろ配慮する必要があって、かなりややこしい。

C と C++のコメントを除去する正規表現は、下記のページをどうぞ。

perlfaq6 – Regular Expressions 訳出 2005/11/2

JavaScript ならではの「条件付きコメント」

さて、C++ と JavaScript のコメント定義は似ているので、ほとんどが流用できるのですが──。

id:amachang の記事で、一気に有名になった、IE だけに実行可能なコメント、/*@cc_on @*/ を配慮する必要がありまして……。

一行で IE の JavaScript を高速化する方法 – IT戦記

通常のコメントは除去しても、この「条件付きコメント」は残す、そしてコメント内部の余分な空白は削除する──などが難しいです。

じっさい、PHP で JavaScript を圧縮するスクリプトは、この条件付きコメントあたりを考慮しているものが、かなり少ない(無い?)です。

「/packer/」が優秀

──と思っていたら、意外にも「/packer/」が優秀でした。

/packer/

「/packer/」といえば、「あー、あの eval で難読化(笑)するヤツ?」と言う人が多いです。「/packer/」で圧縮すると、スクリプトが遅くなる、という人も。

しかし、「encode」オプション(デフォルトは「Base62 encode」)をオフにすれば、

「余分な空白とコメントを除去、条件付きコメントは残す」

という処理だけを行います。これはかなり理想的な結果です。冒頭の縮小したスクリプトも、「/packer/」で作りました。

PHP 版「JavaScript Packer」はイマイチ

おお、これは!──と思って PHP 版の「JavaScript Packer」を試してみたら、これは条件付きコメントを丸ごとカットする、という仕様でした。

JavaScript Packer in PHP

オンライン版はバージョン 3.0 なのに、PHP 版はバージョンは 2.0.2 のエンジンを使っているからでしょうか。

かなり高速で縮小してくれるオンライン版は、じつは「JavaScript で JavaScript を縮小」しています。書いてあるコードもキレイでわかりやすいので、PHP に移植──を、だれか、してくれないかなぁ……(どこまでも他力本願)。

もしくは、「YUI Compressor」の PHP 版、とか。

Yahoo! UI Library: YUI Compressor