#!/usr/bin/env node const fs = require('fs') const path = require('path') const SRC = process.env.BSB_MD_PATH || path.join('bibles', 'bible-bsb.md') const OUT_ABBR = (process.env.EN_ABBR || 'BSB_MD').toUpperCase() const OUT_DIR = process.env.OUTPUT_DIR || path.join('data','en_bible', OUT_ABBR) function ensureDir(p){ fs.mkdirSync(p,{recursive:true}) } function writeJson(file,obj){ ensureDir(path.dirname(file)); fs.writeFileSync(file, JSON.stringify(obj,null,2),'utf-8') } const BOOKS = [ ['Genesis', ['Genesis'],'OT'], ['Exodus',['Exodus'],'OT'], ['Leviticus',['Leviticus'],'OT'], ['Numbers',['Numbers'],'OT'], ['Deuteronomy',['Deuteronomy'],'OT'], ['Joshua',['Joshua'],'OT'], ['Judges',['Judges'],'OT'], ['Ruth',['Ruth'],'OT'], ['1 Samuel',['1\s+Samuel','1\s+Samuel'],'OT'], ['2 Samuel',['2\s+Samuel','2\s+Samuel'],'OT'], ['1 Kings',['1\s+Kings'],'OT'], ['2 Kings',['2\s+Kings'],'OT'], ['1 Chronicles',['1\s+Chronicles'],'OT'], ['2 Chronicles',['2\s+Chronicles'],'OT'], ['Ezra',['Ezra'],'OT'], ['Nehemiah',['Nehemiah'],'OT'], ['Esther',['Esther'],'OT'], ['Job',['Job'],'OT'], ['Psalms',['Psalms|Psalm'],'OT'], ['Proverbs',['Proverbs'],'OT'], ['Ecclesiastes',['Ecclesiastes'],'OT'], ['Song of Songs',['Song\s+of\s+Songs|Song\s+of\s+Solomon'],'OT'], ['Isaiah',['Isaiah'],'OT'], ['Jeremiah',['Jeremiah'],'OT'], ['Lamentations',['Lamentations'],'OT'], ['Ezekiel',['Ezekiel'],'OT'], ['Daniel',['Daniel'],'OT'], ['Hosea',['Hosea'],'OT'], ['Joel',['Joel'],'OT'], ['Amos',['Amos'],'OT'], ['Obadiah',['Obadiah'],'OT'], ['Jonah',['Jonah'],'OT'], ['Micah',['Micah'],'OT'], ['Nahum',['Nahum'],'OT'], ['Habakkuk',['Habakkuk'],'OT'], ['Zephaniah',['Zephaniah'],'OT'], ['Haggai',['Haggai'],'OT'], ['Zechariah',['Zechariah'],'OT'], ['Malachi',['Malachi'],'OT'], ['Matthew',['Matthew'],'NT'], ['Mark',['Mark'],'NT'], ['Luke',['Luke'],'NT'], ['John',['John'],'NT'], ['Acts',['Acts'],'NT'], ['Romans',['Romans'],'NT'], ['1 Corinthians',['1\s+Corinthians'],'NT'], ['2 Corinthians',['2\s+Corinthians'],'NT'], ['Galatians',['Galatians'],'NT'], ['Ephesians',['Ephesians'],'NT'], ['Philippians',['Philippians'],'NT'], ['Colossians',['Colossians'],'NT'], ['1 Thessalonians',['1\s+Thessalonians'],'NT'], ['2 Thessalonians',['2\s+Thessalonians'],'NT'], ['1 Timothy',['1\s+Timothy'],'NT'], ['2 Timothy',['2\s+Timothy'],'NT'], ['Titus',['Titus'],'NT'], ['Philemon',['Philemon'],'NT'], ['Hebrews',['Hebrews'],'NT'], ['James',['James'],'NT'], ['1 Peter',['1\\s+Peter'],'NT'], ['2 Peter',['2\\s+Peter'],'NT'], ['1 John',['1\s+John'],'NT'], ['2 John',['2\s+John'],'NT'], ['3 John',['3\s+John'],'NT'], ['Jude',['Jude'],'NT'], ['Revelation',['Revelation'],'NT'] ] function main(){ if(!fs.existsSync(SRC)) { console.error('Missing source:', SRC); process.exit(1) } const md = fs.readFileSync(SRC,'utf-8') // Collect all verse markers across the entire doc const markers = [] for(const [name, variants] of BOOKS){ const names = variants.join('|') const re = new RegExp('(?:^|[\\n\\r\\f\\s\\|\\(])(?:'+names+')\\s+(\\d+):(\\d+)', 'gi') let m while((m=re.exec(md))!==null){ markers.push({ book:name, chapter:parseInt(m[1],10), verse:parseInt(m[2],10), index:m.index, matchLen:m[0].length }) } } if(markers.length===0){ console.error('No verse markers found'); process.exit(1) } markers.sort((a,b)=>a.index-b.index) // Build text segments per marker (chapter/verse) const entries = [] for(let i=0;i1500) text = text.slice(0,1500).trim() entries.push({ ...cur, text }) } // Aggregate into OT/NT JSON const bookIndex = new Map(BOOKS.map(([n,_,t],i)=>[n,{testament:t, order:i+1}])) const byBook = new Map() for(const e of entries){ if(!byBook.has(e.book)) byBook.set(e.book, new Map()) const chMap = byBook.get(e.book) if(!chMap.has(e.chapter)) chMap.set(e.chapter, new Map()) const vMap = chMap.get(e.chapter) if(!vMap.has(e.verse)) vMap.set(e.verse, e.text) } const otBooks=[]; const ntBooks=[] for(const [name, chMap] of byBook){ const meta = bookIndex.get(name) const chapters=[] for(const [ch, vMap] of Array.from(chMap.entries()).sort((a,b)=>a[0]-b[0])){ const verses=[] for(const [vn, txt] of Array.from(vMap.entries()).sort((a,b)=>a[0]-b[0])){ verses.push({ verseNum: vn, text: txt }) } if(verses.length>0) chapters.push({ chapterNum: ch, verses }) } const bookObj={ name, chapters } if(meta?.testament==='OT') otBooks.push({name,chapters}) else ntBooks.push({name,chapters}) } // Sort books in canonical order otBooks.sort((a,b)=>bookIndex.get(a.name).order-bookIndex.get(b.name).order) ntBooks.sort((a,b)=>bookIndex.get(a.name).order-bookIndex.get(b.name).order) const ot={ testament:'Old Testament', books: otBooks } const nt={ testament:'New Testament', books: ntBooks } const otFile = path.join(OUT_DIR,'old_testament.json') const ntFile = path.join(OUT_DIR,'new_testament.json') writeJson(otFile, ot) writeJson(ntFile, nt) console.log('Wrote:', otFile) console.log('Wrote:', ntFile) console.log('Books parsed:', otBooks.length + ntBooks.length) } main()