doxa-maps/internal/check-quoted-scripts.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

111 lines
5.8 KiB
JavaScript

#!/usr/bin/env node
/**
* check-quoted-scripts.mjs — a doc that QUOTES package.json must still match package.json.
*
* THE BUG THIS EXISTS TO PREVENT (real, twice)
* documentation/README-SECTION-3-cli-commands.md reproduces the `scripts` block verbatim so a reader can
* see every command without opening package.json. It carries its own warning two lines above the
* block: "If you edit package.json, re-transcribe this block — a doc that QUOTES a config is
* claiming to be ground truth, and this one had silently stopped being it."
*
* It then silently stopped being it AGAIN. Two entries had drifted on 2026-09-21:
* - `cap:sync` still showed `bunx cap sync android` — the exact command that does not exist in
* this environment (exit 127) and that package.json had ALREADY been fixed away from.
* - `build:unlocked` was missing `check:public-hygiene` and `check:label-joins`. That is the
* worst shape of this defect: a reader auditing what the build verifies would conclude the
* SECRET-SCAN is not in the chain. An omission in a quoted list is invisible — there is no
* wrong character to notice, just a step that is not there.
*
* ★ WHY A GATE AND NOT A BETTER WARNING. The file already had the warning, written by someone who
* had just been burned, and the warning did not work — because re-transcribing by hand is exactly
* the kind of mechanical task a human or an agent skips when the edit felt unrelated. The rule was
* enforced only by remembering. This is the same failure the library-index gate was built for:
* a documented invariant with no mechanism behind it is a wish.
*
* WHY COMPARE AGAINST package.json AND NOT A SNAPSHOT
* package.json is the thing that RUNS. The doc is the copy. So the authority is unambiguous and
* the fix direction is always "update the doc" — never the reverse.
*
* WHAT IT TOLERATES DELIBERATELY
* - Trailing ` # comment` in a package.json body: several scripts carry an inline explanation
* that the doc block reasonably omits. Compared against the code half only.
* - JSON escaping (\" in the doc's fenced block) and a trailing comma.
* - Entries the doc chooses NOT to quote: this checks the ones it DOES quote, so the doc can
* stay a curated subset. It reports the count so shrinkage is visible.
*
* ★ THE MATCHER MUST NOT SILENTLY MATCH NOTHING. The first version of this regex required each
* line to END in a quote (`"$`). Nine of the ten entries end in `",` — a trailing comma — so it
* matched exactly ONE (`phone`, the last entry, the only one without a comma) and reported
* "0 drifted". That is a pass that means nothing, and it looks identical to a real clean run.
* (My first guess at the cause was "the block is indented"; it is NOT indented — checking rather
* than assuming is what found the trailing comma. A plausible explanation for a broken measurement
* is still a fabricated one until verified.)
* Hence MIN_EXPECTED below: too few matches is itself a FAILURE, not a quiet success.
*
* NEGATIVE-TESTED: reverting `cap:sync` in the doc to `bunx cap sync android` makes this FAIL
* naming that entry and showing both sides; restoring it returns PASS. Breaking the regex so it
* matches < MIN_EXPECTED also FAILs rather than passing vacuously.
*/
import { readFileSync, existsSync } from 'node:fs'
import { resolve, dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..')
const PKG = join(ROOT, 'package.json')
const DOC = join(ROOT, 'documentation', 'README-SECTION-3-cli-commands.md')
// The doc quoted 10 entries when this gate was written. If the matcher suddenly finds far fewer,
// the matcher is broken, not the doc.
const MIN_EXPECTED = 6
if (!existsSync(DOC)) {
console.log('QUOTED-SCRIPTS: SKIP — documentation/README-SECTION-3-cli-commands.md not present.')
process.exit(0)
}
const scripts = JSON.parse(readFileSync(PKG, 'utf8')).scripts || {}
const doc = readFileSync(DOC, 'utf8')
// Indent-tolerant: the block is inside a fenced code block and is indented.
const RE = /^[ \t]*"([a-z0-9:_-]+)":\s+"(.*?)",?[ \t]*$/gm
const drift = [], unknown = []
let quoted = 0
for (const m of doc.matchAll(RE)) {
const name = m[1]
const shown = m[2].replace(/\\"/g, '"').replace(/,$/, '').trim()
if (!(name in scripts)) { unknown.push(name); continue }
quoted++
const real = scripts[name]
const realCode = real.split(/\s{2,}#/)[0].trim() // drop an inline ` # …` explanation
if (shown !== real.trim() && shown !== realCode) drift.push({ name, shown, real: realCode })
}
console.log(`QUOTED-SCRIPTS: ${quoted} quoted entr(ies) compared against package.json.`)
if (quoted < MIN_EXPECTED) {
console.error(`\nQUOTED-SCRIPTS: FAIL — only ${quoted} entries matched (expected >= ${MIN_EXPECTED}).`)
console.error(' The MATCHER is broken, not the doc. A checker that matches almost nothing')
console.error(' reports "0 drifted" and looks exactly like a clean pass — fix the regex in')
console.error(' internal/check-quoted-scripts.mjs before trusting this result.')
process.exit(1)
}
if (unknown.length) {
console.log(` i quoted but not in package.json (ignored): ${unknown.join(', ')}`)
}
if (drift.length) {
console.error(`\nQUOTED-SCRIPTS: FAIL — ${drift.length} quoted script(s) no longer match package.json:`)
for (const d of drift) {
console.error(`\n ✗ ${d.name}`)
console.error(` doc : ${d.shown}`)
console.error(` real : ${d.real}`)
}
console.error('\n package.json is what RUNS; the doc is the copy — update the DOC to match.')
console.error(' An omitted step in a quoted list is invisible: there is no wrong character to')
console.error(' notice, just a check the reader believes is not in the chain.')
process.exit(1)
}
console.log('QUOTED-SCRIPTS: PASS — every quoted script matches package.json.')