Для полноценной интеграции Remark и Rehype с файловой системой
используются стандартные модули Node.js, такие как fs и
path, а также сторонние библиотеки для удобного обхода
каталогов и обработки асинхронных операций. Основная задача — читать
исходные Markdown-файлы, преобразовывать их в дерево AST (Abstract
Syntax Tree), модифицировать или анализировать структуру, а затем
сохранять результат в файл или отправлять дальше по конвейеру
обработки.
Для работы с файловой системой применяется модуль
fs/promises, позволяющий использовать промисы вместо
колбэков:
import fs from 'fs/promises';
import path from 'path';
import {remark} from 'remark';
import remarkHtml from 'remark-html';
const inputFilePath = path.join(process.cwd(), 'example.md');
const outputFilePath = path.join(process.cwd(), 'example.html');
async function processFile() {
const markdown = await fs.readFile(inputFilePath, 'utf-8');
const processed = await remark().use(remarkHtml).process(markdown);
await fs.writeFile(outputFilePath, processed.toString(), 'utf-8');
}
processFile();
Ключевые моменты:
fs.readFile и fs.writeFile используются
для асинхронного чтения и записи файлов.remark().use(remarkHtml).process преобразует Markdown в
HTML через AST.path.join обеспечивает корректную работу с путями на
разных ОС.Для обработки сразу нескольких файлов удобно использовать рекурсивный
обход каталогов. В Node.js можно применить fs.readdir с
опцией { withFileTypes: true }:
async function getMarkdownFiles(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
files.push(...await getMarkdownFiles(fullPath));
} else if (entry.isFile() && entry.name.endsWith('.md')) {
files.push(fullPath);
}
}
return files;
}
Особенности:
entry.isDirectory() позволяет рекурсивно
обойти подкаталоги..md выделяет только
Markdown-файлы.Remark и Rehype используют систему плагинов для анализа, модификации и генерации контента. Плагины могут обрабатывать как синтаксис Markdown, так и дерево HTML:
import remarkSlug from 'remark-slug';
import remarkAutolinkHeadings from 'remark-autolink-headings';
import rehypePrettyCode from 'rehype-pretty-code';
async function transformMarkdown(markdown) {
const result = await remark()
.use(remarkSlug) // Добавляет идентификаторы к заголовкам
.use(remarkAutolinkHeadings, {beh * avior: 'wrap'}) // Оборачивает заголовки ссылками
.use(require('remark-rehype')) // Преобразует AST Markdown в HTML AST
.use(rehypePrettyCode, {theme: 'one-dark'}) // Подсветка кода
.process(markdown);
return result.toString();
}
Ключевые аспекты:
remark-rehype выполняет мост между Markdown AST и HTML
AST, что позволяет применять Rehype-плагины.process позволяет использовать
плагины с промисами.Объединение обхода файлов и трансформации позволяет создавать полноценные конвейеры:
async function buildSite(contentDir, outputDir) {
const files = await getMarkdownFiles(contentDir);
for (const file of files) {
const markdown = await fs.readFile(file, 'utf-8');
const html = await transformMarkdown(markdown);
const relativePath = path.relative(contentDir, file);
const outputPath = path.join(outputDir, relativePath.replace(/\.md$/, '.html'));
await fs.mkdir(path.dirname(outputPath), { recursive: true });
await fs.writeFile(outputPath, html, 'utf-8');
}
}
Особенности:
path.relative и replace помогают сохранить
структуру каталогов при генерации HTML.fs.mkdir с { recursive: true } создаёт
нужные подкаталоги автоматически.Для больших сайтов целесообразно использовать потоковую обработку файлов с асинхронными плагинами. Remark поддерживает плагины, возвращающие промисы:
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
const asyncPlugin = () => async (tree) => {
// Пример асинхронной обработки узлов AST
for (const node of tree.children) {
if (node.type === 'code') {
node.value = await fetchSyntaxHighlight(node.value);
}
}
};
await remark()
.use(remarkParse)
.use(asyncPlugin)
.use(remarkStringify)
.process(markdown);
Особенности:
Markdown-файлы часто содержат фронтматтер YAML для метаданных. Remark поддерживает плагины для его парсинга:
import remarkFrontmatter from 'remark-frontmatter';
import yaml from 'js-yaml';
const metadataPlugin = () => (tree, file) => {
const fmNode = tree.children.find(node => node.type === 'yaml');
if (fmNode) {
file.data.meta = yaml.load(fmNode.value);
}
};
await remark()
.use(remarkFrontmatter, ['yaml'])
.use(metadataPlugin)
.process(markdown);
Особенности:
file.data используется для хранения информации вне
основного AST.Для крупных проектов интеграция с файловой системой обычно строится по следующей схеме:
Эта архитектура позволяет строить сложные статические сайты, документацию, блог-платформы и системы генерации контента с полной интеграцией Remark и Rehype с файловой системой.