Цепочка обработки правил

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


1. Основные этапы обработки

Markdown-it разделяет процесс обработки на несколько последовательных этапов:

  1. Лексический анализ (Lexing) – преобразование текста в токены.
  2. Парсинг блоков (Block Parsing) – создание структуры блоков (paragraph, heading, blockquote и т.д.).
  3. Парсинг встроенного текста (Inline Parsing) – обработка ссылок, эмфазиса, кода и других inline-элементов.
  4. Рендеринг (Rendering) – превращение токенов в HTML через цепочку правил рендеринга.

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


2. Структура правил

Правила в Markdown-it — это функции, которые принимают токены или строки текста и модифицируют их. Они могут быть:

  • Block rules – применяются на уровне блоков, определяют структуру документа.
  • Inline rules – применяются внутри блоков, отвечают за форматирование текста.
  • Core rules – обрабатывают токены после парсинга блоков, выполняя общие трансформации.

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

Пример структуры правила:

function exampleRule(state) {
    state.tokens.forEach(token => {
        if (token.type === 'paragraph_open') {
            token.attrPush(['class', 'custom-paragraph']);
        }
    });
}

3. Управление цепочкой правил

Markdown-it предоставляет гибкие методы для управления правилами:

  • md.core.ruler – управление правилами уровня ядра (core rules).
  • md.block.ruler – управление правилами парсинга блоков.
  • md.inline.ruler – управление inline-правилами.

К каждому из этих объектов применимы методы:

  • before(name, rule) – вставка нового правила перед существующим.
  • after(name, rule) – вставка после указанного правила.
  • push(rule) – добавление в конец цепочки.
  • enable(names) / disable(names) – включение или отключение правил по имени.

Пример добавления inline-правила:

md.inline.ruler.before('emphasis', 'highlight', function(state, silent) {
    const start = state.pos;
    if (state.src[start] !== '^') return false;
    
    const end = state.src.indexOf('^', start + 1);
    if (end === -1) return false;

    if (!silent) {
        const token = state.push('highlight_open', 'mark', 1);
        state.push('text', '', 0).content = state.src.slice(start + 1, end);
        state.push('highlight_close', 'mark', -1);
    }

    state.pos = end + 1;
    return true;
});

В этом примере создаётся пользовательский синтаксис ^текст^, который рендерится как <mark>текст</mark>.


4. Порядок выполнения правил

Цепочка правил критична для корректного парсинга. Markdown-it исполняет правила в следующем порядке:

  1. Core rules – общие преобразования токенов.
  2. Block rules – формируют структуру документа.
  3. Inline rules – обрабатывают содержимое блоков.
  4. Rendering rules – формируют HTML из токенов.

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


5. Модификация существующих правил

Правила можно заменять или модифицировать без изменения исходного кода библиотеки. Например, для изменения рендеринга заголовков:

const defaultHeadingRender = md.renderer.rules.heading_open || function(tokens, idx, options, env, self) {
    return self.renderToken(tokens, idx, options);
};

md.renderer.rules.heading_open = function(tokens, idx, options, env, self) {
    tokens[idx].attrPush(['class', 'custom-heading']);
    return defaultHeadingRender(tokens, idx, options, env, self);
};

Таким образом добавляется CSS-класс к каждому заголовку, сохраняя стандартное поведение рендеринга.


6. Практические советы по работе с цепочкой правил

  • Сохранять порядок правил: при добавлении новых правил важно учитывать, перед или после какого существующего правила они должны выполняться.
  • Использовать silent флаг: при написании inline-правил проверка silent позволяет выполнять только проверку соответствия без модификации токенов, что повышает производительность.
  • Разделять блоки и inline: изменения в block rules не должны напрямую вмешиваться в inline правила, чтобы избежать неожиданных конфликтов.
  • Кэшировать результаты: сложные проверки в inline-правилах можно оптимизировать, чтобы уменьшить количество операций на каждом символе текста.

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