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.
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
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.