ESLint の FlatConfig についての Blog を読んだ
ESLint の設定ファイルの形式が変わった
- ESLint v8.21.0 (#16149) で
FlatESLint
が実装された- FlatESLint は新しい
Primary Node.js API
を表現するクラスで、このクラスが設定ファイルを “flat config” として扱う
- FlatESLint は新しい
- ESLint v8.23.0 (#16235) で CLI が
eslint.config.js
を探すようになった- 2023/04/26 の時点で latest version が v8.39.0 なので、もう、使える 🔥
- 古い書き方は v9 で非推奨になる (ref)
Blog を読んでみる
“flat config” 関する ESLint のブログがあったので、メモを残しておく。
- Part 1: Background
- Part 2: Introduction to flat config
- Part 3: Developer preview (… は、開発者向けの情報なので、飛ばしちゃう)
Part 1: 背景
ref: Part 1: Background
- ESLint が初めてリリースされた当初はシンプルだった
- .eslintrc ファイルでルールの有効/無効を制御する
- 特定のディレクトリのルールを簡単に上書きする (コンフィグカスケード)
- リント対象のファイルと同じディレクトリにある.eslintrc ファイルを探す
- 次にディレクトリ階層を上がってルートに到達するまで、途中で見つかったすべての.eslintrc ファイルから設定をマージする
- 現状の構成システムは複雑
- 簡単にするための新しい構成システムを提案した
現状の構成システムは複雑
分かる…。 “extends” とか “overrides” とか “overrides 内の extends” とか “extends 先の overrides” とか…最終的なリントの振る舞いの把握が難しい…。
Incremental changes leading to maximum complexity
incremental approach
によって改善を進めた (ため、複雑になってしまった)
extends
key で別の設定をインポート出来るようにした- 自動的に個人の設定ファイル(
~/.eslintrc
)を探すようにした .eslintrc
に加えて、複数のファイル形式(.eslintrc.json
,.eslintrc.yml
,.eslintrc.yaml
,.eslintrc.js
)もサポートした- Shareable configs and dependencies
- ちゃんと読み取れなかったけれど、ESLint 内部で行っている依存関係の解決 (
plugins: ["react"]
みたいに文字列で入力したもの?) と npm の peerDeps 周りの問題かな…?
- ちゃんと読み取れなかったけれど、ESLint 内部で行っている依存関係の解決 (
- 把握しきれないコンフィグカスケードによる設定の上書き対策のために、
root
key を追加した - glob ベースで設定を上書きするために
overrides
key を追加した overrides
内でextends
key を使えるようにした
複数のファイル形式
「どの形式で設定ファイルを書いても、設定値は js object に変換して ESLint のコア実装の引数になるから、設定ファイルの形式とコア実装に依存関係はない」と勝手に想像していたけれど、現実は複雑だったみたいだ…。
glob ベースで設定を上書きするために
overrides
key を追加した
今まであまり深く考えていなかったため、「複数の .eslintrc
によるカスケード」と「overrides
によるカスケード」が混在したリポジトリを作っていたことを思い出した。
そして、ちゃんと期待通りルール設定になっているかが自信無くなってきた…。
それぞれに対応する ESLint のモチベーションが同じならば、せめて、どちらかに統一して使用するべきだったな…。
The need for simplification
- 最終的な設定値の計算方法を把握出来なくなってきた
- 1 年半 の RFC 提案 (eslintrc 捨てる/捨てないを含めた議論) の末に、新しい構成システムに着手した
1 年半 の RFC 提案
Flat Config の実装を追跡する GitHub Issues (eslint/eslint#13481) や、 Flat Config の議論 (eslint/rfcs#9) を覗くと、巨大な OSS のリアルな苦労を目の当たりにすることが出来て良い。
Part 2: flat config 入門
ref: Part 2: Introduction to flat config
flat config が実現する目標
- 理にかなったデフォルト値
- 単一の設定方法
- ESLint の rule に変更を加えない
- JavaScript ネイティブの読み込み機能を活用する
- トップレベルのプロパティを整理する
- 既存の plugin を動作させる
- 互換性を可能な限り確保する
Setting logical defaults for linting
1 理にかなったデフォルト値
- ESLint が作られた当初の前提
- ECMAScript 5 が JavaScript の最新バージョン
- ほとんどのファイルは
script
かcommonjs
- ecmaVersion で ECMAScript 6 をオプトイン出来るようにした
- 2022 年の状況
- ECMAScript は常に更新されてる
- ESM はスタンダードなモジュール形式として使われている
- flat config のデフォルト値
- ecmaVersion: “latest” (実装: default-config.js#L42)
- sourceType: “module” (
*.js
,*.mjs
) (実装: default-config.js#L41)) - sourceType: “commonjs” (
*.cjs
) (実装: default-config.js#L63) - “flat config” は .js, .mjs, .cjs ファイルを検索する (実装: default-config.js#L58, default-config.js#L61)
The new config file: eslint.config.js
2 単一の設定方法 4 JavaScript ネイティブの読み込み機能を活用する
- eslintrc の設定方法
- 複数の場所に複数の設定ファイルを書く
- 複数のファイル形式 (json, js, yaml) で設定を書く
- package.json に設定を書く
- flat config の設定方法
- プロジェクトの全ての設定を eslint.config.js に書く
- ESLint の CLI は 1 つの eslint.config.js のみを検索するので、必要なディスクアクセスが減った
- js ファイルを使用することで、JavaScript の機能で情報を読み込むことが出来るようになった (文字列を入力して追加リソースを読み込む機能を ESLint から消せた)
Glob-based configs everywhere
2 単一の設定方法
- ファイルシステムに寄るカスケードを削除して、glob による設定の上書きを採用した
- 各構成オブジェクト内の
files
,ignores
key に glob を書いて、適用するファイルを指定する
Goodbye extends, hello flat cascade
2 単一の設定方法
- eslint.config.js ファイル内にカスケードの仕組みがある
- 配列の先頭から末尾に向かって、構成オブジェクトをマージする
Reimagined language options
5 トップレベルのプロパティを整理する
- JavaScript の評価に関連するすべてのキーを、
languageOptions
という top-level key に移動したecmaVersion
- 構文とグローバル変数を有効にする
- eslintrc では parserOptions として ecmaVersion を指定していた
sourceType
- 構文とスコープ構造の評価方法を切り替える
env
(を削除した)- 環境毎に設定していた機能は
sourceType
で実現できるようになった
- 環境毎に設定していた機能は
globals
env
が持っていた残りの機能 (グローバル変数の設定) をglobals
で持つ- ESLint が持っていた環境情報を globals パッケージに抽出した
- flat config では globals パッケージを直接使用して、グローバル変数を管理する
parser
,parserOptions
はほとんど同じ
languageOptions
設定オブジェクトの構造はかなり分かりやすくなったと思う。
More powerful and configurable plugins
3 ESLint の rule に変更を加えない 4 JavaScript ネイティブの読み込み機能を活用する 5 トップレベルのプロパティを整理する 6 既存の plugin を動作させる
- ESLint の強みは plugin のエコシステム
- flat config で実現すること:
- 既存の plugin が動作する
- plugin が過去にできなかったことをできるようにする
- plugin の使い方
- eslintrc の場合、文字列を使う
`plugins: ["react"]`
- flat configs の場合、はオブジェクトを使う
import jsdoc from "eslint-plugin-jsdoc"; export default [{ plugins: { react } }];
- eslintrc の場合、文字列を使う
- rule の読み込み方
- eslintrc の場合、plugin にバンドルして読み込むか、
--rulesdir
(削除予定) でディレクトリを指定する - flat config の場合、構成オブジェクト内で直接読み込むことも出来る
- 設定ファイル内にのみ存在する runtime plugin を簡単に作れるようになった
- eslintrc の場合、plugin にバンドルして読み込むか、
- plugin が parser を公開できるようにした
runtime plugin を簡単に作れるようになった
eslint-plugin-***
形式以外でも独自のルールを配信しやすくなった…ということだと思うけれど、いまいちユースケースが思いつかない…。
Organized linter options
(大きい更新がないので、省略)
Backwards compatibility utility
7 互換性を可能な限り確保する
- 後方互換性を確保するために、eslintrc パッケージの FlatCompat クラスで eslintrc スタイルの設定を使えるようにした