Создание собственных плагинов

MDX предоставляет гибкий механизм расширения функциональности через плагины, которые позволяют модифицировать процесс разбора Markdown, добавлять новые синтаксические конструкции и интегрировать React-компоненты на уровне AST (Abstract Syntax Tree). Плагины работают на этапе компиляции MDX в JavaScript и могут быть встроены как в конфигурацию сборщика, так и напрямую через API @mdx-js/mdx.


Структура плагина

Плагин в MDX обычно представляет собой функцию, которая получает на вход AST дерева Markdown и объект с настройками. Основные элементы структуры:

function myPlugin(options = {}) {
  return (tree, vfile) => {
    // Логика обработки AST
  };
}
  • tree — это объект AST, который соответствует структуре документа Markdown.
  • vfile — виртуальный файл, содержащий метаинформацию о текущем документе (имя файла, путь, ошибки и предупреждения).
  • options — пользовательские настройки плагина, передаваемые при подключении.

Типы плагинов

  1. Remark-плагины Работают с AST Markdown до того, как содержимое превратится в JSX. Позволяют добавлять новые синтаксические конструкции, модифицировать текст, внедрять теги или метаданные.

  2. Rehype-плагины Работают на уровне HTML-дерева после трансформации Markdown в HTML. Используются для добавления атрибутов, изменения структуры HTML или интеграции с внешними библиотеками.


Работа с AST

AST представляет документ в виде дерева узлов. Каждый узел имеет свойства:

  • type — тип узла (paragraph, heading, text, link, code и т.д.).
  • children — массив дочерних узлов.
  • value — текстовое содержимое узла (для узлов типа text).
  • data — дополнительная информация, которую можно использовать для плагина.

Пример обхода дерева:

function myPlugin() {
  return (tree) => {
    visit(tree, 'text', (node) => {
      if (node.value.includes('TODO')) {
        node.value = node.value.replace(/TODO/g, '⚠️');
      }
    });
  };
}

Здесь используется метод visit из пакета unist-util-visit, позволяющий рекурсивно обходить AST и модифицировать узлы.


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

MDX позволяет интегрировать React-компоненты в Markdown. Плагин может автоматически оборачивать определённые слова или конструкции в компонент.

import { visit } from 'unist-util-visit';

function highlightPlugin({ componentName = 'Highlight' } = {}) {
  return (tree) => {
    visit(tree, 'text', (node, index, parent) => {
      if (!node.value.includes('important')) return;
      
      const parts = node.value.split(/(important)/);
      const newNodes = parts.map((part) => {
        if (part === 'important') {
          return {
            type: 'mdxJsxFlowElement',
            name: componentName,
            attributes: [],
            children: [{ type: 'text', value: part }],
          };
        }
        return { type: 'text', value: part };
      });

      parent.children.splice(index, 1, ...newNodes);
    });
  };
}

Результатом работы будет автоматическое преобразование слова important в JSX-компонент <Highlight>.


Передача параметров плагина

Плагины MDX поддерживают конфигурацию через объект настроек:

import { compile } from '@mdx-js/mdx';
import highlightPlugin from './highlightPlugin.js';

const mdxContent = `
Это очень important текст.
`;

const result = await compile(mdxContent, {
  remarkPlugins: [[highlightPlugin, { componentName: 'CustomHighlight' }]],
});

Такой подход позволяет создавать универсальные плагины, которые легко настраиваются под разные проекты.


Обработка ошибок и предупреждений

MDX поддерживает возможность выдачи предупреждений через vfile:

function todoWarningPlugin() {
  return (tree, vfile) => {
    visit(tree, 'text', (node) => {
      if (node.value.includes('TODO')) {
        vfile.warn('Найдена метка TODO', node);
      }
    });
  };
}
  • vfile.warn(message, node) — добавляет предупреждение с привязкой к конкретному узлу.
  • vfile.fail(message, node) — генерирует ошибку, прерывая компиляцию.

Интеграция с сборщиками

MDX-плагины могут использоваться с:

  • Vite через @mdx-js/rollup или vite-plugin-mdx.
  • Webpack через @mdx-js/loader.
  • Next.js через @next/mdx.

Подключение выглядит одинаково: передача плагина в remarkPlugins или rehypePlugins в конфигурации.

import { MDXProvider } from '@mdx-js/react';
import highlightPlugin from './highlightPlugin.js';

const mdxComponents = {
  Highlight: ({ children }) => <mark>{children}</mark>,
};

<MDXProvider components={mdxComponents}>
  <MDXContent />
</MDXProvider>

Советы по разработке плагинов

  1. Всегда проверять тип узла перед изменением.
  2. Использовать утилиты unist-util-visit и unist-util-visit-parents для безопасного обхода дерева.
  3. Поддерживать опции плагина для максимальной гибкости.
  4. Добавлять предупреждения через vfile.warn вместо прямого изменения текста, если нужно уведомить пользователя.
  5. Для больших проектов разделять логику на несколько мелких плагинов вместо одного монолитного.

Этот подход позволяет создавать мощные и расширяемые плагины для MDX, полностью контролируя процесс компиляции Markdown и интеграцию с React-компонентами.