.eslintrcが廃止されるらしいのでFlat Configに置き換える

Programming

この記事は 六間坂上 Advent Calendar 2024 1日目の記事です。

親の顔より見た.eslintrcが消えるらしい

こんにちは。ここのえです。お久しぶりの人はお久しぶりです。2023年のアドカレ以来のブログ更新です。このところ満足な工場が1.0になったり、宇宙時代が始まったりと工場長には大変忙しい時期になっていますが、皆さんはいかがでしょうか…

ふとアドカレの事を思い出し立ち上げたのですが、記事のネタに「Biomeのサポートってどこまで行ったんかな~2024年末の検証とかすっかな~」とか「脱Tailwindのついでにネタに…」とか構想を練ってたんですが、そんな中びっくりニュースを見かけてしまい…

eslintrc removed in ESLint v10.0.0

Flat config rollout plans - ESLint - Pluggable JavaScript Linter
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code qua...
わたし

廃止まで後どれくらい?

廃止と言われてもVueプロジェクトの用途では、ESLintをやめられるほどBiomeが完全な状態というわけではないので……結局Flat Config対応する必要があります。というかこの世のすべてのESLint採用プロジェクトを数えればたぶん1恒河沙ぐらいあるでしょう。今、全世界のWebエンジニアが試されています(?)。

では廃止までどのような流れになるかというと、最新バージョンであるv9系では、既にFlat Configがデフォルトで採用されています。肝心のv10ですが、リリース予定としては end of 2024 or early 2025 in all likelihood となっています。たぶん後5秒ぐらいで使えなくなります。アカ~ン!

移行する

移行に伴うもろもろの情報はQiitaやZennに多く上がっているため、ここではVue環境をベースに移行する場合の実例を挙げていきます。

移行前の環境と.eslintrc

前提として元々の .eslintrc.yml はこんな感じ。

.eslintrc.yml
env:
  browser: true
  es2021: true
plugins:
  - vue
  - '@typescript-eslint'
extends:
  - 'plugin:vue/vue3-recommended'
  - 'eslint:recommended'
  - '@vue/typescript/recommended'
  - 'prettier'
parserOptions:
  ecmaVersion: 12
rules: {}

シンプル of シンプルな構成で、Vue3をベースにTypescript周りが入っているだけです。またFormatterにPrettierを使っているため、それ関連のルールもextendしています。

ESLint v9へのアップグレード & Migration Toolを使う

ひとまずデフォルトでFlat Configが使えるESLint v9へアップグレードを行います。yarn v4 を使っているので yarn up コマンドです。upgrade から変える必要…ある?

Shell
yarn up eslint

既存の.eslintrcをFlat Configに変換するための公式のMigration Toolが提供されており、npx で簡単に使用できます。以下のコマンドで実行できます。これによりFlat Configのコンフィグファイルであるeslint.config.mjsが生成されます。また globals , @eslint/js, @eslint/eslintrc のインストールも要求されるので合わせて行います。

Shell
# .eslint.ymlの部分は自分の環境に合わせて変更 (.eslintrc.jsonなど)
npx @eslint/migrate-config .eslintrc.yml

yarn add -D globals @eslint/js @eslint/eslintrc

これでかんぺき~です。ESLintを実行します。

Shell
yarn lint # eslint --fix resources/**/*.{ts,vue}が入ってます


Oops! Something went wrong! :(

ESLint: 9.15.0

TypeError: Error while loading rule '@typescript-eslint/no-empty-function': Cannot read properties of undefined (reading 'allow')

どうも @typescript-eslint がエラーを起こしているようです。ESLint v9はルール周りの破壊的変更も含まれているため、併せてESLint関係のパッケージもすべて更新します。

Shell
yarn up @typescript-eslint/eslint-plugin @typescript-eslint/parser @vue/eslint-config-prettier @vue/eslint-config-typescript eslint-plulgin-vue
# @vue-eslint-config-prettierはv3.x以降のPrettierが対象なので、こちらも更新
yarn up prettier

更新により @typescript-eslint は問題なくなりますが、今度は @vue/eslint-config-typescript が見たことないエラーを起こします。やっぱ自動ではダメそうです。

Shell
Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './recommended' is not defined by "exports" in /home/nonuplet/Github/rirfee-dev/webroot/node_modules/@vue/eslint-config-typescript/package.json

自動生成されたeslint.config.mjsを見る

ひとまずMigration Toolによって自動生成された eslint.config.mjs が大丈夫か確認します。

eslint.config.mjs
import vue from "eslint-plugin-vue";
import typescriptEslint from "@typescript-eslint/eslint-plugin";
import globals from "globals";
import path from "node:path";
import { fileURLToPath } from "node:url";
import js from "@eslint/js";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
    baseDirectory: __dirname,
    recommendedConfig: js.configs.recommended,
    allConfig: js.configs.all
});

