Библиотека Marked предоставляет мощный инструмент для парсинга Markdown, позволяя гибко обрабатывать текст и получать HTML. Одним из ключевых механизмов расширения функционала являются расширения уровня блоков (block-level extensions). Они позволяют определять собственные структуры, которые будут распознаваться как отдельные блоки Markdown и корректно преобразовываться в HTML.
Расширения уровня блоков работают на этапе лексического анализа, когда Markdown-текст разбивается на токены. Каждый блок — это отдельный токен, который содержит информацию о типе, содержимом и возможных атрибутах. Расширение может:
В Marked блоковое расширение определяется объектом с обязательным
полем name и методом level. Дополнительно
можно указывать start, tokenizer и
renderer.
Обязательные и ключевые свойства:
const customBlock = {
name: 'customBlock', // уникальное имя расширения
level: 'block', // уровень, должен быть 'block'
start(src) { // необязательный метод для оптимизации поиска
return src.match(/^:::/)?.index;
},
tokenizer(src, tokens) { // функция, которая распознаёт токен
const rule = /^:::\s*(\w+)\n([\s\S]+?)\n:::/;
const match = rule.exec(src);
if (match) {
return {
type: 'customBlock',
raw: match[0], // полный текст блока
blockType: match[1], // тип блока, например alert, note
text: match[2], // содержимое блока
tokens: this.lexer.inlineTokens(match[2]) // токены для inline-парсинга
};
}
},
renderer(token) { // функция генерации HTML из токена
return `<div class="custom-${token.blockType}">${this.parser.parseInline(token.tokens)}</div>`;
}
};
Метод возвращает индекс первого символа, с которого может начинаться
интересующий блок. Это позволяет оптимизировать процесс сканирования
текста. Возврат undefined или -1 означает, что
блок не найден.
Главная функция распознавания. Она получает на вход исходный текст и список токенов, которые уже распознаны. Внутри можно использовать регулярные выражения или любой алгоритм поиска блоков.
Особенности:
raw — обязательно должен содержать полный исходный
текст блока.tokens — используется для inline-парсинга содержимого
блока (ссылки, выделения, эмфазы и т. д.).Отвечает за преобразование токена в HTML. Внутри можно использовать
this.parser.parseInline(token.tokens) для обработки
inline-содержимого.
После создания блокового расширения его нужно подключить к Marked:
import { marked } from 'marked';
marked.use({ extensions: [customBlock] });
const markdown = `
:::alert
Это важное сообщение.
:::
`;
console.log(marked.parse(markdown));
В результате получим HTML:
<div class="custom-alert">Это важное сообщение.</div>
Расширения уровня блоков могут содержать другие блоки Markdown. Для
этого токенизация содержимого выполняется через
this.lexer.blockTokens().
Пример:
const nestedBlock = {
name: 'nestedBlock',
level: 'block',
tokenizer(src) {
const match = /^:::\s*note\n([\s\S]+?)\n:::/m.exec(src);
if (match) {
return {
type: 'nestedBlock',
raw: match[0],
text: match[1],
tokens: this.lexer.blockTokens(match[1])
};
}
},
renderer(token) {
return `<section class="note">${this.parser.parse(token.tokens)}</section>`;
}
};
Использование this.lexer.blockTokens позволяет корректно
обрабатывать заголовки, списки и другие блоки внутри кастомного
блока.
raw для корректного позиционирования
при разборе Markdown.blockTokens для
вложенной токенизации.inlineTokens и parseInline.start(src) для
ускорения парсинга больших документов.name) всем расширениям,
чтобы избежать конфликтов.:::alert … ::: с разными
типами (success, warning,
error).:::quote Автор … :::
для создания структурированных цитат.:::table data-json=… ::: для генерации интерактивных
таблиц.Каждое из этих расширений строится по одной и той же логике: распознавание блока → токенизация содержимого → генерация HTML с возможной стилизацией через CSS.
Marked позволяет использовать несколько блоковых расширений одновременно. Важные моменты:
Расширения уровня блоков предоставляют высокий уровень контроля над Markdown. Они позволяют:
Правильное использование токенизации и методов tokenizer
и renderer обеспечивает совместимость с существующими
Markdown-блоками и поддерживает обработку больших документов без потери
производительности.