Наследование конфигураций: extends

Механизм extends в ESLint определяет порядок наследования и объединения конфигураций из нескольких источников, позволяя строить многоуровневую систему правил на основе базовых пресетов, плагинов и локальных настроек проекта. Он является ключевым инструментом масштабирования линтинга в проектах среднего и крупного размера, где единый набор правил формируется из нескольких слоёв: стандартов языка, командных соглашений, доменных требований и частных исключений.


Базовая модель наследования конфигураций

Конфигурация ESLint в формате .eslintrc может наследовать другие конфигурации через поле:

{
  "extends": ["eslint:recommended"]
}

или в упрощённой форме:

{
  "extends": "eslint:recommended"
}

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


Типы значений extends

Встроенные пресеты ESLint

ESLint предоставляет несколько встроенных конфигураций:

{
  "extends": [
    "eslint:recommended",
    "eslint:all"
  ]
}
  • eslint:recommended — набор базовых правил, считающихся безопасными для большинства проектов
  • eslint:all — включает все доступные правила ESLint в строгом режиме

Эти конфигурации не требуют установки дополнительных пакетов.


Shareable-конфигурации npm-пакетов

Наиболее распространённый вариант наследования — использование пакетов вида eslint-config-*.

{
  "extends": [
    "airbnb",
    "standard",
    "prettier"
  ]
}

Фактическое разрешение имён происходит по правилам:

  • airbnbeslint-config-airbnb
  • @company/eslint-config → scoped пакет
  • @company/eslint-config-base@company/eslint-config-base

Если пакет установлен в node_modules, ESLint автоматически подхватывает его конфигурацию.


Конфигурации плагинов

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

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

Формат:

plugin:<plugin-name>/<config-name>

Такие конфигурации позволяют подключать стандартизированные правила для конкретных технологий без ручной настройки.


Локальные конфигурации

Можно наследовать конфигурации из локальных файлов:

{
  "extends": "./configs/base-eslint.json"
}

или через директории:

{
  "extends": "./eslint/shared.js"
}

Это позволяет разделять конфигурацию проекта на логические уровни.


Порядок применения extends

ESLint обрабатывает массив extends слева направо, что критически важно для понимания приоритетов.

{
  "extends": [
    "eslint:recommended",
    "airbnb",
    "prettier"
  ]
}

Порядок означает:

  1. Загружается eslint:recommended
  2. Поверх него применяется airbnb
  3. Поверх всего применяется prettier
  4. Локальная конфигурация применяется последней

Итоговое правило: последний источник имеет наивысший приоритет.


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

ESLint не просто заменяет конфигурации, а выполняет частичное слияние (merge).

rules

Поле rules объединяется по принципу:

  • если правило объявлено несколько раз — побеждает последнее значение
  • если правило отсутствует — оно добавляется
{
  "extends": ["eslint:recommended"],
  "rules": {
    "eqeqeq": "off"
  }
}

Если в eslint:recommended правило eqeqeq включено, локальная конфигурация его отключит.


env

Поле env объединяется логически:

{
  "env": {
    "browser": true
  },
  "extends": ["some-config"]
}

Если в some-config указано:

{
  "env": {
    "node": true
  }
}

итог:

{
  "env": {
    "browser": true,
    "node": true
  }
}

Значения не перезаписываются, а дополняются.


parserOptions

parserOptions работает по принципу поверхностного переопределения:

{
  "parserOptions": {
    "ecmaVersion": 2022
  }
}

Если базовая конфигурация содержит:

{
  "parserOptions": {
    "sourceType": "module"
  }
}

итог будет:

{
  "parserOptions": {
    "ecmaVersion": 2022
  }
}

Поле не сливается глубоко, а заменяется целиком.


plugins

Массив плагинов объединяется:

{
  "plugins": ["react"]
}

и

{
  "plugins": ["import"]
}

результат:

{
  "plugins": ["react", "import"]
}

Конфликты конфигураций

При наследовании часто возникают конфликты между наборами правил.

Сценарий конфликта

{
  "extends": ["airbnb", "prettier"],
  "rules": {
    "semi": "error"
  }
}
  • airbnb может требовать обязательные точки с запятой
  • prettier может отключать контроль форматирования

Итог зависит от порядка:

  • если prettier последним — он переопределяет форматирующие правила
  • если airbnb последним — форматирование может конфликтовать с Prettier

Конфигурации с несколькими уровнями наследования

Некоторые пакеты сами используют extends внутри себя.

Пример цепочки:

project config
  → airbnb
    → eslint:recommended
      → базовые правила ESLint

Каждый уровень может:

  • добавлять новые правила
  • изменять существующие
  • отключать часть базовых проверок

Наследование и плагины с именованными конфигами

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

{
  "extends": [
    "plugin:vue/base",
    "plugin:vue/vue3-recommended",
    "plugin:vue/strongly-recommended"
  ]
}

Каждый последующий уровень усиливает набор правил:

  • base — минимальный набор
  • recommended — стандарт индустрии
  • strongly-recommended — строгий режим

Разрешение пакетов и поиск конфигураций

При указании:

{
  "extends": "airbnb"
}

ESLint выполняет:

  1. Поиск eslint-config-airbnb
  2. Проверка node_modules
  3. Загрузка package.json пакета
  4. Чтение поля eslintConfig или экспортируемого объекта

Для scoped-пакетов:

{
  "extends": "@company"
}

ESLint ищет:

@company/eslint-config

Переопределение правил после extends

Локальная конфигурация всегда имеет приоритет:

{
  "extends": ["eslint:recommended"],
  "rules": {
    "no-console": "off"
  }
}

Даже если базовая конфигурация включает строгие ограничения, локальные настройки могут их отключить или изменить.


Переиспользование конфигураций в монорепозиториях

В монорепозиториях часто используется единая базовая конфигурация:

/config/eslint/base.json
/packages/app/.eslintrc
/packages/ui/.eslintrc

Каждый пакет:

{
  "extends": "../. ./config/eslint/base.json"
}

или:

{
  "extends": ["@company/eslint-config-base"]
}

Это обеспечивает единый стандарт качества кода.


Комбинация нескольких стратегий наследования

В реальных проектах часто комбинируются разные источники:

{
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "airbnb",
    "prettier"
  ],
  "rules": {
    "react/react-in-jsx-scope": "off",
    "no-unused-vars": "warn"
  }
}

Такой подход формирует многоуровневую модель:

  • базовая безопасность кода (eslint:recommended)
  • технологические правила (react)
  • командный стиль (airbnb)
  • отключение конфликтов форматирования (prettier)
  • локальные исключения

Особенности и ограничения наследования

Механизм extends имеет ряд технических ограничений:

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

Поведение при циклических зависимостях

Если конфигурации наследуют друг друга циклически:

A → B → C → A

ESLint обнаруживает цикл и прекращает загрузку, предотвращая бесконечную рекурсию. Такие конфигурации считаются некорректными и требуют ручного исправления структуры наследования.


Итоговая модель работы extends

Механизм можно представить как последовательное наложение слоёв:

базовая конфигурация
+ встроенные пресеты
+ shareable configs
+ plugin configs
+ локальная конфигурация
= итоговые правила ESLint

Каждый слой способен:

  • добавлять новые правила
  • изменять существующие
  • отключать наследованные настройки

Именно эта модель делает extends центральным инструментом организации сложных конфигураций ESLint в масштабируемых JavaScript-проектах.