doxa-maps/internal/check-no-conflict-markers.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

68 lines
3.2 KiB
JavaScript

/**
* check-no-conflict-markers.mjs — nothing tracked may carry an unresolved merge.
*
* THE INCIDENT. A merge commit from this worktree landed the BUILT bundle
* public/doxa-maps/gospel-resources/gospel-resources.js with 21 conflict markers in it —
* both sides concatenated, 1.13 MB. The browser threw "Identifier Ge has already been
* declared" and every gospel leaf rendered dead, while the SRI hash still matched, every
* request still returned 200, and no probe could see an error.
*
* THE CAUSE WAS A LANDING SCRIPT, NOT A BAD MERGE. The loop resolved the one file it
* expected to conflict and then ran `git add -A public`, which stages EVERY file under
* public/ — including the ones still holding markers. A one-line convenience that turns a
* conflict git correctly refused to guess at into a committed artefact.
*
* AND THE FIRST DIAGNOSIS WAS WRONG. I attributed the duplicate identifier to a module
* imported twice and re-routed an import to "fix" it. That change was harmless but it was
* not the cause; the rebuild that came with it is what repaired the file. A plausible cause
* that a fix appears to confirm is the easiest kind of wrong answer, and the only defence is
* a check that names the real condition.
*
* THE RULE, for a BUILT file especially: never hand-merge, never take both. Take either
* side, REBUILD, commit the rebuild. Then run this before pushing.
*/
import { execSync } from 'node:child_process'
import { readFileSync } from 'node:fs'
import { resolve, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'
const HERE = dirname(fileURLToPath(import.meta.url))
const REPO = resolve(HERE, '..')
const POISON = process.argv.includes('--poison')
/* Built from char codes so this file never contains a marker and never trips itself. */
const OPEN = String.fromCharCode(60).repeat(7)
const MID = String.fromCharCode(61).repeat(7)
const CLOSE = String.fromCharCode(62).repeat(7)
const MARKER = new RegExp('^(' + OPEN + '|' + MID + '$|' + CLOSE + ')', 'm')
const scan = (text) => MARKER.test(text)
if (POISON) {
const fake = 'a\n' + OPEN + ' HEAD\nmine\n' + MID + '\ntheirs\n' + CLOSE + ' other\n'
if (!scan(fake)) {
console.error('NO-CONFLICT-MARKERS: SELF-TEST FAIL — a textbook conflict was not detected.')
process.exit(1)
}
console.log('NO-CONFLICT-MARKERS: SELF-TEST PASS — a textbook conflict is detected.')
process.exit(0)
}
const tracked = execSync('git ls-files -z', { cwd: REPO, maxBuffer: 1 << 28 })
.toString('utf8').split(String.fromCharCode(0)).filter(Boolean)
const hits = []
for (const f of tracked) {
let text
try { text = readFileSync(resolve(REPO, f), 'utf8') } catch { continue }
if (text.indexOf(String.fromCharCode(0)) >= 0) continue
if (scan(text)) hits.push(f)
}
if (hits.length) {
console.error('NO-CONFLICT-MARKERS: FAIL — ' + hits.length + ' tracked file(s) carry an unresolved merge:')
for (const f of hits.slice(0, 10)) console.error(' ' + f)
console.error(' For a BUILT file: take either side, REBUILD the bundle, commit the rebuild.')
console.error(' Never "add -A" a directory mid-merge — it stages the markers with everything else.')
process.exit(1)
}
console.log('NO-CONFLICT-MARKERS: PASS — ' + tracked.length + ' tracked file(s), none mid-merge.')