Система хуков в Marked

Библиотека Marked предоставляет мощный инструмент для работы с Markdown в JavaScript, и одной из её ключевых возможностей является система хуков. Хуки позволяют вмешиваться в процесс парсинга и рендеринга Markdown, модифицировать результат и добавлять собственную логику без изменения исходного кода библиотеки.


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

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

  1. beforeParse — вызывается перед началом парсинга Markdown.
  2. afterParse — вызывается после того, как Markdown преобразован в внутреннее дерево токенов, но до рендеринга в HTML.
  3. beforeRender — вызывается перед рендерингом каждого блока или инлайна.
  4. afterRender — вызывается после рендеринга блока или инлайна в HTML.

Использование хуков позволяет изменять как исходный текст, так и промежуточные данные на любом этапе.


Регистрация хуков

Для подключения хуков используется метод Marked.use(). Он принимает объект с ключами, соответствующими типам хуков, и массивами функций:

import { marked } from 'marked';

marked.use({
  hooks: {
    beforeParse: [
      (markdown) => {
        return markdown.replace(/TODO:/g, '<strong>TODO:</strong>');
      }
    ],
    afterRender: [
      (html) => {
        return html + '<!-- Rendered by Marked -->';
      }
    ]
  }
});
  • beforeParse: функция принимает исходный Markdown и должна возвращать строку Markdown.
  • afterRender: функция принимает результат HTML и возвращает модифицированный HTML.

Хуки выполняются последовательно в порядке регистрации.


beforeParse

beforeParse позволяет вмешиваться в текст до того, как Marked начнёт токенизацию. Это удобно для:

  • Замены специфических маркеров или тегов.
  • Предварительной фильтрации контента.
  • Добавления кастомной синтаксической обработки.

Пример использования:

marked.use({
  hooks: {
    beforeParse: [
      (text) => {
        // Автоматически заменяем смайлики на Unicode-символы
        return text.replace(/:\)/g, '?').replace(/:\(/g, '?');
      }
    ]
  }
});

В этом случае все смайлики в Markdown будут заменены ещё до того, как библиотека начнёт формировать токены.


afterParse

Хук afterParse позволяет работать с массивом токенов, полученных после парсинга Markdown. Каждый токен представляет отдельный блок или инлайн-элемент.

marked.use({
  hooks: {
    afterParse: [
      (tokens) => {
        tokens.forEach(token => {
          if (token.type === 'heading') {
            token.text = token.text.toUpperCase();
          }
        });
        return tokens;
      }
    ]
  }
});

Преимущества работы на этом этапе:

  • Возможность модифицировать структуру документа без вмешательства в HTML.
  • Лёгкая интеграция с системами генерации оглавления или индексирования.

beforeRender и afterRender

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

beforeRender может быть использован для:

  • Динамического добавления атрибутов к тегам.
  • Замены контента определённых типов токенов.
marked.use({
  hooks: {
    beforeRender: [
      (token) => {
        if (token.type === 'link') {
          token.href = token.href + '?utm_source=marked';
        }
        return token;
      }
    ]
  }
});

afterRender позволяет модифицировать окончательный HTML. Это полезно для:

  • Вставки аналитики, рекламных блоков или комментариев.
  • Оборачивания определённых элементов в кастомные контейнеры.
marked.use({
  hooks: {
    afterRender: [
      (html) => `<div class="markdown-content">${html}</div>`
    ]
  }
});

Множественные хуки одного типа

Marked поддерживает регистрацию нескольких функций для одного типа хука. Они вызываются последовательно, и результат предыдущей функции передаётся следующей:

marked.use({
  hooks: {
    beforeParse: [
      text => text.replace(/foo/g, 'bar'),
      text => text.replace(/baz/g, 'qux')
    ]
  }
});

Это позволяет строить сложные цепочки трансформаций без объединения логики в одну функцию.


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

  1. Кастомные синтаксис-расширения: добавление собственных тегов и их обработка.
  2. Автоматическая генерация ID заголовков: для создания якорей и ссылок на разделы.
  3. Безопасность: фильтрация нежелательного HTML или скриптов перед рендерингом.
  4. Интеграция с CMS: добавление динамического контента в Markdown без изменения исходного текста.

Рекомендации по производительности

  • Избегать тяжелых операций внутри хуков на больших текстах, особенно в beforeParse и afterRender.
  • Предпочтительно работать с токенами в afterParse, чтобы минимизировать строковые манипуляции.
  • Для многократного использования лучше создавать отдельные функции-хуки, а не анонимные, чтобы улучшить читаемость и отладку.

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