初めまして。2020年11月にChatworkに入社したフロントエンド開発部のcw-sayamaです。
Chatworkのフロントエンド ではESLintを使いやすいように設定を変更して導入していますが、以下のように扱いにくい部分が出てきました。
- 設定を変更した時に、どのルールを変えたのかが分かりにくい
- ファイルによって設定を変えているが、それぞれの設定が確認しづらい
- ESLint関連のモジュールが更新された時に設定が変更されていても検知しにくい
そこでこれらの問題を解決するべく、ESLintの設定をスナップショットテストする仕組みを作ったので、ここで紹介していこうと思います。
どのように実現しているか
ESLintには --print-config というオプションがあり、現在有効になっている設定が確認できます。
このオプションを使って現在のESLintの設定をJestのスナップショットとして出力しておけば、設定を変更した時は差分として確認できるし、ESLintの対象となるファイルごとにスナップショットを分けておけばそれぞれで設定を確認できるし、ESLint関連のモジュール更新で設定が変更されていた場合スナップショットテストがエラーになるのでそこで変更されたことを検知できます。
実際のコード
ESLintのNode.js APIにはeslint.calculateConfigForFile(filePath) という関数があり、この関数を使うことで --print-config オプションと同等の結果が取得できます。
実際のコードは以下のようになります。
// eslint.spec.ts import { ESLint } from 'eslint'; function getESLintConfig(filePath: string): Promise<any> { const eslint = new ESLint({}); return eslint.calculateConfigForFile(filePath); } test('eslint', async () => { // lintをかける対象のファイル // 実際のファイルでもいいし、テスト用に用意した空のファイルでも良い const filePath = './test-file.ts'; const config = await getESLintConfig(filePath); expect(config).toMatchSnapshot(); });
あとはこのファイルでJestを実行すれば、スナップショットファイルが作成されてESLintの設定を確認できます。
絶対パスでテストが失敗する
ただしこのままだと一点問題があって、出力された設定にはファイルパスが含まれている場合がありそのパスが絶対パスになっているので、実行した環境によって結果が異なるためテストが失敗します。
Chatworkの今の設定だとparserの部分が絶対パスが出力されているので、相対パスに変換するような処理を間に挟んで実行環境に左右されないようにしています。
// eslint.spec.ts import { ESLint } from 'eslint'; interface ESLintConfig { parser?: string; } function getESLintConfig(filePath: string): Promise<ESLintConfig> { const eslint = new ESLint({}); return eslint.calculateConfigForFile(filePath); } function parserPathToRelativePath(config: ESLintConfig): ESLintConfig { const { parser } = config; if (parser) { return { ...config, parser: parser.replace(`${process.cwd()}/`, ''), }; } return config; } test('eslint', async () => { // lintをかける対象のファイル // 実際のファイルでもいいし、テスト用に用意した空のファイルでも良い const filePath = './test-file.ts'; const config = await getESLintConfig(filePath); expect(parserPathToRelativePath(config)).toMatchSnapshot(); });
まとめ
今回はESLintの設定の可視化とスナップショットテストの実装をしてみました。同じようなことで悩んでいる方の参考になれば幸いです。