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.jsonfor the theme'sExportButtoncomponent - Serves ready files through the VitePress dev server
Installation
pnpm add -D @ampernic/vitepress-plugin-exportPDF requires Chromium or Chrome installed on the system and puppeteer-core as a peer/dev dependency of the docs project:
pnpm add -D puppeteer-corejszip, pdf-lib and picocolors ship as regular dependencies of the plugin, no separate install is needed.
Usage
Via createSharedConfig (recommended)
When using @ampernic/vitepress-theme-alt-docs, passing the export option is enough:
// .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
// .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
| Option | Type | Default | Description |
|---|---|---|---|
sidebar | SidebarMulti | - | VitePress sidebar (required) |
distroName | string | - | Distribution slug, used in file names |
formats | ('pdf' | 'html' | 'epub')[] | ['pdf'] | Formats to generate |
versions | string[] | auto | Versions; when omitted, taken from sidebar keys |
outDir | string | .vitepress/dist | Built site directory |
base | string | from Vite | Site base URL |
executablePath | string | from env/system | Path to Chromium/Chrome (for PDF) |
skip | boolean | false | Skip generation (handy in dev) |
tocDepth | number | 2 | Maximum depth of the text table of contents |
outlineDepth | number | tocDepth + 1 | Depth of bookmarks / EPUB nav, independent of tocDepth. Lets you keep a compact table of contents while bookmarks go one level deeper |
waitUntil | string | 'load' | Puppeteer waitUntil (for PDF) |
pagesLimit | number | - | Page limit per version (for debugging) |
concurrency | number | 6 | How many versions to build in parallel (formats within a version also run at the same time) |
shareBrowser | boolean | true | Use a shared Chromium pool instead of spawning a process per PDF. Only disable (false) on very heavy builds |
chromiumPoolSize | number | min(3, concurrency) | How many Chromium instances to keep in the pool; pages of the same slot share a browser, different slots use different browsers |
logoLink | string | - | URL wrapped around the PDF/HTML cover logo. When using createSharedConfig, it is auto-filled from productLinks[distroName] |
debug | boolean | false | Verbose 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:
// .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
- All pages of a version are merged into a single HTML document with a table of contents
- The document opens in Chromium via Puppeteer and prints to PDF
- After generation, GoTo annotations (named destinations) that Chrome wrote for the TOC links are read back from the PDF
- Those annotations are turned into a hierarchical bookmark tree (outline) and injected into the PDF with
pdf-lib
Cover and logo
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:
executablePathoptionPUPPETEER_EXECUTABLE_PATHenv- Standard system paths (
/usr/bin/chromium-browser,/usr/bin/google-chrome, ...)