Работа с массивом токенов

Библиотека Marked предоставляет возможность разбирать Markdown-текст и получать его структурное представление в виде массива токенов. Токены — это объекты, которые содержат информацию о типе элемента Markdown, его содержимом и дополнительных свойствах. Работа с массивом токенов позволяет гибко анализировать, модифицировать и визуализировать Markdown-документы.


Генерация массива токенов

Для получения токенов используется метод Lexer.lex(input), где input — это строка с Markdown-контентом. Результатом является массив объектов, каждый из которых соответствует одному элементу Markdown.

import { Lexer } from 'marked';

const markdown = `
# Заголовок 1
Текст абзаца с **жирным текстом** и *курсивом*.
- Список
- Элемент списка
`;

const tokens = Lexer.lex(markdown);
console.log(tokens);

Каждый объект токена имеет свойство type, указывающее на тип Markdown-элемента (например, heading, paragraph, list, list_item, strong, em). Другие ключевые свойства зависят от типа токена, например:

  • depth — уровень заголовка (heading).
  • text — текстовое содержимое (paragraph, heading, list_item).
  • items — массив вложенных токенов для списка (list).

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

Токен всегда представляет собой объект с обязательным полем type. В зависимости от типа токена присутствуют дополнительные поля:

1. Заголовок (heading)

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

2. Абзац (paragraph)

{
  type: 'paragraph',
  text: 'Текст абзаца'
}

3. Список (list)

{
  type: 'list',
  ordered: false, // true для нумерованного списка
  items: [ /* массив токенов list_item */ ]
}

4. Элемент списка (list_item)

{
  type: 'list_item',
  text: 'Элемент списка',
  tokens: [ /* массив вложенных токенов */ ]
}

5. Стили текста (strong, em)

{
  type: 'strong',
  text: 'жирный текст'
}

Преобразование токенов в текст или HTML

Токены можно использовать для кастомной обработки Markdown. Например, можно самостоятельно формировать HTML или модифицировать содержимое перед выводом.

import { Parser } from 'marked';

const html = Parser.parse(tokens);
console.log(html);

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

const filteredTokens = tokens.filter(token => !(token.type === 'heading' && token.depth === 1));
const htmlFiltered = Parser.parse(filteredTokens);

Рекурсивная работа с токенами

Многие токены содержат вложенные токены: список (list) включает элементы (list_item), а элемент списка может содержать абзац, жирный или курсивный текст. Для обработки таких структур используют рекурсивные функции:

function printTokens(tokens, level = 0) {
  tokens.forEach(token => {
    console.log(' '.repeat(level * 2) + token.type + ': ' + (token.text || ''));
    if (token.tokens) {
      printTokens(token.tokens, level + 1);
    }
    if (token.items) {
      token.items.forEach(item => printTokens([item], level + 1));
    }
  });
}

printTokens(tokens);

Такой подход позволяет:

  • Получить полное представление структуры Markdown.
  • Модифицировать любой уровень вложенности.
  • Создавать кастомные рендереры для HTML, JSON или других форматов.

Кастомизация токенизации

Marked позволяет переопределять поведение токенизатора с помощью Lexer.rules и функций. Например, можно изменить обработку ссылок или добавить поддержку кастомного синтаксиса. Пример добавления правила для выделения ~~зачеркнутого~~ текста:

import { Lexer } from 'marked';

const strikethroughRule = {
  // паттерн для зачеркивания
  regex: /~~(.+?)~~/,
  type: 'del'
};

const lexer = new Lexer();
lexer.rules.inline.del = strikethroughRule;

const tokens = lexer.lex('Это ~~удаленный~~ текст');

После токенизации del будет отдельным токеном с типом del и текстом удаленный.


Использование токенов для анализа Markdown

Массив токенов предоставляет мощный инструмент для:

  • Подсчета количества заголовков по уровням.
  • Проверки наличия ссылок, изображений и списков.
  • Формирования оглавления документа.
  • Автоматического модифицирования Markdown перед рендерингом.

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

const headings = tokens
  .filter(token => token.type === 'heading')
  .map(token => ({ level: token.depth, text: token.text }));

console.log(headings);

Важные моменты при работе с токенами

  • Последовательность токенов соответствует порядку элементов в исходном Markdown.
  • Рекурсивные вложенности требуют аккуратной обработки для списков и элементов с форматированием.
  • Фильтрация и модификация массива токенов безопаснее, чем постобработка с HTML, так как сохраняет структуру документа.
  • Использование кастомных правил позволяет расширять возможности Markdown без изменения исходного текста.

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