Мультипроходный парсинг

Marked — это высокопроизводительная библиотека для парсинга Markdown в JavaScript. Она обеспечивает гибкость и скорость преобразования текста в HTML, а также позволяет детально управлять процессом разборки текста. Одним из продвинутых методов работы с Marked является мультипроходный парсинг, когда исходный Markdown обрабатывается несколькими этапами для достижения большей гибкости или для внедрения нестандартной логики.


Принцип мультипроходного парсинга

Мультипроходный парсинг подразумевает несколько последовательных проходов по исходному тексту Markdown:

  1. Первичный разбор — создание абстрактного синтаксического дерева (AST) или токенизация.
  2. Промежуточная обработка — модификация токенов, внедрение пользовательской логики, добавление метаданных или изменение структуры.
  3. Финальная генерация HTML — преобразование обработанного дерева или токенов в конечный HTML-код.

Такой подход особенно полезен при необходимости:

  • Добавлять пользовательские элементы (например, кастомные блоки, подсветку кода).
  • Обрабатывать специальные конструкции до генерации HTML.
  • Реализовывать различные уровни валидации и фильтрации контента.

Токенизация и первый проход

Первый проход отвечает за токенизацию Markdown. Marked предоставляет функцию marked.lexer, которая преобразует исходный текст в массив токенов:

import { marked } from 'marked';

const markdownText = `
# Заголовок 1
Параграф с **жирным** текстом и [ссылкой](https://example.com).
`;

const tokens = marked.lexer(markdownText);
console.log(tokens);

Токены имеют структуру объектов с полями, такими как:

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

Первый проход позволяет детально анализировать структуру Markdown до генерации HTML.


Промежуточная обработка токенов

После токенизации можно выполнить модификацию токенов, чтобы внедрить кастомные правила:

tokens.forEach(token => {
  if (token.type === 'heading' && token.depth === 1) {
    token.text = `? ${token.text}`; // добавление эмблемы к заголовкам первого уровня
  }

  if (token.type === 'paragraph') {
    token.text = token.text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>'); // кастомная обработка жирного текста
  }
});

Особенности промежуточной обработки:

  • Можно добавлять новые токены для нестандартных блоков.
  • Возможна фильтрация нежелательного контента.
  • Можно собирать метаданные для аналитики или для использования в последующих проходах.

Второй проход и генерация HTML

Второй проход превращает обработанные токены в HTML с использованием функции marked.parser:

const html = marked.parser(tokens);
console.log(html);

На этом этапе Marked выполняет:

  • Преобразование заголовков, параграфов, списков, блоков кода в соответствующие HTML-теги.
  • Поддержку вложенной структуры токенов.
  • Внедренные промежуточные изменения, такие как кастомные вставки.

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

Для полного контроля над HTML можно использовать кастомный Renderer:

const renderer = {
  heading(text, level) {
    return `<h${level} class="custom-heading">${text}</h${level}>`;
  },
  link(href, title, text) {
    return `<a href="${href}" title="${title || ''}" target="_blank">${text}</a>`;
  }
};

const htmlWithRenderer = marked.parser(tokens, { renderer });

Такой подход идеально подходит для мультипроходного парсинга:

  • Первый проход создает структуру токенов.
  • Второй проход модифицирует токены.
  • Рендеринг применяет кастомные правила к HTML.

Интеграция с плагинами и обработчиками

Marked поддерживает интеграцию с плагинами или middleware, что делает мультипроходный парсинг более гибким:

  • Можно подключать обработчики для синтаксиса подсветки кода (highlight.js, Prism).
  • Реализовывать проверку ссылок и встроенную валидацию.
  • Преобразовывать специфические конструкции (например, математические формулы или диаграммы).

Пример подключения подсветки кода:

import hljs from 'highlight.js';

const renderer = {
  code(code, lang) {
    const validLang = hljs.getLanguage(lang) ? lang : 'plaintext';
    const highlighted = hljs.highlight(code, { language: validLang }).value;
    return `<pre><code class="hljs ${validLang}">${highlighted}</code></pre>`;
  }
};

const htmlHighlighted = marked.parser(tokens, { renderer });

Преимущества мультипроходного подхода

  1. Гибкость — позволяет внедрять собственные правила и структуры.
  2. Контроль — точная настройка каждого элемента Markdown.
  3. Расширяемость — легко подключать внешние обработчики и плагины.
  4. Масштабируемость — удобно работать с большими документами или динамическим контентом.

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