5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

前提

本記事は、以下の記事の一部を詳細に書いた記事になります

リンターであるESlintの設定ファイルがFlatConfigという記法に統一されていくらしいです。
今後はeslintrc形式が全く使えなくなるとか。

正直、従来のeslintrc形式でも書いたことなかったので何の設定をしているかの解読から始まりました。
加えて、ESLintの設定にこだわる人が少ないのかFlatConfigへの変換に関する記事が比較的少なく、結局公式ドキュメントを読みながら進めた部分も多かったです。

本題

まず変更前と変更後のコードの全文を載せます

変更前

.eslintrc.js
module.exports = {
  root: true,
  env: {
    node: true,
    es6: true,
  },
  parserOptions: { ecmaVersion: 8, sourceType: 'module' },
  ignorePatterns: ['node_modules/*'],
  extends: ['eslint:recommended'],
  overrides: [
    {
      files: ['**/*.ts', '**/*.tsx'],
      parser: '@typescript-eslint/parser',
      settings: {
        react: { version: 'detect' },
        'import/resolver': {
          typescript: {},
        },
      },
      env: {
        browser: true,
        node: true,
        es6: true,
      },
      extends: [
        'eslint:recommended',
        'plugin:import/errors',
        'plugin:import/warnings',
        'plugin:import/typescript',
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'plugin:jsx-a11y/recommended',
        'plugin:prettier/recommended',
        'plugin:testing-library/react',
        'plugin:jest-dom/recommended',
      ],
      rules: {
        'no-restricted-imports': [
          'error',
          {
            patterns: ['@/features/*/*'],
          },
        ],
        'linebreak-style': ['error', 'unix'],
        'react/prop-types': 'off',

        'import/order': [
          'error',
          {
            groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
            'newlines-between': 'always',
            alphabetize: { order: 'asc', caseInsensitive: true },
          },
        ],
        'import/default': 'off',
        'import/no-named-as-default-member': 'off',
        'import/no-named-as-default': 'off',

        'react/react-in-jsx-scope': 'off',

        'jsx-a11y/anchor-is-valid': 'off',

        '@typescript-eslint/no-unused-vars': ['error'],

        '@typescript-eslint/explicit-function-return-type': ['off'],
        '@typescript-eslint/explicit-module-boundary-types': ['off'],
        '@typescript-eslint/no-empty-function': ['off'],
        '@typescript-eslint/no-explicit-any': ['off'],

        'prettier/prettier': ['error', {}, { usePrettierrc: true }],
      },
    },
  ],
};

変更後

eslint.config.mjs
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
import typeScriptESLintParser from '@typescript-eslint/parser';
import globals from 'globals';

const compat = new FlatCompat();

export default [
  // 推奨設定
  js.configs.recommended,
  // lint対象外のファイル
  {
    ignores: ['**/node_modules/**'],
  },
  // lint対象ファイル
  {
    files: ['**/*.ts', '**/*.tsx'],
  },
  // lint設定
  {
    rules: {
      'no-restricted-imports': [
        'error',
        {
          patterns: ['@/features/*/*'],
        },
      ],
      'linebreak-style': ['error', 'unix'],
      'react/prop-types': 'off',
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
          'newlines-between': 'always',
          alphabetize: { order: 'asc', caseInsensitive: true },
        },
      ],
      'import/default': 'off',
      'import/no-named-as-default-member': 'off',
      'import/no-named-as-default': 'off',
      'react/react-in-jsx-scope': 'off',
      'jsx-a11y/anchor-is-valid': 'off',
      '@typescript-eslint/no-unused-vars': ['error'],
      '@typescript-eslint/explicit-function-return-type': ['off'],
      '@typescript-eslint/explicit-module-boundary-types': ['off'],
      '@typescript-eslint/no-empty-function': ['off'],
      '@typescript-eslint/no-explicit-any': ['off'],
      'prettier/prettier': ['error', {}, { usePrettierrc: true }],
    },
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
        ...globals.es6,
        myCustomGlobal: 'readonly',
      },
      parser: typeScriptESLintParser,
    },
  },
  ...compat.extends(
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:jsx-a11y/recommended',
    'plugin:prettier/recommended',
    'plugin:testing-library/react',
    'plugin:jest-dom/recommended'
  ),
];

解説

eslintrcではextendsとかoverridesとかを使って設定を書いているところを、
flatConfigでは追加したい設定を並列のリスト形式で書き連ねるだけになったということで、全体的に設定を書きやすい+読みやすくなっているみたいです。

正直初見ではeslintrc何書いてあるか全然わからなかった....

root

.eslintrc.jsのroot:trueは、eslintrc特有の設定なのでflatconfigでは使いません。

環境変数

.eslintrc.js
  env: {
    node: true,
    es6: true,
  },
eslint.config.mjs
  {
    languageOptions: {
      globals: {
        ...globals.node,
        ...globals.es6,
      },
    },
  }

