doxa-maps/internal/check-packets-all.mjs
Doxa Maps e19697673d
Some checks are pending
Deploy to GitHub Pages / build (push) Waiting to run
Deploy to GitHub Pages / deploy (push) Blocked by required conditions
Doxa Maps — the map bundler, 7 production plugins and the Experimental shelf
2026-09-25 20:07:59 +00:00

91 lines
5 KiB
JavaScript

#!/usr/bin/env node
// check-packets-all — run check-packet-render over EVERY generated packet.
//
// ★ WHY THIS WRAPPER EXISTS. check-packet-render takes a path, and with no path it
// SKIPs at exit 0 — deliberately, so a build is not failed on a file that may not have
// been generated. The consequence, measured 2026-09-24: the gate was in package.json,
// all 11 locale packets passed it, and it was in NEITHER gates:pre NOR gates:post. It
// only ever ran when a human remembered to type a filename. A gate nobody invokes
// protects nothing — the same "present but unwired" state the two i18n gates were in.
//
// So this sweeps the directory and gates the WHOLE SET. That matters beyond wiring:
// checking one hand-named packet proves one locale. The packet ships in 11, and the
// failures that actually occur are locale-specific — a script font that does not load,
// an RTL document laid out LTR, a translated page header whose word order breaks a
// parser. Those are invisible if the gate is pointed at laos.html and nothing else.
//
// SKIP (exit 0) only when the directory is genuinely absent or empty: packets are
// generated on demand and a clean checkout has none. A skip says so out loud, with the
// count, so "0 packets checked" can never read as "all packets passed".
import { existsSync, readdirSync } from 'node:fs'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { spawnSync } from 'node:child_process'
const HERE = dirname(fileURLToPath(import.meta.url))
// Repo-relative, never an author's worktree: this gate lives in two repos and the
// packets are generated in the one that owns the poster scripts.
const REPO = process.env.DOXA_REPO || resolve(HERE, '..')
const DIR = process.env.PACKET_DIR
|| resolve(REPO, 'library/scripts/posters/out/packets')
if (!existsSync(DIR)) {
console.log(`PACKETS-ALL: SKIP — no packet directory at ${DIR}`)
console.log(' Packets are generated on demand (node library/scripts/posters/packet.mjs);')
console.log(' a clean checkout has none, so this is not a failure.')
process.exit(0)
}
const files = readdirSync(DIR).filter((f) => f.endsWith('.html')).sort()
if (!files.length) {
console.log(`PACKETS-ALL: SKIP — ${DIR} contains no .html packets`)
process.exit(0)
}
const gate = join(HERE, 'check-packet-render.mjs')
const failed = []
for (const f of files) {
const res = spawnSync(process.execPath, [gate, join(DIR, f)], { encoding: 'utf8' })
const ok = res.status === 0
console.log(` ${ok ? '✓' : '✗'} ${f}`)
if (!ok) {
failed.push(f)
// Print the failing gate's own reasons rather than a generic "it failed" —
// the detail is what makes a red build actionable.
const detail = `${res.stdout || ''}${res.stderr || ''}`
.split('\n').filter((l) => l.trim().startsWith('-') || l.includes('FAIL'))
for (const line of detail) console.log(` ${line.trim()}`)
}
}
// ★ SAY HOW MANY ZONES THIS ACTUALLY COVERED. "every generated packet renders" is true and reads
// like "every packet is fine" — but only the zones someone generated are ever here. MEASURED
// 2026-09-24: 11 packets, ALL of them the SAME zone (laos) in 11 locales, against 28 poster zones
// in out/posters. So the suite proved one zone's layout 11 times and had never rendered the other
// 27, any of which could carry a different data shape (fewer groups, a missing photo, an
// unresolvable map). A silent cap reads as full coverage; naming it is the difference between
// "verified" and "verified what happened to be lying around".
// NOT a failure — generating 28 zones x 11 locales means a network fetch per zone and ~100MB of
// artefacts, which is a deliberate trade, not an oversight. The gate's job here is to stop the
// PASS line from overclaiming.
const zoneOf = (f) => f.replace(/\.html$/, '').replace(/\.[a-z]{2}$/, '')
const zones = [...new Set(files.map(zoneOf))].sort()
let totalZones = null
try {
const postersDir = resolve(REPO, 'library/scripts/posters/out/posters')
if (existsSync(postersDir)) totalZones = readdirSync(postersDir).filter((f) => f.endsWith('.html')).length
} catch { /* the denominator is a nicety; its absence must not fail the gate */ }
console.log(`\nPACKETS-ALL: ${files.length} packet(s) checked, ${files.length - failed.length} passed`)
console.log(` zones covered : ${zones.length}${totalZones ? ` of ${totalZones}` : ''} — ${zones.join(', ')}`)
if (totalZones && zones.length < totalZones) {
console.log(` ⚠ ${totalZones - zones.length} zone(s) have NO packet here and were not rendered by`)
console.log(' anything. Generate another with: bun run packet:ci (one zone, committed fixture,')
console.log(' no network), or ONLY=<zone> node library/scripts/posters/packet.mjs.')
}
if (failed.length) {
console.error(`PACKETS-ALL: FAIL — ${failed.length} packet(s): ${failed.join(', ')}`)
process.exit(1)
}
console.log(`PACKETS-ALL: PASS — every generated packet renders, across ${zones.length} zone(s) `
+ 'and every locale present. This does NOT cover zones no one generated.')