ESLint FlatConfigへの移行

この記事は、[GLOBISアドベントカレンダー2024](https://qiita.com/advent-calendar/2024/globis) 8日目の記事です。

## はじめに

こんにちは。GLOBIS学び放題でWebエンジニアをしている富山です。

GLOBIS学び放題のフロントエンド開発ではコードの一貫性を保つためにESLintを導入しています。

v9からはFlatConfigでの記述がデフォルトとなり、これまでの、eslintrc の記法は非推奨となります。そして次期バージョンでは、eslintrc は完全に削除されるアナウンスがされています。

最近、ESLint v9へのアップデート対応をしましたので、本記事ではESLintの新しい記法であるFlatConfigへの移行についてまとめていきます。

## Flat Configとは？
ESLintの新しい記法で configuration object と呼ばれるオブジェクトの配列で記述されます。

これまでは、eslintrcに記述していましたが、v9以降はeslint.config.jsというファイルに記述するのがデフォルトとなります。2025年初頭にかけてリリースが予定されてるv10ではeslintrcの記法が完全に廃止されることがアナウンスされています。

静的解析ツールには、ESLint以外にも代替もありますが、ESLintを継続して使う選択をとるチームも多いのではないかと思います。まだ対応してない場合は、そろそろ対応しておいた方が良いアップデート内容になります。

以下のサンプルコードのように、configuration objectは、ルールを設定したいファイルごとにplugin、rulesなどの設定をオブジェクトとして扱い、配列の中で、重複するものは、後ろのものが適用されていく仕組みになっています。

```js:eslint.config.js
export default [
  // ↓ configuration object
  {
    files: ['src/components/styled.ts'],
    rules: {
      'ts/no-explicit-any': 'off',
    },
  },
  // ↓ configuration object
  {
    files: ['e2e_tests/**/*.spec.ts'],
    plugins: {
      playwright: playwright,
    },
    rules: {
      ...playwright.configs.recommended.rules,
    },
  },
  // ...
];
```

## 移行にあたって
今回のESLintのような広く使われているツールで破壊的な変更が行われる際には、Migration Guideを用意してくれるケースがあります。

ESLintのFlatConfigでも移行にあたっての手引きをまとめてくれてます。

本記事では移行する際に大きな変更箇所のみに触れていますが、個別でハマる箇所は異なると思います。その際は、以下のページを一読するとトラブルシュートにかける時間が短縮されるかと思います。

https://eslint.org/docs/latest/use/configure/migration-guide

ESLintにおいて対応が必要な箇所は以下のポイントです。

- eslintrcに記述していたpluginをimportする
- 各ファイルで設定したruleをconfiguration objectに変更する
- eslintrcで共通利用してるルールやoverrideしてる設定など役割別に定義しておく(任意)
- eslintrcでoverrideしている設定はextendsの設定よりも後段に配置する
- envで設定していたものをlanguageOptionsに移設する

次の項で各プロパティの変更点を見ていきたいと思います。

## 各プロパティで変更のあったもの

### plugins

ここでは、rulesで使用するパッケージを登録します。eslintrcではパッケージごとに決められた命名で文字列を書くと、ESLint側で解釈を行なって適用していましたが、FlatConfigでは、明示的にJSのモジュールとして importする必要があります。


```js:eslintrc
plugins: ['unused-imports', 'react'],
```


```js:eslint.config.js
import unusedImportsPlagin from 'eslint-plugin-unused-imports'
import reactPlugin from 'eslint-plugin-react'

export default [
    files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
    plugins: {
        unusedImports: unusedImportsPlugin,
        react: reactPlugin,
    },
    rules: {
        'react/no-unused-prop-types': 'error',
        'unusedImports/no-unused-imports': 'error',
    }
]
```

### extends (FlatConfigでは廃止)
これまではextendsプロパティを利用して、チームの設定に適用していましたが、FlatConfigになると、extendsプロパティがなくなります。

FlatConfigでは、eslintrcでextendsに記述していた設定は、export defaultに追加します。

用途としてはextendsプロパティのものと変わらず、ベースとなる設定（共有設定、ルールプリセット）を再利用できるようになります。

```js:eslintrc
{
  "extends": [
    "some-other-config",
    "prettier"
  ]
}
```

```js:eslint.config.js
import unusedImportsPlagin from 'eslint-plugin-unused-imports'
import reactPlugin from 'eslint-plugin-react'
import someOtherConfig from "some-other-config";
import eslintConfigPrettier from "eslint-config-prettier";

export default [
    someOtherConfig,
    {
        files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
        plugins: {
            unusedImports: unusedImportsPlugin,
            react: reactPlugin,
        },
        rules: {
            'react/no-unused-prop-types': 'error',
            'unusedImports/no-unused-imports': 'error',
        }
    },
    eslintConfigPrettier,
];
```


### overrides (FlatConfigでは廃止）
extendsと同様にoverridesも無くなっています。
前述した通り、FlatConfigでは、configuration object と呼ばれる各設定情報の単位を配列で表現するようになりました。

そのため、特定のファイルやディレクトリに対して、異なるESLintルールを適用したい場合は、配列の後方に配置する必要があります。overrideするルールについては、切り出して定義しておいた方がチームで管理しやすいかなと思います。

```js:eslint.config.js
import unusedImportsPlagin from 'eslint-plugin-unused-imports'
import reactPlugin from 'eslint-plugin-react'
import someOtherConfig from "some-other-config";
import eslintConfigPrettier from "eslint-config-prettier";

const overrideConfig = {
    {
        files: ['e2e_tests/**/*.spec.ts'],
        extends: ['plugin:playwright/recommended'],
        rules: {
            ...playwright.configs.recommended.rules,
        },
    },
}

export default [
    someOtherConfig,
    {
        files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
        plugins: {
            unusedImports: unusedImportsPlugin,
            react: reactPlugin,
        },
        rules: {
            'react/no-unused-prop-types': 'error',
            'unusedImports/no-unused-imports': 'error',
        }
    },
    eslintConfigPrettier,
    overrideConfig // overrideしたい設定は必ず配列の後方に配置する
];
```


### env (FlatConfigでは廃止)

JavaScriptの解釈に関わるルールはlanguageOptionsに記述するようになります。
FlatConfigに移行する際は、globalsのパッケージが必要となるので、インストールしておきましょう。

https://www.npmjs.com/package/globals


```js:eslintrc
env: {
    browser: true,
    jest: true,
},
parserOptions: {
    parser: '@typescript-eslint/parser',
    project: './tsconfig.json',
},
```

```js:eslint.config.js
languageOptions: {
    globals: {
        ...globals.browser,
        ...globals.jest,
    },
    parser: tsParser,
    parserOptions: {
        project: './tsconfig.json',
    },
},
```

## 最後に
チームの規模や構成にも依りますが、フロントエンドのツールチェーンは1回設定されると、機能開発を優先して、後手に回ってしまいがちなケースもありますが、様々なライブラリに依存してるWebサービスの開発では、定期的にアップデートをかけながら、取り組んでいきたいですね。

私が所属するチームでは、フロントエンド開発において、スプリントの中で、10~20%くらいの枠を使って、改善系の開発も進めています。

現在、新機能開発と並行して、継続的な改善、長期的なアーキテクチャ検討〜推進にも一緒に取り組んでいただけるフロントエンドエンジニアも募集しています！

「学びの未来を作り出し、人の可能性を広げていく」というミッションのもと、toB,toC領域で、学びを通じて、新しい行動につながるようなサービスを目指して、開発に取り組んでいます。

以下のスライドに詳しく掲載されていますが、グロービスの組織文化は「自由と自己責任の原則」や「性善説に則った経営」、「個の爆発」、「Heartful Communication」等、一人ひとりの能力が最大限に活かせるようなポジティブで働きやすい環境があると思います！

興味持っていただけた方は是非、気軽にお話ししましょう！

https://hrmos.co/pages/globis/jobs/gdp_front_tko_t

https://speakerdeck.com/globis_gdp/we-are-hiring-635c989b-5419-45fd-b866-14b5fca0ba4b