Просмотр токенов

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


Основные понятия

Каждый токен в Markdown-it представляет собой объект с набором свойств:

  • type — тип токена (например, paragraph_open, inline, text, heading_open и т.д.).
  • tag — HTML-тег, соответствующий токену (p, h1, li и т.д.).
  • attrs — массив атрибутов [ключ, значение] (например, для ссылок или изображений).
  • map — массив с начальной и конечной строкой токена [startLine, endLine].
  • nesting — значение 1, 0 или -1, указывающее на открывающий, одиночный или закрывающий токен.
  • level — уровень вложенности, полезный для отслеживания структуры документа.
  • children — массив дочерних токенов (используется для токенов типа inline).

Пример базового токена:

{
  type: 'paragraph_open',
  tag: 'p',
  attrs: null,
  map: [0, 1],
  nesting: 1,
  level: 0,
  children: null
}

Получение токенов

Markdown-it позволяет получить токены с помощью метода parse. Вызов выглядит так:

const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

const src = `# Заголовок 1
Это пример текста с **жирным** выделением.`;

const tokens = md.parse(src, {});
console.log(tokens);

tokens — массив объектов, где каждый объект описывает отдельный элемент Markdown: заголовок, параграф, текст, форматирование. Важно, что inline токены содержат свои дочерние токены в children, что позволяет детально разбирать внутренние элементы текста.


Навигация по токенам

Для анализа документа часто используют рекурсивное или итеративное прохождение по массиву токенов. Основные методы работы:

  • Итерация по верхнему уровню:
tokens.forEach(token => {
  console.log(token.type, token.tag, token.nesting);
});
  • Доступ к дочерним токенам:
tokens.forEach(token => {
  if (token.type === 'inline' && token.children) {
    token.children.forEach(child => {
      console.log(child.type, child.content);
    });
  }
});
  • Фильтрация по типу:
const textTokens = tokens.filter(t => t.type === 'text');
textTokens.forEach(t => console.log(t.content));

Практические примеры использования

  1. Извлечение всех ссылок:
tokens.forEach(token => {
  if (token.type === 'inline' && token.children) {
    token.children.forEach(child => {
      if (child.type === 'link_open') {
        const href = child.attrs.find(attr => attr[0] === 'href')[1];
        console.log('Ссылка:', href);
      }
    });
  }
});
  1. Подсчет заголовков по уровню:
const headings = tokens.filter(t => t.type === 'heading_open');
headings.forEach(h => console.log(h.tag));
  1. Извлечение текста без разметки:
let plainText = '';
tokens.forEach(token => {
  if (token.type === 'inline' && token.children) {
    token.children.forEach(child => {
      if (child.type === 'text') plainText += child.content + ' ';
    });
  }
});
console.log(plainText.trim());

Настройка рендеринга через токены

Markdown-it предоставляет возможность перехватывать и изменять токены перед рендерингом. Для этого используют метод core.ruler.push:

md.core.ruler.push('uppercase_text', state => {
  state.tokens.forEach(token => {
    if (token.type === 'inline' && token.children) {
      token.children.forEach(child => {
        if (child.type === 'text') {
          child.content = child.content.toUpperCase();
        }
      });
    }
  });
});

Этот подход позволяет внедрять кастомную обработку Markdown, например, автоматическую замену определенных слов, изменение структуры документа или добавление атрибутов к HTML-элементам.


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

level токена отражает глубину вложенности, начиная с 0. Это особенно важно для списков, блоков кода и вложенных элементов. Пример:

tokens.forEach(token => {
  console.log('Уровень:', token.level, 'Тип:', token.type, 'Тег:', token.tag);
});

Использование level помогает различать, например, параграф внутри списка и параграф на верхнем уровне документа.


Советы по эффективному анализу токенов

  • Использовать type для быстрого фильтрования нужных элементов.
  • Для текстового содержимого всегда проверять children у inline токенов.
  • Обход токенов рекурсивно удобен для вложенных структур, таких как списки, таблицы или цитаты.
  • map помогает сопоставлять токены с исходными строками Markdown, что полезно для подсветки синтаксиса или линтинга документа.

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