doxa-maps/internal/clean.mjs
doxa-maps-88655c70 79bd6d2ac3
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
public/ is one folder: the output tree plus a README, with the Plugin Studio as its front page
Driver decisions (cards BUILD-OUTPUT-IS-ONE-FOLDER, BUILD-PLUGIN-STUDIO-IS-THE-OUTPUT-FRONT-PAGE).

- public/ holds only <tree>/ and a generated README.md. The build no longer writes public/index.html,
  public/assets/ or public/__gallery/; `rm -rf public && bun run build` recreates exactly these two.
- The Plugin Studio is <tree>/index.html (tree-base "."), replacing the staging-forge chooser; nothing
  is written above the tree. check:staging-lang, which tested that chooser, is retired with it.
- Assets are gathered in a temp directory and the fetched allowlist staged into <tree>/assets/ as
  before. fonts/, images/, brand/ and legacy/ existed ONLY in public/assets: they are sources and move
  to data/ (git mv). Poster previews keep their tracked sources in plugin-designs/*/assets/previews
  (all 378 verified present).
- __gallery/ is archived out of public/ (a copy kept in the umbrella's _qa-cycle/archive) and out of
  the build; generate-gallery renders only on request, into a directory you name, never public/.
- Native shells load the output folder: capacitor webDir public/<tree> (cap copy needs an index.html
  there), Tauri frontendDist + window url relative to it. internal/sync-app-shells.mjs keeps both in
  step with the tree's name on every build, so the coming rename stays one config line; the Capacitor
  start page resolves its leaf tree-relative.
- dropin.include now lists only extras (README.md): the tree is always shipped and never named twice;
  excludes are tree-relative ({tree}/...). clean.mjs removes the old root entries from existing
  checkouts; the build lock and public/.gitignore's rules move out of public/.
- check:wp-embed read the BUILT manifest from gates:pre, before the build, so a from-scratch build
  could never pass; it runs in gates:post and reads the tree name from config.
- Stale bundles refreshed by the clean build (key-cities, template-bundle, wall-of-people-groups);
  the CLI doc's quoted scripts re-synced; capability exemplars regenerated; the '#' note leaves
  check:stale-sweep-claim's command (it was already in //scripts).

Proof: rm -rf public && bun run build -> public/ = README.md + doxa-maps/; check:sri, check:drop,
check:parse, check:public-hygiene, check:quoted-scripts, check:capability-exemplars PASS; all 57 pages
(the front page + 56 leaves) mount under /doxa-maps/ on this tree. check:facet-cascade is red as on main.
2026-09-25 17:41:56 +00:00

59 lines
3 KiB
JavaScript

#!/usr/bin/env node
/**
* clean.mjs — wipe + recreate the default output tree root before a build.
*
* The output folder name is configurable (vite.doxa-maps-build-config.json `name`, default
* "doxa-maps"), so this reads the same config instead of hardcoding the name —
* otherwise renaming the tree would leave a stale folder behind. Runs BEFORE the
* per-bundle vite builds (which emit into public/<name>/); generate-tree.mjs runs after.
*/
import { readFileSync, rmSync, mkdirSync, existsSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { TREE_NAME } from './_tree-name.mjs'
const __dirname = dirname(fileURLToPath(import.meta.url))
function config() {
try {
const cfg = JSON.parse(readFileSync(resolve(__dirname, '..', 'vite.doxa-maps-build-config.json'), 'utf8'))
const name = (typeof cfg?.name === 'string' && cfg.name.trim()) ? cfg.name.trim() : TREE_NAME
const paths = Array.isArray(cfg?.paths) ? cfg.paths.filter(p => typeof p === 'string' && p.trim()) : []
return { name, paths }
} catch { return { name: TREE_NAME, paths: [] } }
}
const { name, paths } = config()
// ⭐ BUG FIXED 2026-07-30 — CLEAN EVERY DESTINATION, NOT JUST app/.
// This used to reset ONLY app/<name>/, leaving each `paths` destination untouched.
// Result: a RECURSIVE SELF-NESTING pollution that grew with every build —
// public/doxa-maps/<bundle>/doxa-maps/<bundle>/doxa-maps/...
// because generate-tree copies app/<name>/ into <path>/<name>/, and on the next
// build the (never-cleaned) nested copies were still there to be copied again.
// It shipped 7 duplicate copies of the same 576 KB bundle into git before it was
// caught by a pre-push file-size audit.
// LESSON: a clean step must clear EVERY output destination the build writes to.
// Cleaning only the canonical outDir is a silent, compounding leak.
// The build writes into public/<name>/ directly (public/ is the committed served
// web-root). That ONE subfolder is reset — never public/ itself (write-token's public/api/ lives there).
const roots = [
resolve(__dirname, '..', 'public', name),
...paths.map(p => resolve(__dirname, '..', p, name)),
]
for (const root of roots) {
rmSync(root, { recursive: true, force: true })
mkdirSync(root, { recursive: true })
console.log(`[clean] reset ${root}`)
}
// ONE FOLDER (Driver decision, card BUILD-OUTPUT-IS-ONE-FOLDER, 2026-09-25): public/ now holds only the
// output folder + a generated README.md. These are the entries the OLD layout wrote beside the tree;
// removing them here lets a checkout that still has them converge on the new layout at its next build
// instead of carrying them forever. (public/api/ is left alone: write-token owns it, and only when a
// MAPBOX_TOKEN is supplied.)
for (const legacy of ['index.html', 'assets', '__gallery']) {
const p = resolve(__dirname, '..', 'public', legacy)
if (existsSync(p)) { rmSync(p, { recursive: true, force: true }); console.log(`[clean] removed public/${legacy} (no longer part of the output)`) }
}