ESLint v9にアップデートしflat configに移行する
これはInfocom Advent Calendar 2024 17日目の記事です.
自分用のTypeScriptテンプレートで使っているESLintについて,重い腰を上げてv9にアップデートしflat configに移行しました.
flat config
ESLintはv9で設定ファイルのフォーマットに破壊的変更が入り,今までの.eslintrc.js
などで定義される設定ファイルから,flat configと呼ばれるeslint.config.js
が使われるようになりました.
cf. Configuration Migration Guide - ESLint - Pluggable JavaScript Linter
私の.eslintrc.js
こちらに置いてありますが,以下のような設定ファイルを作っていました.
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
env: {
node: true,
mocha: true,
},
parserOptions: {
ecmaVersion: 'es6',
sourceType: 'module',
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
ignorePatterns: ['dist'],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
rules: {
'no-console': 'error',
semi: ['error', 'never'],
'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
'comma-dangle': ['error', 'always-multiline'],
},
}
ESlintと関連パッケージのアップデート
まずはESlint本体とプラグイン&パーサのメジャーバージョンをアップデートしました.
(元々使っていた@typescript-eslint/eslint-plugin
というプラグインと@typescript-eslint/parser
というパーサはtypescript-eslint
というパッケージに統合されていたようなので,ついでに変更しました.)
$ yarn outdated
yarn outdated v1.22.19
info Color legend :
"<red>" : Major Update backward-incompatible updates
"<yellow>" : Minor Update backward-compatible features
"<green>" : Patch Update backward-compatible bug fixes
Package Current Wanted Latest Package Type URL
@types/chai 4.3.5 4.3.20 5.0.1 devDependencies https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/chai
@types/mocha 10.0.1 10.0.10 10.0.10 devDependencies https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mocha
@types/node 20.5.0 20.17.10 22.10.2 devDependencies https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node
@typescript-eslint/eslint-plugin 6.4.0 6.21.0 8.18.0 devDependencies https://typescript-eslint.io/packages/eslint-plugin
@typescript-eslint/parser 6.4.0 6.21.0 8.18.0 devDependencies https://typescript-eslint.io/packages/parser
chai 4.3.7 4.5.0 5.1.2 devDependencies http://chaijs.com
eslint 8.47.0 8.57.1 9.17.0 devDependencies https://eslint.org
mocha 10.2.0 10.8.2 11.0.1 devDependencies https://mochajs.org/
ts-node 10.9.1 10.9.2 10.9.2 devDependencies https://typestrong.org/ts-node
typescript 5.1.6 5.7.2 5.7.2 devDependencies https://www.typescriptlang.org/
✨ Done in 1.69s.
$ yarn remove @typescript-eslint/eslint-plugin @typescript-eslint/parser
$ yarn add -D eslint@^9 typescript-eslint@^8
マイグレーションツールの実行
公式のガイドの通り,マイグレーションツールで既存の.eslintrc.js
からeslint.config.js
を作成しました.
$ npx @eslint/migrate-config .eslintrc.js
Need to install the following packages:
@eslint/migrate-config@1.3.5
Ok to proceed? (y) y
Migrating .eslintrc.js
WARNING: This tool does not yet work great for .eslintrc.(js|cjs|mjs) files.
It will convert the evaluated output of our config file, not the source code.
Please review the output carefully to ensure it is correct.
Wrote new config to ./eslint.config.mjs
You will need to install the following packages to use the new config:
- globals
- @eslint/js
- @eslint/eslintrc
You can install them using the following command:
npm install globals @eslint/js @eslint/eslintrc -D
The following messages were generated during migration:
- The 'node' environment is used, but the sourceType is 'module'. Using sourceType 'module'. If you want to use CommonJS modules, set the sourceType to 'commonjs'.
以下のファイルが出力されました.
import globals from "globals";
import tsParser from "@typescript-eslint/parser";
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 [{
ignores: ["**/dist"],
}, ...compat.extends(
"eslint:recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking",
), {
plugins: {
"@typescript-eslint": typescriptEslint,
},
languageOptions: {
globals: {
...globals.node,
...globals.mocha,
},
parser: tsParser,
ecmaVersion: "es6",
sourceType: "module",
parserOptions: {
project: "./tsconfig.eslint.json",
tsconfigRootDir: "/Users/otti/src/github.com/ottijp/ts-template",
},
},
rules: {
"no-console": "error",
semi: ["error", "never"],
"brace-style": ["error", "stroustrup", {
allowSingleLine: true,
}],
"comma-dangle": ["error", "always-multiline"],
},
}];
必要な追加パッケージのインストール
マイグレーションツール実行時に案内されたパッケージをインストールしました.
yarn add -D globals@^15 @eslint/js@^9 @eslint/eslintrc@^3
エラーの解決
この状態でyarn lint
をしてもエラーが発生したので,いくつか修正をしました.
ecmaVersionの値
$ yarn lint
yarn run v1.22.19
$ eslint .
Oops! Something went wrong! :(
ESLint: 9.17.0
TypeError: Key "languageOptions": Key "ecmaVersion": Expected a number or "latest".
at new Config (/Users/otti/src/github.com/ottijp/ts-template/node_modules/eslint/lib/config/config.js:195:19)
at [finalizeConfig] (/Users/otti/src/github.com/ottijp/ts-template/node_modules/eslint/lib/config/flat-config-array.js:216:16)
at FlatConfigArray.getConfigWithStatus (/Users/otti/src/github.com/ottijp/ts-template/node_modules/@eslint/config-array/dist/cjs/index.cjs:1178:55)
at FlatConfigArray.getConfig (/Users/otti/src/github.com/ottijp/ts-template/node_modules/@eslint/config-array/dist/cjs/index.cjs:1196:15)
at entryFilter (/Users/otti/src/github.com/ottijp/ts-template/node_modules/eslint/lib/eslint/eslint-helpers.js:282:40)
at async NodeHfs.<anonymous> (file:///Users/otti/src/github.com/ottijp/ts-template/node_modules/@humanfs/core/src/hfs.js:574:24)
at async NodeHfs.walk (file:///Users/otti/src/github.com/ottijp/ts-template/node_modules/@humanfs/core/src/hfs.js:614:3)
at async globSearch (/Users/otti/src/github.com/ottijp/ts-template/node_modules/eslint/lib/eslint/eslint-helpers.js:323:26)
at async Promise.allSettled (index 0)
at async globMultiSearch (/Users/otti/src/github.com/ottijp/ts-template/node_modules/eslint/lib/eslint/eslint-helpers.js:408:21)
error Command failed with exit code 2.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
元々は"es2015"
としていましたが,公式のドキュメントを読むと,languageOptions.ecmaVersion
には2015
という数値を指定するようになったらしいので,そのように変更しました.
tsconfigの設定ファイルにeslint.config.mjsが含まれていない?
$ yarn lint
yarn run v1.22.19
$ eslint .
/Users/otti/src/github.com/ottijp/ts-template/eslint.config.mjs
0:0 error Parsing error: "parserOptions.project" has been provided for @typescript-eslint/parser.
The file was not found in any of the provided project(s): eslint.config.mjs
✖ 1 problem (1 error, 0 warnings)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
イマイチちゃんと理解できていないのですが,languageOptions.parserOptions.project
でTypeScriptパーサに渡しているtsconfigファイルのinclude
にeslint.config.mjsが入っていない(?)ようだったので,tsconfigファイルのinclude
にeslint.config.mjs
を追加しました.
eslint.config.jsのリファクタリング
これでyarn lint
は正常に実行できるようになりました.
$ yarn lint
yarn run v1.22.19
$ eslint .
/Users/otti/src/github.com/ottijp/ts-template/eslint.config.mjs
1:41 error Extra semicolon semi
2:30 error Extra semicolon semi
3:49 error Extra semicolon semi
4:29 error Extra semicolon semi
5:41 error Extra semicolon semi
6:28 error Extra semicolon semi
7:46 error Extra semicolon semi
9:50 error Extra semicolon semi
10:43 error Extra semicolon semi
14:30 error Missing trailing comma comma-dangle
15:3 error Extra semicolon semi
49:3 error Extra semicolon semi
✖ 12 problems (12 errors, 0 warnings)
12 errors and 0 warnings potentially fixable with the `--fix` option.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
ただし,上記のようにeslint.config.js自体が私のESLintルールに違反しているのと,いくつか冗長(不要に複雑)な定義になっていたので,リファクタリングしました(FlatCompat
を使う必要がないのに使っているなど).
このリファクタリングにはTypeScript公式のeslint.config.js
を参考にしました.
この際,@eslint/eslintrc
は不要になったので削除しました.
yarn remove @eslint/eslintrc
完成
リファクタリングしたeslint.config.js
がこちらです.
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import eslint from '@eslint/js'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import tsParser from '@typescript-eslint/parser'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
export default tseslint.config(
{
ignores: ['**/dist'],
},
eslint.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
globals: {
...globals.node,
...globals.mocha,
},
parser: tsParser,
ecmaVersion: 2015,
sourceType: 'module',
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
},
rules: {
'no-console': 'error',
semi: ['error', 'never'],
'brace-style': ['error', 'stroustrup', {
allowSingleLine: true,
}],
'comma-dangle': ['error', 'always-multiline'],
},
}
)
ソースはこちらに置いてあります.
refs
- typescript-eslint v6 アップデートガイド
- ESLint を eslintrc から Flat Config に移行する、ハマりポイントを添えて。 #JavaScript - Qiita
- How I Collected Bumps During the Migration to ESLint 9 | by Andrey Enin | Medium
- ESLintのFlat Config移行でやって良かったこと
- Getting Started | typescript-eslint
- ESLint v9への移行
- ESLint を v9 にアップデートして Flat Config に移行する