6 февраля 2026 вышел ESLint 10.0.0, и для многих команд это не релиз из категории “обновим потом”. В v10 не просто чуть поменяли правила. Тут добили старый формат конфигурации, отключили часть привычных CLI-флагов и сделали flat config единственной нормальной точкой входа.

Из-за этого главный вопрос звучит не как “какие новые правила появились в ESLint 10”. Главный вопрос звучит так: можно ли вашей кодовой базе обновляться прямо сейчас, и если да, то в каком порядке делать миграцию, чтобы не устроить неделю ложных тревог в CI, редакторе и ревью.

Эта статья про практический маршрут. Ниже разберем, кому уже пора на v10, кому лучше пока остаться на v9, что именно ломает старая схема настройки и как провести переход так, чтобы это был один предсказуемый инфраструктурный PR, а не бессмысленная война за кодстайл.

Обновлено 1 апреля 2026: статья переписана. Добавлены правило выбора, новый сценарий миграции и более практическая структура.

Стоит ли обновляться на ESLint 10 прямо сейчас

Есть два очень разных сценария.

Первый: проект уже живет на eslint.config.js, Node у команды и CI не ниже 20.19.0, а плагины и переиспользуемые конфиги поддерживаются без костылей. В таком случае переход на ESLint 10 обычно выглядит как нормальное техническое обновление. Да, новые ошибки могут появиться, но это уже управляемая история.

Второй: проект все еще на .eslintrc*, часть людей запускает линтер на старом Node, а в CI и редакторе живут разные способы конфигурации. В этом состоянии апгрейд почти никогда не является “обновлением линтера”. Это миграция всего слоя линтинга.

Обновляться прямо сейчас разумно, если:

  • у вас уже есть flat config или вы готовы мигрировать на него в этом же PR;
  • локально и в CI используется Node 20.19.0+;
  • у проекта нет критичного старого пресета, который полностью блокирует flat config;
  • у команды есть ресурс довести переход до зелёного состояния, а не бросить на полпути.

Лучше пока не торопиться, если:

  • проект сидит на Node 18 или раннем Node 20;
  • у вас большой монорепозиторий на старых переиспользуемых конфигах;
  • редакторная интеграция и CI живут на разных версиях и флагах;
  • сейчас нет времени нормально отделить PR с миграцией от последующей чистки правил.

Если сжать всё до одного правила, оно такое: ESLint 10 стоит ставить не тогда, когда “релиз уже вышел”, а тогда, когда команда готова одновременно закрыть runtime, конфиг и скрипты.

Что именно в v10 ломает старую конфигурацию

Ниже не полный changelog релиза, а именно то, что чаще всего бьет по обычному фронтенд-проекту.

  • Node.js ниже 20.19.0, а также ветки 21.x и 23.x, больше не поддерживаются.
  • Старый конфиг .eslintrc* больше не поддерживается вообще. Нужен eslint.config.js.
  • .eslintignore больше не читается автоматически.
  • CLI-флаги --env, --ignore-path, --no-eslintrc, --resolve-plugins-relative-to и --rulesdir больше не работают с flat config.
  • /* eslint-env */ комментарии теперь репортятся как ошибки.
  • eslint:recommended получил новые правила: no-unassigned-vars, no-useless-assignment, preserve-caught-error.
  • ESLint начал корректно учитывать JSX references, поэтому часть старых React-обходных путей больше не нужна.
  • Новый алгоритм поиска eslint.config.* стал дефолтным, а старый флаг больше не нужен.

Это важный момент: для многих команд основная боль после апгрейда приходит не из приложения и не из компонентов, а из того, что старый способ настройки ESLint в v10 просто перестал существовать.

Порядок миграции без лишнего шума

Самая частая ошибка здесь одна и та же: обновить всё сразу, получить поток репортов и начать чинить их без разбора. Намного лучше работает другой порядок.

1. Фиксация текущего состояния

Перед апгрейдом полезно убедиться, что текущий линтер уже зеленый. Если проект красный и до обновления, ESLint 10 ничего не прояснит. Он просто смешает старый техдолг с новыми ломающими изменениями.

Практически это значит одно: апгрейд лучше делать отдельной веткой и отдельным PR, без параллельной реформы кодстайла.

2. Runtime и редактор — до изменений в правилах

