Build & Deployment Internals
How the three build outputs are produced. If you only want to deploy modernGraphTool, see Guide for Admins → Deployment instead — this page is about how the build itself works.
Outputs
| Command | Output | What it is |
|---|---|---|
npm run build | dist/ | Static SPA, prerendered, SPA fallback via .htaccess |
npm run build:cdn | dist-cdn/ | Thin loader for jsDelivr-hosted assets |
npm run build:site-template | dist-site-template/ | The GitHub Pages template repo |
The CDN build emits a thin cdn-index.html loader plus cdn/loader.js; set MGT_CDN_BASE to
rewrite the _app/ URLs.
The defaults/ mechanism
A Vite plugin serves defaults/ as a fallback during development and copies its contents into
dist/ at build time — without overwriting files that already exist under static/. That is how
an operator's local customizations survive an upgrade.
Files an operator is expected to edit in dist/: config.js, theme.css, data/,
assets/strings/.
Never import from static/ or defaults/ as modules. fetch() them at runtime. They are
operator-editable deployment artifacts, not build inputs — importing one bakes a copy into the bundle
and the operator's edit silently stops taking effect.
GitHub Pages template
potatosalad775/modernGraphTool_site is a separate repository that operators copy with "Use this template".
It is generated output, not a hand-maintained repo. npm run build:site-template assembles it,
and .github/workflows/sync-site-template.yml force-syncs the result on every push to main that
touches defaults/, site-template/, or the script. Edit the sources in the main repo — anything
committed straight to the template repo is overwritten on the next sync.
- Most of it is
defaults/verbatim (theme.css,data/,assets/), which is the whole reason the sync exists — that content drifts the momentdefaults/changes. config.jsisdefaults/config.jswith the commentedCDN_MODEstub spliced out andsite-template/config-cdn-mode.jsspliced in, so new config options reach the template for free. The splice is anchored on the stub's first and last lines and throws if it stops matching — a template with no liveCDN_MODEis a blank page, since itsindex.htmlis only a loader. That file is a JS fragment, so it sits in.prettierignoreand ESLint's ignores; the generatedconfig.jsis syntax-checked by the build script instead.site-template/index.htmlis source, not derived fromcdn/cdn-index.html— keep the shared parts in sync by hand. Two deliberate divergences:- It always loads
loader.jsfrom jsDelivr (no localhost branch — operators never iterate on the loader). - It carries an inline base-path detector that sets
CDN_MODE.BASE_PATHfrom the first path segment on*.github.iohosts, so bothusername.github.io/<repo>/andusername.github.io/work with no config edit, and a repo rename survives. It defers to an explicitBASE_PATH. This lives in the template rather than incdn/loader.jsbecause a loader change only reaches deployments after the nextcdnbranch publish.
- It always loads
- The workflow needs a
SITE_TEMPLATE_TOKENsecret (fine-grained PAT,Contents: writeon the template repo) —GITHUB_TOKENcannot push across repositories.