Unified — это универсальный движок обработки синтаксических деревьев (AST) в JavaScript, который позволяет работать с различными форматами текста, включая Markdown и HTML. Remark и Rehype — это плагины для unified: Remark преобразует Markdown в AST, а Rehype работает с HTML. Создание CLI на их основе подразумевает создание цепочек плагинов для анализа, трансформации и генерации текста.
Основная структура CLI строится вокруг следующих компонентов:
Для работы с unified необходимо установить соответствующие пакеты:
npm install unified remark-parse remark-stringify rehype rehype-stringify commander
unified — ядро обработки AST.remark-parse — парсер Markdown.remark-stringify — генератор Markdown.rehype — парсер и трансформер HTML.rehype-stringify — генератор HTML.commander — удобная библиотека для CLI.Структура проекта может быть следующей:
my-cli/
├─ src/
│ ├─ cli.js
│ ├─ processor.js
│ └─ plugins/
│ ├─ customRemarkPlugin.js
│ └─ customRehypePlugin.js
├─ package.json
└─ README.md
Процессор — это функция, которая объединяет парсер, плагины и генератор. Пример для Markdown:
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import remarkGfm from 'remark-gfm';
import fs from 'fs';
export function createMarkdownProcessor() {
return unified()
.use(remarkParse) // Преобразование Markdown в AST
.use(remarkGfm) // Поддержка расширений GitHub Flavored Markdown
.use(customRemarkPlugin) // Пользовательский плагин для трансформации AST
.use(remarkStringify); // Преобразование AST обратно в Markdown
}
Принцип работы: сначала текст разбирается на дерево
(AST), затем оно проходит через цепочку плагинов, которые
модифицируют узлы дерева, после чего дерево сериализуется обратно в
Markdown или HTML.
Плагины — это функции, которые получают AST и могут изменять его
узлы. В Remark используется формат unist. Пример простого
плагина для Remark:
export function customRemarkPlugin() {
return (tree) => {
visit(tree, 'heading', (node) => {
node.children.unshift({
type: 'text',
value: '[TITLE] '
});
});
};
}
Для Rehype структура плагина аналогична, но используется HTML-дерево
(hast):
export function customRehypePlugin() {
return (tree) => {
visit(tree, 'element', (node) => {
if (node.tagName === 'p') {
node.children.unshift({
type: 'text',
value: '[PARAGRAPH] '
});
}
});
};
}
Функция visit из пакета unist-util-visit
позволяет рекурсивно обходить дерево и изменять узлы.
Командная строка обеспечивает передачу файлов и параметров:
#!/usr/bin/env node
import { program } from 'commander';
import fs from 'fs';
import { createMarkdownProcessor } from './processor.js';
program
.version('1.0.0')
.argument('<input>', 'Входной Markdown файл')
.argument('<output>', 'Выходной Markdown файл')
.option('-u, --uppercase', 'Преобразовать текст в верхний регистр')
.action(async (input, output, options) => {
const text = fs.readFileSync(input, 'utf-8');
const processor = createMarkdownProcessor();
let result = await processor.process(text);
if (options.uppercase) {
result = result.toString().toUpperCase();
}
fs.writeFileSync(output, result);
});
program.parse();
Особенности реализации:
async/await для обработки промисов, так
как unified поддерживает асинхронные плагины..option().fs.Для больших документов или сетевых запросов плагины могут быть асинхронными:
export function asyncRemarkPlugin() {
return async (tree) => {
await visit(tree, 'text', async (node) => {
node.value = await fetchDataForText(node.value);
});
};
}
Unified автоматически поддерживает промисы, если плагин возвращает
Promise.
Иногда необходимо преобразовать Markdown в HTML. Для этого используют
remark-rehype:
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
const processor = unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype) // Преобразование AST Markdown в AST HTML
.use(customRehypePlugin) // Трансформация HTML AST
.use(rehypeStringify); // Генерация HTML
Такой подход позволяет создавать полноценные конвейеры: Markdown → AST → трансформация → HTML.
plugins/ и подключать их
динамически.config.js или JSON-файлах.processor.js) для повторного использования в тестах и
других скриптах.Понимание структуры AST важно для эффективной трансформации. Основные
узлы Remark (mdast):
root — корневой узел документа.heading — заголовки.paragraph — параграфы.text — текстовые узлы.list, listItem — списки и элементы
списка.Для Rehype (hast) ключевые узлы:
root — корневой элемент.element — HTML-теги с tagName и
children.text — текст.comment — комментарии.Знание этих структур позволяет создавать точные трансформации и оптимизировать CLI для любых текстовых данных.