Перед любыми изменениями в конфиге стоит убедиться, какой Node реально запускает ESLint локально, в CI и в редакторе.

node -v
npx eslint -v
npm ls eslint

Если проект сидит на Node 18 или раннем Node 20, “частичный” апгрейд только добавит шум. В таком состоянии разумнее оставаться на ESLint 9, пока runtime не будет поднят хотя бы до 20.19.0.

3. Если в проекте еще .eslintrc*, сначала миграция, потом обновление

Официальный стартовый маршрут выглядит так:

npx @eslint/migrate-config .eslintrc.json
npm i -D eslint@10 @eslint/js globals

Если конфиг был не JSON, а .eslintrc.js с функциями, условиями или переменными, утилита миграции даст только стартовую заготовку. Это нормально. Ее задача не заменить архитектурное решение команды, а быстро перевести старый формат в рабочую базу.

Если проект зависит от переиспользуемого конфига, который еще не умеет работать с flat config, обычно нужен переходный мост:

npm i -D @eslint/eslintrc @eslint/compat

@eslint/eslintrc нужен для FlatCompat, а @eslint/compat полезен, если проекту нужно подтянуть .gitignore через includeIgnoreFile() или временно смягчить несовместимости экосистемы.

4. Перенос старых точек настройки в новые места

После апгрейда люди часто чинят не то, что сломалось, а то, что сами забыли перенести из старой системы. Удобнее сразу смотреть на замену один-к-одному:

Было до v10 Что делать в v10
.eslintrc* перейти на eslint.config.js
.eslintignore или --ignore-path использовать ignores или includeIgnoreFile()
--env browser,node переносить globals в languageOptions.globals
noInlineConfig, reportUnusedDisableDirectives в старом конфиге переносить в linterOptions
extends: ["eslint:recommended"] импортировать @eslint/js и брать js.configs.recommended
/* eslint-env node */ удалить комментарий и объявить globals в конфиге
старый пресет без flat config временно подключать через FlatCompat

Если не сделать этот перенос сразу, дальше разбирать придется не ошибки кода, а мусор от поломанной конфигурации.

5. Обновление скриптов в package.json и CI

Конфиг может быть уже новым, а команда всё равно может видеть хаос, потому что скрипты запускают ESLint по старым правилам.

То, что нужно убрать из команд:

  • --env
  • --ignore-path
  • --no-eslintrc
  • --resolve-plugins-relative-to
  • --rulesdir
  • --flag v10_config_lookup_from_file

Если проект раньше полагался на старый поиск от текущей директории, в v10 конфиг нужно задавать явно:

eslint . --config ./eslint.config.js

Это же место, где стоит проверить переменные окружения и старые CI-шаблоны. Особенно если в пайплайне кто-то когда-то добавил ESLINT_FLAGS и потом все забыли, зачем.

6. Разбор новых сообщений линтера

После этого уже есть смысл запускать линтер и раскладывать результат по типам:

  • реальные новые ошибки из eslint:recommended;
  • шум от старых React/JSX-обходных решений;
  • старые eslint-disable, которые раньше никто не трогал;
  • правила, которые команда больше не хочет поддерживать;
  • исторический мусор, который просто случайно совпал по времени с апгрейдом.

Самое полезное правило на этом этапе: PR с миграцией и идеологическая чистка конфигурации не должны смешиваться. Сначала апгрейд должен стать предсказуемым. Уже потом можно отдельно спорить о стилистических правилах.

Минимальный eslint.config.js, от которого можно стартовать

Ниже минимальный eslint.config.js, от которого можно оттолкнуться в обычном JS/JSX-проекте. Он не решает весь стек целиком, но показывает, куда теперь переезжают базовые настройки.

import { defineConfig } from "eslint/config";
import js from "@eslint/js";
import globals from "globals";

export default defineConfig([
  {
    ignores: ["dist/**", "coverage/**", "**/.*"],
  },
  js.configs.recommended,
  {
    files: ["**/*.{js,mjs,cjs,jsx}"],
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
      },
      parserOptions: {
        ecmaFeatures: {
          jsx: true,
        },
      },
    },
    linterOptions: {
      reportUnusedDisableDirectives: "warn",
    },
  },
]);

