Библиотека Marked в JavaScript предоставляет мощный механизм для парсинга Markdown и преобразования его в HTML. Один из ключевых аспектов расширения возможностей Marked — это создание пользовательских рендереров, позволяющих полностью контролировать процесс генерации HTML для различных типов элементов Markdown.
В Marked рендерер — это объект с методами, каждый из которых отвечает за конкретный элемент Markdown. Стандартный рендерер содержит методы для следующих блоков и встроенных элементов:
code(code, language, isEscaped) — блок кодаblockquote(quote) — цитатаhtml(html) — HTML-блокheading(text, level, raw, slugger) — заголовокhr() — горизонтальная линияlist(body, ordered, start) — списокlistitem(text) — элемент спискаcheckbox(checked) — чекбокс внутри спискаparagraph(text) — абзацtable(header, body) — таблицаtablerow(content) — строка таблицыtablecell(content, flags) — ячейка таблицыКаждый метод принимает аргументы, содержащие текстовую или структурированную информацию, и возвращает строку HTML. Переопределяя эти методы, можно менять HTML-структуру, добавлять классы, атрибуты или стили.
import { marked } from 'marked';
const customRenderer = {
heading(text, level) {
return `<h${level} class="custom-heading">${text}</h${level}>`;
},
paragraph(text) {
return `<p class="custom-paragraph">${text}</p>`;
},
code(code, language) {
const langClass = language ? ` class="language-${language}"` : '';
return `<pre><code${langClass}>${code}</code></pre>`;
}
};
marked.use({ renderer: customRenderer });
const markdown = `
# Заголовок 1
Параграф с **жирным текстом**.
\`\`\`javascript
console.log('Пример кода');
\`\`\`
`;
console.log(marked(markdown));
В этом примере заголовки, абзацы и блоки кода получают свои кастомные CSS-классы, что упрощает стилизацию и интеграцию с дизайном.
Рендерер может быть динамическим, позволяя изменять поведение в зависимости от содержимого. Например, можно выделять определённые ключевые слова или добавлять иконки:
const dynamicRenderer = {
heading(text, level) {
if (text.includes('Важно')) {
return `<h${level} style="color:red">${text}</h${level}>`;
}
return `<h${level}>${text}</h${level}>`;
}
};
Такой подход позволяет интегрировать логические правила прямо на уровне HTML-генерации, без постобработки.
Для сложных случаев можно создать объект-наследник стандартного рендерера. Это полезно, когда нужно сохранить базовое поведение для большинства элементов, но изменить несколько ключевых.
import { Renderer } from 'marked';
class ExtendedRenderer extends Renderer {
listitem(text) {
return `<li class="custom-list-item">${text}</li>`;
}
}
marked.use({ renderer: new ExtendedRenderer() });
Такой подход минимизирует дублирование кода и позволяет комбинировать стандартный рендеринг с кастомным.
Пользовательский рендерер легко интегрировать с библиотеками
подсветки синтаксиса, генераторами таблиц или инструментами для работы с
метаданными. Например, интеграция с highlight.js:
import hljs from 'highlight.js';
const rendererWithHighlight = {
code(code, language) {
const validLang = hljs.getLanguage(language) ? language : 'plaintext';
const highlighted = hljs.highlight(code, { language: validLang }).value;
return `<pre><code class="hljs ${validLang}">${highlighted}</code></pre>`;
}
};
Здесь метод code использует highlight.js
для подсветки синтаксиса, при этом сохраняется возможность добавлять
кастомные стили через CSS.
Кроме блоков, рендерер позволяет переопределять встроенные элементы Markdown:
strong(text) — жирный текстem(text) — курсивcodespan(code) — встроенный кодlink(href, title, text) — ссылкаimage(href, title, text) — изображениеНапример, можно обернуть ссылки в элемент с иконкой внешней ссылки:
const linkRenderer = {
link(href, title, text) {
const icon = href.startsWith('http') ? ' ?' : '';
return `<a href="${href}" title="${title || ''}">${text}${icon}</a>`;
}
};
Для таблиц пользовательский рендерер может добавлять классы для строк и ячеек, упрощая стилизацию:
const tableRenderer = {
table(header, body) {
return `<table class="custom-table">
<thead>${header}</thead>
<tbody>${body}</tbody>
</table>`;
},
tablerow(content) {
return `<tr class="custom-row">${content}</tr>`;
},
tablecell(content, flags) {
const tag = flags.header ? 'th' : 'td';
return `<${tag} class="custom-cell">${content}</${tag}>`;
}
};
Такой подход даёт полный контроль над внешним видом таблиц без дополнительных CSS-селекторов по структуре HTML.
Marked позволяет комбинировать несколько рендереров, используя
опции use. Это удобно для модульной
архитектуры, когда один рендерер отвечает за стилизацию текста, а другой
— за визуализацию блоков кода или таблиц.
marked.use({
renderer: customRenderer,
mangle: false,
headerIds: true
});
Опции mangle и headerIds позволяют
управлять генерацией идентификаторов заголовков и экранированием
специальных символов, что важно для интеграции с якорями и SEO.
Создание пользовательского рендерера в Marked предоставляет полный контроль над HTML-выходом, позволяет добавлять стили, логические правила и интегрироваться с внешними библиотеками. Это ключевой инструмент для построения гибких Markdown-парсеров в современных веб-приложениях.