Кастомизация вывода

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

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

Markdown-it использует объект renderer для преобразования токенов в HTML. Каждый тип токена имеет соответствующий метод рендеринга.

const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

md.renderer.rules.paragraph_open = (tokens, idx) => {
  return '<p class="custom-paragraph">';
};

md.renderer.rules.paragraph_close = () => '</p>';

const result = md.render('Пример параграфа.');
console.log(result);
// <p class="custom-paragraph">Пример параграфа.</p>

Ключевой момент: можно переопределять любые стандартные правила, включая заголовки (heading_open/heading_close), ссылки (link_open/link_close) и изображения (image).

2. Создание собственных правил

Markdown-it поддерживает кастомные токены, что позволяет добавлять нестандартные элементы Markdown.

md.core.ruler.push('highlight_text', function(state) {
  state.tokens.forEach(token => {
    if (token.type === 'inline') {
      token.children.forEach(child => {
        if (child.type === 'text' && child.content.includes('IMPORTANT')) {
          child.type = 'html_inline';
          child.content = `<span class="highlight">${child.content}</span>`;
        }
      });
    }
  });
});

const html = md.render('Это IMPORTANT текст.');
console.log(html);
// <p>Это <span class="highlight">IMPORTANT</span> текст.</p>

Принцип работы:

  • Сначала создаётся правило для core.ruler или block/ruler_inline.
  • Проходятся токены Markdown, ищутся нужные элементы.
  • Токены преобразуются в HTML через html_inline или html_block.

3. Настройка рендеринга ссылок и изображений

С помощью renderer.rules можно изменять атрибуты ссылок и картинок.

md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
  const hrefIndex = tokens[idx].attrIndex('href');
  if (hrefIndex >= 0) {
    tokens[idx].attrs[hrefIndex][1] = tokens[idx].attrs[hrefIndex][1] + '?ref=custom';
  }
  tokens[idx].attrPush(['target', '_blank']);
  return self.renderToken(tokens, idx, options);
};

const htmlLink = md.render('[Сайт](https://example.com)');
console.log(htmlLink);
// <p><a href="https://example.com?ref=custom" target="_blank">Сайт</a></p>

Особенности:

  • attrIndex проверяет, существует ли атрибут.
  • attrPush добавляет новый атрибут.
  • self.renderToken используется для стандартного рендеринга после изменений.

4. Использование плагинов для расширения функциональности

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

function emoji_plugin(md) {
  const emojiMap = { ':smile:': '?', ':heart:': '❤️' };
  
  md.core.ruler.push('emoji_replace', state => {
    state.tokens.forEach(token => {
      if (token.type === 'inline') {
        token.children.forEach(child => {
          if (child.type === 'text') {
            Object.keys(emojiMap).forEach(code => {
              child.content = child.content.replaceAll(code, emojiMap[code]);
            });
          }
        });
      }
    });
  });
}

md.use(emoji_plugin);

console.log(md.render('Привет :smile: :heart:'));
// <p>Привет ? ❤️</p>

Принцип:

  • Плагин использует core.ruler для изменения контента до финального рендеринга.
  • Возможна комбинация с существующими правилами рендеринга.

5. Кастомизация выводимого HTML через fence и code_block

Для блоков кода можно изменить HTML-структуру или добавить подсветку:

md.renderer.rules.fence = (tokens, idx) => {
  const token = tokens[idx];
  const langClass = token.info ? ` class="language-${token.info}"` : '';
  return `<pre${langClass}><code>${token.content}</code></pre>`;
};

const codeHtml = md.render('```js\nconsole.log("Hello");\n```');
console.log(codeHtml);
// <pre class="language-js"><code>console.log("Hello");</code></pre>

Важные моменты:

  • token.info содержит указание языка (если есть).
  • token.content — исходный код блока.
  • Позволяет интегрировать сторонние библиотеки подсветки синтаксиса.

6. Контроль над безопасностью вывода

Markdown-it поддерживает строгий контроль вывода через опцию html и использование фильтров:

const mdSafe = new MarkdownIt({ html: false });
const unsafeHtml = mdSafe.render('<script>alert("XSS")</script>');
console.log(unsafeHtml);
// &lt;script&gt;alert("XSS")&lt;/script&gt;

Рекомендации по безопасности:

  • Использовать html: false для блокировки raw HTML.
  • Можно подключить sanitize-html или аналогичные библиотеки для дополнительной фильтрации.

7. Итоговая архитектура кастомизации

Для полноценной кастомизации рекомендуется комбинировать:

  • Рендер-правила (renderer.rules) — для управления HTML-выводом конкретных токенов.
  • Плагины (use) — для внедрения новых конструкций Markdown.
  • Core- и inline-правила (core.ruler, inline.ruler) — для изменения содержания токенов на этапе парсинга.
  • Настройки безопасности (html, xhtmlOut) — для предотвращения внедрения небезопасного HTML.

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