Рендерер и его роль

Рендерер в Markdown-it — это компонент, который отвечает за превращение токенов, созданных парсером, в итоговый HTML. Парсер сначала анализирует исходный Markdown и строит дерево токенов, отражающее структуру документа. Рендерер обрабатывает эти токены по очереди и формирует строку HTML, обеспечивая точное соответствие разметки стандарту.

Каждый токен имеет несколько ключевых свойств:

  • type — тип токена (например, paragraph_open, inline, link_open).
  • tag — HTML-тег, который будет использован при рендеринге (например, p, strong, a).
  • attrs — массив атрибутов в формате [имя, значение], применяемых к тегу.
  • content — текстовое содержимое токена, используется для inline элементов.
  • children — массив вложенных токенов, формирующих структуру сложных элементов.
  • nesting — указывает, открывающий (1), закрывающий (-1) или самостоятельный (0) токен.

Роль и структура рендерера

Рендерер представлен объектом с набором функций для каждого типа токена. Стандартный рендерер Markdown-it реализован в классе Renderer и использует метод render(tokens, options, env):

const md = require('markdown-it')();
const tokens = md.parse('# Заголовок\n\nТекст', {});
const html = md.renderer.render(tokens, {}, {});

Здесь:

  • tokens — массив токенов, полученный после парсинга.
  • options — опции рендеринга, наследуемые от настроек Markdown-it.
  • env — объект окружения, используемый для передачи дополнительных данных между токенами и плагинами.

Настройка рендерера

Переопределение правил рендеринга

Markdown-it позволяет переопределять рендеринг отдельных токенов через объект md.renderer.rules. Каждое правило имеет сигнатуру (tokens, idx, options, env, self).

Пример кастомного рендеринга заголовков:

md.renderer.rules.heading_open = (tokens, idx) => {
  const level = tokens[idx].tag.slice(1);
  return `<h${level} class="custom-heading">`;
};

md.renderer.rules.heading_close = (tokens, idx) => {
  const level = tokens[idx].tag.slice(1);
  return `</h${level}>`;
};

В этом примере каждому заголовку добавляется класс custom-heading. Это позволяет контролировать HTML без изменения исходного Markdown.

Встроенные методы рендерера

Основные методы рендерера:

  • render(tokens, options, env) — полное преобразование массива токенов в HTML.
  • renderInline(tokens, options, env) — преобразует только inline-токены, игнорируя блочные элементы.
  • renderToken(tokens, idx, options) — рендеринг одного токена с учётом вложенных children.

Пример рендеринга inline-содержимого:

md.renderer.renderInline(tokens[1].children, md.options, {});

Особенности работы с вложенными токенами

Токены могут иметь вложенные children, например, текст с форматированием: жирный текст внутри параграфа. Рендерер автоматически вызывает рендеринг дочерних токенов при обработке родительского inline токена.

Пример:

Markdown:

Это **жирный текст** в параграфе.

Разбор:

  • paragraph_open<p>

  • inlinechildren токены:

    • text → “Это”
    • strong_open<strong>
    • text → “жирный текст”
    • strong_close</strong>
  • paragraph_close</p>

Рендерер объединяет их, создавая корректный HTML.

Работа с окружением (env)

Параметр env позволяет плагинам и пользователю передавать дополнительные данные через процесс рендеринга. Например, можно хранить счётчик ссылок, генерацию якорей для заголовков или собирать статистику по документу.

Пример использования env для генерации уникальных идентификаторов заголовков:

const env = { headingIds: {} };

md.renderer.rules.heading_open = (tokens, idx, options, env) => {
  const title = tokens[idx + 1].content;
  const id = title.toLowerCase().replace(/\s+/g, '-');
  env.headingIds[id] = title;
  return `<${tokens[idx].tag} id="${id}">`;
};

Кастомные рендереры и плагины

Markdown-it построен модульно, поэтому легко создавать плагины, которые добавляют новые синтаксические конструкции или изменяют рендеринг существующих. Плагин может добавлять токены, изменять их свойства или полностью заменять правила рендерера.

Пример добавления кастомного блока:

md.use(function customPlugin(md) {
  md.block.ruler.before('paragraph', 'spoiler', function(state, startLine, endLine, silent) {
    // определение нового блока, генерация токена
  });
  md.renderer.rules.spoiler_open = () => '<div class="spoiler">';
  md.renderer.rules.spoiler_close = () => '</div>';
});

Таким образом, рендерер является ядром визуального преобразования Markdown в HTML, обеспечивая полную гибкость и контроль над выводом документа. Его ключевые возможности включают: кастомизацию тегов, работу с атрибутами, поддержку вложенных элементов, передачу данных через env и интеграцию с плагинами.