@1000ch (id:hc0001) です。掲題の通り、少し前にドクターズプライムの Frontend プロジェクトで使う lint ツールとして ESLint ではなく XO を使っていく方針に切り替えました。最近その振り返りを行ったので、その備忘録として文字に起こします。
経緯と課題
これまでは Create React App に付属する ESLint に加えてルールを少しカスタマイズして、それをいくつかのプロジェクトで使っていました。これにはいくつかの課題が存在していました。
- ESLint およびその周辺プラグインの依存関係を含めたバージョンアップをケアし続ける必要がある
- renovate や dependabot などを用いて(半)自動化できるものの、依存の数や大きさに応じて依然としてコストが高い
- ESLint のルールを中長期的にメンテナンスする必要がある
- 「このルールは採用する」「このルールは気にしない」など、妥当性の確認が難しく、往々にして好みが介在しやすい
- プロジェクト間で統一された lint ルールがなく、担当プロジェクトが変わった時のキャッチアップが難しい
これらの課題は、lint を適用するプロジェクトの数・大きさ・経年などに応じて、複合的に大きくなっていきます。ソースコードの、より効率的な品質維持のために lint ツールを切り替える提案をしました。
Frontend の lint 事情
JavaScript や TypeScript の lint には、複数の流派もとい選択肢が存在しています。古くから有名なのは Airbnb の JavaScript Style Guide に同梱される eslint-config-airbnb や GitHub の eslint-plugin-github などでしょうか。しかし、これらに関しても組織それぞれの好みを含めたルールが反映されています。また、大きな組織だからといって永続的にメンテナンスされる保証はなく、Google の eslint-config-google ですら放置されている状態からわかるように、それぞれの組織の事情はあれど、自分たちで維持・運用を続けていくのは相応のコストが伴います。
Go のように、公式で提供される有無を言わせないフォーマット機構があれば良いのですが、幸か不幸か Frontend 界隈にはそれがありません。「opinionated だけど、とりあえずこれに従ってください系」のフォーマッターという意味では Prettier がそうした立ち位置には近いと思いますが、文法の複雑さに応じて書き方に対しても lint が必要というところで ESLint と併せて適用することが望ましいでしょう。Prettier と ESLint のコンビネーションについては以下の記事がよくまとまっています。
- ESLint と Prettier の共存設定とその根拠について | blog.ojisan.io
- Prettier と ESLint の組み合わせの公式推奨が変わり plugin が不要になった | blog.ojisan.io
- ESLintとPrettierを併用するときの設定(eslint-plugin-prettier, eslint-config-prettier) - dackdive's blog
なぜ XO なのか
挙げた課題を大幅に改善してくれる、というか XO のコンセプトがまさにこれらの課題感から生まれています。
Opinionated but configurable ESLint wrapper with lots of goodies included. Enforces strict and readable code. Never discuss code style on a pull request again! No decision-making. No
.eslintrc to manage.
It just works!GitHub - xojs/xo: ❤️ JavaScript/TypeScript linter (ESLint wrapper) with great defaults
これによって、XO のバージョンアップデートだけ気にすれば良く、XO のルールに従うことさえチームで握ればルールを運用する必要がない、そして XO を統一的に横断して導入することでキャッチアップのコストが小さくなります。
ルールについては sindresorhus 氏の opinionated であり、それも好みが分かれるといえばそれまでではありますが、個人的には納得感のあるルールが大半です。カスタマイズしないことに重きを置いたツールであり、カスタマイズしないことも今回の提案の一部ですが、何が何でも譲れないルールは上書きして良いでしょう(実際、ドクターズプライムでもタブインデントではなく 2 スペースインデントにしています)。ただ、ルールの上書きをし始めるとキリがないですし、中長期的な各種コストを減らすためにも限定的にすべきです。
XO そのものが継続的にメンテナンスされていくのかも気になるところですが、少なくとも現時点では活発にメンテナンスされています。まだメジャーバージョンがリリースされておらず、時折 breaking change が含まれるのが玉に瑕ですが、頻発しているわけでもなく xo --fix
で自動的に修正できるので呑める我慢処として捉えています。
「ルールのカスタマイズを極小化したい」という点だけにフォーカスするならば、eslint-plugin-react の plugin:react/recommended
や @typescript-eslint/eslint-plugin の plugin:@typescript-eslint/recommended
といったように「ESLint の各種プラグインが推奨するプリセットルールのみを使用する」というアプローチもありそうです。しかし「どのプラグインを組み合わせて使うのか、あるいは ESLint 本体とのバージョン互換性はどうなのか」などに対応するコストが残ってきますし、プリセットのルールでは緩く、より実装のブレを減らそうとするともう少し強固なルールセットが必要そうに思います(ただし、後者に関しては状況や握り方次第だと思います)。
変更してみて
課題感だったパッケージやルールのアップデートに関するコストは、期待通り緩和されています。既存プロジェクトに対して、変更された lint ルールの適用は xo --fix
でも自動で修正しきれないため現在進行中ですが、このコストは徐々になくなっていくでしょう。