Алгоритм поиска и слияния конфигураций

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

В классическом (legacy) режиме используются следующие источники:

  • конфигурационные файлы .eslintrc.* (JSON, YAML, JS)
  • поле eslintConfig в package.json
  • глобальные конфигурации, заданные через CLI
  • конфигурации, подключённые через extends

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


Поиск конфигурации в файловой системе

При анализе конкретного файла ESLint начинает поиск конфигурации, ориентируясь на его расположение. В legacy-режиме применяется восходящий обход директорий.

Алгоритм поиска выглядит следующим образом:

  1. Определяется директория анализируемого файла.

  2. В текущей директории проверяются конфигурационные сущности:

    • .eslintrc.js
    • .eslintrc.cjs
    • .eslintrc.json
    • .eslintrc.yaml / .yml
    • package.json (поле eslintConfig)
  3. Если конфигурация не найдена, происходит переход на уровень выше.

  4. Процесс повторяется до достижения корня файловой системы.

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

В Flat Config подходе поиск как таковой отсутствует: конфигурация задаётся явно через массив, обычно в eslint.config.js, и применяется целиком.


Слияние конфигураций: базовый принцип

ESLint не заменяет конфигурации целиком при обнаружении новой, а выполняет глубокое слияние объектов. При этом учитываются разные типы полей:

  • простые значения (строки, числа, булевы)
  • объекты (rules, settings)
  • массивы (plugins, extends)

Общий принцип:

более специфичная конфигурация переопределяет более общую


Слияние правил (rules)

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

  • если правило отсутствует в более поздней конфигурации — оно сохраняется
  • если правило определено повторно — применяется последнее значение
  • если используется массив конфигураций (extends) — порядок имеет значение

Пример логики:

  1. базовая конфигурация:

    rules: {
      semi: "error",
      quotes: "warn"
    }
  2. расширяющая конфигурация:

    rules: {
      quotes: "error"
    }

Итог:

rules: {
  semi: "error",
  quotes: "error"
}

Алгоритм обработки extends

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

  • встроенную конфигурацию ESLint
  • npm-пакет (например eslint-config-airbnb)
  • локальный файл

Последовательность обработки:

  1. Разбор массива extends слева направо.

  2. Для каждого элемента выполняется загрузка конфигурации.

  3. Конфигурации рекурсивно разворачиваются, если они сами содержат extends.

  4. Результаты объединяются в порядке:

    • сначала базовые конфигурации
    • затем расширяющие
    • затем локальная конфигурация

Обработка overrides

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

Алгоритм применения:

  1. Основная конфигурация формируется как базовая.
  2. Для каждого файла проверяются правила совпадения glob-шаблонов.
  3. Если файл подходит под files, применяется соответствующий override.
  4. Override сливается с базовой конфигурацией с более высоким приоритетом.

Пример логики приоритета:

  • базовые правила
  • extends
  • глобальная конфигурация
  • overrides (самый высокий приоритет в legacy)

Глубокое слияние объектов settings и parserOptions

Поля settings и parserOptions объединяются рекурсивно.

settings

  • одинаковые ключи перезаписываются
  • вложенные объекты объединяются

Пример:

settings: {
  react: {
    version: "detect"
  }
}

и

settings: {
  react: {
    pragma: "React"
  }
}

результат:

settings: {
  react: {
    version: "detect",
    pragma: "React"
  }
}

Приоритеты конфигураций

В legacy-модели итоговый порядок влияния выглядит следующим образом:

  1. базовые конфигурации (встроенные и extends)
  2. конфигурация из файлов выше по дереву
  3. локальная конфигурация проекта
  4. overrides
  5. CLI-параметры (например --rule)
  6. inline-комментарии в коде (/* eslint-disable */)

Inline-директивы имеют наивысший приоритет, поскольку действуют на уровне конкретной строки или блока.


Inline-конфигурации и их влияние на алгоритм

Комментарии внутри исходного кода модифицируют результат после основного этапа слияния.

Типы директив:

  • eslint-disable
  • eslint-enable
  • eslint-disable-next-line
  • eslint-env

Алгоритм:

  1. формируется итоговая конфигурация файла
  2. выполняется линтинг AST
  3. при встрече директив правила временно отключаются или изменяются
  4. результат корректируется перед выводом ошибок

Игнорирование файлов

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

Источники игнорирования:

  • .eslintignore
  • поле ignorePatterns
  • глобальные ignore настройки CLI
  • встроенные исключения (node_modules и др.)

Алгоритм:

  1. проверка глобальных ignore
  2. проверка .eslintignore
  3. проверка ignorePatterns
  4. если совпадение найдено — файл исключается до стадии конфигурации

Flat Config: упрощённый алгоритм слияния

В Flat Config модель конфигурация представлена массивом объектов:

export default [
  baseConfig,
  reactConfig,
  {
    files: ["**/*.test.js"],
    rules: { "no-unused-expressions": "off" }
  }
]

Основные принципы:

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

Алгоритм:

  1. загрузка массива конфигураций
  2. фильтрация по files
  3. последовательное применение конфигураций
  4. поверхностное или частично глубокое слияние
  5. фиксация итогового набора правил

Разрешение конфликтов правил

При конфликте одинаковых правил ESLint применяет детерминированный подход:

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

Роль кеширования в процессе конфигурации

Для ускорения повторного анализа ESLint использует кеширование:

  • результатов загрузки конфигураций
  • разобранных extends
  • результатов glob-матчинга

Алгоритм:

  1. вычисление ключа кеша на основе пути и конфигурации
  2. проверка наличия готового результата
  3. при наличии — возврат без повторного слияния
  4. при отсутствии — полная пересборка конфигурации

Итоговая модель обработки конфигурации

Внутренний процесс можно представить как последовательность этапов:

  1. определение анализируемого файла
  2. проверка ignore-правил
  3. загрузка конфигурации (file-based или flat)
  4. разворачивание наследования (extends)
  5. глубокое слияние объектов
  6. применение overrides
  7. обработка CLI-переопределений
  8. финальная модификация через inline-директивы
  9. запуск линтинга с итоговым набором правил