こんにちは、株式会社カミナシのエンジニア@tomiです。
今回は、開発途中からESLintのルールを見直す際にどうやって行ったかを書こうと思います。
開発途中からESLintを見直すとなると、膨大にあるコードがあるので一気に直すとなると、恐ろしい量の修正が必要です。
カミナシで理想的なESLintのルールに修正しようと思ったら、修正箇所が4000件を超えていました。。。
とても一人で直すには無理な量です。更に修正中も開発は進むわけですから、コンフリクトも頻繁に発生するでしょう。
ESLintを見直すための道のり
では、一気に直すのは無理なので、はじめに考えたのは「段階的にルールを厳しくしていく」ことでした。
何個もルールをoffにしてあるので、それを一つずつerrorにしてプルリクを作成していく
そこで考えたのは、pre-commitを使ってコミットされるファイルに対して、新ルールのESLintを実行する方法です。
他の開発を行いながら徐々にコードがキレイになっていく仕組みを作ろうと考えました。
ただし、pre-commitでESLintを実行するだけでは駄目で、以下の要件をクリアする必要がありました。
- 新ルールはガチガチの厳しいものにする
- 開発環境の起動時は旧ルールで動く
- ビルド時のlintチェックは旧ルールで動く
- 編集したファイルのみ新ルールを適用する
既存の.eslintrc.json
を厳しいルールに変更してしまうと、開発環境が起動しなくなったり、ビルドが失敗してしまったりと問題が起こるので、コミットされる時のみ新ルールが適用されるように書く必要があります。
新ルールだけをまとめた設定ファイルを作る
ESLintには設定ファイルを上書きして読み込める機能があるので、それを利用します。
例えば、.eslintrc.json
は以下だとして、
{ "env": {...}, "parser": "@typescript-eslint/parser", "parserOptions": {...}, "plugins": [...], "rules": { "no-unused-vars": "off", "no-empty": "off" } }
ESLintを実行すると、自動で読み込まれます。
$ eslint --print-config src/App.tsx ... "rules": { "no-unused-vars": [ "off" ], "no-empty": [ "off" ] }, "settings": {}, "ignorePatterns": [] }
さらに、別ファイル.eslintrc-override.json
を作成する
{ "rules": { "no-unused-vars": "error" } }
こちらには、.eslintrc.json
でオフ設定したルールに対して、エラーになるように設定した。
.eslintrc-override.json
を-c
オプションを使って読み込むと、
$ eslint -c .eslintrc-override.json --print-config src/App.tsx ... "rules": { "no-unused-vars": [ "error" ], "no-empty": [ "off" ] }, "settings": {}, "ignorePatterns": [] }
.eslintrc.json
の上に.eslintrc-override.json
が上書きされていることがわかる。
このオプションを使えば、pre-commit時だけ別ルールというのは比較的に簡単に実装できそう。
予期せぬ上書きが起こる
ただし、注意して設定を行わないと思っていたルールと違う設定になってしまう場合がある。
それが、上書きする側.eslintrc-override.json
にextends
を使うときである。
.eslintrc-override.json
を以下のようにextends
を追加してみる。
{ "extends": ["eslint:recommended"], "rules": { "no-unused-vars": "error" } }
そして先程同様にESLintを実行すると、
$ eslint -c .eslintrc-override.json --print-config src/App.tsx
**.eslintrc.json
でoffにしていたはずが、errorになっている。**
eslint:recommended
では内部では"no-empty": ["error"]
が設定されているためである。
中身を把握してextendsを設定するなら問題ないが、意図しないルール変更が行われる匂いがプンプンする。
そこで、元々あったルールで変更したくないものがあるときは、更に別ファイルに分けて、.eslintrc.json
と.eslintrc-override.json
に読み込ませるのが安全だろう。
最終的な形としては、以下の3ファイルを用意する。
// .eslintrc.json { "env": {...}, "parser": "@typescript-eslint/parser", "parserOptions": {...}, "plugins": [...], "extends": [ "./.eslintrc-rules.json" ], "rules": { "no-unused-vars": "off" } }
// .eslintrc-override.json { "extends": [ "eslint:recommended", "./.eslintrc-rules.json" ], "rules": { "no-unused-vars": "error" } }
// .eslintrc-rules.json { "rules": { "no-empty": "off" } }
extends
で変更したくないルールをそれぞれのファイルに読み込ませることで、上書きされる危険性がなくなる。
pre-commit時のみ新ルールを適用する
これはもう説明も不要かもしれませんが、記録に残して起きます。
CIで実行されるlintチェックは、yarn lint
で実行させます。
pre-commitはhuskyを使って、.eslintrc-override.json
を適用させます。
"scripts": { "lint": "eslint ./src" } "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "./src/**/*.{js,jsx,ts,tsx}": [ "prettier --config .prettierrc --write", "eslint -c ./.eslintrc-override.json" ] },
これでコミットするファイルだけ厳しいルールが適用され、ルールを守らないとコミットできない仕組みができました。
4000件以上の修正はしないといけないのは変わらないですが、みんなで少しずつコードの整理をしていきましょう!!
将来的に
まだ実現できてないのですがやりたいことがあります。
それは残りのエラー件数を定期的に通知する仕組みです。
今回実装したものの課題は、このままだとずっとビルド時のlintチェックは緩いままで決して健全とは言えません。なのでいつかは新ルールがすべての場所に適用されるのが理想です。
そのためには、エラーがなくなったかを知る仕組みが必要かなと思いました。
いつか、yarn lint -c ./eslintrc-override.json src
が定期実行されて、Slackに通知される仕組みも作っておきたいなぁと思っています。