Build workflow

Reading time 5 min

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.