Этапы обработки файла

Обработка файла в ESLint представляет собой последовательность детерминированных этапов, в ходе которых исходный текст JavaScript-кода превращается в структурированное представление (AST), затем анализируется набором правил, после чего формируется отчёт о проблемах и, при необходимости, выполняется автоматическое исправление. Каждый этап строго изолирован, но обменивается данными через промежуточные структуры — конфигурацию, абстрактное синтаксическое дерево и контекст линтинга.


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

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

ESLint применяет механизмы исключения ещё до чтения содержимого:

  • .eslintignore (в классической конфигурации)
  • поле ignores в flat-конфигурации
  • глобальные паттерны вроде node_modules
  • пользовательские glob-выражения

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

Разрешение конфигурационных областей

На этом же этапе определяется, какая конфигурация применяется к файлу:

  • глобальная конфигурация проекта
  • overrides (условные блоки по пути)
  • конфигурации плагинов
  • локальные переопределения через eslint-disable комментарии (учитываются позже, но метаданные подготавливаются заранее)

Результатом становится финальная «эффективная конфигурация» для конкретного файла, объединяющая все источники правил и настроек parser/options.


Загрузка и нормализация конфигурации

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

Слияние конфигураций

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

  • flat config (eslint.config.js)
  • legacy .eslintrc
  • конфигурации плагинов (extends)
  • встроенные пресеты

Происходит глубокое слияние:

  • объединяются массивы плагинов
  • разрешаются конфликты правил (последний источник имеет приоритет)
  • нормализуются значения уровней ("off", "warn", "error" → 0, 1, 2)
  • расширяются псевдонимы конфигураций

Подготовка rule map

Внутренне ESLint преобразует список правил в структуру:

  • ключ: имя правила (no-unused-vars)
  • значение: функция-реализация из плагина
  • метаданные: schema, docs, fixable, messages

Это позволяет обеспечить O(1)-доступ к правилу во время обхода AST.


Чтение исходного файла

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

На этом этапе выполняется:

  • чтение текстового содержимого
  • нормализация кодировки (UTF-8 как основной стандарт)
  • определение типа файла (JS, JSX, TS через parser)
  • предварительная обработка для нестандартных синтаксисов через кастомный parser

Важно, что ESLint не интерпретирует код — он работает исключительно с текстом, передавая его парсеру.


Парсинг и построение AST

Один из ключевых этапов — преобразование кода в абстрактное синтаксическое дерево (AST).

Выбор парсера

ESLint делегирует парсинг внешним или встроенным парсерам:

  • Espree (по умолчанию для JavaScript)
  • @typescript-eslint/parser для TypeScript
  • Babel ESLint parser для экспериментального синтаксиса

Парсер возвращает:

  • AST
  • таблицу токенов
  • диапазоны символов
  • комментарии

Структура AST

AST строится по ESTree-совместимой модели:

  • Program (корневой узел)
  • Statement nodes (IfStatement, ForStatement)
  • Expression nodes (CallExpression, MemberExpression)
  • Literal, Identifier и др.

Каждый узел содержит:

  • type
  • range (начало и конец в исходном тексте)
  • loc (строка и колонка)
  • ссылки на дочерние узлы

Построение контекста линтера

После парсинга создаётся контекст выполнения правил.

Контекст включает:

  • AST файла
  • список активных правил
  • конфигурацию
  • таблицу сообщений
  • utilities API (context.report, context.getSourceCode)
  • информацию о filename и cwd

Также создаётся объект SourceCode, который предоставляет:

  • доступ к тексту файла
  • токены
  • комментарии
  • методы поиска узлов по диапазону

Обход AST и запуск правил

Основная фаза анализа — traversal AST с применением правил.

Механизм подписки правил

Каждое правило экспортирует объект вида:

  • visitor-методы: Identifier(node), CallEx * pression(node)
  • или create(context) → возвращает visitors

ESLint строит единый visitor-map, объединяя все активные правила.

Глубинный обход

AST обходится в глубину (depth-first traversal):

  1. вход в узел
  2. вызов всех правил, подписанных на тип узла
  3. обход дочерних узлов
  4. завершение узла (если есть leave hooks)

Каждый узел обрабатывается всеми релевантными правилами независимо.


Генерация диагностических сообщений

Когда правило обнаруживает нарушение, оно вызывает:

  • context.report()

Внутренне создаётся объект проблемы:

  • ruleId
  • message
  • node или range
  • severity
  • дополнительные data (для шаблонов сообщений)

Сообщения могут использовать шаблоны:

  • "Unexpected var '{{name}}'"

подстановка выполняется на этапе формирования результата.


Фаза накопления результатов

Все сообщения собираются в единый массив проблем.

На этом этапе выполняются дополнительные операции:

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

Также учитываются inline-disable комментарии:

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

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


Автоматическое исправление (Fixing phase)

Если включён режим --fix, ESLint запускает дополнительный этап.

Сбор фиксов

Правила могут возвращать объект fix:

  • функция, принимающая fixer API
  • возвращающая изменения диапазонов текста

Каждый фикс описывает:

  • start / end range
  • replacement text

Применение фиксов

Фиксы агрегируются и применяются с учётом конфликтов:

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

Многопроходность

После применения фиксов ESLint может:

  • заново запустить линтинг
  • проверить, не появились ли новые нарушения
  • повторить цикл до стабилизации

Это важно для правил, которые взаимно корректируют код.


Формирование итогового результата

После завершения анализа формируется итоговая структура:

  • список ошибок и предупреждений
  • позиции (line, column, endLine, endColumn)
  • идентификаторы правил
  • уровни severity
  • предложения фиксов (если доступны)

Результат готов к форматированию в CLI, IDE или CI-инструментах.


Кэширование результатов обработки

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

  • по хэшу содержимого файла
  • по конфигурации
  • по списку активных правил

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

Кэш особенно эффективен в больших проектах, где повторный анализ идентичных файлов встречается часто.


Взаимодействие с плагинами и расширениями

На всех этапах обработки файла плагины могут вмешиваться:

  • добавлять собственные парсеры
  • расширять AST-проходы
  • внедрять новые правила
  • модифицировать контекст
  • предоставлять shared utilities

Таким образом, ESLint работает как расширяемая система анализа, где базовый pipeline остаётся неизменным, а логика анализа дополняется внешними модулями.


Синхронизация стадий и изоляция ответственности

Архитектура обработки файла разделена на строгие уровни:

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

Такое разделение обеспечивает предсказуемость и повторяемость анализа, независимо от размера проекта и количества подключённых правил.