Настройка глубины оглавления

Remark и Rehype — это экосистема инструментов для работы с Markdown и HTML в JavaScript, где Remark отвечает за парсинг Markdown в абстрактное синтаксическое дерево (AST), а Rehype — за работу с HTML-представлением. Для генерации оглавления (Table of Contents, TOC) часто используют плагин remark-toc, который строит дерево заголовков документа. Управление глубиной оглавления позволяет указать, какие уровни заголовков включать, обеспечивая читаемость и компактность TOC.


Подключение и базовая настройка

import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkStringify from 'remark-stringify';
import remarkToc from 'remark-toc';
import fs from 'fs';

const markdown = fs.readFileSync('example.md', 'utf8');

const processor = unified()
  .use(remarkParse)
  .use(remarkToc, { heading: 'Содержание', maxDepth: 3 }) // ограничение глубины
  .use(remarkStringify);

processor.process(markdown).then(file => {
  console.log(String(file));
});

Ключевой момент: опция maxDepth определяет максимальный уровень заголовков, которые попадут в TOC. Уровни заголовков в Markdown обозначаются символами #, где # — первый уровень, ## — второй, и так далее. Значение maxDepth: 3 включает заголовки до ###.


Динамическая настройка глубины

Для больших документов может потребоваться вычислять глубину оглавления динамически. Это можно реализовать через функцию:

import remarkToc from 'remark-toc';

const depth = (documentLength) => {
  if (documentLength < 500) return 2;
  if (documentLength < 2000) return 3;
  return 4;
};

const processor = unified()
  .use(remarkParse)
  .use(remarkToc, { heading: 'Содержание', maxDepth: depth(markdown.length) })
  .use(remarkStringify);

Пояснение: функция depth анализирует длину документа и определяет оптимальный уровень вложенности TOC, чтобы не перегружать оглавление.


Взаимодействие с Rehype

После генерации AST Markdown с TOC можно преобразовать документ в HTML с помощью Rehype:

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

const htmlProcessor = unified()
  .use(remarkParse)
  .use(remarkToc, { heading: 'Содержание', maxDepth: 3 })
  .use(remarkRehype)
  .use(rehypeStringify);

htmlProcessor.process(markdown).then(file => {
  console.log(String(file));
});

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

  • remarkRehype конвертирует Markdown AST в HTML AST.
  • Глубина TOC определяется на этапе Remark и сохраняется при переходе в HTML.
  • Для кастомного форматирования HTML можно добавлять плагины Rehype, например rehype-slug для генерации идентификаторов заголовков или rehype-autolink-headings для автоматической привязки ссылок.

Использование кастомных селекторов заголовков

Плагин remark-toc позволяет фильтровать заголовки через опцию filter:

.use(remarkToc, {
  heading: 'Содержание',
  maxDepth: 3,
  filter: (node) => !node.children[0].value.includes('Пропустить')
})

Смысл: заголовки, содержащие определённые слова или метки, исключаются из TOC, что повышает гибкость при построении оглавления для больших учебных материалов.


Стилизация и форматирование TOC в HTML

После конвертации в HTML можно управлять внешним видом TOC через CSS:

.toc {
  list-style: none;
  padding-left: 0;
}

.toc li {
  margin-left: 1em;
}

.toc li li {
  margin-left: 2em;
}

Принцип: вложенность списков в TOC отражает уровни заголовков. CSS позволяет визуально ограничить или расширить эту вложенность без изменения maxDepth в Remark.


Продвинутые техники управления глубиной

  1. Скрытие заголовков по условию: использование плагина unist-util-visit для обхода AST и модификации узлов перед генерацией TOC.
  2. Разделение TOC по разделам: генерация отдельных TOC для разных частей документа с разной глубиной.
  3. Комбинирование с Rehype-плагинами: например, rehype-highlight для подсветки кода в оглавлении или rehype-toc для кастомных HTML-структур.

Эти методы позволяют создавать гибкие, читаемые и масштабируемые оглавления, которые подстраиваются под структуру документа и требования к уровню вложенности.