Препроцессинг

Препроцессинг в контексте MDX — это этап подготовки исходных файлов с разметкой и кодом перед их компиляцией в React-компоненты. Он позволяет интегрировать JavaScript-логику и специальные трансформации текста, обеспечивая гибкость и расширяемость MDX-проекта.


1. Основы препроцессинга

MDX сочетает Markdown и JSX, что создаёт необходимость обрабатывать два разных типа синтаксиса одновременно. Препроцессинг выполняет следующие функции:

  • Парсинг Markdown: превращение обычного Markdown в AST (Abstract Syntax Tree), который затем может быть модифицирован.
  • Интеграция JSX: определение и корректная вставка React-компонентов в поток Markdown.
  • Трансформация AST: применение плагинов, например, для подсветки синтаксиса, генерации оглавления, авто-добавления атрибутов к заголовкам.

Для выполнения этих задач чаще всего используют remark (Markdown-парсер) и rehype (HTML-парсер), которые интегрируются с MDX через плагины.


2. Remark и Rehype: роли в препроцессинге

Remark работает на уровне Markdown AST. Основные возможности:

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

Пример подключения плагина remark:

import { defineMdxComponents } from '@mdx-js/react';
import remarkGfm from 'remark-gfm';
import { MDXProvider } from '@mdx-js/react';

const components = defineMdxComponents({
  h1: (props) => <h1 style={{ color: 'blue' }} {...props} />,
});

export default function MdxWrapper({ children }) {
  return (
    <MDXProvider components={components}>
      {children}
    </MDXProvider>
  );
}

// В конфигурации MDX:
{
  remarkPlugins: [remarkGfm],
}

Rehype выполняет обработку HTML-подобного AST. Основные задачи:

  • Манипуляция DOM-подобной структурой перед рендером.
  • Интеграция с библиотеками подсветки синтаксиса, например, Prism.
  • Валидация и модификация HTML-структуры.

Пример Rehype-плагина:

import rehypeSlug from 'rehype-slug';
import rehypeAutolinkHeadings from 'rehype-autolink-headings';

{
  rehypePlugins: [
    rehypeSlug,
    [rehypeAutolinkHeadings, { beh * avior: 'wrap' }]
  ]
}

3. Компиляция MDX

После препроцессинга MDX-файл превращается в валидный React-компонент. Этот процесс состоит из нескольких шагов:

  1. Парсинг Markdown с JSX — создание единого AST.
  2. Применение плагинов Remark и Rehype — трансформация дерева и расширение функционала.
  3. Генерация кода React-компонента — создаётся JS-файл, который можно импортировать и рендерить.

Для настройки этого процесса используются функции compile или compileSync из @mdx-js/mdx:

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

const mdxSource = `
# Пример

<MyComponent />
`;

const compiled = await compile(mdxSource, {
  remarkPlugins: [remarkGfm],
  rehypePlugins: [rehypeSlug]
});

Результат compiled можно интегрировать в проект на React через MDXProvider или динамический рендер.


4. Кастомизация препроцессинга

MDX позволяет создавать собственные плагины для препроцессинга:

  • Remark-плагины: анализируют текст Markdown, добавляют атрибуты, заменяют шаблоны или внедряют переменные.
  • Rehype-плагины: работают с HTML-структурой, например, добавляют классы к тегам, вставляют обёртки для кода, автоматизируют генерацию ссылок.

Пример пользовательского Remark-плагина для замены ключевых слов:

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

function remarkReplaceWord(word, replacement) {
  return (tree) => {
    visit(tree, 'text', (node) => {
      node.value = node.value.replace(new RegExp(word, 'g'), replacement);
    });
  };
}

// Использование
remarkPlugins: [remarkReplaceWord('MDX', 'MDX.js')]

5. Препроцессинг с TypeScript и MDX

MDX полностью поддерживает TypeScript, но для корректной работы необходимо:

  • Установить соответствующие типы для JSX-компонентов.
  • Конфигурировать компилятор для понимания TS-расширений в MDX.
  • Применять Remark/Rehype плагины до стадии компиляции, чтобы избежать ошибок типов.

Пример настройки:

import { compile } from '@mdx-js/mdx';
import remarkGfm from 'remark-gfm';

const compiled = await compile(`
# Заголовок

<MyTSComponent someProp={42} />
`, {
  remarkPlugins: [remarkGfm],
  providerImportSource: '@mdx-js/react'
});

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

MDX может быть интегрирован в проекты с Webpack, Vite, Next.js. Препроцессинг позволяет:

  • Использовать MDX как источник данных для роутов.
  • Автоматически подключать стили и компоненты.
  • Поддерживать горячую перезагрузку компонентов.

Пример конфигурации для Next.js:

// next.config.js
const withMDX = require('@next/mdx')({
  extension: /\.mdx?$/,
  options: {
    remarkPlugins: [require('remark-gfm')],
    rehypePlugins: [require('rehype-slug')]
  }
});

module.exports = withMDX({
  pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx']
});

7. Особенности и лучшие практики

  • Порядок применения плагинов важен: сначала Remark, затем Rehype.
  • Избегать тяжелых синхронных операций в препроцессинге, чтобы не замедлять компиляцию.
  • Разделять функциональность плагинов: один плагин — одна задача, для упрощения поддержки.
  • Использовать AST-визуализаторы при разработке сложных трансформаций, чтобы отслеживать изменения на каждом этапе.

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