ESLint в современной архитектуре flat config предоставляет функцию
defineConfig, предназначенную для формирования
конфигурационных массивов с улучшенной структурой, типизацией и
предсказуемым поведением объединения настроек. В отличие от
классического формата .eslintrc, где конфигурации строились
через вложенные объекты с неявным наследованием, flat config опирается
на явные массивы конфигурационных объектов, где порядок и композиция
играют ключевую роль.
Функция defineConfig используется как обёртка над
массивом конфигурационных объектов ESLint. Её основная задача
заключается в том, чтобы:
Фактически defineConfig не изменяет семантику
конфигурации, а формализует её структуру, делая её более строгой и
предсказуемой.
Flat config в ESLint строится вокруг принципа последовательного применения конфигурационных объектов. Каждый объект в массиве рассматривается как независимый слой настроек, который может:
files и
ignores.defineConfig выступает как синтаксическая оболочка над
этим массивом, не добавляя дополнительной логики выполнения, но фиксируя
структуру данных.
Структурно использование defineConfig сводится к
передаче массива конфигурационных объектов:
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: 2022,
sourceType: "module"
},
rules: {
semi: ["error", "always"],
quotes: ["error", "single"]
}
}
]);
Каждый объект в массиве описывает отдельный слой конфигурации. Порядок следования имеет значение, так как последующие элементы могут переопределять предыдущие.
Конфигурационный объект внутри defineConfig обычно
включает несколько ключевых секций:
Поле files определяет область применения конфигурации.
Поддерживаются glob-шаблоны:
files: ["src/**/*.ts", "tests/**/*.ts"]
Этот механизм заменяет концепцию overrides из старого
формата, делая правила более локализованными и явными.
Раздел rules определяет поведение линтера:
rules: {
no-console: "warn",
eqeqeq: "error"
}
Каждое правило может принимать строковое значение или массив с конфигурацией параметров.
Секция languageOptions управляет синтаксическим
анализом:
languageOptions: {
ecmaVersion: "latest",
sourceType: "module",
globals: {
window: "readonly"
}
}
Эта часть напрямую влияет на работу парсера и интерпретацию кода.
Подключение плагинов осуществляется через явное перечисление:
import js from "@eslint/js";
export default defineConfig([
{
plugins: {
js
}
}
]);
Плагины становятся объектами, доступными для правил через пространство имён.
Одним из ключевых аспектов defineConfig является
предсказуемое объединение конфигурационных слоёв. При наличии нескольких
объектов в массиве применяется последовательная стратегия:
Пример композиции:
export default defineConfig([
{
rules: {
quotes: ["error", "single"],
semi: ["error", "always"]
}
},
{
rules: {
quotes: ["error", "double"]
}
}
]);
В результате правило quotes будет переопределено, а
semi сохранится из первого объекта.
defineConfig активно используется в TypeScript-среде
благодаря возможности вывода типов конфигурации. Это снижает вероятность
ошибок в ключах и значениях.
Типизация позволяет:
Пример:
import { defineConfig } from "eslint/config";
export default defineConfig([
{
rules: {
"no-unused-vars": "error"
}
}
]);
При использовании типизированного окружения некорректные ключи правил выявляются на этапе компиляции.
Конфигурация через defineConfig упрощает подключение
внешних наборов правил. Плагины часто экспортируют готовые конфигурации,
которые могут быть включены напрямую:
import js from "@eslint/js";
import react from "eslint-plugin-react";
export default defineConfig([
js.configs.recommended,
{
plugins: {
react
},
rules: {
"react/react-in-jsx-scope": "off"
}
}
]);
Такая композиция позволяет собирать конфигурацию из готовых модулей и локальных переопределений.
В ряде случаев конфигурация формируется динамически.
defineConfig допускает использование выражений,
возвращающих массив:
const isProduction = process.env.NODE_ENV === "production";
export default defineConfig([
{
rules: {
"no-debugger": isProduction ? "error" : "off"
}
}
]);
Такой подход позволяет адаптировать поведение линтера под окружение без изменения структуры конфигурационного файла.
В flat config игнорирование реализуется через отдельные объекты:
export default defineConfig([
{
ignores: ["dist/**", "node_modules/**"]
}
]);
Игнорируемые пути исключаются из анализа до применения правил, что влияет на производительность и объём проверяемых файлов.
Механизм приоритета основан на последовательности массива:
files конфигурации перекрывают
общие.Пример многоуровневой конфигурации:
export default defineConfig([
{
rules: {
semi: ["error", "always"]
}
},
{
files: ["**/*.test.js"],
rules: {
semi: "off"
}
}
]);
В данном случае тестовые файлы исключаются из требования обязательной точки с запятой.
export default defineConfig([
{
files: ["src/**/*.js"],
rules: {
"no-console": "warn"
}
},
{
files: ["scripts/**/*.js"],
rules: {
"no-console": "off"
}
}
]);
export default defineConfig([
{
files: ["**/*.test.js"],
languageOptions: {
globals: {
describe: "readonly",
it: "readonly"
}
}
}
]);
import js from "@eslint/js";
export default defineConfig([
js.configs.recommended,
{
rules: {
"no-var": "error",
prefer-const: "error"
}
}
]);
При конфликте правил применяется принцип «последний применённый имеет приоритет». Однако некоторые правила ESLint обладают внутренней логикой объединения:
defineConfig не вводит собственную семантику выполнения.
Его ограничения связаны исключительно с моделью flat config:
.eslintrc без
миграционного слоя.Использование defineConfig приводит к изменению подхода
к организации конфигурации:
extends;Такой подход делает конфигурацию ближе к функциональной композиции, где каждый элемент массива выполняет роль трансформационного слоя над предыдущими.