MDX предоставляет мощную возможность комбинировать Markdown и JSX, но сложность его экосистемы приводит к тому, что при использовании множества плагинов могут возникать конфликты. Конфликты плагинов чаще всего проявляются при обработке AST (Abstract Syntax Tree) Markdown, где два плагина пытаются изменить одну и ту же часть дерева или влияют на порядок обработки узлов.
Типичные признаки конфликтов:
@mdx-js/mdx или
remark/rehype плагинов.TypeError или undefined свойства.MDX использует цепочку обработки через remark-плагины и rehype-плагины. Понимание порядка их применения критично:
mdast).hast), которое формируется после Markdown.Конфликты часто возникают, когда один плагин предполагает определённое состояние AST, а другой изменяет это состояние до его обработки.
Плагины MDX выполняются строго по порядку их объявления. Если плагин, который модифицирует структуру заголовков, идёт после плагина, который рассчитывает на стандартные заголовки, возникает конфликт. Решение: всегда размещать плагин, меняющий структуру документа, раньше тех, кто зависит от этой структуры.
import remarkSlug from 'remark-slug';
import remarkAutolinkHeadings from 'remark-autolink-headings';
const mdxOptions = {
remarkPlugins: [
remarkSlug, // Сначала добавляем id к заголовкам
remarkAutolinkHeadings, // Затем делаем ссылки на заголовки
],
};
Если плагин слишком агрессивно модифицирует AST, его можно обернуть в функцию, которая применяет изменения только к определённым узлам:
function selectivePlugin() {
return (tree) => {
visit(tree, 'heading', (node) => {
if (node.depth === 2) {
node.data = node.data || {};
node.data.custom = true;
}
});
};
}
Плагины часто используют разные версии unist-util-visit
или других утилит. Совмещение несовместимых версий вызывает ошибки
выполнения. Решение: унифицировать версии зависимостей
через package.json или resolutions (в
Yarn):
"resolutions": {
"unist-util-visit": "4.1.1"
}
Для сложных интеграций можно вставлять промежуточные шаги: сначала преобразовать Markdown в AST, затем применить свои модификации, и только после этого запускать плагин, который может конфликтовать.
import {unified} from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const processor = unified()
.use(remarkParse)
.use(customPlugin)
.use(remarkStringify);
MDX не всегда выдаёт подробные ошибки при конфликтах. Использование
console.log AST на каждом этапе или
unist-util-debug помогает определить точное место
конфликта.
import {inspect} from 'unist-util-inspect';
const logTree = (tree) => console.log(inspect(tree, {colors: true}));
processor.use((tree) => logTree(tree));
MDX позволяет вставлять JSX прямо в Markdown. Некоторые плагины для
Markdown могут ошибочно интерпретировать JSX как текстовые узлы. Решение
— использовать плагин remark-mdx перед всеми
плагинами, работающими с текстовыми узлами.
Плагины типа remark-images или
remark-external-links могут модифицировать одинаковые узлы.
Важно объединять их в цепочку аккуратно и тестировать каждый шаг.
Некоторые плагины добавляют или модифицируют атрибуты узлов
(data, className). Если несколько плагинов
записывают в одно и то же поле, итоговое значение может быть
непредсказуемым. Решение — использовать уникальные ключи или функции
слияния атрибутов.
unist-util-inspect.Эти подходы позволяют создавать сложные MDX-проекты без неожиданных ошибок и сохраняют предсказуемость поведения плагинов.