Регистрация правил в цепочке

Markdown-it представляет собой мощный парсер Markdown на JavaScript с поддержкой гибкой кастомизации. Центральным элементом его архитектуры является цепочка правил (rule chain), которая отвечает за преобразование текста Markdown в HTML. Каждый этап обработки текста можно модифицировать, добавлять новые правила или удалять существующие, что делает Markdown-it крайне адаптивным инструментом.


Порядок обработки и структура цепочки

Цепочка правил состоит из двух основных фаз:

  1. Inline Rules – обработка встроенных элементов, таких как ссылки, изображения, выделение текста и т.д.
  2. Block Rules – обработка блочных элементов: заголовков, списков, цитат, таблиц.

Каждая фаза представлена массивом функций, называемых правилами. Каждое правило принимает на вход объект состояния (state) и выполняет свою задачу: модифицирует массив токенов или добавляет новые токены, которые затем используются для генерации HTML.


Регистрация новых правил

Регистрация правил осуществляется с помощью методов md.core.ruler, md.block.ruler и md.inline.ruler.

  • Блочные правила:
md.block.ruler.before('paragraph', 'my_custom_block', function(state, startLine, endLine, silent) {
    // логика обработки блока
});
  • Встроенные правила:
md.inline.ruler.after('emphasis', 'my_custom_inline', function(state, silent) {
    // логика обработки встроенного элемента
});
  • Ядро (core) правила – используются для глобальной обработки токенов перед генерацией HTML:
md.core.ruler.push('my_core_rule', function(state) {
    // модификация state.tokens
});

Параметры методов регистрации:

  • before(name, ruleName, fn) – вставляет правило перед существующим.
  • after(name, ruleName, fn) – вставляет правило после существующего.
  • push(ruleName, fn) – добавляет правило в конец цепочки.
  • fn – функция правила с определённой сигнатурой.

Сигнатуры функций правил

  • Блочные правила:
function(state, startLine, endLine, silent)
  • state – объект состояния документа.

  • startLine и endLine – номера строк в исходном тексте.

  • silent – режим проверки (без фактического добавления токенов).

  • Встроенные правила:

function(state, silent)
  • Core правила:
function(state)

Здесь state.tokens содержит массив токенов, с которыми можно производить любые операции: вставлять, удалять, менять порядок.


Управление порядком выполнения правил

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

md.block.ruler.before('heading', 'custom_title_block', customBlockFunction);

Это гарантирует, что custom_title_block будет выполнено до обработки заголовков, что важно для кастомных синтаксисов, которые должны преобразовываться заранее.


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

Существующие правила можно удалять и переопределять через методы:

md.block.ruler.disable(['paragraph', 'blockquote']);
md.block.ruler.enable(['paragraph']);

Это позволяет временно отключать стандартные правила или полностью заменять их на свои.


Примеры реального применения

  1. Создание кастомного блока с тегом <note>:
function noteBlock(state, startLine, endLine, silent) {
    let pos = state.bMarks[startLine] + state.tShift[startLine];
    let max = state.eMarks[startLine];

    if (state.src.slice(pos, pos + 6) !== ':::note') return false;
    if (silent) return true;

    let token = state.push('note_open', 'div', 1);
    token.attrs = [['class', 'note']];

    state.line = startLine + 1;

    token = state.push('note_close', 'div', -1);
    return true;
}

md.block.ruler.before('paragraph', 'note_block', noteBlock);
  1. Встроенное правило для выделения специального синтаксиса ==highlight==:
function highlightInline(state, silent) {
    const max = state.posMax;
    const start = state.pos;

    if (state.src.charCodeAt(start) !== 0x3D/* = */) return false;
    if (start + 1 >= max || state.src.charCodeAt(start + 1) !== 0x3D) return false;

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

    state.pos = max;
    return true;
}

md.inline.ruler.after('emphasis', 'highlight', highlightInline);

Работа с токенами внутри цепочки

Каждое правило взаимодействует с массивом state.tokens. Основные операции:

  • Добавление токена: state.push(type, tag, nesting)
  • Удаление токена: state.tokens.splice(index, 1)
  • Изменение содержимого: token.content = 'новое содержимое'
  • Манипуляция атрибутами: token.attrs = [['class', 'highlight']]

Это даёт полную свободу в создании любых кастомных элементов Markdown и их последующем преобразовании в HTML.


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

  • Минимизировать количество операций над state.tokens внутри inline правил, чтобы не замедлять парсинг.
  • Использовать silent для проверки корректности синтаксиса без генерации токенов.
  • Разделять блоковые и inline правила по назначению для поддержания читаемости кода.
  • При работе с core правилами избегать глобальных изменений структуры документа без крайней необходимости, чтобы не ломать стандартные правила Markdown.

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