こんにちは。kickflow エンジニアの芳賀と申します。
今回は入社後初めて行った「ESLint の flat config 移行」についてお話します。
自身も久しぶりにガッツリ ESLint と向き合う機会だったので初心に帰り、調べ調べ行いました。
ESLint の eslintrc はなぜ廃止の方向になったのか?
結論、年を追う事に複雑化してしまったのが要因です。
はじめはシンプルな記載だったのですが、漸進的な改修が積み重なり、扱いづらい成長を遂げてしまいました。
その歴史を振り返りつつ、どのような成長を辿ったか振り返ってみます。
まずは extends
key (ルールの継承)の導入され、便利になった感じがします。
{ "extends": ["./other-config.json"], "rules": { "semi": "warn" } }
これだけならまだ…という感じもしますが、その後に override
key (ルールの上書き)が導入され、少しづつ複雑性が増し、
{ "rules": { "quotes": ["error", "double"] }, "overrides": [ { "files": ["bin/*.js", "lib/*.js"], "excludedFiles": "*.test.js", "rules": { "quotes": ["error", "single"] } } ] }
極めつけは override
key の中で extends
が使えるようになり、「ESLint って難しい」と思えるような構文になってきました。
{ "rules": { "quotes": ["error", "double"] }, "overrides": [ { "files": ["bin/*.js", "lib/*.js"], "excludedFiles": "*.test.js", "extends": ["eslint:recommended"], "rules": { "quotes": ["error", "single"] } } ] }
また、上記のサンプルは簡単のための記載なのでまだわかりやすいですが、実開発の現場だと他のプラグインが入ったり、良からぬところで override
されないよう気をつけて改修したりと、目に見えない不安とも戦っているかと思います。ESLint の作者もシステムの複雑さに不安を感じ flat config へのモチベーションとなっていったそうです。
ESLint の flat config とは?
ESLint v9.0.0 とともにリリースされた ESLint の新しいフォーマットとなります。
(新しい、とはいいつつも flat config の RFC は 2019 年から合ったとのことです)
各種設定を配列のようにフラットに記載できるのがメリットで、 より馴染みやすく、より強力に設定できることを狙い としています。
その中でも中核をなす変更といえば extends
key の削除と、flat cascade でしょうか。
そのため、振る舞いとしては配列の先頭から末尾に向かって設定がマージされていく形になります。
import customConfig from "eslint-config-custom"; export default [ customConfig, { files: ["**/*.js", "**/*.cjs"], rules: { "semi": "error", "no-unused-vars": "error" } }, { files: ["**/*.js"], rules: { "no-undef": "error", "semi": "warn" } } ];
説明がなくとも、「外部から設定を持ち込んで、順番にルールを書き換えているんだな」というのが一発でわかるようになっているかと思います。これだけでも十分可読性が向上していますね。
どのように移行するか
さて、このような flat config がリリースされた一方、kickflow では eslintrc のまま利用していました。
しかし、ELint v10.0.0 がリリースされる際に既存の eslintrc フォーマットは削除される予定 のため、このタイミングで対応することとなりました。
現状を調査
まずは現状の調査です。一般的な eslintrc のフォーマットとして以下になるかと思いますが、extends 、plugins 、rules 、overrides のそれぞれのセクションで影響度合いを調べることにしました。
(実際のコードについてはご了承ください…)
module.exports = { root: true, extends: [ // 調査箇所① ], plugins: [ // 調査箇所② ], rules: { // 調査箇所③ }, overrides: [ // 調査箇所④ ] }
調査箇所①、調査箇所②
vue + nuxt + prettier 、と基本的なものに加えて、a11y や i18n などで構成されており、特段複雑性を持ったプラグインなどはありませんでした。また、ほぼすべてのプラグインが flat config に対応してたため、移行コストも軽微であると見積もれました。
実は CTO と入社前面接で ESLint の話で盛り上がったのですが、その際に「なるべく不要なプラグインは入れない、シンプルに構成する」という方針で進めている旨の会話をしており、その結果かなと思っております(個人的にもこの考えはアグリーです)。
調査箇所③
基本的にプラグインのルールを単純に書き換えている & プラグイン同士も競合し合っていないので特段問題ありませんでした。
ただ、様々なプラグインのルールがベタ書きされており、この時点で「ルールのセクション分割」については必要そうと見据えていました。
調査箇所④
こちらは複雑な override がなく、flat config に吸収できる程度のものだったので割愛します。
案を考える
事前案として「Progressive に移行する」という案も合ったのですが、上記の調査結果を踏まえると段階的に移行するよりも一気に移行したほうがコストパフォーマンスが低く、以下の方針で進めることにしました。
- 一気に移行を行う(中間状態や compat を利用しない)
- 調査結果から、設定がシンプル = 移行手順を踏むこと自体がオーバーヘッド
- ルールの記述は量が多いため、 必要なセクションごとに切り出しを行う
結果
以下のようになりました。 (実際のコードについてはご了承ください…)
各設定ごとに分割して記載することができ、可読性・保守性が大幅に向上することができました。
// ① 各設定ごとに分割して記載 const jsLintConfig = [ // 各種設定 ] // ① 各設定ごとに分割して記載 const vueLintConfig = [ // 各種設定 ] // ② 最後に合成 export default withNuxt( ...jsLintConfig, ...vueLintConfig, .... )
振り返り
- タイミングが良かった
- flat config が出たばかりのころだと対応・未対応で四苦八苦しているところだったかと思います
- ある程度 eslintrc がシンプルな構成だったのも功を奏しました
- ある程度整ったタイミングで一気に移行できたのは開発者視点でとても楽でした
- 改めて 技術を取り入れるタイミングはとても大事 であると実感しました
- flat config が出たばかりのころだと対応・未対応で四苦八苦しているところだったかと思います
- 書き心地がよい
- セクションごとに分けて記載できる点が大きいです
- とにかく可読性に優れています
- また、前述の通り設定ごとに変数を分けて定義することができたので、可読性・保守性が向上させることができました
- これからはよりルールが最適化できないか見ながら進めていきたいと考えています
- セクションごとに分けて記載できる点が大きいです
We are Hiring!
kickflow(キックフロー)は、運用・メンテナンスの課題を解決する「圧倒的に使いやすい」クラウドワークフローです。
サービスを開発・運用する仲間を募集しています。株式会社kickflowはソフトウェアエンジニアリングの力で社会の課題をどんどん解決していく会社です。こうした仕事に楽しさとやりがいを感じるという方は、カジュアル面談、ご応募お待ちしています!