Skip to content

vitepress-plugin-single-page

Build-time Vite-плагин: для каждой версии документации генерирует single.md - все главы версии, склеенные в одну предрендеренную страницу. Зачем:

  • Ctrl+F по всему документу - мгновенно, без N клиентских запросов.
  • Pagefind (или другой статический поиск) индексирует версию как единый документ.
  • Открытие - обычная статическая HTML-страница: ни iframe, ни JS-водопада.

Установка

bash
npm install -D @ampernic/vitepress-plugin-single-page

Использование

ts
// .vitepress/config.ts
import { defineConfig } from 'vitepress'
import { SinglePagePlugin } from '@ampernic/vitepress-plugin-single-page'

export default defineConfig({
  vite: {
    plugins: [
      SinglePagePlugin({ distroName: 'alt-server' }),
    ],
  },
})

Каждая директория версии под docs/<locale>/ получает single.md, который VitePress собирает в маршрут /<version>/single.

Опции

ОпцияТипПо умолчаниюОписание
srcDirstring'docs'Исходная директория VitePress
localestring'ru'Папка локали внутри srcDir
distroNamestring-Slug; нужен для поиска .vitepress/sidebars/<distro>/<v>.ts
versionPatternRegExp/^(\d+\.\d+(\.\d+)*|p\d+)$/Какие директории считать версиями
pageNamestring'single'Имя генерируемого файла
titleTemplatestring'{distro} {version} - на одной странице'Шаблон заголовка ({distro} и {version} подставляются). Если у книги-корня есть свой title, используется '{book} - на одной странице'
frontmatterobject-Frontmatter, добавляемый к каждой странице (мержится поверх дефолтов outline/prev/next/breadcrumbs: false)
dropInitialTOCbooleanfalseСрезать висячий ## Содержание (заголовок и его тело) из первой главы: single-страница сама и есть документ, TOC вверху дублирует контент
initialTOCHeadingsstring[]['Содержание', 'Table of Contents']Тексты, по которым dropInitialTOC ищет заголовок для среза (без учёта регистра)
injectTOCbooleanfalseВставить компонент <ADBookTOC /> в начало склеенной страницы. Позволяет держать статический TOC на multi-page маршруте отдельно от single-page
tocComponentstring'ADBookTOC'Имя компонента для injectTOC
sidebarOrder(srcDir, version, options) => string[]встроенныйСвоя функция порядка глав
debugbooleanfalseПодробный лог: обнаруженные версии, склеенные главы, источник заголовка. При использовании темы включается через createSharedConfig({ debug: true })

Как работает

  1. На configResolved (до того как VitePress сканирует дерево) плагин обходит директории версий.
  2. Для каждой версии читает порядок глав из .vitepress/sidebars/.../<v>.ts (или из sidebarOrder).
  3. Склеивает index.md версии и всех глав, переписывая относительные пути ассетов так, чтобы они резолвились от single.md (Vite дедуплицирует ассеты по контент-хэшу).
  4. Пишет single.md в дерево до сканера VitePress - страница появляется как обычный маршрут.

Каждой главе присваивается стабильный якорь sp-<slug> (см. singleAnchorId), на который ссылается клиентский ребайнд сайдбара в теме.

Известные ограничения / edge cases

  • Юникод в путях: слаги глав нормализуются через [^\p{L}\p{N}_-]+ (не через \w, который в JS охватывает только латиницу). Без этого кириллический путь install-packages-advanced/введение/ схлопнулся бы в тот же префикс, что и родительская страница install-packages-advanced/, и якоря {#part-id} дублировались бы между страницами.
  • Дедупликация анкоров: явные {#id} и внутристраничные ссылки ](#id) префиксуются slug'ом главы, чтобы VitePress не падал на «duplicate user-defined id».
  • dropInitialTOC: срезает висячий ## Содержание (заголовок и всё, что до конца документа), если книга-рут уже содержит текстовый TOC. Опция независима от injectTOC, который вставляет <ADBookTOC> в начало склейки.