Skip to content

Latest commit

 

History

History

builder

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

tbdocs

Node.js static site generator for docs.twinbasic.com. Replaces the original Jekyll + just-the-docs pipeline (retired in the Phase 10 cutover; the legacy Ruby source set has since been removed from the tree).

Quickstart

Requires Node.js 22+. There is no builder/package.json; the whole dependency set lives in one package.json at the repository root.

npm ci                                    # from the repository root
node builder/tbdocs.mjs --src docs        # -> docs/_site/, _site-offline/, _site-pdf/

The day-to-day drivers are build.bat, serve.bat, check.bat and book.bat, all at the repository root -- they invoke tbdocs with the right arguments and route the browser-dependent gates and the PDF render behind the same wrappers. build.bat passes --check-audit-index, so an ordinary build also link-checks every tree it produced.

CLI flags:

Flag Effect
--src <path> Source root (default docs).
--dest <path> Online tree destination (default <src>/_site). The offline tree lands at <dest>-offline, the PDF tree at <dest>-pdf, each removed instead when --no-offline or --no-pdf skips it. Only the default docs/_site rewrites the committed baselines and the add-in's index copy; another --dest compares and reports. It may not be or contain <src>, and inside <src> it must be, or be inside, a folder directly under it whose name starts with _site, _serve or _pdf; the build refuses any other.
--baseurl <prefix> Overrides _config.yml's baseurl (used by CI to inject the GitHub Pages base path on fork deployments).
--url <origin> Overrides _config.yml's url (used by CI so canonical URLs match the deployment origin).
--dry-run Skip every filesystem write.
--no-offline Skip the offline-tree pass (Phase 7).
--no-pdf Skip the PDF-tree pass (Phase 8).
--tolerate-missing-images Downgrade Phase 8's missing-image error to a warning.
--profile-offline Per-substep timing for Phase 7.
--fetch-assets / --no-fetch-assets Force remote-asset vendoring on or off. Default: download on a dev box, never when $CI is set.
--check / --no-check Run (or skip) the link + integrity check over the HTML the build already holds in memory. Sets the exit code, never aborts the graph.
--check-audit-index Implies --check; also diffs the derived tree index against what landed on disk. What build.bat passes.
--check-findings <path> Implies --check; writes the findings as JSON for scripts/check_links_diff.mjs.
--serve Start the long-lived dev server (watch + rebuild + SSE live-reload).
--port <N> HTTP port for --serve mode (default 4000).

Serve mode

tbdocs --serve starts a long-lived dev process: HTTP server on port 4000 (override with --port), recursive watcher on the source tree, debounced rebuild on changes, and SSE-driven browser auto-reload. The offline and PDF passes are skipped each rebuild (restore them with a non---serve invocation).

node builder/tbdocs.mjs --src docs --serve

Or via the wrapper at the repository root: serve.bat.

Ctrl+C exits cleanly (closes the server, aborts the watcher, drains SSE clients).

Documentation

  • PLAN.md -- architecture overview and the 12-phase pipeline.
  • PLAN-1.md .. PLAN-13.md -- per-phase specs (inputs, outputs, edge cases, acceptance checklists). Phases 1-8 are the build itself; Phase 9 was the QoL consolidation pass; Phase 10 was the Jekyll cutover; Phase 11 is the output-changing parity update; Phase 12 adds --serve watch mode; Phase 13 is build-time SVG inlining with the zoom / export controls.
  • PLAN-sab-pull-scheduler.md -- the shipped scheduler, and maintained reference rather than a frozen design record: pull model, SAB layout, Phases 5-18, and worked examples that are corrected when the code moves -- most recently to the landed registerBarrier form in tbdocs.mjs. It names pipeline tasks throughout; read them as current. PLAN-scheduler.md is the superseded push-based design, and PLAN-scheduler-offline.md covers moving the offline rewrite into the render workers.
  • PLAN-checks.md -- folding the link checker into the build's task graph; Phase A shipped, the axe follow-ons are designed but not implemented.
  • PLAN-a11y.md -- the WCAG 2.2 AA work. PLAN-axe-perf.md -- the axe-core performance investigation, complete.
  • REVIEW-*.md / PLAN-REVIEW-*.md -- frozen audit snapshots of a commit range, not maintained reference documentation.
  • FUTURE-WORK.md -- open follow-ups, grouped by divergence investigations / deferred enhancements.
  • vendor/just-the-docs/README.md -- the vendored theme: what was taken, every in-tree patch to its _sass/ and just-the-docs.js with the commit that made it, and the re-vendoring procedure.

The end-user-facing documentation about how the build pipeline works lives on the site itself under Documentation Development: Tools and Scripts and its seven group pages are the cheat sheet; tbdocs Internals is the architecture overview; Extending the Builder is the how-to for adding a pipeline task, a markdown-it plugin or a render-worker sub-stage.

