First commit on the local Gitea server, made after moving the tree off the Windows machine. Build outputs, node_modules and the AI models stay out (see .gitignore); everything the installer build needs is tracked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
98 lines
3.9 KiB
JavaScript
98 lines
3.9 KiB
JavaScript
/*
|
|
* merge.cjs — add the missing OT verses to interlinear/<Book>.json.
|
|
*
|
|
* ADDITIVE ONLY. Every verse already present keeps its existing value byte-for-byte; this only
|
|
* fills keys that are absent or empty. That matters because 36 shipped verses disagree with a
|
|
* fresh conversion (Qere/Ketiv variant picks, empty-word placeholders, and the verse-boundary
|
|
* cases TAHOT's own header lists) — those are left exactly as they are rather than "corrected"
|
|
* into a change nobody asked for.
|
|
*
|
|
* Keys are written in canonical chapter|verse order. The app looks verses up by key, but
|
|
* server.js /api/interlinear iterates Object.keys() for whole-chapter requests, so a predictable
|
|
* order is worth having. Output is minified with no trailing newline, matching the shipped files
|
|
* (verified: JSON.stringify round-trips them byte-identically).
|
|
*/
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const { parseFile } = require('./tahot-convert.cjs');
|
|
|
|
const REPO = process.argv[2] || 'C:/Users/media/Downloads/bible-app';
|
|
const BACKUP = process.argv[3];
|
|
const DRY = process.argv.includes('--dry');
|
|
const DIR = __dirname + '/tahot';
|
|
|
|
if (!BACKUP && !DRY) { console.error('usage: merge.cjs <repo> <backupDir> [--dry]'); process.exit(1); }
|
|
|
|
// ---- collect every TAHOT verse ----
|
|
const all = new Map(); // "Book|ch|v" -> words
|
|
for (const f of fs.readdirSync(DIR).filter((x) => x.endsWith('.txt'))) {
|
|
const { verses } = parseFile(path.join(DIR, f));
|
|
for (const [k, v] of verses) all.set(k, v);
|
|
}
|
|
console.log('TAHOT verses parsed:', all.size);
|
|
|
|
const byBook = new Map();
|
|
for (const [k, v] of all) {
|
|
const i = k.indexOf('|');
|
|
const book = k.slice(0, i);
|
|
if (!byBook.has(book)) byBook.set(book, new Map());
|
|
byBook.get(book).set(k.slice(i + 1), v); // "ch|v" -> words
|
|
}
|
|
|
|
// ---- back up before touching anything ----
|
|
if (!DRY) {
|
|
fs.mkdirSync(BACKUP, { recursive: true });
|
|
for (const f of fs.readdirSync(path.join(REPO, 'interlinear'))) {
|
|
fs.copyFileSync(path.join(REPO, 'interlinear', f), path.join(BACKUP, f));
|
|
}
|
|
console.log('backed up interlinear/ ->', BACKUP);
|
|
}
|
|
|
|
const sortKey = (k) => { const [c, v] = k.split('|').map(Number); return c * 10000 + v; };
|
|
|
|
let totalAdded = 0, totalWords = 0, booksChanged = 0;
|
|
const report = [];
|
|
|
|
for (const [book, tahotVerses] of byBook) {
|
|
const p = path.join(REPO, 'interlinear', book + '.json');
|
|
if (!fs.existsSync(p)) { report.push([book, 'NO SHIPPED FILE', 0]); continue; }
|
|
const original = fs.readFileSync(p, 'utf-8');
|
|
const shipped = JSON.parse(original);
|
|
|
|
const merged = {};
|
|
let added = 0, words = 0;
|
|
|
|
// Union of shipped keys and TAHOT keys, canonical order.
|
|
const keys = new Set([...Object.keys(shipped), ...tahotVerses.keys()]);
|
|
for (const k of [...keys].sort((a, b) => sortKey(a) - sortKey(b))) {
|
|
const have = shipped[k];
|
|
if (have && have.length) { merged[k] = have; continue; } // untouched
|
|
const fresh = tahotVerses.get(k);
|
|
if (fresh && fresh.length) { merged[k] = fresh; added++; words += fresh.length; }
|
|
else if (have) { merged[k] = have; } // preserve an empty array as-is
|
|
}
|
|
|
|
// Safety: no shipped verse may change.
|
|
for (const k of Object.keys(shipped)) {
|
|
if (shipped[k] && shipped[k].length &&
|
|
JSON.stringify(merged[k]) !== JSON.stringify(shipped[k])) {
|
|
throw new Error('REFUSING TO WRITE: ' + book + ' ' + k + ' would change');
|
|
}
|
|
}
|
|
|
|
if (added) {
|
|
booksChanged++;
|
|
totalAdded += added;
|
|
totalWords += words;
|
|
report.push([book, added + ' verses', words + ' words']);
|
|
if (!DRY) fs.writeFileSync(p, JSON.stringify(merged));
|
|
}
|
|
}
|
|
|
|
console.log('\nbooks changed:', booksChanged, '| verses added:', totalAdded, '| words added:', totalWords);
|
|
report.sort((a, b) => parseInt(b[1]) - parseInt(a[1]));
|
|
report.forEach((r) => console.log(' ' + String(r[0]).padEnd(18) + String(r[1]).padStart(12) + ' ' + r[2]));
|
|
if (DRY) console.log('\n(dry run — nothing written)');
|