export default [...compat.extends(
    "plugin:vue/vue3-recommended",
    "eslint:recommended",
    "@vue/typescript/recommended",
    "prettier",
), {
    plugins: {
        vue,
        "@typescript-eslint": typescriptEslint,
    },

    languageOptions: {
        globals: {
            ...globals.browser,
        },

        ecmaVersion: 12,
        sourceType: "script",
    },

    rules: {},
}];

先程のエラーですが、@vue/eslint-config-typescript のドキュメントを読みに行くと、Flat Configにしか対応していないと明記されています。そこを互換モードで突っ込もうとしたので、非対応でエラーを起こしています。

そこで、上記の自動生成Flat Configの問題を探っていくと、ここに登場する FlatCompat は、Flat Configに対応していないプリセットを変換するためのものです。つまり……Migration Toolがやってるのはextendsを全部互換モードでとりあえず突っ込んでるだけ……?

┗┏┗┏┗┏┗┏(‘o’)┓┛┓┛┓┛┓┛┓┛コラコラコラコラコラコラコラコラコラコラコラ

一旦落ち着きます。とはいえ、丁寧な生活は丁寧なコンフィグからというのは古事記にも書かれています。動かないんで当然書き換えが必要なんですが、どっちにしろ動いたとしても美しくないのでこのままでは到底認められません。全部書き直します。

env, globals, parserOptionsはlanguageOptionsに統合

FlatConfigにおいて、envglobals, parserOptionslaunguageOptions の中に統合されました。そのため下記のように書き換えることができます。

.eslintrc.yml (旧コンフィグ)
env:
  browser: true
  es2021: true
parserOptions:
  ecmaVersion: 12
eslint.config.mjs
import globals from 'globals'

export default [
  {
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.es2021,
      },
      parserOptions: {
        ecmaVersion: 12,
      }
    },
  },
]

envは消滅して、globals内に書く形になっています。 importしている globals パッケージは各環境の設定が入ったJSONファイルの集合体になっており、スプレッド構文( ...)を使ってglobalsオプション内で展開する形になりました。別途 globals パッケージをインストールする必要があるのは少し手間ではありますが、個人的には良い変更だと思います。

Pluginの読み込み

次に vue, @typescript-eslint のプラグインを読み込みます。これも .eslintrc の時と異なり、ESLintが独自に名前解決するのではなく、通常のJSと同じようにimportする形に変わりました。

.eslintrc.yml (旧コンフィグ)
plugins:
  - vue
  - '@typescript-eslint'
eslint.config.mjs
...
import tsLint from '@typescript-eslint/eslint-plugin'

export default [
  {
    plugins: {
      // eslint-plugin-vueのドキュメントを見ると、pluginにvueを記述するのは不要のようです
      '@typescript-eslint/eslint-plugin': tsLint,
    },
    ...
  }
]

コンフィグの読み込み

.eslintrc 時代の extends に関しては、Flat Configではexport default の頭でコンフィグを読み込むことになります。こちらもロードする場合はimportして使う形になります。

コンフィグを読み込む方法についてはパッケージによってまちまちになっています。Flat Configの弊害として若干取り扱いづらさは否めませんが、中身の透明性は増したような気もするのでまあ良いという事にしておきましょう……。

.eslintrc.yml (旧コンフィグ)
extends:
  - 'plugin:vue/vue3-recommended'
  - 'eslint:recommended'
  - '@vue/typescript/recommended'
  - 'prettier'