ignoreファイル

リンターの対象にしないファイルを指定することができます。

.eslintrc.js:L2
  ignorePatterns: ['node_modules/*'],
eslint.config.mjs
  ignores: ['**/node_modules/**'],

対象ファイル

リンターの対象にするファイルを指定することができます。
以下の設定では、typescriptファイルのみを指定しています。

.eslintrc.js:L2
  files: ['**/*.ts', '**/*.tsx'],
eslint.config.mjs
  {
    files: ['**/*.ts', '**/*.tsx'],
  }

overrides

従来オーバーライドして追加していた設定を、並列して追記するだけになっています。
rulesの中身はコピペしていて、parserやenvなどはlanguageOptionsに書くよう仕様変更されています。

.eslintrc.js:L2
  overrides: [
    {
      parser: '@typescript-eslint/parser',
      settings: {
        react: { version: 'detect' },
        'import/resolver': {
          typescript: {},
        },
      },
      env: {
        browser: true,
        node: true,
        es6: true,
      },
      rules: {
        'no-restricted-imports': [
          'error',
          {
            patterns: ['@/features/*/*'],
          },
        ],
        'linebreak-style': ['error', 'unix'],
        'react/prop-types': 'off',
        'import/order': [
          'error',
          {
            groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
            'newlines-between': 'always',
            alphabetize: { order: 'asc', caseInsensitive: true },
          },
        ],
        'import/default': 'off',
        'import/no-named-as-default-member': 'off',
        'import/no-named-as-default': 'off',
        'react/react-in-jsx-scope': 'off',
        'jsx-a11y/anchor-is-valid': 'off',
        '@typescript-eslint/no-unused-vars': ['error'],
        '@typescript-eslint/explicit-function-return-type': ['off'],
        '@typescript-eslint/explicit-module-boundary-types': ['off'],
        '@typescript-eslint/no-empty-function': ['off'],
        '@typescript-eslint/no-explicit-any': ['off'],
        'prettier/prettier': ['error', {}, { usePrettierrc: true }],
      },
    },
  ],,
eslint.config.mjs
  languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
        ...globals.es6,
        myCustomGlobal: 'readonly',
      },
      parser: typeScriptESLintParser,
    },
    rules: {
      'no-restricted-imports': [
        'error',
        {
          patterns: ['@/features/*/*'],
        },
      ],
      'linebreak-style': ['error', 'unix'],
      'react/prop-types': 'off',
      'import/order': [
        'error',
        {
          groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object'],
          'newlines-between': 'always',
          alphabetize: { order: 'asc', caseInsensitive: true },
        },
      ],
      'import/default': 'off',
      'import/no-named-as-default-member': 'off',
      'import/no-named-as-default': 'off',
      'react/react-in-jsx-scope': 'off',
      'jsx-a11y/anchor-is-valid': 'off',
      '@typescript-eslint/no-unused-vars': ['error'],
      '@typescript-eslint/explicit-function-return-type': ['off'],
      '@typescript-eslint/explicit-module-boundary-types': ['off'],
      '@typescript-eslint/no-empty-function': ['off'],
      '@typescript-eslint/no-explicit-any': ['off'],
      'prettier/prettier': ['error', {}, { usePrettierrc: true }],
    },
  },

プラグイン

Flatconfigに対応している場合は以下のようにimportしてリストに追加するだけです。

.eslintrc.js:L2
  extends: ['eslint:recommended'],
eslint.config.mjs
import js from '@eslint/js';

export default [
  js.configs.recommended,
  ...

しかし調べてみたところ現在はFlatconfigに対応していないプラグインも多く、その場合はcompat.extendsというものを使うと利用できるようです。

.eslintrc.js:L2
      extends: [
        'eslint:recommended',
        'plugin:import/errors',
        'plugin:import/warnings',
        'plugin:import/typescript',
        'plugin:@typescript-eslint/recommended',
        'plugin:react/recommended',
        'plugin:react-hooks/recommended',
        'plugin:jsx-a11y/recommended',
        'plugin:prettier/recommended',
        'plugin:testing-library/react',
        'plugin:jest-dom/recommended',
      ]
eslint.config.mjs
import { FlatCompat } from '@eslint/eslintrc';

const compat = new FlatCompat();

export default [
  {},
  {},
   ...compat.extends(
    'plugin:import/errors',
    'plugin:import/warnings',
    'plugin:import/typescript',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:jsx-a11y/recommended',
    'plugin:prettier/recommended',
    'plugin:testing-library/react',
    'plugin:jest-dom/recommended'
  ),

終わりに

とりあえずESlintの設定ファイルをベストプラクティスに沿って作成することができました!

調べながら変換しているので、もしかしたら誤っている部分もあるかもしれません。
ぜひご指摘頂けると幸いです。

参考サイト

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?