🧶

ESLintのFlat Config移行でやって良かったこと

2024/04/07に公開

ESLintのv9.0.0ではFlat Config(eslint.config.js)がデフォルトになりました。これまでの.eslintrcの形式のファイルも使えますが、ESLINT_USE_FLAT_CONFIG環境変数を設定する必要があるようです。

https://eslint.org/blog/2024/04/eslint-v9.0.0-released/

これを機にFlat Configで普段使っているルールを整理して書いてみることにしました。その際やって良かったことをまとめます。

作成した設定はSharable Configとして公開しています。

https://github.com/mkizka/eslint-config

typescript-eslintのtseslint.configを使う

Flat configはこのように配列で書きます。

export default [
  {
    rules: {
      "no-unused-vars": "error",
      "no-undef": "error"
    }
  }
];

これでも良いのですが型があると便利です。typescript-eslintではtseslint.configというこの配列部分に相当する型を提供してくれる関数を使用できます。

https://typescript-eslint.io/getting-started

// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
  eslint.configs.recommended,
  ...tseslint.configs.recommended,
  {
    rules: {
      // ここにルールを追加していきます
    }
  }
);

tseslint.configはextendsが使える

tseslint.configは引数に対して型を提供しそのまま配列にして返すだけの関数ですが、さらにextendsというプロパティが使用できます。

このextendsは以下のように使用します。

export default tseslint.config({
  files: ['**/*.ts'],
  extends: [
    eslint.configs.recommended,
    ...tseslint.configs.recommended,
  ],
  rules: {
    '@typescript-eslint/array-type': 'error',
    '@typescript-eslint/consistent-type-imports': 'error',
  },
});

これは以下と同等になります。

export default [
  {
    files: ['**/*.ts'],
    rules: {
      // eslint.configs.recommendedが持っているたくさんのルール
    }
  }
   {
    files: ['**/*.ts'],
    rules: {
      // tseslint.configs.recommendedが持っているたくさんのルール
    }
  }
  // その他のtseslint.configs.recommendedのルール(省略)
  {
    files: ['**/*.ts'],
    rules: {
      // 最後にもともと指定していたルール
      '@typescript-eslint/array-type': 'error',
      '@typescript-eslint/consistent-type-imports': 'error',
    },
  },
];

この関数ではfiles(またはignores)で指定した値を、extendsの各ルールとextends以外それぞれに適用して返すというものです。extendsを持っているオブジェクトは、最終的にexntendsの要素数+1個のオブジェクトになります。

実装を確認してみるとより分かりやすいです。このような短い関数になっています。

https://github.com/typescript-eslint/typescript-eslint/blob/679ed8efacd0cdecc3daab8f5dd0625c1075afb9/packages/typescript-eslint/src/config-helper.ts#L85-L107

これによってeslintやtypescript-eslintの複数の設定にまとめてfilesを指定することが出来ます。前述の例のように、特定の拡張子のファイルにだけ適用したいルールがある時に便利です。

@eslint/eslintrcと組み合わせる

@eslint/eslintrcのFlatCompatと組み合わせると、以下のように書くこともできます。

[
   {
    extends: compat.extends("Flat Configに対応していないライブラリ")
    rules: {
      // ↑のライブラリのルール
    },
  }
]

これは↓と結局同じ結果になりますが、1つのプラグインとルールが1つのオブジェクトにまとまっていて見やすいです(感想)。

[
   ...compat.extends("Flat Configに対応していないライブラリ")
  {
    rules: {
      // ↑のライブラリのルール
    },
  }
]

作成したルールのスナップショットテストを書く

Flat Config移行前後でルールの変化を見たり、ルールを維持したまま設定の書き方を変えたい場合、スナップショットテストが便利です。

スナップショットテストにはESLintまたはFlatESLintクラスのcalculateConfigForFileメソッドが使用できます。

https://eslint.org/docs/latest/integrate/nodejs-api#-eslintcalculateconfigforfilefilepath

以下のように書くことで.jsファイルへの設定をテストできます。

import { ESLint } from "eslint";
test("index.js", async () => {
  const eslint = new ESLint();
  const result = await eslint.calculateConfigForFile("index.js");
  expect(result).toMatchSnapshot();
});

ESLintがv8の場合

上記のテストはeslintの9.0.0で動作を確認しています。

ESLintのv8では.eslintrcの形式にはESLintクラスが、Flat ConfigにはFlatESLintクラスが対応しています。

FlatESLintクラスはeslint/use-at-your-own-riskに入っています。自分が試した際は型定義がありませんでしたが、importして使用できました。

import { FlatESLint as ESLint } from "eslint/use-at-your-own-risk.js";

https://eslint.org/blog/2022/08/new-config-system-part-3/#using-flat-config-with-the-eslint-class

これを利用してESLintクラスで以前の設定を出力し、FlatESLintクラスで新しい設定を出力して比較するということもできるはずです。

ESLintプラグインはFlatCompat無しでも動く(こともある)

eslintのライブラリのうちextendsして使うeslint-config-xxx系のライブラリはFlat Configに対応していなければFlatCompatextendsメソッドを使用する必要があります。

eslint-plugin-xxx系のライブラリもFlatCompatpluginsメソッドが使えますが、そのままでも意外と動く場合があります。

例えばeslint-plugin-unused-importsでは以下のように書くと動きました。

import unusedImports from "eslint-plugin-unused-imports";
// 省略
  {
    plugins: {
      "unused-imports": unusedImports,
    },
    rules: {
      "@typescript-eslint/no-unused-vars": "off",
      "unused-imports/no-unused-imports": "error",
    },
  },

リポジトリのissueとしても同じ内容のものがありました。

https://github.com/sweepline/eslint-plugin-unused-imports/issues/74

@eslint/config-inspectorが便利

これについても書こうと思っていたのですが、書き始めた当日ちょうど公式から記事が出たのでそのまま載せておきます。

https://eslint.org/blog/2024/04/eslint-config-inspector/

設定したルールが最終的にどのようになったかをUIで確認できるツールです。

おわり

ESLintの設定をFlat Configで書いてみてやったことをまとめました。自分用のSharable Configを作るのおすすめです。

GitHubで編集を提案

Discussion