eslint.config.mjs
...
import pluginVue from 'eslint-plugin-vue'
import tsLint from '@typescript-eslint/eslint-plugin'
import js from '@eslint/js'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import prettierConfig from '@vue/eslint-config-prettier'

export default [
  ...pluginVue.configs['flat/recommended'],
  js.configs.recommended,
  ...vueTsEslintConfig(),
  prettierConfig,
  {
    ...
  }
]

完成版

こんな感じのコンフィグになります。今回は全てのプラグインがFlat Configに対応だったため、FlatCompat は使用していません。いよいよv10のリリースが間近なので、メンテナンスされているパッケージはFlat Config対応済みのものが多くなっているようです。逆に今回のように、.eslintrc の互換切りが行われている事で引っかかるパターンもあるので、Migration Toolを使うより結局手で作業しちゃった方が早そうです。

eslint.config.mjs
import globals from 'globals'
import pluginVue from 'eslint-plugin-vue'
import tsLint from '@typescript-eslint/eslint-plugin'
import js from '@eslint/js'
import vueTsEslintConfig from '@vue/eslint-config-typescript'
import prettierConfig from '@vue/eslint-config-prettier'

export default [
  ...pluginVue.configs['flat/recommended'],
  js.configs.recommended,
  ...vueTsEslintConfig(),
  prettierConfig,
  {
    plugins: {
      '@typescript-eslint/eslint-plugin': tsLint,
    },
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.es2021,
      },
      parserOptions: {
        ecmaVersion: 12,
      },
    },
  },
]

一例として <v-for>:key を設定しないルール違反を起こしたうえで、試しに yarn lint を回してみます。エラーが表示され、ESLintが正常動作していることが分かります。

Shell
  74:9   error    Elements in iteration expect to have 'v-bind:key' directives  

他の片の記事を見ているとVue用のパーサーを別途導入しないと動かないといったものも見かけましたが、自環境では特に問題なく動作しました。対応が進んだ結果なのかもしれません。

脱線:ESLint v9.15.0のバグを踏んだ話

実はこの記事の検証作業中に、変なバグを踏み抜きました。

Shell
Oops! Something went wrong! :(

ESLint: 9.15.0

TypeError: Error while loading rule '@typescript-eslint/no-unused-expressions': Cannot read properties of undefined (reading 'allowShortCircuit') 

マイグレーション中に「コンフィグ周りのパッケージも最新なのに何故…?」と数時間悩んでたんですが、ESLint v9.15.0 のバグでした。次のリリースで修正されるようです。地雷回収界のtouristなので頻繁にいつもこういうのを踏みます。もうさァッ無理だよ こんなバグわかんないんだからさァッ

eslintのエラー 「TypeError: Error while loading rule '@typescript-eslint/no-unused-expressions': Cannot read properties of undefined (reading 'allowShortCircuit')」 - Qiita
更新こちら修正された模様です。起きたことLaravel / Vue3 / TypeScript / Eslint の環境構築でESlintが動かなかったのでVSCodeの拡張機能のeslin…
Bug: [no-unused-expressions, no-empty-functions, possibly others...] extension rules crash with eslint v9.15.0 · Issue #10338 · typescript-eslint/typescript-eslint
Before You File a Bug Report Please Confirm You Have Done The Following... I have tried restarting my IDE and the issue ...

まとめ

Flat Config化ですが、意外と要領が分かってしまえば思ったより簡単でした。今は公式のマイグレーションガイドを読む→使用中のプラグインのドキュメントを見て移行、という流れをとれば大きな問題もなく対応できると思います。まあESLintのバグみたいなイレギュラーはよっぽどないでしょう

そもそも職人芸みたいな .eslintrc を書いてたわけでもないので、それも移行を容易にした一因だと思っています。自分のスタンスとして「Recommendは文字通り推奨であり、それを極力そのまま使う」「細かなカスタムルール作りに没頭して消耗しない」をポリシーにしているのですが、移行コストの面でも精神的に良いのかもしれません。多分今後もそのまま行くと思います。

という事で長い記事でしたが、ご拝読頂きありがとうございました!明日の記事は mjtaknonさんの「Cloud RunでLaravelのバッチとかキューワーカーとか動かす」です。

コメント

タイトルとURLをコピーしました