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/」といえば、「あー、あの eval
で難読化(笑)するヤツ?」と言う人が多いです。「/packer/」で圧縮すると、スクリプトが遅くなる、という人も。
しかし、「encode」オプション(デフォルトは「Base62 encode」)をオフにすれば、
「余分な空白とコメントを除去、条件付きコメントは残す」
という処理だけを行います。これはかなり理想的な結果です。冒頭の縮小したスクリプトも、「/packer/」で作りました。
PHP 版「JavaScript Packer」はイマイチ
おお、これは!──と思って PHP 版の「JavaScript Packer」を試してみたら、これは条件付きコメントを丸ごとカットする、という仕様でした。
オンライン版はバージョン 3.0 なのに、PHP 版はバージョンは 2.0.2 のエンジンを使っているからでしょうか。
かなり高速で縮小してくれるオンライン版は、じつは「JavaScript で JavaScript を縮小」しています。書いてあるコードもキレイでわかりやすいので、PHP に移植──を、だれか、してくれないかなぁ……(どこまでも他力本願)。
もしくは、「YUI Compressor」の PHP 版、とか。