Расширение стандартной токенизации

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


Структура токенизатора

Токенизатор в Marked представлен как набор методов, каждый из которых отвечает за распознавание определённых элементов Markdown:

  • block-level токены: заголовки, списки, блоки цитирования, кодовые блоки;
  • inline-level токены: ссылки, изображения, эмфаза, код в строке.

Каждый метод возвращает объект токена с набором полей:

{
  type: 'heading',      // тип токена
  depth: 2,             // уровень заголовка
  text: 'Пример'        // текст содержимого
}

Если токен не распознан, метод возвращает false, позволяя токенизатору проверять другие правила.


Создание кастомного токена

Расширение стандартной токенизации начинается с наследования стандартного токенизатора или создания собственного класса, совместимого с API Marked. Для этого используется объект Tokenizer:

const { Tokenizer } = require('marked');

class CustomTokenizer extends Tokenizer {
  // Метод для распознавания кастомного блока
  customBlock(src) {
    const match = /^:::(.+?)\n([\s\S]+?)\n:::/m.exec(src);
    if (match) {
      return {
        type: 'custom',
        name: match[1].trim(),
        text: match[2].trim(),
        raw: match[0]
      };
    }
    return false;
  }
}

Принцип работы: метод получает исходный текст (src) и пытается сопоставить его с регулярным выражением. Если совпадение найдено, создается токен с полями type, raw и любыми дополнительными параметрами. Если совпадения нет — возвращается false.


Подключение кастомного токенизатора

Для того чтобы Marked начал использовать новый токенизатор, необходимо передать его в конфигурацию:

const marked = require('marked');

const tokenizer = new CustomTokenizer();

marked.use({ tokenizer });

const markdown = `
:::note
Это важное сообщение
:::
`;

console.log(marked.parse(markdown));

В результате кастомный блок будет правильно распознан и обработан стандартным рендерером.


Расширение inline-токенов

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

class InlineTokenizer extends Tokenizer {
  strongDouble(src) {
    const match = /^\*\*(.+?)\*\*/.exec(src);
    if (match) {
      return {
        type: 'strongDouble',
        raw: match[0],
        text: match[1]
      };
    }
    return false;
  }
}

marked.use({ tokenizer: new InlineTokenizer() });

Токен strongDouble будет интегрирован в последовательность inline-токенов и передан рендереру.


Кастомные рендереры для новых токенов

После создания токена важно определить, как он будет визуализирован. Для этого используется объект Renderer:

const renderer = {
  custom(token) {
    return `<div class="custom-block" data-name="${token.name}">${token.text}</div>`;
  },
  strongDouble(token) {
    return `<strong class="double-strong">${token.text}</strong>`;
  }
};

marked.use({ renderer });

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


Интеграция с существующими правилами

Для корректной работы расширенного токенизатора необходимо учитывать порядок применения правил:

  1. Блоки проверяются последовательно сверху вниз.
  2. Inline-токены обрабатываются после блока.
  3. Новые правила должны быть размещены выше стандартных, если требуется приоритет.

Это предотвращает конфликт распознавания с базовыми Markdown-конструкциями.


Использование регулярных выражений и экранирования

Регулярные выражения — основной инструмент при создании кастомных токенов. Важно:

  • Использовать многострочный режим (/m) для блоков;
  • Захватывать весь блок в raw, чтобы Marked корректно заменял исходный текст на токен;
  • Экранить специальные символы Markdown, чтобы не нарушать стандартный парсинг.

Примеры практических расширений

  1. Сноски:
Это пример текста с[^1] сноской.

[^1]: Текст сноски
  1. Теги предупреждений или заметок с произвольным цветом:
:::warning
Опасность!
:::
  1. Инлайновые формулы или специальные конструкции типа ==выделение==.

Для каждой конструкции создается токен с кастомным методом в токенизаторе и соответствующий рендерер.


Поддержка Markdown-плагинов

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

  • Диаграмм Mermaid;
  • Синтаксиса для задач и чеклистов;
  • Кастомных таблиц или интерактивных элементов.

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