Блочный токенайзер является центральным компонентом библиотеки Marked, отвечающим за разбор исходного Markdown-текста на структурные единицы — токены, которые затем преобразуются в HTML. Основная цель токенайзера — разбить текст на логические блоки, такие как заголовки, списки, цитаты, блоки кода и параграфы, при этом сохраняя порядок элементов и их вложенность.
Блочный токенайзер работает пошагово, анализируя текст строку за строкой. Каждая строка проверяется на соответствие определённому регулярному выражению, представляющему конкретный Markdown-элемент.
Каждый токен представляет собой объект с набором ключей, которые позволяют интерпретировать его содержание и тип. Основные поля токена:
heading,
paragraph, list, blockquote,
code).Пример токена заголовка:
{
"type": "heading",
"raw": "## Пример заголовка",
"text": "Пример заголовка",
"depth": 2
}
Блочный токенайзер в Marked реализован как класс Lexer с
набором методов для обработки текста:
lex(src)Метод lex выполняет полную разметку текста, превращая
его в массив токенов. Он последовательно применяет регулярные выражения
для каждого типа блочного элемента и формирует соответствующие объекты
токенов.
Пример использования:
import { Lexer } from 'marked';
const markdownText = `
# Заголовок 1
Параграф текста.
- Пункт списка 1
- Пункт списка 2
`;
const tokens = Lexer.lex(markdownText);
console.log(tokens);
token(src, top)Метод token — это ядро парсинга,
вызываемое рекурсивно для каждого блока. Параметр top
указывает, что текущий уровень — верхний, что позволяет корректно
обрабатывать вложенные списки и блоки цитат. Метод анализирует текст и
возвращает массив токенов, готовых к дальнейшей обработке
рендерером.
Списки в Markdown могут быть упорядоченными и неупорядоченными, а также содержать вложенные пункты. Токенайзер обрабатывает их с учётом нескольких условий:
-, * или + для неупорядоченных
списков, числа с точкой для упорядоченных.list_item.token для подблоков.Пример токенизации списка:
{
"type": "list",
"ordered": false,
"start": null,
"loose": false,
"items": [
{
"type": "list_item",
"text": "Пункт списка 1",
"tokens": []
},
{
"type": "list_item",
"text": "Пункт списка 2",
"tokens": []
}
]
}
Заголовки распознаются по количеству символов # в начале
строки. Глубина заголовка соответствует количеству символов
#. Параграфы объединяют последовательные строки текста,
разделённые пустой строкой, в один токен paragraph.
Регулярное выражение для заголовка верхнего уровня:
/^(#{1,6})\s+(.*)$/gm
Принцип работы токенайзера:
heading с
полями depth и text.Кодовые блоки могут быть двух типов:
``` или ~~~, с возможным
указанием языка.Цитаты (blockquote) начинаются с
символа > и могут содержать вложенные элементы.
Токенайзер обрабатывает цитаты рекурсивно, вызывая token
для внутренних блоков.
Пример токена блока цитаты:
{
"type": "blockquote",
"raw": "> Цитата\n> Продолжение цитаты",
"tokens": [
{
"type": "paragraph",
"text": "Цитата\nПродолжение цитаты"
}
]
}
Marked позволяет переопределять стандартные правила блочного
токенайзера через объект Lexer.rules. Это полезно для
поддержки нестандартного синтаксиса Markdown или расширения
функционала.
Пример добавления нового правила:
import { Lexer, LexerRules } from 'marked';
Lexer.rules.customHeading = /^!!!\s+(.*)$/m;
const customTokens = Lexer.lex("!!! Важный заголовок");
В результате будет создан токен с типом customHeading,
который можно обработать отдельным рендерером.
Ключевым элементом блочного токенайзера является поддержка
рекурсивной структуры Markdown. Например, список может
содержать блоки кода, цитаты и параграфы. Токенайзер строит дерево
токенов, где каждый родительский блок содержит массив вложенных токенов
tokens, обеспечивая полное сохранение иерархии
документа.
После обработки текста все блоки объединяются в массив токенов, готовый для передачи рендереру. Рендерер, в свою очередь, последовательно проходит по массиву токенов и формирует итоговый HTML. Правильная работа блочного токенайзера обеспечивает точное соответствие Markdown-синтаксиса и визуального представления документа.