Flat config: новый формат eslint.config.js

Flat config — это новый подход к конфигурации ESLint, в котором вместо каскадного наследования и множества конфигурационных файлов используется единый массив конфигурационных объектов. Основная точка входа — файл eslint.config.js (или eslint.config.mjs), который полностью заменяет классические .eslintrc.*.

Ключевая идея flat config заключается в том, что конфигурация становится линейной, явной и предсказуемой: порядок объектов в массиве напрямую определяет приоритет и область действия правил.


Базовая структура eslint.config.js

Flat config представляет собой JavaScript-модуль, который экспортирует массив:

export default [
  {
    files: ["**/*.js"],
    rules: {
      semi: ["error", "always"],
      quotes: ["error", "single"]
    }
  }
];

Каждый объект массива описывает отдельный слой конфигурации. ESLint применяет их последовательно, объединяя параметры.


Принцип работы конфигурационного массива

Flat config полностью отказывается от концепции “extends” в классическом виде. Вместо этого используется:

  • явное перечисление конфигураций
  • порядок элементов массива как механизм приоритета
  • локальное переопределение через files

Применение правил происходит сверху вниз:

export default [
  {
    rules: {
      quotes: ["error", "single"]
    }
  },
  {
    rules: {
      quotes: ["error", "double"]
    }
  }
];

Итоговое правило — double, так как второй объект перекрывает первый.


Поле files и область действия конфигурации

Ключ files задаёт, к каким файлам применяется конфигурационный объект.

export default [
  {
    files: ["src/**/*.js"],
    rules: {
      semi: ["error", "always"]
    }
  },
  {
    files: ["test/**/*.js"],
    rules: {
      semi: ["off"]
    }
  }
];

Файлы сопоставляются по glob-шаблонам. Если files не указан, конфигурация считается глобальной.


Игнорирование файлов через ignores

В flat config игнорирование встроено в саму систему:

export default [
  {
    ignores: ["dist/**", "node_modules/**"]
  }
];

Особенность заключается в том, что ignores может находиться в любом конфигурационном объекте и влияет на все последующие правила.

Допускается глобальное игнорирование без files:

export default [
  {
    ignores: ["coverage/**"]
  },
  {
    files: ["**/*.js"],
    rules: {
      no-console: "warn"
    }
  }
];

languageOptions: управление окружением и парсером

В классическом ESLint использовались parserOptions, env, globals. В flat config они объединены в languageOptions.

export default [
  {
    languageOptions: {
      ecmaVersion: 2023,
      sourceType: "module",
      globals: {
        window: "readonly",
        document: "readonly"
      }
    }
  }
];

parser

Парсер задаётся явно:

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

export default [
  {
    languageOptions: {
      parser: babelParser
    }
  }
];

rules: система правил

Поле rules работает аналогично классическому ESLint, но без наследования через extends.

export default [
  {
    rules: {
      eqeqeq: "error",
      curly: ["error", "all"],
      "no-unused-vars": "warn"
    }
  }
];

При конфликте правил действует принцип последнего применённого объекта.


plugins: импорт как объект

Flat config больше не использует строковые идентификаторы плагинов. Плагины импортируются как модули.

import js from "@eslint/js";
import react from "eslint-plugin-react";

export default [
  js.configs.recommended,
  {
    plugins: {
      react
    },
    rules: {
      "react/jsx-uses-react": "error"
    }
  }
];

Ключевой момент: plugin становится объектом, а не строкой.


Подключение готовых конфигураций

Многие пакеты ESLint теперь экспортируют flat-конфиги напрямую:

import js from "@eslint/js";

export default [
  js.configs.recommended
];

Это заменяет привычные:

  • extends: "eslint:recommended"
  • extends: "plugin:react/recommended"

Порядок применения конфигураций

Flat config использует строгий порядок:

  1. глобальные конфигурации
  2. ignores
  3. конфигурации с files
  4. более поздние объекты перекрывают ранние

Пример:

export default [
  {
    rules: {
      semi: "error"
    }
  },
  {
    files: ["test/**"],
    rules: {
      semi: "off"
    }
  }
];

Для тестовых файлов правило semi отключается полностью.


Разделение конфигураций по файлам

Flat config поддерживает модульную структуру:

import base from "./eslint.base.js";
import node from "./eslint.node.js";
import react from "./eslint.react.js";

