ESLint — статический анализатор JavaScript, который ищет ошибки, опасные конструкции и расхождения с командным стилем. Он не запускает программу и не заменяет тесты: линтер строит дерево разбора и применяет к нему набор правил. В 2026 году основной считается ветка ESLint 9 и новее, где плоская конфигурация eslint.config.js вытеснила старые файлы .eslintrc. Ниже — установка с нуля, рабочая конфигурация, запуск в редакторе и CI, а также разбор ситуаций, когда линтер молчит или ругается не туда.
Зачем линтер, если есть типы и тесты
Типизация ловит несовпадение типов, тесты — отклонения поведения, но ни то, ни другое не замечает неиспользуемую переменную, забытый await, сравнение через == или console.log, оставленный в продакшене. Линтер работает до запуска и стоит дешевле отладки. Вторая его роль — договорённость: правила фиксируют спорные мелочи вроде кавычек и порядка импортов, и обсуждать их приходится один раз, а не в каждом пул-реквесте.
Установка
Нужен Node.js 18.18 или новее. Интерактивный мастер npm init @eslint/config@latest создаёт конфигурацию под проект и сам ставит зависимости. Если хочется контролировать состав пакетов, установите вручную: eslint — движок, @eslint/js — набор рекомендованных правил, globals — описания глобальных объектов Node.js и браузера.
npm init -y
npm install --save-dev eslint @eslint/js globals
npx eslint --version
npx eslint --help
Проверьте, что в проекте нет старого .eslintrc: при наличии обоих файлов легко запутаться, какой из них применяется. На время миграции переменная ESLINT_USE_FLAT_CONFIG=false возвращает прежнее поведение, но новые проекты лучше сразу заводить на плоском формате.
Плоская конфигурация
Файл eslint.config.js экспортирует массив объектов. Каждый объект применяется к файлам по шаблонам files, а порядок важен: следующие элементы перекрывают предыдущие. Ключи env и extends из старого формата не работают, вместо них languageOptions и готовые конфиги. Игнорирование выносится в отдельный объект только с ключом ignores: внутри обычного блока он не сработает.
import js from "@eslint/js";
import globals from "globals";
export default [
{ ignores: ["dist/**", "coverage/**", ".eslintcache"] },
js.configs.recommended,
{
files: ["**/*.js"],
languageOptions: {
ecmaVersion: 2024,
sourceType: "module",
globals: { ...globals.node, ...globals.browser }
},
rules: {
eqeqeq: "error",
"no-var": "error",
"prefer-const": "warn",
"no-unused-vars": ["warn", { argsIgnorePattern: "^_" }]
}
}
];
Здесь recommended включает базовые проверки, ecmaVersion задаёт синтаксис, sourceType переключает модули, а globals убирает ложные срабатывания no-undef. Правила можно ослаблять точечно, но глобальное отключение целой категории обычно скрывает реальные дефекты. Для TypeScript добавляют пакет typescript-eslint и его парсер, иначе синтаксис типов просто не разберётся.
Запуск и автоисправление
В девятой версии команда eslint . сама обходит проект, отдельный шаблон *.js не нужен. Флаг --fix правит то, что поддаётся безопасному автоматическому изменению, --max-warnings 0 превращает предупреждения в ошибку сборки, а --cache ускоряет повторные прогоны.
npx eslint .
npx eslint src/index.js
npx eslint . --fix
npx eslint . --max-warnings 0 --cache
npx eslint --print-config src/index.js
Файл .eslintcache добавьте в .gitignore: его содержимое привязано к локальным путям. Если непонятно, откуда взялось правило, --print-config печатает итоговую конфигурацию для конкретного файла, а --inspect-config открывает в браузере разбор всех слоёв. Отладка вида DEBUG=eslint:cli-engine npx eslint . показывает, какие файлы реально проверяются.
Редактор и коммиты
Расширение ESLint для VS Code подхватывает плоский конфиг автоматически. Включите исправление при сохранении, но не полагайтесь только на него: часть правил не имеет автофикса. Для проверки перед коммитом удобна связка husky и lint-staged — она прогоняет линтер лишь по изменённым файлам и не тормозит работу на большом репозитории.
npm install --save-dev husky lint-staged
npx husky init
npx lint-staged
В package.json достаточно указать, что для *.js выполняется eslint --fix. Если хук обходит линтер через git commit --no-verify, проверка в CI всё равно поймает проблему — в этом и смысл двойного барьера.
CI
В конвейере линтер должен падать, а не предупреждать. Кэш npm и ключ --cache экономят минуты на больших проектах; версию Node фиксируйте явно, чтобы локальный и серверный разбор совпадали.
steps:
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx eslint . --max-warnings 0 --cache
Отдельный шаг для --fix в CI не нужен: исправления должны появляться в коде до merge, иначе сборка будет менять файлы незаметно для ревью.
Точечные исключения
Иногда правило мешает по делу: например, в конфигурационном файле нужен console.log, а в тестах доступны глобальные функции фреймворка. Отключать правило целиком нельзя — лучше ограничить область. Комментарий eslint-disable-next-line действует на одну строку, eslint-disable — на весь файл, а в конфигурации можно задать отдельный блок для тестов.
/* eslint-disable-next-line no-console */
console.log("debug");
{
files: ["**/*.test.js"],
languageOptions: { globals: globals.jest }
}
Каждое исключение стоит сопровождать причиной в комментарии: через полгода никто не вспомнит, почему правило выключено. Если подавлений становится много, это сигнал пересмотреть правило, а не засыпать ими весь код. Отдельный блок для тестов заодно снимает ложные предупреждения о функциях, которые определены глобально.
Типичные проблемы
- Файл игнорируется: проверьте отдельный объект
ignoresи вывод--debug. - Ложное
no-undef: добавьте нужный набор изglobals, а не отключайте правило. - Конфликт с Prettier: пакет
eslint-config-prettierставят последним, чтобы он снял стилевые правила. - Правило не исправляется: у него нет fixer, правка только ручная.
- Слишком медленно: включите кэш и исключите собранные каталоги.
- Разные результаты у коллег: зафиксируйте версии ESLint и плагинов в lock-файле.
Итог
ESLint окупается, когда правила лежат в репозитории, а проверка встроена в редактор и CI. Начните с recommended, добавьте globals и несколько действительно важных правил, затем подключите кэш и pre-commit. Не включайте сотню правил сразу: шум от линтера приводит к тому, что его отключают целиком, вместе с полезными проверками.