Дальше на этот базовый слой добавляются официальные конфиги для конкретного стека: TypeScript, React, Testing Library, правила для импортов и так далее. Полезно именно то, что ось теперь одна: eslint.config.js, а не набор старых файлов, комментариев и CLI-обходных путей.

Если в проекте есть старый пресет, который еще не умеет работать с flat config, переписывать его целиком в один вечер нерационально. На переходном этапе его можно подключить через FlatCompat, пережить апгрейд ядра, а уже потом вынести совместимость отдельной задачей.

Где переход ломается чаще всего

React и JSX

Одна из самых полезных практических изменений в v10 в том, что ESLint теперь нормально учитывает JSX references. Для React-проекта это означает, что часть старых обходных правил может стать просто ненужной.

Если какое-то правило или плагин раньше был нужен только для того, чтобы <Card /> не считался “неиспользованным импортом”, после апгрейда его стоит перепроверить. Очень часто шум идет именно отсюда.

TypeScript и переиспользуемые конфиги

Если конфиг написан на TypeScript, важно не забыть про jiti: это загрузчик, который позволяет Node.js и инструментам вокруг него читать TypeScript- и ESM-файлы без отдельной предварительной компиляции. Для ESLint 10 нужна версия jiti не ниже 2.2.0.

Вторая типичная проблема здесь не в TypeScript как таковом, а в старых переиспользуемых конфигах. Если они еще не поддерживают flat config, не превращай апгрейд в переписывание всей внутренней платформы. Подключи совместимость, стабилизируй переход и только потом разбирайся с архитектурной чисткой.

CI и редактор

После миграции конфиг может быть правильным, а команда всё равно может видеть разные результаты локально и в пайплайне.

Что здесь проверить:

  • CI действительно запускает ту же команду, что и локально;
  • в CI нет старых переменных вроде ESLINT_FLAGS=v10_config_lookup_from_file;
  • VS Code extension не слишком старый;
  • если цвета вывода внезапно поменялись, стоит проверить NO_COLOR и NODE_DISABLE_COLORS.

Отдельно про редактор: в официальной документации ESLint указано, что поддержка нового flat config для vscode-eslint была добавлена в 3.0.10. Если в команде кто-то сидит на более старом расширении, он может видеть другой linting, чем тот, что реально запускается в CI.

Старые eslint-env и исторические исключения

Если в кодовой базе остались строки вроде:

/* eslint-env node */

их нужно убирать. В v10 такой комментарий не “помогает конфигу”, а ломает lint run.

То же относится к старым eslint-disable, забытым пресетам и отключениям, смысл которых уже никто не помнит. Это не повод вычищать все сразу, но очень хороший сигнал, что после миграции нужен отдельный PR на чистку.

Когда лучше остаться на ESLint 9

Иногда самая взрослая техническая стратегия здесь не “обновиться быстрее”, а честно отложить переход.

Остаться на ESLint 9 еще на один цикл разумно, если:

  • команда не может быстро перейти на Node 20.19.0+;
  • проект сильно завязан на старый .eslintrc*-стек и непонятные переиспользуемые конфиги;
  • нет ресурса закончить миграцию до зеленого CI;
  • апгрейд почти наверняка сольется с большой перестройкой правил, что уничтожит пользу от отдельного PR.

В этом нет ничего постыдного. Плох не “поздний апгрейд”, а апгрейд, который команда бросила в полуразобранном состоянии и после которого линтеру больше никто не доверяет.

Где читать первоисточник

Для первоисточника полезны три ссылки:

Если конкретно разваливается переход с .eslintrc на eslint.config.js, первым источником должен быть гайд по миграции. Если нужен список ломающих изменений, полезно открыть migrate-to-v10 и release notes вместе.

Итог

У ESLint 10 главный смысл не в том, что он “строже” или “умнее”. Главный смысл в том, что эпоха старого конфига закончилась окончательно. Поэтому и апгрейд надо воспринимать не как обновление одной зависимости, а как аккуратную миграцию слоя линтинга.

Самый практичный путь здесь один: отдельный PR, сначала runtime, потом eslint.config.js, потом скрипты и CI, и только потом разбор новых сообщений линтера. Если проект готов к flat config и Node 20.19.0+, это обычно нормальный рабочий апгрейд. Если не готов, лучше честно остаться на ESLint 9, чем превратить линтер в источник фонового шума еще на полгода.