MDX (Markdown + JSX) представляет собой расширение Markdown, которое позволяет включать в текст JSX-компоненты, сохраняя при этом привычный синтаксис Markdown. Основной задачей парсинга MDX является корректная обработка такого смешанного синтаксиса и преобразование его в структуру данных, пригодную для рендеринга в React-приложениях.
В основе парсинга лежат следующие шаги: лексический анализ, синтаксический анализ, трансформация AST и компиляция в JavaScript.
Лексический анализ в MDX выполняет разделение текста на токены,
которые затем используются для построения дерева синтаксического анализа
(AST). Для этого применяются библиотеки remark и
micromark.
Основные типы токенов:
< и заканчивающиеся >, которые
интерпретируются как React-компоненты.{}.Пример выделения токенов:
import {compile} from '@mdx-js/mdx';
const mdxSource = `
# Заголовок
`;
const compiled = await compile(mdxSource);
На этом этапе MDX разбивается на узлы 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 сформировано, следующим шагом является трансформация узлов для подготовки к компиляции в JSX. Трансформации могут включать:
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-компонента, которую можно
использовать напрямую.
Финальный этап парсинга — компиляция MDX в JavaScript, обычно в виде 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-slug добавляет уникальные
идентификаторы к заголовкам.Пример подключения плагина:
import rehypeHighlight from 'rehype-highlight';
const jsx = String(await compile(mdxSource, {
rehypePlugins: [rehypeHighlight]
}));
MDX позволяет включать JavaScript-выражения внутри текста:
{new Date().toLocaleDateString()}
При парсинге такие узлы идентифицируются как
jsxExpression в AST. На этапе компиляции они преобразуются
в корректный JSX, который будет выполнен во время рендера
компонента.
При парсинге MDX часто встречаются синтаксические ошибки в JSX или Markdown. Основные техники обработки:
compile.esbuild или
Vite, чтобы интегрировать MDX в современный
фронтенд-процесс.