Tools and Scripts
One-line-per-tool reference for every executable in the documentation repository: the Windows batch wrappers under docs/, the cross-platform Node and Python scripts under scripts/, the tbdocs orchestrator and its CLI flags, and the PDF render driver. If you are looking for the day-to-day workflow rather than a cheat sheet, the Building and Deployment page is the gentler read; if you are modifying the build pipeline itself, the tbdocs Internals page goes one level deeper.
Batch wrappers under docs/
Each batch file uses @pushd "%~dp0" to run from the repository root regardless of where it is invoked from. POSIX equivalents are listed in the per-batch entry below.
build.bat
build.bat [extra tbdocs flags]
Renders the documentation. Wraps node builder\tbdocs.mjs --src docs and forwards extra arguments through %*. Produces _site/, _site-offline/, and _site-pdf/, modulo the --no-offline / --no-pdf flags and the also_build_offline / also_build_pdf keys in _config.yml. Build time on the current tree is ~3 seconds end-to-end.
serve.bat
serve.bat
Starts a long-lived dev process. Wraps node builder\tbdocs.mjs --src docs --serve and forwards extra arguments through %*. After an initial build, an HTTP server binds to port 4000 (pass --port <N> to use a different port), a recursive source-tree watcher fires a debounced rebuild on each change, and a browser connected to the page auto-reloads via SSE on each successful rebuild. Offline and PDF passes are skipped each rebuild. Ctrl+C exits cleanly. Only failures (4xx, 5xx, server exceptions) are logged --- successful requests are silent.
check.bat
check.bat
Runs scripts/check_links.mjs against the rendered _site/ and _site-offline/ trees in two parallel passes. The offline pass also runs --forbid "https://docs.twinbasic.com" to flag any surviving live-site link the offline rewrite missed. Both passes assert link integrity, HTML well-formedness, duplicate-id detection, anchor resolution, and accessibility hints; the online pass additionally checks sitemap.xml and the search index. Requires build.bat to have run first.
book.bat
book.bat
Renders the PDF book from docs\_site-pdf\book.html into docs\_pdf\twinBASIC Book.pdf. Calls node book\render-book.mjs (see below). Requires build.bat to have populated _site-pdf/ and a Chromium install from npx puppeteer browsers install chrome. The first invocation auto-runs npm install if puppeteer is missing. The output filename is set by the -o argument here; to rename the PDF, update it in book.bat and in .github/workflows/jekyll-gh-pages.yml.
CLI tools
tbdocs --- node builder/tbdocs.mjs
Entry point for the static site generator. build.bat invokes it as node builder\tbdocs.mjs --src docs; CI invokes it the same way.
Full invocation:
node builder/tbdocs.mjs [--src <path>] [--dest <path>]
[--baseurl <prefix>] [--url <origin>]
[--dry-run]
[--no-offline] [--no-pdf] [--tolerate-missing-images]
[--profile-offline]
[--serve] [--port <N>]
| Flag | Effect |
|---|---|
--src <path> | Source root. Default: docs relative to the working directory. |
--dest <path> | Online-tree destination. Default: <src>/_site. The offline tree lands at <dest>-offline, the PDF tree at <dest>-pdf. |
--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 actual deployment origin rather than the configured production host. |
--dry-run | Skip every filesystem write. Useful for benchmarking or validating discovery / compute / render. |
--no-offline | Skip the offline tree pass. |
--no-pdf | Skip the PDF tree pass. |
--tolerate-missing-images | Downgrade Phase 8's missing-image error to a warning. Use when the source tree is mid-edit and may temporarily reference an image that does not yet exist. |
--profile-offline | Print per-substep timing for the offline tree pass. |
--serve | Start the long-lived dev server (watch + rebuild + SSE live-reload). Offline and PDF passes are skipped each rebuild. |
--port <N> | HTTP port for --serve mode. Default: 4000. |
scripts/check_links.mjs
node scripts/check_links.mjs [pass-args...] [/sep/ [pass-args...] ...]
Offline (filesystem-only) link checker plus optional integrity checks. Multiple /sep/-separated passes run in parallel through worker_threads. The relevant flags:
| Flag | Effect |
|---|---|
--offline | Required. Online (network) link checking is not implemented. |
--root-dir <path> | Filesystem root to resolve root-absolute URLs against. |
--fallback-extensions <list> | Comma-separated list of extensions to append when a link target does not exist as-is. Use html to mirror GitHub Pages' extensionless-URL behaviour. |
--index-files <list> | Comma-separated list of filenames to try when a URL resolves to a directory. Use 'index.html,.' to also accept the directory itself as a valid target. |
--base-path <prefix> | Strip this prefix from root-absolute URLs before resolving. Used in CI when --baseurl is set. |
--include-fragments | Resolve #fragment anchors against the target page's IDs. |
--forbid <prefix> | Repeatable. Fail the run if any extracted link starts with prefix. Used by the offline pass to catch live-site links the offlinify rewrite missed (the bare prefix and prefix/ are exempt). |
--check-html | Assert HTML well-formedness. |
--check-a11y | Surface accessibility hints (missing alt, etc.). |
--check-ids | Flag duplicate id attributes within a page. |
--check-sitemap | Assert sitemap.xml covers every page. |
--check-search | Assert search-index entries resolve to existing pages. |
--check-canonical | Assert each page's canonical URL matches its location. |
--no-fail | Downgrade failures to informational output (exit 0 even with broken links). |
Exit code 1 indicates broken links; exit code 2 indicates integrity-only failures (the integrity checks share the same SAX parse pass as link extraction). The script dedupes (target, fragment) so each unique filesystem check fires exactly once regardless of how many pages link to the same target --- on the current tree (~733k link occurrences, ~12k unique targets across 1,127 HTML files / 124 MB) each pass runs in ~2.2 seconds on a development box.
scripts/crawl_check.mjs
node scripts/crawl_check.mjs <start-url> [--concurrency N] [--timeout MS] [--skip-external]
Online link crawler for the deployed site. Starts at <start-url>, GETs every same-origin / same-base-path page recursively, extracts links, and verifies that each link responds 2xx (HEAD for cross-origin, GET for same-origin). Exits 0 if all links are reachable, 1 if any are broken. Use it after a manual workflow_dispatch deploy to verify the published site --- check_links.mjs covers the local filesystem; crawl_check.mjs covers the live deployed site.
scripts/convert_em_dash_separators.py
python scripts/convert_em_dash_separators.py
Normalises literal en-dash / em-dash characters in markdown source under docs/ to their kramdown smart-quotes ASCII source form (-- for en-dash, --- for em-dash). The site forbids literal – / — in source --- this is the canonical fixer if any slip back in. Skips fenced code blocks and inline code spans.
book/render-book.mjs
node book/render-book.mjs <input.html> -o <output.pdf> [options]
The PDF renderer that book.bat calls. It is a generic HTML-to-PDF converter: it takes the pre-built _site-pdf/book.html as its sole document input and has no knowledge of _data/book.yml --- all chapter structure, heading levels, and outline entries are already embedded in the HTML by tbdocs Phase 8. Uses puppeteer + paged.js + pdf-lib directly, so it controls pdf-lib's parseSpeed (the default yields the event loop between every 100 objects on load, adding ~32 seconds to a 100-second build for no reason in Node --- see perf/README.md for the diagnosis). Replaces an earlier npx pagedjs-cli ... invocation.
Key options used by book.bat:
| Flag | Effect |
|---|---|
-o <output.pdf> | Output PDF path. |
--outline-tags h1,h2,h3,h4 | Heading levels to include in the PDF outline / bookmarks. |
--additional-script <path> | Path to a script injected before paged.js runs. book.bat passes perf\detach-pages.js, which hides each finalised page from Chromium's layout tree and restores them all before page.pdf() runs, dropping render time from ~104s to ~51s on the 1,638-page book by sidestepping paged.js's quadratic overflow walker. |
Configuration files
The build pipeline also reads a handful of declarative files. They are not executable but the build's behaviour depends on them.
| File | Effect |
|---|---|
docs/_config.yml | Site config. tbdocs reads url, baseurl, title, logo, also_build_offline, also_build_pdf, offline_exclude, exclude, the footer / aux-link knobs, the GitHub edit-link knobs, and the offline-download-link knobs. Jekyll-only keys (markdown, kramdown, theme, highlighter, the defaults block, the compress_html block) are ignored. |
docs/_book.yml | The PDF book's chapter manifest. Entries are resolved to pages via the selector schema (page / pages / nav_page / nav_pages / no_descent) and control PDF outline behaviour via landing_page:, landing_is_target:, no_outline_entry:, no_heading_shift:, and outline_closed:. Full schema is documented in the file header. Phase 2 resolves chapter arrays; Phase 8 assembles book.html. |
builder/themes/Light.theme, Dark.theme, Classic.theme | twinBASIC IDE theme files, vendored from the BETA installer. builder/highlight-theme.mjs parses them into a Symbol-keyed palette that drives both the renderer's scope-to-class mapping and the generated tb-highlight.css. Refresh from the installer when the IDE adds new palette entries. |
builder/twinbasic.tmLanguage.json | TextMate grammar for the twinBASIC language. Shiki uses it to tokenise every ```vb code block. |