Назначение процессоров

В экосистеме ESLint процессоры (processors) представляют собой механизм предварительной обработки и постобработки кода перед его анализом линтером. Они расширяют стандартную модель работы ESLint, позволяя применять правила не только к чистому JavaScript-файлу, но и к содержимому, извлечённому из других форматов: шаблонов, Markdown-документов, Vue-компонентов, HTML-фрагментов и любых кастомных текстовых структур.

Роль процессоров в архитектуре анализа кода

ESLint изначально проектировался для анализа JavaScript-кода, представленного как линейный текст. Однако в реальных проектах код редко существует в изоляции: он часто встроен в многоформатные контейнеры. Процессоры решают задачу извлечения «чистого» кода из таких контейнеров и обратного сопоставления ошибок с исходным источником.

Процессор выполняет две ключевые функции:

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

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

Модель работы процессора

Общий цикл обработки можно представить следующим образом:

  1. ESLint получает файл из файловой системы
  2. Передаёт содержимое процессору
  3. Процессор преобразует входной текст в массив «виртуальных документов»
  4. Каждый документ анализируется стандартным механизмом ESLint
  5. Результаты объединяются и корректируются в postprocess-фазе
  6. Итоговые сообщения возвращаются пользователю

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

Интерфейс процессора

Процессор в ESLint определяется как объект с двумя методами:

module.exports = {
  preprocess(text, filename) {
    return [text];
  },

  postprocess(messages, filename) {
    return messages.flat();
  }
};

Каждый метод имеет строго определённую ответственность.

preprocess

Метод preprocess получает:

  • исходный текст файла
  • имя файла

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

Основные задачи:

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

Пример извлечения скриптов из HTML:

preprocess(text) {
  const scripts = [];
  const regex = /<script>([\s\S]*?)<\/script>/g;

  let match;
  while ((match = regex.exec(text)) !== null) {
    scripts.push(match[1]);
  }

  return scripts;
}

В данном случае каждый блок <script> становится отдельной единицей анализа.

postprocess

Метод postprocess принимает:

  • массив массивов сообщений об ошибках
  • имя файла

И возвращает единый массив сообщений.

Задачи postprocess:

  • объединение результатов анализа нескольких виртуальных файлов
  • восстановление исходных координат ошибок
  • фильтрация или трансформация сообщений

Пример объединения результатов:

postprocess(messages) {
  return messages.reduce((acc, curr) => acc.concat(curr), []);
}

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

Смещение координат и сопоставление ошибок

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

Для решения используется механизм смещения:

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

Пример логики смещения:

postprocess(messages, filename) {
  return messages.map((block, index) => {
    const offset = lineOffsets[index];

    return block.map(msg => ({
      ...msg,
      line: msg.line + offset,
      filename
    }));
  }).flat();
}

Применение процессоров в экосистеме

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

Vue-компоненты

Файлы .vue содержат сразу несколько языковых секций:

  • <template>
  • <script>
  • <style>

Процессор выделяет JavaScript-часть и передаёт её в ESLint.

Markdown-документы

В Markdown часто встречаются блоки кода:

# Документация

```js
const x = 10;

Процессор извлекает кодовые блоки и анализирует их отдельно.

#### HTML-файлы

HTML может содержать inline-скрипты, которые требуют отдельной обработки.

### Отличие процессоров от парсеров

Процессор и парсер выполняют разные задачи в архитектуре ESLint:

- **Парсер** преобразует JavaScript в AST (абстрактное синтаксическое дерево)
- **Процессор** подготавливает входной текст и управляет его сегментацией

Процессор работает до этапа парсинга и не взаимодействует с AST напрямую.

### Ограничения процессоров

Несмотря на гибкость, процессоры имеют ряд ограничений:

- невозможность изменения логики правил ESLint
- отсутствие доступа к AST
- необходимость ручного управления координатами
- усложнение отладки при большом количестве виртуальных файлов

Также неправильная реализация postprocess может привести к некорректному отображению ошибок.

### Взаимодействие с конфигурацией ESLint

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

```json
{
  "plugins": ["html"],
  "processor": "html/html"
}

Или через plugin-конфигурацию:

module.exports = {
  processors: {
    ".html": require("./processors/html")
  }
};

Это позволяет привязывать обработку к конкретным расширениям файлов.

Многоуровневая обработка данных

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

Пример:

  • HTML процессор извлекает <script>
  • Vue процессор обрабатывает результат как отдельный модуль
  • дополнительный процессор нормализует JSX внутри скрипта

Практическое значение в архитектуре линтинга

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