Экосистема ESLint исторически строилась вокруг плагинов и shareable-конфигураций, распространяемых через npm. Совместимость между версиями линтера и сторонними расширениями зависит от нескольких слоёв: версии ядра ESLint, формата конфигурации, API плагинов и механизма резолва зависимостей. Любое изменение в одном из этих слоёв способно вызвать каскад несовместимостей, особенно при переходе между крупными версиями ESLint.
Плагин ESLint представляет собой npm-пакет, экспортирующий набор правил, а также иногда дополнительные конфигурации и процессоры. Базовая структура плагина выглядит следующим образом:
rules — набор правил линтингаconfigs — предустановленные конфигурацииprocessors — обработка нестандартных форматов
файловenvironments (в старых плагинах) — описание глобальных
переменныхСовместимость определяется тем, как плагин взаимодействует с:
Ключевой фактор стабильности — соответствие
peerDependencies в плагине. Большинство современных
плагинов явно указывают диапазон поддерживаемых версий ESLint:
{
"peerDependencies": {
"eslint": ">=7.0.0 <9.0.0"
}
}
Такой диапазон означает, что плагин не гарантирует корректную работу вне указанных версий ядра.
Основные категории несовместимости:
При переходе между ESLint 7 → 8 и особенно 8 → 9 происходили изменения:
context API)Плагины, использующие внутренние или неофициальные свойства
context, часто ломаются первыми.
ESLint 8 стал промежуточной стабильной веткой, где большинство экосистемы обновилось:
eslint-plugin-import,
eslint-plugin-react, eslint-plugin-jsx-a11y)
адаптировались.eslintrc.*Однако даже в этой версии возникали проблемы с:
ESLint 9 усилил переход к flat configuration
(eslint.config.js). Это изменило модель совместимости
радикально.
Ключевые изменения:
.eslintrc.* как основного механизма (legacy
режим теперь опционален)Пример flat-конфигурации:
import js from "@eslint/js";
import react from "eslint-plugin-react";
export default [
js.configs.recommended,
{
plugins: {
react
},
rules: {
"react/jsx-uses-react": "error"
}
}
];
Проблемы совместимости в этом переходе:
extendsСовременная экосистема Node.js усилила разрыв между CommonJS и ESM. ESLint 9 ориентирован на ESM-first подход, что приводит к проблемам:
module.exports = {
rules: {
"no-foo": require("./rules/no-foo")
}
};
Такие плагины могут работать через interop, но:
createRequireexport const rules = {
"no-foo": rule
};
ESM-формат требует корректной настройки type: module в
package.json, иначе загрузка плагина ломается.
Shareable-конфигурации (eslint-config-*) особенно
чувствительны к изменениям ESLint.
Основные источники проблем:
extendsLegacy-конфигурации завязаны на:
{
"extends": ["plugin:react/recommended"]
}
В flat config это заменяется явным импортом, и старые конфиги становятся частично несовместимыми.
Shareable config часто включает плагины как peer dependencies:
{
"peerDependencies": {
"eslint": ">=8",
"eslint-plugin-react": ">=7"
}
}
Несовпадение версий приводит к:
Правила ESLint зависят от структуры AST, формируемой парсером (например, Espree, Babel ESLint, TypeScript ESLint).
Совместимость нарушается при:
Пример проблемного сценария:
OptionalChainingExpressionПлагины регистрируются через namespace:
{
"plugins": ["react"]
}
И используются как:
react/jsx-uses-react
Проблемы возникают при:
Flat config требует явного связывания:
plugins: {
react: reactPlugin
}
ESLint использует Node resolution algorithm, но плагины могут быть:
Типовые проблемы:
Особенно чувствительны:
@typescript-eslint является наиболее критичным примером
зависимости от версии ESLint.
Причины сложности:
@typescript-eslint/parser)Совместимость определяется тройкой:
@typescript-eslint/*Несовпадение любой из этих частей приводит к:
Использование фиксированных диапазонов:
{
"eslint": "8.57.0",
"eslint-plugin-react": "7.34.0"
}
уменьшает вероятность неожиданных разрывов API.
Выбор конфигов, поддерживающих несколько поколений ESLint:
В монорепозиториях применяется стратегия:
При разработке собственных плагинов учитываются:
Экосистема ESLint постепенно движется к:
Это снижает хаос совместимости, но увеличивает стоимость миграций. Плагины, не обновлённые под новые стандарты, постепенно выпадают из экосистемы или требуют wrapper-адаптеров.
Совместимость становится не статическим свойством, а управляемым состоянием конфигурации проекта, зависящим от версии линтера, набора плагинов и выбранного формата конфигурации.