Fork me on GitHub

Graspを使ったJavaScriptのリファクタリング

Edit on GitHub 編集履歴を見る

Refactoring your JavaScript code with Grasp | Grasp - JavaScript structural search, replace, and refactor

という記事では、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で、 remoteremoteConnection という名前にリネームしたいと考えます。

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では他にも色々なユースケースが紹介されていて、

等について書かれています。


後記

今回紹介した 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を使えば細かい事ができるようになっていて使い道は色々あるので、一度触ってみるといいかもしれません。

この記事へ修正リクエストをする
JSer.info Slackに参加する