Skip to content

vitepress-plugin-export

Generates documentation export files (PDF, HTML, EPUB) after vitepress build. The plugin walks every version page in the sidebar, assembles a single document with a table of contents and writes the result into the dist directory.

What it does:

  • Generates PDF via Chromium/Chrome with accurate bookmarks (uses named destinations that Chrome writes into the PDF)
  • Generates a standalone HTML with inlined CSS
  • Generates EPUB 3 with a hierarchical table of contents and working internal links
  • Detects versions automatically from sidebar keys (/11.2/, /11.1/ etc.)
  • Writes export-manifest.json for the theme's ExportButton component
  • Serves ready files through the VitePress dev server

Installation

bash
pnpm add -D @ampernic/vitepress-plugin-export

PDF requires Chromium or Chrome installed on the system and puppeteer-core as a peer/dev dependency of the docs project:

bash
pnpm add -D puppeteer-core

jszip, pdf-lib and picocolors ship as regular dependencies of the plugin, no separate install is needed.

Usage

When using @ampernic/vitepress-theme-alt-docs, passing the export option is enough:

ts
// .vitepress/config/index.mts
import { createSharedConfig } from '@ampernic/vitepress-theme-alt-docs/config'
import { sidebar } from './sidebar'

const shared = createSharedConfig({
  distroName: 'alt-kworkstation',
  export: {
    sidebar,
    distroName: 'alt-kworkstation',
    formats: ['pdf', 'html', 'epub'],
    tocDepth: 2,
  },
})

Directly in the VitePress config

ts
// .vitepress/config.mts
import { defineConfig } from 'vitepress'
import { ExportPlugin } from '@ampernic/vitepress-plugin-export'
import { sidebar } from './sidebar'

const exportPlugin = ExportPlugin({
  sidebar,
  distroName: 'alt-kworkstation',
  formats: ['pdf', 'html', 'epub'],
})

export default defineConfig({
  buildEnd: async () => {
    await exportPlugin.buildEnd()
  },
  vite: {
    plugins: [...exportPlugin.vite.plugins],
  },
})

Options

OptionTypeDefaultDescription
sidebarSidebarMulti-VitePress sidebar (required)
distroNamestring-Distribution slug, used in file names
formats('pdf' | 'html' | 'epub')[]['pdf']Formats to generate
versionsstring[]autoVersions; when omitted, taken from sidebar keys
outDirstring.vitepress/distBuilt site directory
basestringfrom ViteSite base URL
executablePathstringfrom env/systemPath to Chromium/Chrome (for PDF)
skipbooleanfalseSkip generation (handy in dev)
tocDepthnumber2Maximum depth of the text table of contents
outlineDepthnumbertocDepth + 1Depth of bookmarks / EPUB nav, independent of tocDepth. Lets you keep a compact table of contents while bookmarks go one level deeper
waitUntilstring'load'Puppeteer waitUntil (for PDF)
pagesLimitnumber-Page limit per version (for debugging)
concurrencynumber6How many versions to build in parallel (formats within a version also run at the same time)
shareBrowserbooleantrueUse a shared Chromium pool instead of spawning a process per PDF. Only disable (false) on very heavy builds
chromiumPoolSizenumbermin(3, concurrency)How many Chromium instances to keep in the pool; pages of the same slot share a browser, different slots use different browsers
logoLinkstring-URL wrapped around the PDF/HTML cover logo. When using createSharedConfig, it is auto-filled from productLinks[distroName]
debugbooleanfalseVerbose log

File naming

Files are written to the root of outDir:

  • {distroName}-{version}.pdf, e.g. alt-kworkstation-11.2.pdf
  • {distroName}-{version}.html, e.g. alt-kworkstation-11.2.html
  • {distroName}-{version}.epub, e.g. alt-kworkstation-11.2.epub

When distroName is missing: export-{version}.pdf etc.

Download button component

The plugin ships a Vue component ExportButton that reads export-manifest.json and renders a dropdown for downloads. In vitepress-theme-alt-docs it is wired into the aside-outline-after slot automatically.

Manual wiring:

ts
// .vitepress/theme/index.ts
import { h } from 'vue'
import DefaultTheme from 'vitepress/theme'
import ExportButton from '@ampernic/vitepress-plugin-export/components/ExportButton.vue'

export default {
  extends: DefaultTheme,
  Layout() {
    return h(DefaultTheme.Layout, null, {
      'aside-outline-after': () => h(ExportButton),
    })
  },
}

How PDF works

  1. All pages of a version are merged into a single HTML document with a table of contents
  2. The document opens in Chromium via Puppeteer and prints to PDF
  3. After generation, GoTo annotations (named destinations) that Chrome wrote for the TOC links are read back from the PDF
  4. Those annotations are turned into a hierarchical bookmark tree (outline) and injected into the PDF with pdf-lib

The PDF/HTML cover block looks for a branding/{distroName}/logo-col.svg file in the built dist (VitePress copies docs/public/branding/... as-is). When present, it is inserted at the top of the cover before the h1. When absent, the cover has only the title. For raster logos, wrap a PNG in an SVG container with <image href="data:image/png;base64,...">. That same file keeps the .svg extension and is picked up without touching the plugin.

Part-landing TOC dedup

A part page whose only content is the auto-generated table of contents (the converter emits it for part containers where inline <chapter> sections now live as separate pages) already contains <h2>Contents</h2> with the chapter list. injectPartToc checks for that heading in the HTML and does NOT add a second block, otherwise the PDF/EPUB would render two contents in a row.

Chromium

The plugin looks for Chromium in this order:

  1. executablePath option
  2. PUPPETEER_EXECUTABLE_PATH env
  3. Standard system paths (/usr/bin/chromium-browser, /usr/bin/google-chrome, ...)