という記事では、Grasp を使ったJavaScriptコードのリファクタリングについて書かれています。
この記事は Refactoring your JavaScript code with Grasp | Grasp - JavaScript structural search, replace, and refactor の簡単な紹介です
Grasp
Grasp は JavaScript ASTを元にgrepのような検索、sedのような置換などを行えるコマンドラインツールです。
GraspはNodeで(実際にはLiveScriptで)書かれているツールで npm
経由でインストールできます。
npm install -g grasp
紹介されてるユースケースはインストールしなくても、Demoから実際に入力して試すことが出来ます。
識別子のリネーム
Renaming an identifier では、 JavaScriptの識別子 - 変数や関数名のリネームを例にASTを元にした置換のメリットが紹介されています。
例えば、以下のようなJSで、 remote
を remoteConnection
という名前にリネームしたいと考えます。
var remote = createConnection('remote', 'user', false);
remote.send(msg);
remote.on('receive', function (data) {
if (data) {
remote.send(process(data));
} else {
remote.end();
}
});
この時に、単純な文字列置換(sedで 's/remote/remoteConnection/g'
のように)した場合は、文字列である 'remote'
も置換されてしまいます。
var remoteConnection = createConnection('remoteConnection', 'user', false);
Graspでは、文字列とSqueryというCSSセレクタライクなASTセレクタを利用してどのように処理をするかを指定します。 (別途Example Queryという別の書式を利用するオプションも用意されています)
そのため、remote
という 識別子(Squeryでは#を付ける)を remoteConnection
という風にリネームする場合は以下のように指定すれば識別子だけをリネーム出来ます。
$ grasp '#remote' -R remoteConnection file.js
Result :
var remoteConnection = createConnection('remote', 'user', false);
remoteConnection.send(msg);
remoteConnection.on('receive', function (data) {
if (data) {
remoteConnection.send(process(data));
} else {
remoteConnection.end();
}
});
Grasp はデフォルトでdry-runとなっていて、上記のコマンドでは標準出力に結果が表示されるだけですが、 実際にファイルの内容も置換したい場合は -i, —in-place オプションを指定することで行えます。
Refactoring your JavaScript code with Grasp | Grasp - JavaScript structural search, replace, and refactorでは他にも色々なユースケースが紹介されていて、
- Multiple arguments to a single options object では引数を一つのオブジェクトにまとめるリファクタリング
- Reducing the scope of your changes では特定のスコープ(関数内)ある変数だけをリネーム
- Refactoring a function call では、引数に指定している値によって別々のリファクタリング
something(true)
とsomething(false)
で別々のリファクタリングを適応するような事
- To Yoda conditions, we change では、
==
の演算子の左右を反転させるリファクタリング - Changing how you do default arguments では、デフォルト引数のリファクタリング
等について書かれています。
後記
今回紹介した Renaming an identifier のような簡単なものなら、WebStormのようなIDEには同等の事ができるかもしれませんが、 IDEの置換は基本的に一つのコンテキスト(変数とそれを参照してる)が範囲になるので、ファイルを超えた適応やChanging how you do default argumentsのような変更をまとめて行うのは難しいはずです。
Grasp ではsquery or equery という多少セレクタの書き方を学ぶ必要がありますが、 かなり柔軟でASTを元に置換をするので単純な置換ツールよりは正確なものが行える事が特徴的です。
他にも似たような事をするJS ASTを元にしたツールとしては、GHCの置換ルールのようにコメントで置換する内容を指定して行うRephrase等や、自分が実験的に書いたQunitをJasmineのテストに変換するreQUnitもJS ASTを元にしたツールです。
この辺のツールやライブラリについては tkbjsでJavaScript ASTについて発表してきました | Web scratch 等を見ていただくといいかもしれません。
他の言語でもPerlでメソッドが呼ばれたときに呼出元のソースコードを書き換えるやつ - hitode909の日記 や QafooLabs/php-refactoring-browser など似たような趣旨のツールがあったりします。
Grasp は結構完成度が高くて、equeryの方は学習コストも小さく使えて、 squeryを使えば細かい事ができるようになっていて使い道は色々あるので、一度触ってみるといいかもしれません。