В библиотеке Marked процесс преобразования Markdown в HTML строится на двух ключевых компонентах: токенайзере (lexer) и рендерере (renderer). Токенайзер разбивает исходный Markdown-текст на логические единицы — токены, каждый из которых имеет тип и содержимое. Рендерер отвечает за преобразование этих токенов в итоговый HTML-код.
Токенайзер выполняет синтаксический разбор Markdown и создает массив объектов с описанием элементов. Основные свойства токена:
heading, paragraph, list).heading).list).Пример создания токенов с помощью стандартного лексера:
import { marked } from 'marked';
const markdown = `
# Заголовок 1
- Пункт 1
- Пункт 2
`;
const lexer = new marked.Lexer();
const tokens = lexer.lex(markdown);
console.log(tokens);
Результат — массив объектов, где каждый объект представляет собой отдельный элемент Markdown.
Рендерер получает токены и преобразует их в HTML. В Marked предусмотрен стандартный рендерер, но его можно переопределить для кастомного HTML.
Пример использования стандартного рендерера:
const renderer = new marked.Renderer();
const html = marked.parser(tokens, { renderer });
console.log(html);
Для управления выводом можно создать свой класс, наследующийся от
marked.Renderer или объект с методами, соответствующими
типам токенов.
Пример кастомного рендерера:
const customRenderer = {
heading(text, level) {
return `<h${level} class="custom-heading">${text}</h${level}>`;
},
list(body, ordered) {
const tag = ordered ? 'ol' : 'ul';
return `<${tag} class="custom-list">${body}</${tag}>`;
},
listitem(text) {
return `<li class="custom-item">${text}</li>`;
}
};
const htmlCustom = marked.parser(tokens, { renderer: customRenderer });
console.log(htmlCustom);
Marked позволяет не только использовать стандартные компоненты, но и комбинировать их для тонкой настройки обработки Markdown. Варианты комбинаций:
Можно изменить поведение лексера для специфичных Markdown-правил, но выводить стандартный HTML:
class CustomLexer extends marked.Lexer {
paragraph(src) {
// Добавление префикса ко всем параграфам
const token = super.paragraph(src);
token.text = `Префикс: ${token.text}`;
return token;
}
}
const customLexer = new CustomLexer();
const customTokens = customLexer.lex(markdown);
const htmlFromCustomLexer = marked.parser(customTokens);
Используется стандартный анализ Markdown, но HTML формируется по своим правилам:
const htmlCustomRendererOnly = marked.parser(tokens, { renderer: customRenderer });
Можно одновременно создать свой токенайзер и рендерер, что дает полный контроль над процессом:
class FullCustomLexer extends marked.Lexer {
heading(src) {
// Превращаем все заголовки в заголовки 2 уровня
const token = super.heading(src);
token.depth = 2;
return token;
}
}
const fullLexer = new FullCustomLexer();
const fullTokens = fullLexer.lex(markdown);
const fullCustomRenderer = {
heading(text, level) {
return `<h${level} style="color: red;">${text}</h${level}>`;
},
paragraph(text) {
return `<p style="font-style: italic;">${text}</p>`;
}
};
const fullHtml = marked.parser(fullTokens, { renderer: fullCustomRenderer });
Для более сложных проектов можно создавать цепочки токенайзеров и рендереров, где каждый этап выполняет отдельное преобразование:
Такой подход позволяет внедрять промежуточные шаги без изменения исходного Markdown или базового рендерера.
sanitize.marked.parse и
marked.parser. Важно проверять результат на разных видах
Markdown.Комбинирование токенайзеров и рендереров позволяет строить гибкие, настраиваемые пайплайны обработки Markdown, полностью контролируя как разбор, так и визуальное представление контента.