Создание инлайн-токенайзера

В библиотеке Marked инлайн-токенизация отвечает за разбор содержимого строк Markdown на составляющие элементы, такие как ссылки, изображения, эмфаза текста, коды и другие встроенные конструкции. Основной объект для работы — Lexer с методом inlineTokens, а также возможность создания собственных токенов через расширения.

Синтаксис и структура инлайн-токенов

Инлайн-токен в Marked представлен объектом с определёнными ключами:

  • type — тип токена (text, strong, em, link, image, codespan и т. д.).
  • raw — исходная строка, соответствующая этому токену.
  • text — текстовое содержимое токена (без Markdown-разметки).
  • href — ссылка для токенов link или image.
  • title — атрибут title для ссылок и изображений.
  • tokens — массив вложенных токенов для сложных элементов, например, эмфазы внутри ссылки.

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

{
  "type": "link",
  "raw": "[пример](https://example.com)",
  "text": "пример",
  "href": "https://example.com",
  "title": null,
  "tokens": [
    { "type": "text", "raw": "пример", "text": "пример" }
  ]
}

Использование встроенного Lexer

Базовый инлайн-лексер вызывается через метод Lexer.lexInline или Lexer.lex. Для примера:

import { Lexer } from 'marked';

const md = "Это **жирный** текст и [ссылка](https://example.com)";
const tokens = Lexer.lexInline(md);
console.log(tokens);

Результат — массив токенов, где emphasis, strong, link будут выделены как отдельные объекты.

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

Для расширения стандартного поведения Marked используется объект extensions, содержащий правила для новых токенов. Структура правила включает:

  • name — уникальное имя токена.
  • levelinline или block.
  • start — функция или регулярное выражение, определяющее возможное начало токена.
  • tokenizer — функция, которая принимает строку и возвращает токен.
  • renderer — функция для преобразования токена в HTML.

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

import { marked } from 'marked';

const highlightExtension = {
  name: 'highlight',
  level: 'inline',
  start: (src) => src.indexOf('=='),
  tokenizer: (src) => {
    const match = /^==(.+?)==/.exec(src);
    if (match) {
      return {
        type: 'highlight',
        raw: match[0],
        text: match[1]
      };
    }
  },
  renderer: (token) => `<mark>${token.text}</mark>`
};

marked.use({ extensions: [highlightExtension] });

const html = marked.parse('Это ==важно==!');
console.log(html); // <p>Это <mark>важно</mark>!</p>

Вложенные токены

Инлайн-токен может содержать вложенные токены. Это необходимо для правильного разбора Markdown внутри ссылок, эмфазы и других конструкций. Метод Lexer.inlineTokens рекурсивно создаёт массив токенов для вложенного содержимого. Пример:

const md = "Текст с *курсивом* внутри [ссылки *важно*](https://example.com)";
const tokens = Lexer.lexInline(md);

Внутри токена link появится массив tokens с объектами text и em, что позволяет корректно рендерить HTML с вложенной разметкой.

Настройка поведения стандартных токенов

Marked позволяет переопределять существующие токены через расширения:

const strongOverride = {
  name: 'strong',
  level: 'inline',
  tokenizer: (src) => {
    const match = /^\*\*(.+?)\*\*/.exec(src);
    if (match) {
      return {
        type: 'strong',
        raw: match[0],
        text: match[1],
        tokens: Lexer.inlineTokens(match[1])
      };
    }
  },
  renderer: (token) => `<b>${token.text}</b>`
};

marked.use({ extensions: [strongOverride] });

Это полезно для добавления кастомного HTML или других атрибутов к стандартным токенам.

Практические советы

  • Для сложных токенов рекомендуется всегда использовать tokens: Lexer.inlineTokens(text), чтобы вложенные Markdown-элементы были корректно разобраны.
  • start функции критичны для производительности: они позволяют Marked быстро пропускать части строки, которые точно не содержат токен.
  • Регулярные выражения в tokenizer должны быть «ленивыми» (+?), чтобы не поглощать лишний текст.
  • Любое новое расширение должно иметь уникальное имя, иначе может возникнуть конфликт с существующими токенами.

Отладка токенов

Для визуализации структуры токенов удобно использовать console.dir(tokens, { depth: null }). Это позволяет увидеть полную вложенность токенов, включая все свойства raw, text, tokens, href и title.


Хотите, я могу дополнительно сделать подробный разбор реальных сложных примеров Markdown с несколькими вложенными инлайн-токенами, чтобы показать работу Marked «в бою»?