Библиотека Marked предоставляет мощные возможности
для работы с Markdown, включая контроль над процессом разборки текста и
его рендеринга. Одним из ключевых инструментов для расширенной
кастомизации является модификация токенов перед
рендерингом. Токены — это структурированные объекты,
представляющие элементы Markdown, такие как заголовки, списки, ссылки,
изображения и параграфы. Изменение токенов позволяет вмешиваться в
процесс преобразования Markdown в HTML на раннем этапе, предоставляя
гибкость, недоступную при стандартном использовании API
marked.parse.
Каждый токен — это объект с набором полей:
type — тип токена (heading,
paragraph, list, list_item,
link, image, code,
blockquote и т.д.).raw — исходный фрагмент Markdown, соответствующий этому
токену.text — текстовое содержимое токена без синтаксиса
Markdown.tokens — массив вложенных токенов, если элемент
содержит другие элементы (например, список содержит токены элементов
списка).depth для заголовков, href и
title для ссылок.Пример токена заголовка:
{
type: 'heading',
raw: '### Пример заголовка',
text: 'Пример заголовка',
depth: 3,
tokens: [ /* вложенные токены */ ]
}
Для модификации токенов необходимо сначала преобразовать Markdown в
массив токенов с помощью метода marked.lexer():
import { marked } from 'marked';
const markdown = `
# Заголовок 1
Текст параграфа с [ссылкой](https://example.com)
`;
const tokens = marked.lexer(markdown);
console.log(tokens);
Метод lexer возвращает массив токенов, отражающий
структуру документа. В этом виде можно анализировать и изменять токены
перед тем, как передать их рендереру.
Модификация токенов может быть полезна для:
Пример добавления атрибута class к заголовкам:
tokens.forEach(token => {
if (token.type === 'heading') {
token.text = `<span class="custom-heading">${token.text}</span>`;
}
});
Важно учитывать, что вложенные токены тоже могут содержать заголовки, параграфы или ссылки. Для полного обхода структуры используют рекурсивные функции:
function modifyTokens(tokens) {
tokens.forEach(token => {
if (token.type === 'heading') {
token.text = `<span class="custom-heading">${token.text}</span>`;
}
if (token.tokens) {
modifyTokens(token.tokens);
}
});
}
modifyTokens(tokens);
После модификации токенов их можно передать рендереру. Marked позволяет создавать кастомные рендереры, где каждая функция отвечает за генерацию HTML для определённого типа токена.
Пример кастомного рендерера для заголовков:
const renderer = {
heading(token) {
return `<h${token.depth} class="custom-heading">${token.text}</h${token.depth}>`;
}
};
const html = marked.parser(tokens, { renderer });
console.log(html);
Сочетание модификации токенов и кастомного рендерера позволяет полностью контролировать финальный HTML без вмешательства в исходный Markdown.
Токены ссылок и изображений можно изменять для внедрения динамических
URL, добавления атрибутов target или rel:
tokens.forEach(token => {
if (token.type === 'link') {
token.href = token.href.replace('http://', 'https://');
token.title = token.title || 'Динамическая ссылка';
}
if (token.tokens) modifyTokens(token.tokens);
});
Для изображений можно автоматически добавлять alt или
класс:
tokens.forEach(token => {
if (token.type === 'image') {
token.text = token.text || 'Изображение';
token.href = token.href;
token.title = token.title || 'Изображение с подписью';
}
});
Marked поддерживает hook-функции, которые позволяют вмешиваться в процесс парсинга и рендеринга. На уровне токенов полезны хуки:
walkTokens — вызывается для каждого токена после
лексера, но до рендеринга.Пример:
marked.use({
walkTokens(token) {
if (token.type === 'text') {
token.text = token.text.replace(/\bJavaScript\b/g, '<strong>JavaScript</strong>');
}
}
});
Хук walkTokens особенно удобен для глобальных изменений
текста или автоматического обогащения Markdown без необходимости
рекурсивно обходить массив токенов вручную.
walkTokens, чтобы
обрабатывать токены в один проход.marked.lexer(markdown) —
преобразование Markdown в токены.text, href, tokens и др. через
рекурсию или walkTokens.marked.parser(tokens, { renderer }) — генерация HTML с
кастомной логикой для каждого типа токена.Такой подход позволяет реализовать сложные сценарии, включая кастомные стили, динамические данные, расширения Markdown и интеграцию с различными фронтенд-фреймворками, обеспечивая полный контроль над финальным HTML.