Система расширений Marked

Библиотека Marked предоставляет гибкий механизм расширений, позволяющий изменять и дополнять стандартный процесс парсинга Markdown. Расширения включают три ключевых компонента: лексеры, парсеры и рендереры, каждый из которых можно кастомизировать для достижения специфических целей.


Лексеры

Лексер в Marked отвечает за разделение текста на токены. Каждый токен представляет собой отдельный элемент Markdown — заголовок, список, ссылку или блок кода.

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

const marked = require('marked');

const lexerOptions = {
  extensions: [
    {
      name: 'customLexer',
      level: 'block', // уровень: block или inline
      start(src) { return src.match(/^!!!/)?.index; }, // определение начала токена
      tokenizer(src, tokens) {
        const rule = /^!!!(.*)\n([\s\S]+?)\n!!!/;
        const match = rule.exec(src);
        if (match) {
          return {
            type: 'customBlock',
            raw: match[0],
            content: match[2].trim()
          };
        }
      }
    }
  ]
};

const lexer = new marked.Lexer(lexerOptions);
const tokens = lexer.lex('!!!warning\nВнимание! Это кастомный блок.\n!!!');
console.log(tokens);

Ключевой момент: метод tokenizer позволяет определить структуру нового токена, а start ускоряет обработку, указывая, где искать начало токена.


Парсеры

После того как текст разделен на токены, парсер превращает их в HTML или другую форму вывода. В Marked можно полностью заменить стандартный парсер или добавить обработку новых типов токенов.

Пример расширения парсера:

const parserOptions = {
  extensions: [
    {
      name: 'customParser',
      renderer(token) {
        if (token.type === 'customBlock') {
          return `<div class="alert">${token.content}</div>`;
        }
        return false; // fallback на стандартный рендер
      }
    }
  ]
};

const parser = new marked.Parser(parserOptions);
const html = parser.parse(tokens);
console.log(html);

Важная особенность: метод renderer получает токен и возвращает HTML. Если возвращается false, используется стандартная обработка.


Рендереры

Рендерер в Marked отвечает за генерацию финального HTML. Можно создавать кастомные рендереры для отдельных элементов или глобально переопределять весь процесс.

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

const renderer = {
  link(href, title, text) {
    return `<a href="${href}" title="${title || ''}" target="_blank">${text}</a>`;
  }
};

marked.use({ renderer });

const htmlLink = marked('[Google](https://google.com)');
console.log(htmlLink);

Особенности работы:

  • Все стандартные методы рендерера можно переопределять (heading, list, code и др.).
  • Поддержка inline и block элементов.
  • Совместимость с существующими расширениями через метод use.

Пакетное подключение расширений

Marked поддерживает несколько расширений одновременно. Это позволяет комбинировать кастомные лексеры, парсеры и рендереры для сложных сценариев.

marked.use({
  extensions: [customLexer, customParser],
  renderer: customRenderer
});

Принцип работы: библиотека сначала применяет все лексеры, затем парсеры, и в конце рендереры, что обеспечивает гибкость и предсказуемость результатов.


Встроенные хуки

Кроме стандартной системы расширений, Marked предоставляет хуки, которые позволяют вмешиваться в процесс парсинга на отдельных этапах:

  • walkTokens(token) — проход по всем токенам после лексинга.
  • renderer — настройка вывода HTML для каждого токена.
  • tokenizer — добавление новых правил распознавания Markdown элементов.

Пример использования walkTokens:

marked.use({
  walkTokens(token) {
    if (token.type === 'text') {
      token.text = token.text.replace(/TODO:/g, '<strong>TODO:</strong>');
    }
  }
});

Уровни расширений

Расширения делятся на два уровня:

  1. Block — работают с блочными элементами: списки, заголовки, блоки кода.
  2. Inline — работают с элементами внутри строки: ссылки, жирный текст, изображения.

Разделение по уровням позволяет точечно управлять обработкой Markdown, не затрагивая лишние элементы.


Ключевые рекомендации

  • Использовать start для ускорения работы лексера при обработке больших документов.
  • Всегда возвращать false в рендерере для токенов, которые не обрабатываются кастомно, чтобы сохранить совместимость со стандартным парсингом.
  • Комбинировать несколько расширений через marked.use для модульного подхода к кастомизации.
  • Проверять порядок применения расширений, так как он влияет на результат HTML.

Система расширений Marked обеспечивает полный контроль над процессом преобразования Markdown, позволяя добавлять новые типы блоков, кастомизировать вывод и интегрировать библиотеку в сложные проекты с нестандартными требованиями к разметке.