Architecture
High-level overview of how zpress is structured, its design philosophy, and how data flows through the system.
Overview
zpress is a documentation framework for monorepos. It takes a single config file, syncs markdown content into a structured output directory, and builds a static site via Rspress. The information architecture -- sections, navigation, sidebar, landing pages -- is derived entirely from the config.
The codebase follows a functional, immutable, composition-first design. There are no classes, no let, no throw statements, and no loops. Errors are returned as Result tuples. Side effects (process exit, terminal output) are pushed to the outermost edges.
Package Ecosystem
zpress (wrapper)
The public-facing package. Three entry points:
Users import defineConfig from zpress (or zpress/config) in their config file. The CLI bin resolves to zpress/cli.
Layers
CLI Layer
Package: @zpress/cli
The command-line interface. Uses yargs for argument parsing and @clack/prompts for styled terminal output. Commands orchestrate the core sync engine and Rspress build APIs.
Core Layer
Package: @zpress/core
The sync engine and config system. This is where the information architecture is resolved:
UI Layer
Package: @zpress/ui
The Rspress theme and plugin:
Sync Engine
The sync engine is the heart of zpress. It transforms a config file into a complete documentation site structure.
Pipeline
The sync() function in packages/core/src/sync/index.ts runs this pipeline:
- Setup -- Create output directories, seed default assets, load previous manifest
- Workspace synthesis -- Convert
apps/packages/workspacesinto entry sections - Resolve entries -- Walk the config tree, resolve globs, derive text, merge frontmatter
- Enrich cards -- Attach workspace metadata (icon, scope, tags, badge) to matched entries
- Inject landing pages -- Generate virtual MDX pages for sections with children but no page
- Collect pages -- Flatten the resolved tree into a flat page list
- Generate home -- Create default home page from config metadata (when no explicit index.md)
- Copy pages -- Write all pages with injected frontmatter, track SHA256 hashes
- Generate sidebar + nav -- Build multi-sidebar JSON and nav array
- Clean stale files -- Remove files present in old manifest but absent in new
- Save manifest -- Record file hashes for incremental sync on next run
Returns: { pagesWritten, pagesSkipped, pagesRemoved, elapsed }
Entry Resolution
The entry resolver (sync/resolve/index.ts) recursively walks the config tree and resolves each entry:
- Single file -- Source file with explicit link (e.g.,
from: 'docs/getting-started.md') - Virtual page -- Generated content with link (e.g.,
content: () => '# Hello') - Glob section -- Pattern that discovers files (e.g.,
from: 'docs/guides/*.md') - Recursive glob -- Directory-driven nesting (e.g.,
from: 'docs/**/*.md', recursive: true) - Explicit items -- Hand-written child entries
Text derivation is configurable via textFrom:
Incremental Sync
Every page is tracked in a manifest (manifest.json) with its SHA256 content hash. On subsequent syncs, pages with unchanged hashes are skipped. Stale files (present in old manifest but not new) are removed. This enables fast re-syncs during development.
Multi-Sidebar
zpress generates a multi-sidebar structure for Rspress. Root entries share the / namespace. Isolated sections (workspace items, explicit isolated: true) get their own namespace (e.g., /apps/api/). This allows each section to have an independent sidebar tree.
Config System
The config file (zpress.config.ts) is the single source of truth for the entire documentation site:
Config is loaded via c12 and validated at the boundary in defineConfig(). Validation errors exit immediately with a descriptive message.
Output Structure
The sync engine writes everything to .zpress/:
Data Flow
Step-by-step
- Load config -- c12 discovers and parses
zpress.config.ts - Validate --
defineConfig()validates structure, exits on first error - Sync entries -- Resolve globs, derive text, merge frontmatter, deduplicate
- Generate metadata -- Sidebar JSON, nav JSON, workspace data, home page
- Write content -- Copy/generate all pages to
.zpress/content/with frontmatter injection - Build -- Rspress reads the generated content and metadata, renders with zpress theme
- Output -- Static HTML/CSS/JS written to
.zpress/dist/
Error Handling
zpress uses the Result<T, E> tuple pattern for expected failures:
Result tuples are used for operations that can fail (config parsing, file I/O, glob resolution):
Config validation exits immediately in defineConfig() -- config errors are always fatal because nothing can proceed without a valid config.
Design Decisions
- Config is the information architecture -- A single file defines content structure, routing, navigation, and metadata. No separate sidebar/nav config files.
- Factories over classes -- All components are factory functions returning plain objects.
- Result tuples over throw -- Expected failures use
Result<T, E>. No exceptions. - Incremental sync -- SHA256 hashes skip unchanged pages. Manifest comparison removes stale files.
- Virtual pages via MDX -- Landing pages are generated at sync time as MDX with React components.
- Multi-sidebar from config -- Isolated sections get independent sidebar namespaces automatically.
- Glob-driven content discovery -- Patterns auto-discover files without manual entry per page.
- Frontmatter inheritance -- Entries inherit frontmatter from ancestors in the config tree.