Build phases

Phase Module(s) Job
1 discover.mjs + publish-policy.mjs Read .md/.html + frontmatter, enumerate static files, refuse any that is not a publishable type
2 nav.mjs / seo.mjs / book.mjs / build-info.mjs / data.mjs Compute nav tree, SEO, book chapters, git commit info, _data/*.yml
3 render.mjs + highlight.mjs + highlight-theme.mjs Markdown -> HTML body
4 template.mjs + compress.mjs Wrap in layout, anchor-heading injection, whitespace compress
5 write.mjs Write _site/
6 redirects.mjs / sitemap.mjs / search.mjs / symbols.mjs + symbol-baseline.mjs + addin-index.mjs Auxiliaries (stubs, sitemap.xml, search-data.json, tB/symbols.json, the drift guard on its URLs and the help add-in's copy of it)
7 offline.mjs Mirror to _site-offline/ with file:// URL rewrites
8 pdf.mjs + book.mjs (renderer half) Sparse _site-pdf/ tree (book.html + CSS + images)

Two seed tasks run alongside discover: dot.mjs regenerates stale *.svg from their .dot siblings via the WASM build of Graphviz (with dot-metrics.mjs installing Inter's real advance widths first, so boxes are sized for the font the browser will paint), and vendor-assets.mjs downloads any referenced-but- uncommitted YouTube poster frame or GitHub user-attachment image -- never under CI, where a missing asset is a hard error instead.

Verification

Two gates abort the build outright, because their failure means the output itself is wrong rather than merely incomplete: nav.mjs's permalink and nav-integrity checks, and publish-policy.mjs, which holds the allowlist of file types that may reach a published tree. Everything under docs/ that is not a page is copied into the output verbatim, so a denylist only refuses what somebody named in advance -- before the allowlist, a stray .bak, .pem, .twin or frontmatter-less .md published at a public URL on a green build. It is enforced over the static-file inventory in discover and over each tree's derived inventory in dispatch, neither behind --check. scripts/check_publish_policy.mjs is the gate on that gate: a clean build says only that nothing is currently refused, which is also what an allowlist widened to nothing would say.

Regression detection runs inside the build. tbdocs --check (which build.bat passes) walks each tree's final HTML on the worker lane that produced it -- offline link check + duplicate-id detection, anchor resolution, remote-asset detection, sitemap, search-index and canonical integrity. A failing check sets the exit code but never aborts the graph. check.bat then runs the gates that need a browser. A clean build.bat && check.bat is the bar for "ready to commit".

test.bat is the other wrapper, and it is the one that judges an edit made here: it runs the gates that test the toolchain rather than the site, so a change under builder/ owes it whether or not any page moved. Two of its gates key on this directory by name. scripts/check_regex_safety.mjs refuses a regex that can backtrack exponentially --- both that shipped lived in render.mjs, and each hung a render worker in silence rather than failing, so the gate asks the question of the pattern instead of waiting for a page to ask it. It reads constructed patterns too, so assembling one from string constants is not an escape. scripts/check_code_regions.mjs refuses a pre-render rewrite that alters the contents of a code fence or code span, which four rewrites in render.mjs have done --- so a new one belongs inside applyPreRenderRewrites, between maskCode and its restore. Its probes also hold the rewrites over rendered HTML, which go through replaceOutsideCode from code-guard.mjs, to leaving a raw <pre> or <code> as written. Tools and Scripts lists every gate in both wrappers, in running order.

The pure core lives in link-check.mjs, and the check over it in check.mjs; check-tree.mjs is the build-side index. scripts/check_links.mjs runs the same check as a standalone tool, for trees this build did not produce. Neither CI workflow invokes it directly any more -- both reach it through check_links_diff.mjs, which runs it in-process against the fixtures. scripts/check_links_diff.mjs is the gate that says the two agree -- run it whenever any of the three changes; test/README.md covers the fixtures that give it something to disagree about, the exact finding counts they must keep provoking, and why a template change can turn the gate red without touching the fixture or the checker. See PLAN-checks.md.

The per-phase verify-phase{1..8}.mjs harnesses and the bulk-triage tools (_triage.mjs, _diff.mjs, etc.) were retired in the Phase 10 cutover; they asserted byte-equivalence with Jekyll, which is no longer the acceptance bar.

Every gate above checks the site. eval/ checks the documentation about the site: it hands an evaluator a goal and a mirror of the repository with all source stubbed unreadable, and asks whether the goal can be reached. That is a different question from whether the prose is accurate, and it catches a class of defect no gate here can see -- a page can be true in every sentence and still send a reader to the wrong remedy. See eval/README.md for how to run a round, and REVIEW-USECASES-874896e.md for what the first one found.