Build Pipeline
How Blog-Doc's static site generator works — pre-flight checks, every build step, and the output structure.
The build engine lives in admin/functions/build.js. It is a
pure, idempotent function — every run wipes _site/ and
recreates it from scratch. Running a build twice in a row produces identical output.
Triggering a build
From the admin: Click Build Site on the Dashboard.
Via API: POST /admin/api/build
Download the output: Click Download Site on the Dashboard,
or GET /admin/api/build/download. This streams _site/ as a ZIP
named blog-doc-site-YYYY-MM-DD.zip.
Pre-flight check
Before _site/ is touched, the build verifies the active theme contains both
required templates:
templates/home.htmltemplates/404.html
If either is missing, the build throws immediately and _site/ is left
untouched. This prevents a half-built output directory.
Build steps
The build runs these steps in order:
| Step | What happens |
|---|---|
| 1 |
Load settings.json, menus.json,
active-theme.json
|
| 2 |
Pre-flight: verify home.html and 404.html exist in the
active theme
|
| 3 | Initialise the LiteNode renderer |
| 4 | Wipe _site/ entirely and recreate it |
| 5 | Read all published pages from app/content/pages/ |
| 6 |
Read all published posts from app/content/posts/, sorted
newest-first
|
| 7 |
Render each page with page.html →
_site/{url}/index.html
|
| 8 |
Render each post with post.html →
_site/blog/{slug}/index.html
|
| 9 |
Render paginated blog index with blog.html →
_site/blog/index.html, _site/blog/page/{n}/index.html
|
| 10 | Render 404.html → _site/404.html |
| 11 |
Render taxonomy pages (categories + tags) if taxonomy.html exists
|
| 12 | Render home.html → _site/index.html |
| 13 |
Rewrite live-server paths in all rendered .html files (/app/themes/{id}/assets
→ /assets, /images/ → /images/)
|
| 14 | Copy theme assets → _site/assets/ |
| 15 | Copy uploaded images → _site/images/ |
| 16 | Generate _site/sitemap.xml |
| 17 | Generate _site/rss.xml |
| 18 |
Generate _site/robots.txt and _site/data/search.json
|
Steps 7–12 skip draft content silently.
Optional: prettier — if installed, formats static build output. Not required; the build works identically without it (see below).
Output structure
_site/
├── index.html
├── 404.html
├── blog/
│ ├── index.html
│ ├── page/2/index.html
│ └── {slug}/index.html
├── categories/
│ └── {category}/
│ ├── index.html
│ └── page/2/index.html
├── tags/
│ └── {tag}/
│ ├── index.html
│ └── page/2/index.html
├── pages/{slug}/index.html # Non-root depth-1 pages
├── {slug}/index.html # Root pages (root: true)
├── {slug}/{child}/index.html # Depth-2 pages
├── {slug}/{child}/{grandchild}/index.html # Depth-3 pages
├── assets/
├── images/
├── data/
│ └── search.json
├── sitemap.xml
├── rss.xml
└── robots.txt
Optional output formatting
If Prettier is installed in the project (npm install --save-dev prettier
or npm i -D prettier), the build automatically formats generated HTML and any
theme/asset files Prettier can parse (CSS, JS, JSON, etc.) as part of copying them into
_site/. Files .prettierignore excludes, or that Prettier has no
parser for (images, fonts, binaries), are copied unchanged.
Prettier is not a dependency of Blog-Doc — it's entirely optional. If it isn't installed, the build runs exactly as before: output is unformatted, and a one-time warning is logged.
Two starter config files are included at the project root as a starting point:
-
.prettierrc— matches Blog-Doc's own code style (4-space indent, no semicolons, double quotes) -
.prettierignore— excludes already-minified assets (**/*.min.js,**/*.min.css,**/*.min.js.map,**/*.css.map) so themes shipping minified files aren't reformatted
Both files are inert unless Prettier is installed — they're only read by Prettier itself, never by Blog-Doc's own code.
Generated files
sitemap.xml — Standard XML sitemap with priority values:
Homepage 1.0, Blog index 0.9, Pages 0.8, Posts
0.7 (uses post date as lastmod).
rss.xml — RSS 2.0 feed of the 20 most recent published posts.
Includes title, link, description,
pubDate, and guid per item.
robots.txt — Allows all crawlers and includes the sitemap URL.
data/search.json — One entry per published page and post. Same
file served at /data/search.json during live development. The fetch URL is
identical in both environments.
isGenerateStatic
When the build engine renders a template, isGenerateStatic is
true. During live development it is false. Use it to conditionally
disable live-only features:
{{#if isGenerateStatic}}
<!-- nothing — static output needs no dev toolbar -->
{{#else}}
<script src="/admin/assets/js/live-reload.js"></script>
{{/if}}
Error handling
The build engine collects per-item errors without aborting. If page.html is
missing and 3 pages fail to render, the build continues and reports all 3 errors in the
errors array of the response. The HTTP status is 200 on full
success, 500 if any errors occurred.
The stats object always reflects what actually completed, not what was
attempted.
Deploying to Cloudflare Pages
Blog-Doc builds are static — they deploy cleanly to Cloudflare Pages, Netlify, GitHub Pages, or any static host.
For Cloudflare Pages, add a _redirects file to your theme's assets:
/* /index.html 200
This ensures client-side routing works correctly for clean URL paths.