この記事は 六間坂上 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 LinterA 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
はこんな感じ。
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
から変える必要…ある?
yarn up eslint
既存の.eslintrcをFlat Configに変換するための公式のMigration Toolが提供されており、npx
で簡単に使用できます。以下のコマンドで実行できます。これによりFlat Configのコンフィグファイルであるeslint.config.mjsが生成されます。また globals
, @eslint/js
, @eslint/eslintrc
のインストールも要求されるので合わせて行います。
# .eslint.ymlの部分は自分の環境に合わせて変更 (.eslintrc.jsonなど)
npx @eslint/migrate-config .eslintrc.yml
yarn add -D globals @eslint/js @eslint/eslintrc
これでかんぺき~です。ESLintを実行します。
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関係のパッケージもすべて更新します。
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
が見たことないエラーを起こします。やっぱ自動ではダメそうです。
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
が大丈夫か確認します。
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において、env
や globals
, parserOptions
は launguageOptions
の中に統合されました。そのため下記のように書き換えることができます。
env:
browser: true
es2021: true
parserOptions:
ecmaVersion: 12
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する形に変わりました。
plugins:
- vue
- '@typescript-eslint'
...
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の弊害として若干取り扱いづらさは否めませんが、中身の透明性は増したような気もするのでまあ良いという事にしておきましょう……。
extends:
- 'plugin:vue/vue3-recommended'
- 'eslint:recommended'
- '@vue/typescript/recommended'
- 'prettier'
...
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を使うより結局手で作業しちゃった方が早そうです。
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が正常動作していることが分かります。
74:9 error Elements in iteration expect to have 'v-bind:key' directives
他の片の記事を見ているとVue用のパーサーを別途導入しないと動かないといったものも見かけましたが、自環境では特に問題なく動作しました。対応が進んだ結果なのかもしれません。
脱線:ESLint v9.15.0のバグを踏んだ話
実はこの記事の検証作業中に、変なバグを踏み抜きました。
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なので頻繁にいつもこういうのを踏みます。もうさァッ無理だよ こんなバグわかんないんだからさァッ
まとめ
Flat Config化ですが、意外と要領が分かってしまえば思ったより簡単でした。今は公式のマイグレーションガイドを読む→使用中のプラグインのドキュメントを見て移行、という流れをとれば大きな問題もなく対応できると思います。まあESLintのバグみたいなイレギュラーはよっぽどないでしょう…
そもそも職人芸みたいな .eslintrc
を書いてたわけでもないので、それも移行を容易にした一因だと思っています。自分のスタンスとして「Recommendは文字通り推奨であり、それを極力そのまま使う」「細かなカスタムルール作りに没頭して消耗しない」をポリシーにしているのですが、移行コストの面でも精神的に良いのかもしれません。多分今後もそのまま行くと思います。
という事で長い記事でしたが、ご拝読頂きありがとうございました!明日の記事は mjtaknonさんの「Cloud RunでLaravelのバッチとかキューワーカーとか動かす」です。
コメント