🪴

ESLint Flat Config に移行し、ついでに Shopify の shareable config を導入した

2024/09/30に公開

趣味開発のプロジェクトで ESLint の設定を Flat Config に移行した

また従来は自作の shareable config を利用していたが、メンテが面倒になってきたので、適当な tech company のものにエイヤで乗り換えることにした

以下の2点を基準に選定し、@shopify/eslint-plugin を採用した

  • Flat Config に対応済みである
  • JavaScript 向け / TypeScript 向け / ブラウザ向け / Node向けのように分割された config を提供している

移行作業

eslint.config.js の型

/** @type {import('eslint').Linter.Config[]} */
const config = [
  // ...
];

export default config;

import('eslint').Linter.FlatConfig[] としている記事も見かけたが、deprecate されていたので今は Config を使うようだ

Config[] で提供されている shareable config の適用ファイルを絞りたい

@shopify/eslint-plugin は以下のように config 配下に TypeScript 向けや Node 向け、React 向けといった設定値(型としては Config[]) が生えており、それを spread syntax で展開して使用するインターフェイスとなっている

import shopifyEslintPlugin from "@shopify/eslint-plugin";

/** @type {import('eslint').Linter.Config[]} */
const config = [
  ...shopifyEslintPlugin.configs.typescript, // <- Config[]
  ...shopifyEslintPlugin.configs.react, // <- Config[]
];

ここで困るのが、プロジェクト内にブラウザ向けの React コード(configs.react を適用させたい)と Node 向けコード(configs.node を適用させたい)が混在するようなケース

上のような設定例で configs.react を展開すると当然 Node 向けコードにも React 系の plugin や rules が有効になってしまい、ESLint がエラーで実行できなかったり不要な warnings が出たりする

どうにかして files property と組み合わせて適用ファイルを絞りたいが、素朴に書いた以下のような設定は動かない

import shopifyEslintPlugin from "@shopify/eslint-plugin";

/** @type {import('eslint').Linter.Config[]} */
const config = [
  // ...

  {
    files: [
      // React files
    ],
    // ↓ Config[] をここで spread しても動かない
    ...shopifyEslintPlugin.configs.react,
  },

  // ...
];

試行錯誤した結果、とりあえず以下のような設定で動かせた

import shopifyEslintPlugin from "@shopify/eslint-plugin";

/** @type {import('eslint').Linter.Config[]} */
const config = [
  // ...

  ...shopifyPlugin.configs.react.map((c) => ({
    ...c,
    files: [
      // React files
    ],
  })),

  // ...
];

files だけではなく ignores などももちろん利用できる

ただこれは extend (とは呼ばないだろうが)した configs の持つ files を完全に上書きしてしまっているので、場合によっては適用ファイルが不必要に広がることになる
config の定義を見て問題がないかよく確認した方が良いかもしれない

Flat Config 未対応の plugin

@eslint/eslintrc の提供する FlatCompat を利用する

pnpn add -D @eslint/eslintrc
import { fileURLToPath } from "node:url";
import { FlatCompat } from "@eslint/eslintrc";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const compat = new FlatCompat({
  baseDirectory: __dirname,
});

const config = [
  ...compat.plugins("foobar"), // 従来 `plugins: string[]` に書いていた識別子

  // ...
];

その他

Flat Config 導入前後で設定値に変化がないことを確認するためのツールを開発している方もいるようだ

https://zenn.dev/cybozu_frontend/articles/introduce-eslint-config-compat

https://github.com/sajikix/check-eslint-config-compat

今回は設定値を変化させないことをハナから目指していなかったが、一定以上の規模のプロジェクトではかなり役に立ちそう

Discussion