textlint 3.6.1でコードベースをES6に書き直して、特に問題無く動いてるっぽいのでどういう手順で書き換えたかについての話。

具体的な作業は以下に残ってます。

事前準備

  • テストを書く

テストが書かれてると変換した時にエラーがスグ発見できるのでカバレッジが高いテストがあると安心して変換出来ます。

面倒な時はExampleテストを追加してみるといい気がします。

そのライブラリのユースケースを考えて幾つかのパターンを実行出来るExampleテストを作っておくと、普通に実行してエラーにならないことが保証できるのでカバレッジが上げやすい気がします。

textlintの場合は普通にコマンドラインで叩く場合.textlintrcの設定ファイルを使う場合--rulesdirを使う場合と3つぐらいのExampleテストを事前に追加しました。

xto6で書き換え

まずxto6を使って大雑把にコードをES6に書き換えました。 まず、lib/に現状のES5なコードを置いておいて、ES6のコードを置くsrc/ディレクトリを作っておきます。

.
├── LICENSE
├── lib (ES5のコード)
├── src (ES6用のディレクトリ)
└── test

そうしたら、lib/ディレクトリ内で以下のようにしてそれぞれファイルを変換した感じです。

find . -type f -not -path "*node_modules*" -name "*.js" -print0 | xargs -0 -I % xto6 % -o ../src/%

これで、constとかclassとかの書き換えはある程度いい感じにできますが、 いくつか変換バグ的なものもありました。

  • コンストラクタ関数の仮引数が消える
    • classに変換された時になぜか仮引数が消えるので付け加えた
  • try/catch内でのvarletconstになって解決できなくなる
    • Babelとかでコンパイルすれば普通にわかるのでそれで直せる

とかぐらいで、escodegenがまだES6をフルサポートしてないのもあるので、全ての変換が上手く出来るわけではないですが、意外といい感じに変換してくれると思います。 ある程度テスト書かれてれば、テストが落ちやすい部分だったのでどこが間違ってるのか分からない的な変換はなかったと思います。

テストをES6対応する

元々のテストは以下の構成でした。

これをテストからそのままsrc/にあるES6のファイルを使ってテスト出来るように以下の構成に変更しました。

テストからlib/を参照している場合はsrc/に書き換えたのと、mocha.optsファイルを一行変更しただけで済みました。

- --require intelli-espower-loader
+ --compilers js:espower-babel/guess

細かいリファクタリング

xto6みたいに構造的にそのまま一致しない場合は変換できないので、いいタイミングだったので一緒にリファクタリングした。

パッケージの情報を書き換え

ライブラリをES6で書いて公開する所から始めようでやっているES6で書いたライブラリ構成になるということなので、package.jsonを色々書き換えます。

  • "main"のパスが変わってるなら変更
  • .gitignore/libを追加
  • lib/をGitの管理下から外す(git rm)

JSCSでスタイルチェック

元々JSCSを導入してありましたが、 JSCSはES6も対応してるので、特に設定は必要なくそのままES6でも使えます。

jscs -x src/

xto6でファイル末尾の改行が消えるので、jscsの--fix (-x)オプションを使えば直せます。

おわり

後はマージしてpublishしただけです。

textlintは元々処理をモジュールで分けて使ってるので、コアとなるのは1000行程のプロジェクトです。

実際にやってみてES6だと100行弱ぐらいは減ってたので1割弱ぐらいはコードベースが小さくなる気がします。 大部分はxto6の自動的な書き換えでよくて、単純にES6で動くだけの変換なら30分ぐらいでできた気がします(併せて色々リファクタリングしたのでもうちょっとかかった)

書き換えたくなった理由としては、デフォルト引数使いたい場面やArrow Function使えばこのthatいらないのにと思ったり、これclassで良いよな的な状況がちょくちょくでてきたので書き換えました。

書き換えしなくてもBabelで変換するだけならES5のコードそのものをBabelで変換しても問題ないはずですが、中途半端に追加していくのはダルそうだったのでザクっと変換しました。

書き換えするのにあるといいのは、カバレッジが高いテストコードですが、今回はそれの補強材としてExampleテストを幾つか追加してから変換しました。

textlintの作りはESLintの作りを大体継承してますが、 ES5だとやっぱり見た目からどういう状態を持ってるのかが分かりにくい問題が起きやすい部分があると思います。

特にeslint.jsapiという巨大なシングルトンみたいな感じになっています。 このシングルトンの扱いをclass自体とそれをnewしたシングルトンオブジェクトをそれぞれexportするというパターンに修正したかったのもあります。

今回のES5 to ES6のリファクタリングでコード自体は見やすくなったのではないかなーと思います。