Unified — это экосистема инструментов для обработки текстовых форматов через AST (Abstract Syntax Tree). Основные библиотеки в этой экосистеме — Remark для Markdown и Rehype для HTML. Для проектов на JavaScript и TypeScript важным аспектом является интеграция этих инструментов с Webpack, что позволяет обрабатывать контент как модули, автоматически превращая Markdown или HTML в готовый к использованию JSX, строки HTML или AST-объекты.
Webpack loader — это функция, которая получает содержимое файла и возвращает результат его трансформации. Для интеграции с unified создаются специализированные loaders, которые:
Ключевые моменты:
this.callback внутри
loader-а.this.cacheable() для оптимизации сборки.const remark = require('remark');
const remarkHtml = require('remark-html');
module.exports = function(source) {
const callback = this.async();
this.cacheable();
remark()
.use(remarkHtml)
.process(source)
.then(file => {
callback(null, `export default ${JSON.stringify(String(file))};`);
})
.catch(err => {
callback(err);
});
};
Разбор кода:
this.async() создаёт асинхронный callback для
loader-а.this.cacheable() сообщает Webpack, что результат
трансформации может быть закеширован.remark().use(remarkHtml) превращает Markdown в
HTML.export default), чтобы его можно было импортировать в
коде.Если проект работает с HTML или требует дополнительные преобразования после Markdown, используют Rehype:
const rehype = require('rehype');
const rehypeStringify = require('rehype-stringify');
module.exports = function(source) {
const callback = this.async();
this.cacheable();
rehype()
.data('settings', { fragment: true })
.use(rehypeStringify)
.process(source)
.then(file => {
callback(null, `export default ${JSON.stringify(String(file))};`);
})
.catch(err => callback(err));
};
Особенности:
fragment: true позволяет обрабатывать HTML-фрагменты
без корневого тега <html>.Для сложных проектов часто создают цепочку loaders:
Пример конфигурации:
module.exports = {
module: {
rules: [
{
test: /\.md$/,
use: [
'babel-loader',
path.resolve(__dirname, 'loaders/rehype-loader.js'),
path.resolve(__dirname, 'loaders/remark-loader.js'),
],
},
],
},
};
Последовательность важна: сначала обрабатывается исходный Markdown через Remark, затем результат конвертируется Rehype, и наконец через Babel преобразуется в модуль JS/JSX.
Плагины — основной механизм расширения функциональности:
remark-gfm, remark-slug,
remark-autolink-headings.rehype-highlight, rehype-format,
rehype-react (для преобразования в JSX).Пример подключения плагина для подсветки синтаксиса:
const remark = require('remark');
const remarkHtml = require('remark-html');
const remarkGfm = require('remark-gfm');
const rehypeHighlight = require('rehype-highlight');
module.exports = function(source) {
const callback = this.async();
this.cacheable();
remark()
.use(remarkGfm)
.use(remarkHtml)
.process(source)
.then(file => {
return rehype()
.use(rehypeHighlight)
.process(String(file));
})
.then(file => {
callback(null, `export default ${JSON.stringify(String(file))};`);
})
.catch(err => callback(err));
};
Особенности интеграции:
this.cacheable() и кешировать результат
process при неизменных файлах.Для использования с React или Vue:
Пример интеграции с React через rehype-react:
const rehypeReact = require('rehype-react');
const React = require('react');
rehype()
.use(rehypeReact, { createElement: React.createElement })
.processSync('<h1>Hello</h1>')
.result; // React элемент
Такой подход позволяет Markdown-файлы хранить как контент и динамически рендерить их в приложении, не превращая их в статические строки HTML.
src/
content/
post1.md
loaders/
remark-loader.js
rehype-loader.js
components/
MarkdownRenderer.jsx
webpack.config.js
Markdown-файлы становятся полноценными модулями, которые можно импортировать и использовать с любыми React/Vue компонентами.
Хотите, я могу подготовить следующую часть с реальными примерами цепочек Remark → Rehype → rehype-react для динамического рендеринга React компонентов с продвинутой обработкой плагинов и настроек Webpack?