Build workflow
The builder converts Markdown, templates, configuration and public assets into a generated reader. It is a command-line process, not a host, review system or remote deployment. The optional editor’s Publish Changes uses this builder through a separate staged workflow.
Run from the intended V2 checkout with Node.js 24+ and locked dependencies. Check your working directory, git rev-parse HEAD and node --version first. Use new dedicated output paths for experiments, never source, account data, backups or a live web root.
Normal writing cycle
npm ci
npm test
npm run build
npm run serve
Let install, tests and build finish before starting preview. Preview prints a loopback address, normally http://127.0.0.1:3000, and remains running until Ctrl+C. Open it on that machine. Inspect home, a deep page, heading search, code copy, theme, diagrams and media. A command exit is not the whole reader check.
npm ci installs the committed lockfile-defined dependency set. It is not required for every text edit. With unchanged installed dependencies, save the source, build and refresh preview. Do not regenerate browser bundles or upgrade dependencies simply because a paragraph changed.
Exact selectors
| Selector | Scope |
|---|---|
| Content | --content: Markdown tree to read |
| Output | --out: directory to remove/recreate |
| Runtime | --data: settings, links and icons |
| Watch | --watch: initial build and supported watched rebuilds |
Folder operands resolve against the current working directory. A missing operand, or one beginning with --, prints that the flag needs a folder and exits 2. The parser is not a guard against choosing a valuable output path.
npm run build -- --content ./docs-source --out ./review-output --data ./docs-data
Npm forwards flags after --. Equivalent direct invocation:
node build.js --content ./docs-source --out ./review-output --data ./docs-data
Review/create the selected content tree before running this example. Static assets, templates and siteconfig.json still come from beside the installed builder. --content is not a whole-project selector and does not load a config found inside that folder.
| Runtime priority | Selection |
|---|---|
| First | Explicit --data |
| Second | MARKSTACK_DATA_DIR |
| Third | Parent of MARKSTACK_USERS_FILE |
| Last | Installation .markstack |
The editor derives runtime data beside its selected users file. Align those choices when you want the same branding/header links. A build-only selector does not relocate accounts. Wrong data selection can look like ignored settings while source is perfectly valid. See Site settings.
Output deletion and preflight
WARNING Never select source, private runtime, project root, backups or another valuable folder as
--out. A normal build removes its selected output after route preflight.
The builder clears its route map/index, scans title-derived routes and checks collisions before touching old output. A duplicate URL or frontmatter failure during that scan leaves the previous output in place. This is a narrow preflight protection, not a general transactional build guarantee.
After preflight, output is removed/recreated. A later copy, template, render or disk error can leave incomplete files. Building directly over a served directory can create a reader gap. Use isolated output with a deliberate deployment/switch or the administrator publisher when appropriate.
npm run clean deletes default dist/ only; it does not remember a prior --out. Normal builds already clean selected output. Clean does not reset accounts or preserve source history.
Generation sequence
| Step | Result |
|---|---|
| Route scan | Register title-derived paths; refuse collisions |
| Reset output | Remove/recreate selected generated directory |
| Copy assets | Copy installation static files |
| Header links | Read data and copy selected shown icons |
| Render content | YAML/Markdown, navigation, metadata and page furniture |
| Special pages | Home and 404.html |
| Search | Page/heading entries in search-index.json |
Explicit titles, not filenames alone, control normalized folder/page routes. Section title changes can move descendants. Hidden sections still generate/search; dot-prefixed source entries are skipped. Section integer order is not a general ordinary-page sorting system. See Content structure.
A normal page is an index.html under its generated route. Stock build does not automatically generate sitemap, robots, RSS, PDF or redirects. Supplied static files with such names may be copied, but those are your assets, not built-in generators. Review every copied byte’s audience.
The log’s search message says “pages” although its count includes headings. Do not report it as the Markdown page count. Logs show generated routes, header count, elapsed time and output location; success does not verify authored links, resource requests, audience or host policy.
Watch and serve separately
# Terminal one
npm run watch
# Terminal two
npm run serve
Watch performs an initial full build and handles change, add and unlink for content, static assets and templates. It does not serve HTTP or deploy. Wait for rebuild completion, then refresh the served page.
| Changed layer | Required check |
|---|---|
| Markdown, assets, templates | Supported watched event and successful full rebuild |
| Site configuration | New explicit build process or watch restart |
| Settings, links, icons | Explicit build/publication with correct data selection |
| Dependencies and bundles | Separate reviewed maintenance change |
| Hosting | Independent host configuration/deploy test |
Config is loaded before watched rebuilds. An unrelated content event does not prove fresh config was read. Handlers do not establish a complete directory-only add/remove/rename service. Verify the resulting section tree and run a fresh explicit build when uncertain. An initial watch failure still needs inspection of actual process/exit state.
Local preview contract
Bundled preview serves default dist/, not an arbitrary earlier --out. Do not inspect an old default build and call it a check of new review-output. Serve that directory through an appropriate HTTP server or build the intended default preview in a separate checkout.
| Property | Behavior |
|---|---|
| Default | Loopback 127.0.0.1, port 3000 |
| Port selector | Environment value: integer 1-65535 |
| Methods | GET/HEAD; others 405 with Allow header |
| Page path | Directory/extensionless route resolves to index.html |
| Missing page | 404 file with status 404 when available |
| Headers | No-store and nosniff; MIME by extension |
| Boundary | Reject traversal, backslash, NUL and outside-root symlinks |
Set the preview selector with MARKSTACK_PREVIEW_PORT. An invalid port fails. Unix alternate port:
MARKSTACK_PREVIEW_PORT=3100 npm run serve
PowerShell:
$env:MARKSTACK_PREVIEW_PORT = '3100'
npm.cmd run serve
It does not change editor port 3001, add TLS/login or expose a production service. Loopback refers to the machine running preview, not a colleague’s laptop. Use served HTTP rather than file:// so resource/index paths behave correctly.
Diagnose the failed layer
| Symptom | Safe recheck |
|---|---|
| Source missing | Working directory/content selection; do not redirect output into source |
| YAML/collision | Fix named source/title, rebuild and verify links |
| Incomplete output | Preserve source/prior checked reader; fix late failure and rebuild isolated output |
| Preview old | Confirm newly built default directory is the served one |
| Port busy | Inspect listener before stop/new port |
| Search old | Current index built and deployed/served, not just refreshed browser |
| Unexpected public file | Review static input, remove unintended source asset and rebuild |
Record engine, Node, input/config/data/output, test result and browser checks. Follow Review and release. Implementation regression tests do not validate every handbook link, physical browser, external host or workload. Validation limits separates those scopes.