なぜこのツールが必要になったのか?
レガシーJS を prettier、eslint、tsc を使ってリファクタリングしていたときのことです。テストコードなし、E2Eテストは一部だけです。
prettier をかけると、インデントの他にもかなりの量の diff ができました。細かく見ていくのは骨が折れそう。prettierはフォーマッターだから動きは変えないはず。えいやっでマージしようとも思いましたが、これまでの数々のやってもうたが頭をよぎり、踏みとどまりました。
そこでリファクタ前後の js の AST(抽象構文木)のハッシュ値を比較することにしました。
AST(抽象構文木)とは
ソースコードの見た目が違っても、ASTが同じなら、同じプログラムです。不具合も含めて、稼働中のプログラムと同じだから、diffが大きくても安心してリリースしてできるよ、という技術的な根拠です。

ASTから除外するもの
prompt.md は用意しませんでしたが、ブラウザ版Gemini に初期バージョンを実装してもらい、標準入力バージョンに改造。
実際に使いながら、Geminiに相談して、ASTをJSON化する前にキーをソートしたり、ASTから削除する項目を調整しました。
ミニファイのハッシュ値はボツ
ミニファイしてハッシュ値を比較する案もありましたが、引用符の種類(' か " か)やセミコロンの有無にハッシュ値が左右されてしまいそうです。
JSのAST(抽象構文木)を試してみると、引用符の種類(' か " か)やセミコロンの有無が違っても、ASTは同じでした。
他にもミニファイの「変数名の圧縮」「糖衣構文を使った最適化」は、決定論的かどうかがわかりませんでした。ここをつきつめて調べるよりも、ASTを使うことにしました。
工夫したこと
diff の原因ごとに、PRを分けました。
- インデント、空行
- 関数やブロック開始の波カッコの位置
- 行末セミコロンの有無
- 文字列の引用符をシングルクォートからダブルクォートへ揃える
- js内のオブジェクトリテラルのキーのクォートが削除される
修正前後で ASTが同じなら、動作確認なしで。ASTが違うときは、同じ種類の原因だけに揃えてから、かんたんなページ表示できることを確認しました。
tscを使いながら、JSDocのキャストを追加するときは、JSDoc追加の前後でASTが一致することを確認して、純粋に型注釈だけを足したことを確認しました。なぜなら、レガシーJSを見ているとうっかりリファクタしていることがあります。意図しない修正が紛れ込まないように注意しました。
学習の成果
AIとの協働: プロンプトをガチガチに固めなくても、ブラウザ版Geminiと対話しながら、自分の欲しい『ちょうどいいツール』を爆速で作ることができたこと。
