ESLintのv9で使われるFlatConfigの設定値をeslintrcとの違いから理解する

JavaScript

ESLintはバージョン9を境に
旧来の.eslintrc.jsonからeslint.config.mjsのようなフォーマットに変更しなければならない。

この記事では、新しい設定ファイルをなるべくササッと理解できることを目標に記載する。

参考

この記事に書いてある情報は全て以下の公式ドキュメントに記載されている。 公式ドキュメントを読むのが面倒な人向けの記事としている。

Configuration Migration Guide - ESLint - Pluggable JavaScript Linter
A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code qua...

すぐ理解できるようにかなり簡単に抜粋してあるので、細かいところを確認したい場合は公式ドキュメントを見てほしい。

変更点の概要

移行自体はたしかに面倒であるが、良く見てみると結構ありがたい仕様変更が行われている。

以前の仕様と比べて、かなりシンプルで書きやすくなっている。

  • 文字列指定してたものが、importしたモジュールを指定できるようになった
  • Configuration Objectというオブジェクトに「対象ファイル」「対象外ファイル」「適用するパーサーやプラグイン」「適用するルール」などの設定がひとまとまりになった
  • 部分的に後方互換ある書き方ができるので、全てのライブラリの対応を待たずとも移行できる

FlatConfigの構造

はじめに、FlatConfigの構造を簡単に紹介する。 良く使いそうなフィールドだけ抜粋している。

ルート要素は配列になっており、以下の構造体を複数定義することができる。

■Configuration Object

  • name:設定オブジェクトの名前。任意。
  • files:設定を適用するファイルを記載。
  • ignores:設定の適用対象から除外するファイルを記載。
  • languageOptions
    • parser:パーサーがあれば指定
    • globals:window, processなどの環境固有のグローバル変数の設定
  • plugins:プラグインを指定
  • rules:Lintのルールを指定

Pluginの定義方法が変わった

旧:文字列でプラグインを指定する

// .eslintrc.js

module.exports = {
    // ...other config
    plugins: ["jsdoc"], // ←文字列で指定
    // ...other config
};

新:importやrequireしたモジュールを指定する

importやrequireしたモジュールをそのまま使えるので、文字列のtypoがなくなって嬉しい。

// eslint.config.js

import jsdoc from "eslint-plugin-jsdoc";

export default [
    {
        files: ["**/*.js"],
        plugins: {
            jsdoc: jsdoc // ←importした「jsdoc」を使う
        },
    }
];

Parserの定義方法が変わった

旧:パーサーを文字列で指定する

// .eslintrc.js

module.exports = {
    // ...other config
    parser: "@babel/eslint-parser",  // ←文字列で指定
    // ...other config
};

新: languageOptions.parserにモジュールを指定する

場所が変わったことに加え、
Plugin同様モジュールを指定するルールになったためtypoすることがなくなった。

// eslint.config.js

import babelParser from "@babel/eslint-parser";

export default [
    {
        // ...other config
        languageOptions: {
            parser: babelParser  // ←ここにimportしたモジュールを指定する
        }
        // ...other config
    }
];

特定ファイルにのみ適用したい設定

旧:特定のファイルに対する設定は overridesを使って設定

// .eslintrc.js

module.exports = {
    // ...other config
    overrides: [
        {
            // プロダクトコードに対する個別設定
            files: ["src/**/*"],
            rules: {
                semi: ["warn", "always"]
            }
        },
        {
            // テスト系ファイルに対する個別設定
            files:["test/**/*"],
            rules: {
                "no-console": "off"
            }
        }
    ]
};

新:Configuration Objectの files プロパティを使って設定

基本的には js.configs.recommendedを全てのファイルに適用し
Configuration Objectを下に記載していくことで
上書きしながら個別設定として細かいチューニングができるようになっている。

// eslint.config.js

import js from "@eslint/js";

export default [
    js.configs.recommended, // Recommended config applied to all files
    // File-pattern specific overrides
    {
        // プロダクトコードとテストコードに対する設定
        files: ["src/**/*", "test/**/*"],
        rules: {
            semi: ["warn", "always"]
        }
    },
    {
        // テストコードに対する個別の設定を上書き
        files:["test/**/*"],
        rules: {
            "no-console": "off"
        }
    }
    // ...other configurations
];

グローバル変数

旧: env , globals , perserOptions を使って設定

// .eslintrc.js

module.exports = {
    env: {
        browser: true,
        node: true
    },
    globals: {
        myCustomGlobal: "readonly",
    },
    parserOptions: {
        ecmaVersion: 2022,
        sourceType: "module"
    }
    // ...other config
}

新:globals と perserOptionsは languageOptions の下に移動

  • グローバル変数の一覧は globalsというnpmパッケージに一覧で載っている
  • 全て使いたければ ... を使って全部展開すると良い
// eslint.config.js

import globals from "globals";

export default [
    {
        languageOptions: {
            ecmaVersion: 2022,
            sourceType: "module",
            globals: {
                ...globals.browser,
                ...globals.node,
                myCustomGlobal: "readonly"
            }
        }
        // ...other config
    }
];

定義済みの推奨設定を使う

旧: extends を使って定義済みのConfigを使う

// .eslintrc.js

module.exports = {
    // ...other config
    extends: "eslint:recommended",  // ←ここが、ESLintの推奨設定を使う記述
    rules: {
        semi: ["warn", "always"]
    },
    // ...other config
}

新:importしたモジュールの推奨設定を展開すれば良い

eslint/packages/js/src/configs/eslint-recommended.js at main ?? eslint/eslint
Find and fix problems in your JavaScript code. Contribute to eslint/eslint development by creating an account on GitHub.

上記を見ると、 オブジェクトの中にrulesがひたすら記述されているだけなので
以下のように書くことでちょうどそのまま反映されることが分かる。

// eslint.config.js

import js from "@eslint/js";
import customConfig from "./custom-config.js";
import myConfig from "eslint-config-my-config";

export default [
    // js.configs.recommendedに推奨設定が書かれているので、
    js.configs.recommended,
    customConfig,
    myConfig,
    {
        rules: {
            semi: ["warn", "always"]
        },
        // ...other config
    }
];

階層構造が多少異なっていても ...〇〇.configs.recommendedのようにスプレッド構文で展開して整合性が合うように調整すればOK。

ちなみに、 @eslint/jsはインストールが必要。

$ npm install @eslint/js --save-dev

FlatConfigのフォーマットに未対応のものがある場合

ライブラリによっては、FlatConfigに対応していないものも存在するだろう。

その場合は FlatCompatというユーティリティを使うことで一時的に移行可能である。

$ npm install @eslint/eslintrc --save-dev

以下のコードは「eslint-config-my-config」という
旧来の extends 箇所で利用していたライブラリがFlatConfig対応していない場合の書き方である。

import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const compat = new FlatCompat({
    baseDirectory: __dirname
});

export default [

    // mimic ESLintRC-style extends
    ...compat.extends("eslint-config-my-config"),
];

うまくいっているかどうかを確かめるには、コンソールに出力してみると良い。

import { FlatCompat } from "@eslint/eslintrc";
import path from "path";
import { fileURLToPath } from "url";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const compat = new FlatCompat({
    baseDirectory: __dirname
});

// 一旦変数に格納
const configs = [

    // mimic ESLintRC-style extends
    ...compat.extends("eslint-config-my-config"),
];

console.dir(configs, {depth: 4}) // 出力してみて、Configuration Objectの構造になっていればOK
export default configs
タイトルとURLをコピーしました