Markdown-it — мощная и гибкая библиотека для парсинга Markdown в JavaScript, которая предоставляет богатый API для кастомизации рендеринга. Одной из ключевых возможностей является условный рендеринг, позволяющий изменять вывод Markdown в зависимости от контекста, настроек или содержимого документа.
При обработке Markdown каждая конструкция (тег, блок, inline-элемент) разбивается на токены. Каждый токен содержит тип, контент, уровень вложенности и дополнительные атрибуты. Пример структуры токена:
{
type: 'paragraph_open',
tag: 'p',
attrs: null,
map: [0, 1],
nesting: 1,
level: 0,
children: null,
content: ''
}
Эти токены затем проходят через рендерер, который генерирует HTML или другой формат. Условный рендеринг достигается через переопределение функций рендеринга токенов.
Markdown-it предоставляет объект md.renderer.rules, где
ключами являются типы токенов, а значениями — функции рендеринга.
Стандартная функция имеет вид:
function(tokens, idx, options, env, self) {
return self.renderToken(tokens, idx, options);
}
Для условного рендеринга функция может проверять любые условия:
содержимое токена, параметры окружения (env), глобальные
настройки.
Пример: условный рендеринг заголовков в зависимости от уровня:
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();
md.renderer.rules.heading_open = (tokens, idx, options, env, self) => {
const token = tokens[idx];
if (token.tag === 'h1' && env.hideH1) {
return ''; // пропускаем H1
}
return self.renderToken(tokens, idx, options);
};
const env = { hideH1: true };
const result = md.render('# Скрытый заголовок\n## Видимый заголовок', env);
console.log(result);
// Выведет только <h2>Видимый заголовок</h2>
Markdown-it позволяет создавать собственные синтаксические конструкции через плагины. Плагин может добавлять токены с уникальным типом, а затем их рендерить условно.
Пример добавления кастомного блока spoiler:
function spoilerPlugin(md) {
md.block.ruler.before('paragraph', 'spoiler', (state, startLine, endLine, silent) => {
const pos = state.bMarks[startLine] + state.tShift[startLine];
const max = state.eMarks[startLine];
const lineText = state.src.slice(pos, max);
if (!lineText.startsWith('!!!spoiler')) return false;
if (!silent) {
const token = state.push('spoiler_open', 'div', 1);
token.attrs = [['class', 'spoiler']];
token.content = lineText.replace('!!!spoiler', '').trim();
const closeToken = state.push('spoiler_close', 'div', -1);
}
state.line = startLine + 1;
return true;
});
md.renderer.rules.spoiler_open = (tokens, idx, options, env, self) => {
if (env.showSpoilers) {
return `<div class="spoiler">${tokens[idx].content}`;
}
return '';
};
md.renderer.rules.spoiler_close = (tokens, idx) => {
return '</div>';
};
}
md.use(spoilerPlugin);
const env = { showSpoilers: true };
console.log(md.render('!!!spoiler Секретная информация', env));
В данном примере содержимое блока рендерится только при условии
env.showSpoilers = true.
envПараметр env передается во все функции рендеринга и
является удобным способом передавать контекст:
Пример условного вывода параграфов:
md.renderer.rules.paragraph_open = (tokens, idx, options, env, self) => {
if (env.skipParagraphs) return '';
return self.renderToken(tokens, idx, options);
};
const env = { skipParagraphs: true };
console.log(md.render('Первый параграф\n\nВторой параграф', env));
// Вывод: пусто, параграфы пропущены
В реальных проектах часто требуется учитывать несколько параметров одновременно. Например, скрывать определенные заголовки, заменять текст переменными и рендерить кастомные блоки:
md.renderer.rules.text = (tokens, idx, options, env, self) => {
let content = tokens[idx].content;
if (env.uppercaseText) {
content = content.toUpperCase();
}
if (env.replaceVars) {
content = content.replace(/\{\{username\}\}/g, env.username || 'Guest');
}
return content;
};
const env = {
uppercaseText: true,
replaceVars: true,
username: 'Иван'
};
console.log(md.render('Привет, {{username}}!', env));
// Вывод: ПРИВЕТ, ИВАН!
env заранее.renderToken, чтобы сохранять
совместимость с другими плагинами.Условный рендеринг в Markdown-it открывает широкие возможности кастомизации и позволяет создавать динамические, контекстно-зависимые HTML-документы, оставаясь при этом в рамках легковесного и производительного парсера Markdown.