Markdown-файлы в современных проектах часто выступают не только как документация, но и как контейнер для исполняемых фрагментов JavaScript. Внутри таких файлов встречаются блоки кода, примеры API, конфигурации и тестовые сниппеты, которые должны соответствовать тем же стандартам качества, что и основной код базы.
Проблема возникает из-за того, что линтер, ориентированный на анализ
JavaScript, работает с файлами .js, .ts, но
Markdown представляет собой смешанный формат. Внутри одного файла могут
одновременно существовать текстовая разметка и множество независимых
JavaScript-фрагментов.
Именно для решения этой задачи в архитектуре ESLint предусмотрен механизм processor, позволяющий преобразовывать входной файл в набор виртуальных файлов, пригодных для анализа.
Processor в ESLint — это слой предварительной обработки, который отвечает за:
Процессор реализует два ключевых метода:
preprocess(text, filename) — разбор входного файла и
выделение блоков кода;postprocess(messages, filename) — агрегация результатов
линтинга и сопоставление с исходным файлом.Таким образом, Markdown-файл не анализируется напрямую как JavaScript. Вместо этого создаётся набор временных модулей, которые проходят стандартный цикл линтинга.
Markdown содержит структурированные блоки вида:
# Документация
Пример функции:
```js
function sum(a, b) {
return a + b
}
Processor извлекает только содержимое fenced code blocks, игнорируя текстовую часть.
На этапе `preprocess` происходит:
- разбиение Markdown на токены;
- поиск блоков ```js, ```javascript, ```ts;
- создание массива виртуальных файлов.
Пример результата:
```js
[
{
text: "function sum(a, b) { return a + b }",
lineOffset: 4
}
]
Каждый блок превращается в отдельный файл, который затем передаётся в стандартный пайплайн ESLint.
Наиболее распространённая реализация Markdown Processor — плагин eslint-plugin-markdown.
Он автоматически подключает processor для файлов .md,
расширяя возможности анализа.
Ключевая особенность заключается в том, что каждый кодовый блок рассматривается как изолированный JavaScript-файл.
Поддерживаются языковые идентификаторы:
jsjavascriptmjsts (в зависимости от конфигурации)Блоки без указания языка игнорируются, так как невозможно гарантировать их синтаксическую корректность.
После этапа preprocess ESLint работает с набором
виртуальных документов.
Каждый виртуальный файл получает:
file.md[1]);Когда линтер обнаруживает ошибку, сообщение содержит координаты
относительно виртуального файла. Затем postprocess
выполняет обратное преобразование:
Это позволяет отображать ошибки прямо в Markdown-файле, сохраняя контекст документации.
Подключение Markdown Processor обычно происходит через конфигурацию ESLint:
module.exports = {
overrides: [
{
files: ["*.md"],
processor: "markdown/markdown"
}
]
}
После этого ESLint начинает обрабатывать Markdown как контейнер для JavaScript-блоков.
Дополнительно можно включать правила, ориентированные на документацию:
module.exports = {
plugins: ["markdown"],
overrides: [
{
files: ["*.md"],
processor: "markdown/markdown",
rules: {
"no-undef": "error",
"no-unused-vars": "warn"
}
}
]
}
Каждый извлечённый блок проходит через стандартный parser ESLint. Чаще всего используется встроенный парсер Espree.
Возможные альтернативы:
Важно, что Markdown Processor не заменяет parser, а только подготавливает данные для него.
Несмотря на гибкость, Markdown Processor имеет ряд ограничений:
Также существует ограничение на точность отображения ошибок при глубокой вложенности документации, особенно если code fences расположены внутри нестандартных Markdown-расширений.
Processor строит промежуточное представление документа:
Упрощённо:
Markdown → Segments → Code Blocks → Virtual JS Files → ESLint Core → Results → Mapping
Эта модель позволяет сохранить единый пайплайн анализа для всех типов входных данных.
ESLint допускает создание собственных processors, что расширяет применение Markdown-подобной логики.
Базовая структура:
module.exports = {
preprocess(text, filename) {
return extractBlocks(text)
},
postprocess(messages, filename) {
return mergeMessages(messages)
},
supportsAutofix: false
}
Такой подход позволяет:
Markdown Processor часто используется в связке с инструментами документирования:
В среде Node.js подобные процессы особенно важны, поскольку позволяют поддерживать консистентность между документацией и реальным кодом исполнения.
Не все правила ESLint одинаково полезны для Markdown-контекста. Некоторые категории правил особенно актуальны:
no-undef,
no-unused-vars);При этом правила, завязанные на структуру проекта (например, импорт модулей), могут работать некорректно из-за отсутствия полноценного контекста файла.
Каждый code block получает собственную систему координат. Processor хранит метаданные:
Эти данные используются для точного отображения ошибок в исходном документе, несмотря на трансформацию структуры.
При работе с большими Markdown-документами возникают особенности производительности:
В крупных документационных системах часто применяются стратегии:
Processor выступает связующим звеном между нетипичными форматами данных и ядром ESLint. Он обеспечивает:
Таким образом, Markdown перестаёт быть «неподдерживаемым форматом» и становится полноценной средой для статического анализа JavaScript-кода внутри документации.