ESLintのFlat Configへの移行は進んでますでしょうか?試してみたでしょうか?
今回はドワンゴの新卒エンジニアが初仕事として取り組んだ、ESLintのFlat Configへの移行に関して「その方法と嵌ったところの乗り越え方」をお伝えします。
この記事で言及すること
- Flat Configに書き変えるときに見る資料
- ESLintのconfigをFlat Configに移行するとき、configs.recommendedなどのプリセットを用いる場合はFlatCompatを使う
- eslint-plugin-importを使用してると嵌る
- どうやって新旧configが同じになっていることを示すのか?
ESLintのFlat Configを書くことになりました
こんにちは。N予備校 Web フロントエンド開発チームのsokunoです。私は現在4ヶ月に渡る新卒研修を経て、この8月から今のチームに参加しました。
ドワンゴの新卒研修はN予備校の教材を使ったWeb開発の基礎に始まり、ブログサービスを作ったり、動画投稿サイトを作ったりと、非常に実践的で配属後の業務でもその経験は非常に役立っています。その詳細は他のメンバーが目下執筆中のようですので、公開されたら是非読んでみてください。
チームに参加してからは、開発で使用するwebpackやstorybookなどのツールを学ぶため、ちょうどよいissueをピックアップしていただいて勉強しながら取り組んでいます。
今回のESLintのFlat Configへの移行もそのissueのひとつです。
意気揚々と手をつけた私ですが、そこについていた先輩のコメント「新しい設定ファイルの形式は私も詳しくないので」や、チームのリーダーのberlysiaさんが放った「Flat Configへの移行は嵌ったなぁ」というセリフが、不穏な影を落としていることに、残念ながらまだ気づくことができませんでした。
どうして移行するのか?
2023年10月10日の記事"Flat config rollout plans"によれば、eslintrcは近々リリースされるv9以降で非推奨になり、2024年末ごろにリリースが計画されているv10では削除されるそうです。
そのため、私達のようなCLIユーザーに関していえばv10までにはFlat Configへの書き換えを完了しておく必要があります。
Flat Configに移行するには
ESLintの設定をFlat Configに書き直す方法に関しては、以下の公式ドキュメント(migration guide)で概ね網羅されています。
https://eslint.org/docs/latest/use/configure/migration-guide
また、それだけでなくFlat Configのドキュメントと、これまでのconfig(以下、eslintrc)に関するドキュメントを見比べることも役立ちました。
作業方針を決める
Flat Configに書き換えるために、ESLintのドキュメントを眺めていた私は、
- とりあえず、上記のmigration guideに沿って書き写す
ことに決めました。
また、新旧のconfigが同じになっていることを確かめる必要があると考え、主に以下の2点を確認することにしました。
- lintが適用されるファイルが変わっていないか
- eslintrcとFlat Configで、ファイル毎に適用するルールが同じになっているか
で、何につまずいたのさ?
書き換えていくにあたって、どのプロパティをどこに書き換えていけばよいかといった、事細かな説明はmigration guideを参照していただくこととしましょう。
私もmigration guideにしたがって書き換えていき、見かけ上は問題なさそうなeslint.config.jsができあがりました。
そのため、eslint.config.jsを読ませてESLintを実行します。「行けてくれー!」と思いながらEnterキーを押しますが、当然ながらすんなりとはいきません。Flat Configに対応していないpluginがいくつかのエラーを吐き、その対応をすることになりました。
次の2節で発生したエラーと対処方法を説明します。
つまずきポイント①: A config object has a "plugins" key defined as an array of strings. みたいなのが出る
例えばeslintrcでpluginのrecommended
などをextends
に入れていた場合、Flat Configでは以下のように書きたくなります。
//.eslintrc.js の一部 (これだけでは動作しません) module.exports = { extends: [ "eslint:recommended", "plugin:react/recommended", "plugin:import/typescript", ......
// eslint.config.js の一部 (これだけでは動作しません) module.exports = [ js.configs.recommended, reactHookPlugin.configs.recommended. reactPlugin.configs.recommended, importPlugin.configs.typescript, ......
しかし、現時点ではFlat Config形式をサポートしていないpluginもあり、表現方法が変わった設定を含むものを加えようとするとそのkeyが正しい形式でない旨のエラーが出るようです。
私の場合は以下のエラーが頻出しました。
A config object has a "plugins" key defined as an array of strings.
この問題に関しては、eslintrc形式のconfigをFlat Config形式に変換するために、FlatCompatが提供されています。 これを用いて以下のように書き換えることができます。
// eslint.config.js の一部 (これだけでは動作しません) const { FlatCompat } = require("@eslint/eslintrc"); const compat = new FlatCompat({}); module.exports = [ // プラグインの対応に応じてcompatをやめる js.configs.recommended, //互換性に問題なければこれでOK ...compat.extends("plugin:react/recommended"), ...compat.extends("plugin:react-hooks/recommended"), ...compat.extends("plugin:import/typescript"),
こうすることでほとんどのpluginのconfigs
がFlat Configでも使用できるようになります。
つまずきポイント②: import/namespace のルールがエラーになる
次につまずいたのは、eslint-plugin-importの一部のルールで以下のようなエラーが出ることでした。
4:44 error Parse errors in imported module '@testing-library/dom': parserPath or languageOptions.parser is required! (undefined:undefined) import/namespace
①はドキュメントにも書いてある話になりますが、こちらはpluginのコードを眺めながら原因と回避策を探すくらいに手こずってしまいました。
最終的には、以下のissueが参考になりました。
https://github.com/import-js/eslint-plugin-import/issues/2556
要点はsetting["import/parsers"]
で拡張子を網羅することだと考えられます。
そのため、issue中で提案されていた以下のような記述で改善できる可能性があります。
// https://github.com/import-js/eslint-plugin-import/issues/2556#issuecomment-1555998681 // eslint.config.js の一部 (これだけでは動作しません) "import/parsers": { espree: [".js", ".cjs", ".mjs", ".jsx"], "@typescript-eslint/parser": [".ts"], },
私達の場合は全部 @typescript-eslint/parser
に食わせました。
// eslint.config.js の一部 (これだけでは動作しません) 'import/parsers': { "@typescript-eslint/parser": [".js", ".jsx", ".ts", ".tsx"], },
ルールが新旧configで一致しているかを確認する
さて、こうしたエラーとの戦いを乗り越えて完了!というわけには行きません。作業方針でも述べたように、eslintrcからFlat Configに移行したことによって欠落してしまった設定がないかを確かめる必要があります。少なくとも、①対象となるファイルが変わっていないか、②適用されるルールが変わっていないかは確認したいところです。
以下ではその2点に関して私がどのように確かめたかを書き残します。
①対象となるファイルが変わっていないか
これは、eslintを実行したときにどのファイルがlintされたかを知る必要があります。--debug
オプションを指定したときに出力されるeslintのデバッグ出力を活用して、lintが適用されたファイル一覧を作成します。
Flat Configでの一覧は以下のようなコマンドで取得でき、
npx -c ' eslint --debug "**/*.{js,ts,tsx}"' |& grep -Po "(?<=Parsing\ssuccessful:)\s\S*"
eslintrcでの一覧は ESLINT_USE_FLAT_CONFIG=false
を定義して同様に実行すれば取得できます。
npx -c 'ESLINT_USE_FLAT_CONFIG=false eslint --debug "**/*.{js,ts,tsx}"' |& grep -Po "(?<=Parsing\ssuccessful:)\s\S*"
単に行数が同じであるかを確認するのでも十分そうですが、それぞれのconfigで出力されるファイルの順番は変わってしまいます。そのため、diffを見る場合は行をソートする必要があります。
②適用されるルールが変わっていないか
これはeslintコマンドの --print-config
オプションを使って確かめられます。このオプションを使うと、任意のファイルに適用される設定がJSONで出力できます。この中のrules
がeslintrcのときと、Flat Configのときで変わっていないかを確認します。
$ npx eslint --print-config hoge.js { "settings": { //略 }, "linterOptions": { //略 }, "languageOptions": { //略 }, "plugins": [ //略 ], "rules": { "constructor-super": [ 2 ], "for-direction": [ 2 ], "getter-return": [ 2 ], //以下略 } }
overrideがされていないものと、overrideごとに該当するファイルをひとつ以上選んで確かめます。もちろん、①ですべてのファイルのパスが判明しているのでそれを活用して全てのファイルをチェックしても良いかもしれません。
eslintrcとFlat Configのそれぞれで出力されるrules
オブジェクトが得られたら比較していきます。オブジェクト同士の比較にはdeepStrictEqualを用いることができます。
注意点として、rules
オブジェクトのseverity
が0
, 1
, 2
で表されている場合とoff
, warn
, error
で表されている場合があるので、それはどちらかに統一して比較する必要があります。
まとめ
今回はFlat Configへの移行においてつまずいたところと、正しくできたかを確認するための方法という2点に関して述べました。
私自身もESLintに詳しくないまま移行作業をしたので、果たして満点回答かはわかりません。また、ESLintやpluginのアプデによって状況は変わりますので最新の状況をご自身でも是非ご確認いただくようお願い致します。
もし、同様のエラーを踏んでしまった方にはその解決の一助になれば幸いです。
関連リンク
Flat config rollout plans
ESlintのFlat Configに関する今後の展望を説明するブログです
Configuration Files (New)
Flat Configのドキュメントです
Configuration Files
eslintrcのドキュメントです
Configuration Migration Guide
eslintrcからFlat Configへの移行ガイドです
ESLint's new config system, Part 2: Introduction to flat config
Flat Configの紹介ブログです
We are hiring!
株式会社ドワンゴの教育事業では、一緒に未来の当たり前の教育をつくるメンバーを募集しています。 カジュアル面談も行っています。 お気軽にご連絡ください!
開発チームの取り組み、教育事業の今後については、他の記事や採用資料をご覧ください。