@pagenary/publisher
v2026.6.0
Published
Multi-tenant static publishing component for Pagenary platform.
Downloads
1,445
Readme
Pagenary Publisher
Where documentation takes shape.
@pagenary/publisher is the static site generator behind Pagenary — it turns one shared template catalog into many branded, tenant-specific documentation sites. Zero runtime dependencies, hash-based routing, full-text search, and a Git-aware build pipeline. Install it as a dev dependency and drive it with the pagenary CLI.
npm install --save-dev @pagenary/publisher # add Pagenary to your project
npx pagenary build:tenants my-docs # build your docs tenant
npx pagenary serve # serve on http://localhost:5173Docs Site · Quick Start · Features · Tenant Workflow · Documentation
What It Is
The publisher takes a catalog of shared section templates plus per-tenant content and configuration and produces a self-contained documentation bundle for each tenant. Each bundle is a static single-page app — hash-based routing (#/page-id), no server-side rendering, no runtime dependencies — that you build once and host anywhere that serves files. Tenants share the template catalog but keep isolated content, branding, navigation, and domains, so one repository can publish a dozen distinct sites.
Quick Start
Install the package and drive it with the pagenary CLI — no clone required.
New here? Follow the Getting Started guide.
npm install --save-dev @pagenary/publisher
npx pagenary build:tenants my-docs # build your tenant (see Tenant Registry below)
npx pagenary serve # preview on http://localhost:5173Commands: build, build:tenants [id], tenants:list, serve (run
npx pagenary --help). The package also ships a compiled reference site under site/ — the Pagenary docs, built by Pagenary itself.
Building from source (contributors / modifying Pagenary):
npm install
npm run dev # build + serve with watch mode
npm run build # build default bundle to dist/Features
Content Authoring
- Markdown — write in
.mdfiles with full CommonMark support - HTML — direct markup control with
.htmlfiles - JavaScript Modules — dynamic content with
.jsfiles returning{ html, afterRender? } - Nested Directories — organize content in subdirectories (
content/guides/setup.md)
Rich Content
- Mermaid Diagrams — flowcharts, sequence diagrams, state machines, and more
- Syntax Highlighting — Prism.js with 10+ language support
- Markdown Tables — full table syntax with alignment support
- HTML Components — spec tables, layer stacks, box diagrams, cards
- Internal Links — auto-resolved
#section-idlinks in Markdown
External Links
- Navigation Links — add external URLs directly in the manifest with a
urlproperty - Smart Link Handling — external links open in a new tab with security headers (
rel="noopener noreferrer") - Visual Indicators — a subtle ↗ icon marks external destinations
- CTA Styling — button-like
external-ctaclass for prominent external links
Navigation & Search
- Command Palette —
Ctrl/Cmd+Kor/opens a global finder - Full-Text Search — searches all content, not just titles
- Manifest-Driven Nav — declarative navigation structure
- Keyboard Navigation — arrow keys, Enter to select
Theming & Branding
- Custom Colors —
accentColorandsurfaceColorper tenant - Brand Identity — logo text, tagline, copyright
- Typography — IBM Plex Sans/Mono defaults, customizable
SEO (built in)
- Absolute URLs — declare a
domain(orseo.siteUrl) and the sitemap, canonical,og:url, androbotsURLs become fully-qualified - Static snapshots — crawler-friendly
/pages/<id>.htmlfor every section, self-canonical (the SPA hash route isn't crawlable) sitemap.xml,robots.txt,llms.txt— generated automatically- JSON-LD + Open Graph —
TechArticle/BreadcrumbListper page, optional Organization data, andog:image/twitter:imageviaseo.ogImage
Export & Sharing
- Export Options — Current Page or Entire Site
- Branded Exports — tenant logo, brand name, and tagline in the export header
- Document Export — one-click HTML export with a table of contents, print-optimized for PDF
Tenant Content Workflow
Directory Structure
my-tenant/
├── config.json # Branding, theme, and SEO settings
├── manifest.json # Navigation structure (optional)
├── content/ # Content files
│ ├── welcome.md # Root-level content
│ ├── guides/ # Nested directory
│ │ ├── _manifest.json # Section manifest
│ │ ├── getting-started.md
│ │ └── advanced.md
│ └── api/
│ ├── _manifest.json
│ └── reference.md
└── overrides/ # Post-build file replacements (optional)Content Types
Markdown (.md)
# Getting Started
Welcome to the docs. Here's a code example:
\`\`\`javascript
console.log('Hello, Pagenary!');
\`\`\`
And a Mermaid diagram:
\`\`\`mermaid
graph LR
A[Start] --> B[Build]
B --> C[Deploy]
\`\`\`HTML (.html)
<section class="section doc">
<h1>Custom HTML Section</h1>
<table class="spec-table">
<tr><th>Feature</th><th>Status</th></tr>
<tr><td>Search</td><td>Ready</td></tr>
</table>
</section>JavaScript (.js)
export async function load() {
const data = await fetch('/api/metrics.json').then(r => r.json());
return {
html: `<section><h1>Metrics: ${data.count}</h1></section>`,
afterRender(container) {
// DOM manipulation after render
}
};
}Manifest Configuration
Root manifest.json (optional — auto-generated from content/ if omitted):
[
{ "id": "welcome", "title": "Welcome", "file": "welcome.md" },
{
"id": "guides",
"title": "Guides",
"subsections": [
{ "id": "guides/getting-started", "title": "Getting Started", "file": "guides/getting-started.md" },
{ "id": "guides/advanced", "title": "Advanced Usage", "file": "guides/advanced.md" }
]
}
]Section _manifest.json (in content subdirectories):
{
"title": "API Reference",
"sections": [
{ "id": "overview", "title": "Overview", "file": "overview.md" },
{ "id": "endpoints", "title": "Endpoints", "file": "endpoints.md" }
]
}External links in the manifest (use url instead of file):
[
{ "id": "welcome", "title": "Welcome", "file": "welcome.md" },
{ "title": "Support Portal", "url": "https://support.example.com" }
]Branding & SEO Configuration
config.json:
{
"title": "My Documentation",
"description": "Comprehensive guide to our platform",
"brandMark": "ACME",
"brandSub": "Docs",
"tagline": "Build better, faster",
"copyright": "ACME Corp",
"accentColor": "#6366F1",
"surfaceColor": "#F7FAFC",
"domain": "docs.acme.com",
"seo": {
"siteUrl": "https://docs.acme.com",
"ogImage": "/assets/og-card.png",
"structuredData": { "organizationName": "ACME Corporation" }
}
}| Property | Description | Default |
|----------|-------------|---------|
| title | Browser tab title | "Documentation" |
| description | Meta description for SEO | - |
| brandMark | Primary brand text (bold) | "DOCS" |
| brandSub | Secondary brand text (light) | "TOOLKIT" |
| tagline | Subtitle under brand | - |
| copyright | Footer copyright text | - |
| accentColor | Links, buttons, highlights | #111111 |
| surfaceColor | Background color (hex) | #ffffff |
| domain | Canonical domain; also the SEO base URL when seo.siteUrl is unset | - |
| seo | SEO block — see Tenant Configuration | - |
Build Commands
With the package installed (the default):
npx pagenary build # build the default bundle to dist/
npx pagenary build:tenants # build all enabled tenants
npx pagenary build:tenants my-tenant # build a specific tenant
npx pagenary tenants:list # list configured tenants
npx pagenary serve # serve dist/ on localhost:5173From source (clone — adds dev/utility scripts):
npm run build:incremental my-tenant # git-aware incremental rebuild
npm run dev # build + serve with watch
npm run lint:content # check trailing whitespace/tabs
npm run check:seo # verify SEO metadata
npm run check # run all checks
npm test # run test suiteTenant Registry
Register tenants in a tenants.json at your project root (validated by the
bundled tenants.schema.json):
{
"tenants": [
{
"id": "my-docs",
"source": { "type": "local", "path": "./docs" },
"strictLinks": true
},
{
"id": "client-portal",
"source": { "type": "git", "url": "https://github.com/org/client-docs.git", "ref": "main" },
"domains": ["docs.client.com"]
}
]
}Source types:
- Local:
{ "type": "local", "path": "./relative/or/abs/path" } - Git:
{ "type": "git", "url": "https://…", "ref": "main", "path": "subdir" }
Per-tenant options include enabled (default true), strictLinks (default
true — fail the build on broken internal links), and domains (for the
multi-tenant Caddy router). See Tenant Configuration.
Docker Caddy Workflow
For multi-tenant domain testing:
# Add to /etc/hosts:
# 127.0.0.1 my-docs.local client-portal.local
npm run build:tenants # build tenants
npm run caddy:up # start Caddy
# Visit http://my-docs.local or http://client-portal.local
npm run caddy:logs # tail logs
npm run caddy:reload # reload config without restart
npm run caddy:restart # full restart
npm run caddy:down # stop containerUse a non-privileged port: DOCS_TOOLKIT_PORT=5173 npm run caddy:up
Repository Layout
apps/publisher/
├── src/
│ ├── index.html # SPA shell
│ ├── app.js # Router and core logic
│ ├── styles.css # All styling
│ ├── manifest.js # Default navigation
│ ├── seo.js # Runtime meta tag management
│ ├── mermaid-init.js # Diagram rendering
│ ├── syntax-highlight.js # Code highlighting
│ └── lib/ # search, router, export
├── scripts/
│ ├── build.js # Core build script
│ ├── build-tenants.js # Multi-tenant builder
│ ├── serve.js # Dev server
│ └── lib/seo-generator.js # Sitemap, robots, snapshots, JSON-LD
├── tenants/ # Built-in example tenants
├── docs/ # Documentation
└── Caddyfile, docker-compose.yml # Multi-tenant routingDocumentation
The full documentation site is published at docs.pagenary.com — built by this publisher from the source below. Read it online, or browse the source:
- Getting Started — start here: zero to a published site with the npm package
- Quick Start Guide — step-by-step tenant creation
- Tenant Configuration — all config options (branding, theme, SEO)
- Architecture — system design
- API Reference — module documentation
- Deployment — hosting patterns
- Extending — customization guide
License
GNU Affero General Public License v3.0 — strong copyleft. You may use, modify, and distribute Pagenary, but if you run a modified version to provide a network service, you must make the modified source available to its users. See LICENSE.
Made with care by Joseph Magly
