Парсинг MDX

MDX (Markdown + JSX) представляет собой расширение Markdown, которое позволяет включать в текст JSX-компоненты, сохраняя при этом привычный синтаксис Markdown. Основной задачей парсинга MDX является корректная обработка такого смешанного синтаксиса и преобразование его в структуру данных, пригодную для рендеринга в React-приложениях.

В основе парсинга лежат следующие шаги: лексический анализ, синтаксический анализ, трансформация AST и компиляция в JavaScript.


Лексический анализ

Лексический анализ в MDX выполняет разделение текста на токены, которые затем используются для построения дерева синтаксического анализа (AST). Для этого применяются библиотеки remark и micromark.

Основные типы токенов:

  • Текстовые токены – обычные элементы Markdown: заголовки, абзацы, списки, цитаты.
  • JSX-токены – блоки, начинающиеся с < и заканчивающиеся >, которые интерпретируются как React-компоненты.
  • Инлайн-токены – вставки JSX или выражения в фигурных скобках {}.

Пример выделения токенов:

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

const mdxSource = `
# Заголовок


`;

const compiled = await compile(mdxSource);

На этом этапе MDX разбивается на узлы AST, в которых каждый элемент хранится с типом и содержимым.


Синтаксический анализ и AST

AST (Abstract Syntax Tree) — это структура данных, которая представляет исходный документ в виде дерева. Каждая нода описывает конкретный элемент документа: параграф, заголовок, список, JSX-компонент или выражение.

Примеры основных узлов AST:

  • root – корневой узел документа.
  • paragraph – абзац текста.
  • heading – заголовок, с уровнем вложенности (depth).
  • jsx – JSX-компонент или блок JSX.
  • inlineCode – инлайн-код.
  • text – текстовое содержимое.

AST MDX формируется с помощью плагина remark-mdx, который расширяет стандартный Markdown AST, добавляя поддержку JSX и встроенных выражений.

import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkMdx from 'remark-mdx';

const processor = unified()
  .use(remarkParse)
  .use(remarkMdx);

const ast = processor.parse(mdxSource);

Трансформация AST

После того как AST сформировано, следующим шагом является трансформация узлов для подготовки к компиляции в JSX. Трансформации могут включать:

  • Преобразование заголовков и списков в React-компоненты.
  • Обработка выражений JSX, чтобы корректно интегрировать их с React.
  • Оптимизация структуры AST, например объединение смежных текстовых узлов.

MDX предоставляет утилиту mdx-bundler или @mdx-js/mdx для автоматической трансформации AST в JSX-код:

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

const jsx = String(await compile(mdxSource, {outputFormat: 'function-body'}));

Здесь outputFormat: 'function-body' означает, что результат будет готовой функцией React-компонента, которую можно использовать напрямую.


Компиляция в JavaScript

Финальный этап парсинга — компиляция MDX в JavaScript, обычно в виде React-компонента. Этот этап включает:

  • Преобразование AST в JSX.
  • Вставку импортов React и дополнительных компонентов.
  • Обработка динамических выражений и переменных.

Пример итоговой структуры с использованием MDX как компонента:

import * as React from 'react';
import {MDXProvider} from '@mdx-js/react';
import MyComponent from './MyComponent';

const Content = () => (
  
    
    

Заголовок

); export default Content;

Расширение функциональности с помощью плагинов

MDX поддерживает плагины для remark и rehype, что позволяет расширять возможности парсинга:

  • remark-плагины обрабатывают Markdown и AST до этапа компиляции. Например, remark-slug добавляет уникальные идентификаторы к заголовкам.
  • rehype-плагины работают с HTML-представлением документа и позволяют применять дополнительные трансформации на этапе рендеринга.

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

import rehypeHighlight from 'rehype-highlight';

const jsx = String(await compile(mdxSource, {
  rehypePlugins: [rehypeHighlight]
}));

Работа с динамическими выражениями

MDX позволяет включать JavaScript-выражения внутри текста:

{new Date().toLocaleDateString()}

При парсинге такие узлы идентифицируются как jsxExpression в AST. На этапе компиляции они преобразуются в корректный JSX, который будет выполнен во время рендера компонента.


Обработка ошибок и валидация

При парсинге MDX часто встречаются синтаксические ошибки в JSX или Markdown. Основные техники обработки:

  • Проверка корректности AST перед компиляцией.
  • Использование try/catch при компиляции через compile.
  • Логирование ошибок с указанием позиции в исходном документе для облегчения отладки.

Практические советы

  • Разделять MDX-контент на отдельные файлы для каждого компонента или страницы.
  • Использовать строгие линтеры и плагины для контроля качества Markdown и JSX.
  • Автоматизировать сборку с помощью esbuild или Vite, чтобы интегрировать MDX в современный фронтенд-процесс.