Процессор в ESLint представляет собой механизм предварительной обработки исходного кода перед этапом линтинга и последующей агрегации результатов анализа. Он используется в ситуациях, когда файл не может быть напрямую интерпретирован как стандартный JavaScript, либо содержит несколько логических блоков кода, требующих раздельной проверки. На практике процессоры применяются для Markdown, HTML, Vue/SFC-подобных структур, кастомных DSL и любых форматов, где JavaScript встроен внутрь другого синтаксиса.
ESLint ожидает, что входные данные для анализа — это текст JavaScript-модуля. Процессор разрывает это ограничение, преобразуя исходный файл в набор виртуальных фрагментов, каждый из которых анализируется как отдельный источник кода.
Работа процессора состоит из двух фаз:
Таким образом формируется промежуточный слой между файловой системой и ядром анализа ESLint.
Процессор описывается как объект с определённой структурой:
module.exports = {
processors: {
".ext": {
preprocess(text, filename) {
return [];
},
postprocess(messages, filename) {
return [];
}
}
}
};
Ключ .ext связывает процессор с типом файлов. Например,
.md или .html.
Функция preprocess получает:
text — исходное содержимое файлаfilename — путь к файлуВозвращаемое значение — массив строк, где каждая строка рассматривается ESLint как отдельный виртуальный файл.
Простейший пример:
preprocess(text, filename) {
const scripts = [];
const matches = text.match(/```js([\s\S]*?)```/g) || [];
for (const block of matches) {
scripts.push(block.replace(/```js|```/g, ""));
}
return scripts;
}
В этом случае Markdown-файл разбивается на отдельные JavaScript-блоки.
Каждый элемент массива становится отдельным модулем, который ESLint анализирует независимо.
После выполнения preprocess ESLint создаёт виртуальные
источники с индексированными именами:
example.md0
example.md1
example.md2
Индекс добавляется автоматически, что позволяет сопоставлять сообщения диагностики с конкретным блоком исходного файла.
Это важно для корректного отображения ошибок в редакторах, где позиционирование должно ссылаться на исходный документ, а не на преобразованный фрагмент.
После завершения анализа всех виртуальных модулей ESLint передаёт результаты обратно в процессор:
postprocess(messages, filename) {
return messages.flat();
}
Аргумент messages — массив массивов диагностик, где
каждый вложенный массив соответствует одному виртуальному файлу.
Структура сообщения ESLint:
{
ruleId: "no-undef",
message: "x is not defined",
line: 1,
column: 5,
severity: 2
}
Задача postprocess — объединить результаты и при
необходимости скорректировать координаты или фильтровать сообщения.
Типичный кейс — извлечение JavaScript из Markdown:
module.exports = {
processors: {
markdown: {
preprocess(text) {
const blocks = [];
const regex = /```(?:js|javascript)\n([\s\S]*?)```/g;
let match;
while ((match = regex.exec(text)) !== null) {
blocks.push(match[1]);
}
return blocks;
},
postprocess(messages) {
return messages.flat();
}
}
}
};
Такой процессор позволяет анализировать только кодовые блоки, игнорируя текст документа.
Для активации процессора он должен быть подключён в конфигурации:
module.exports = {
plugins: ["custom"],
overrides: [
{
files: ["*.md"],
processor: "custom/markdown"
}
]
};
Здесь custom — имя плагина, в котором экспортирован
процессор.
Процессоры работают на уровне текстового преобразования и не имеют доступа к полноценному AST исходного файла до разбивки. Это накладывает ряд ограничений:
Кроме того, процессор не изменяет правила линтинга — он только формирует входные данные для уже существующего механизма анализа.
При разбиении файла возникает задача сопоставления строк виртуального кода с оригинальным документом. ESLint использует смещения, но при сложных процессорах требуется ручная корректировка:
postprocess(messages) {
return messages.map((blockMessages, index) => {
return blockMessages.map(msg => {
msg.line += index * 1000;
return msg;
});
}).flat();
}
В реальных реализациях вместо условных смещений применяется точное отображение позиций через таблицы соответствия.
HTML-файлы часто содержат встроенные скрипты:
<script>
var a = 1;
</script>
Процессор может извлечь содержимое <script>
тегов:
preprocess(text) {
const scripts = [];
const regex = /<script[^>]*>([\s\S]*?)<\/script>/g;
let match;
while ((match = regex.exec(text))) {
scripts.push(match[1]);
}
return scripts;
}
Такой подход позволяет применять JavaScript-правила ESLint к коду внутри HTML без изменения основного пайплайна.
Процессор занимает промежуточный слой между источником данных и ядром анализа:
Файл → preprocess → виртуальные модули → ESLint rules → postprocess → результат
Эта архитектура позволяет расширять ESLint без модификации его внутреннего движка, сохраняя совместимость с правилами, плагинами и конфигурациями.
Важно различать процессоры и парсеры:
Процессор не заменяет parser, а лишь подаёт ему корректные входные данные. Внутри ESLint они работают последовательно, но решают разные задачи трансформации кода.