export default [
  ...base,
  ...node,
  ...react
];

Каждый файл экспортирует массив конфигураций.


Отличия от .eslintrc

Flat config заменяет несколько фундаментальных механизмов:

Исчезает наследование extends

Вместо цепочек:

{
  "extends": ["eslint:recommended", "plugin:react/recommended"]
}

используется:

export default [
  js.configs.recommended,
  react.configs.flat.recommended
];

Исчезает env

"env": { "browser": true }

заменяется на:

languageOptions: {
  globals: {
    window: "readonly",
    document: "readonly"
  }
}

Исчезают overrides

Вместо:

{
  "overrides": [
    {
      "files": ["*.test.js"],
      "rules": {}
    }
  ]
}

используется отдельный объект:

export default [
  {
    files: ["*.test.js"],
    rules: {}
  }
];

Использование defineConfig

Для улучшения типизации и читаемости применяется вспомогательная функция:

import { defineConfig } from "eslint/config";

export default defineConfig([
  {
    rules: {
      quotes: ["error", "single"]
    }
  }
]);

Она не меняет поведение, но улучшает автодополнение и проверку структуры.


Поддержка CommonJS и ESM

Flat config ориентирован на ESM:

export default [...]

Однако возможна и CommonJS-форма:

module.exports = [
  {
    rules: {
      semi: "error"
    }
  }
];

ESM считается основным сценарием, особенно при использовании плагинов через import.


Пример комплексной конфигурации

import js from "@eslint/js";
import react from "eslint-plugin-react";
import globals from "globals";

export default [
  js.configs.recommended,

  {
    languageOptions: {
      ecmaVersion: 2023,
      sourceType: "module",
      globals: globals.browser
    },
    rules: {
      "no-console": "warn"
    }
  },

  {
    files: ["src/**/*.{js,jsx}"],
    plugins: { react },
    rules: {
      "react/jsx-uses-react": "error",
      "react/jsx-uses-vars": "error"
    }
  },

  {
    files: ["**/*.test.js"],
    rules: {
      "no-console": "off"
    }
  }
];

Механика объединения конфигураций

Каждый объект конфигурации рассматривается как независимый слой. ESLint выполняет:

  • сбор всех подходящих объектов по files
  • объединение languageOptions
  • объединение rules с приоритетом последнего значения
  • применение ignores до анализа файлов

Конфликтующие поля разрешаются через:

  • перезапись примитивов
  • глубокое объединение объектов там, где это возможно (например globals)

Особенности поведения ignores

ignores имеет приоритет над files. Даже если файл соответствует files, он исключается при совпадении с ignores.

export default [
  {
    ignores: ["src/legacy/**"]
  },
  {
    files: ["src/**/*.js"],
    rules: {
      strict: "error"
    }
  }
];

Файлы в src/legacy полностью исключаются из анализа.


Совместимость с плагинами старого формата

Некоторые плагины продолжают поддерживать только legacy-конфигурации. Для них используется адаптер:

import compat from "@eslint/eslintrc";

const { FlatCompat } = compat;
const compatInstance = new FlatCompat();

export default [
  ...compatInstance.config({
    extends: ["plugin:react/recommended"]
  })
];

Это механизм переходного периода между системами конфигурации.


Приоритет и детерминированность

Flat config устраняет неоднозначность каскадирования:

  • нет скрытых конфигураций из node_modules
  • нет неявного наследования
  • нет глобального env-магического поведения

Каждое правило определяется конкретным объектом массива и его позицией.


Объединение languageOptions

Если несколько объектов задают languageOptions, происходит частичное объединение:

export default [
  {
    languageOptions: {
      ecmaVersion: 2022
    }
  },
  {
    languageOptions: {
      sourceType: "module"
    }
  }
];

Итог:

  • ecmaVersion: 2022
  • sourceType: module

Типичные структуры конфигурации

Flat config часто организуется слоями:

  • базовый слой правил
  • слой среды (browser/node)
  • слой фреймворка (React/Vue)
  • слой тестов
  • слой исключений

Каждый слой — отдельный объект или импортируемый массив.


Поведение при отсутствии files

Если files не указан:

  • конфигурация считается глобальной
  • применяется ко всем файлам проекта
  • может быть переопределена более специфичными слоями ниже по массиву
export default [
  {
    rules: {
      "no-debugger": "error"
    }
  }
];

Это правило действует повсеместно.