Метаданные и frontmatter

Frontmatter — это специальный блок метаданных, помещаемый в начало Markdown-документа. Обычно он оформляется с использованием YAML (но возможны JSON и TOML) и заключён между тройными дефисами ---. Frontmatter используется для хранения информации о документе: заголовок, автор, дата, категории, теги, ключи конфигурации и любые пользовательские данные.

Пример YAML-frontmatter:

---
title: "Основы Remark и Rehype"
author: "Иван Иванов"
date: 2026-03-22
tags:
  - javascript
  - markdown
  - rehype
draft: false
---

После блока frontmatter идёт сам Markdown-контент, который будет преобразован библиотеками Remark и Rehype.


Обработка frontmatter в Remark

Для работы с frontmatter в Remark используется плагин remark-frontmatter. Он позволяет распознавать YAML, TOML и другие блоки и преобразовывать их в узлы синтаксического дерева (AST).

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkFrontmatter from 'remark-frontmatter';
import yaml from 'js-yaml';

const markdown = `
---
title: "Пример статьи"
date: "2026-03-22"
---

# Заголовок
Контент статьи.
`;

const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml']);

const tree = processor.parse(markdown);

// Пример извлечения YAML
tree.children
  .filter(node => node.type === 'yaml')
  .forEach(node => {
    const data = yaml.load(node.value);
    console.log(data.title); // "Пример статьи"
  });

Ключевые моменты:

  • remarkFrontmatter добавляет узлы типа yaml или toml в AST.
  • Для извлечения данных используется библиотека парсинга YAML (js-yaml) или TOML.
  • Можно комбинировать с другими плагинами для дальнейшей обработки метаданных.

Интеграция frontmatter с Rehype

Remark обрабатывает Markdown в AST MDAST, а Rehype работает с HTML-деревом HAST. Для передачи контента с метаданными используют remark-rehype.

import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';

const html = await unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml'])
  .use(remarkRehype)
  .use(rehypeStringify)
  .process(markdown);

console.log(String(html));

Особенности:

  • Метаданные frontmatter не конвертируются в HTML напрямую. Для их использования необходимо предварительно извлечь их из AST.
  • Можно создать отдельный объект metadata, который передаётся в шаблоны или дальнейшие плагины Rehype.

Создание пользовательских плагинов для обработки frontmatter

Remark позволяет создавать собственные плагины для обработки frontmatter и добавления его в контекст обработки документа.

function remarkExtractFrontmatter() {
  return (tree, file) => {
    const yamlNode = tree.children.find(node => node.type === 'yaml');
    if (yamlNode) {
      const data = yaml.load(yamlNode.value);
      file.data.frontmatter = data;
    }
  };
}

const processor = unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml'])
  .use(remarkExtractFrontmatter)
  .use(remarkRehype)
  .use(rehypeStringify);

const file = await processor.process(markdown);
console.log(file.data.frontmatter.title); // "Пример статьи"

Примечания:

  • file.data — объект, используемый для хранения пользовательских данных, доступных на всех этапах обработки.
  • Можно использовать frontmatter для динамической генерации заголовков, мета-тегов или настроек рендеринга.

Советы по организации метаданных

  1. Стандартизация полей — рекомендуется определять набор ключевых полей (title, date, tags, draft) для унификации обработки.
  2. Проверка типов — можно использовать zod или joi для валидации frontmatter перед дальнейшей обработкой.
  3. Комбинирование с другими плагинами — frontmatter удобно использовать вместе с remark-toc, remark-slug и другими, чтобы динамически создавать оглавление или идентификаторы.
  4. Разделение контента и метаданных — извлечённые frontmatter-данные лучше хранить отдельно от AST Markdown для гибкой интеграции с рендерингом HTML.

Пример полного конвейера

  1. Чтение Markdown с frontmatter.
  2. Извлечение YAML в объект metadata.
  3. Обработка Markdown через Remark и Rehype.
  4. Встраивание HTML-контента и метаданных в шаблон страницы.
import fs from 'fs';
import path from 'path';

const filePath = path.join(process.cwd(), 'article.md');
const markdownContent = fs.readFileSync(filePath, 'utf-8');

const file = await unified()
  .use(remarkParse)
  .use(remarkFrontmatter, ['yaml'])
  .use(remarkExtractFrontmatter)
  .use(remarkRehype)
  .use(rehypeStringify)
  .process(markdownContent);

const htmlContent = String(file);
const metadata = file.data.frontmatter;

console.log({ htmlContent, metadata });

Такой подход позволяет отделить структуру документа и метаданные, что упрощает генерацию статических сайтов, блогов и документации с динамическими заголовками, тегами и настройками рендеринга.