Add Strong's dictionary reference package
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
import { mkdir, readFile, readdir, rm, writeFile } from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
import AdmZip from 'adm-zip'
|
||||
import { downloadFile, readJson, sha256File, writeJson } from './lib.js'
|
||||
|
||||
const manifestPath = process.argv[2]
|
||||
if (!manifestPath) {
|
||||
console.error('Usage: node scripts/import-strongs.js sources/<manifest>.json')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const root = process.cwd()
|
||||
const manifest = await readJson(path.resolve(root, manifestPath))
|
||||
const archivePath = path.join(root, 'cache', manifest.id, path.basename(manifest.source.download_url))
|
||||
const extractDir = path.join(root, 'cache', manifest.id, 'source')
|
||||
const packageDir = path.join(root, 'packages', 'json', manifest.id)
|
||||
|
||||
await downloadFile(manifest.source.download_url, archivePath)
|
||||
const sourceSha = await sha256File(archivePath)
|
||||
if (sourceSha !== manifest.checks.expected_sha256.toLowerCase()) {
|
||||
throw new Error(`${manifest.id}: source checksum mismatch. Expected ${manifest.checks.expected_sha256}, got ${sourceSha}`)
|
||||
}
|
||||
|
||||
await rm(extractDir, { force: true, recursive: true })
|
||||
await mkdir(extractDir, { recursive: true })
|
||||
new AdmZip(archivePath).extractAllTo(extractDir, true)
|
||||
|
||||
const sourceRoot = await findExtractedRoot(extractDir)
|
||||
const greek = parseDictionary(
|
||||
await readFile(path.join(sourceRoot, 'greek', 'strongs-greek-dictionary.js'), 'utf8'),
|
||||
'strongsGreekDictionary',
|
||||
'greek',
|
||||
)
|
||||
const hebrew = parseDictionary(
|
||||
await readFile(path.join(sourceRoot, 'hebrew', 'strongs-hebrew-dictionary.js'), 'utf8'),
|
||||
'strongsHebrewDictionary',
|
||||
'hebrew',
|
||||
)
|
||||
|
||||
const entries = [...hebrew, ...greek].sort((a, b) => {
|
||||
if (a.system !== b.system) return a.system.localeCompare(b.system)
|
||||
return a.number_value - b.number_value
|
||||
})
|
||||
|
||||
await mkdir(packageDir, { recursive: true })
|
||||
await writeJsonl(path.join(packageDir, 'hebrew.jsonl'), hebrew)
|
||||
await writeJsonl(path.join(packageDir, 'greek.jsonl'), greek)
|
||||
await writeJsonl(path.join(packageDir, 'entries.jsonl'), entries)
|
||||
|
||||
const files = {
|
||||
entries_jsonl: await fileInfo(path.join(packageDir, 'entries.jsonl')),
|
||||
hebrew_jsonl: await fileInfo(path.join(packageDir, 'hebrew.jsonl')),
|
||||
greek_jsonl: await fileInfo(path.join(packageDir, 'greek.jsonl')),
|
||||
}
|
||||
|
||||
await writeJson(path.join(packageDir, 'catalog.json'), {
|
||||
schema_version: 'librebible.resource-catalog.v1',
|
||||
project: manifest.project_name ?? 'LibreBible',
|
||||
id: manifest.id,
|
||||
resource_type: manifest.resource_type,
|
||||
title: manifest.title,
|
||||
short_title: manifest.short_title,
|
||||
abbreviation: manifest.abbreviation,
|
||||
alternate_ids: manifest.alternate_ids ?? [],
|
||||
language: manifest.language,
|
||||
script: manifest.script,
|
||||
canon: manifest.canon,
|
||||
contributors: manifest.contributors ?? [],
|
||||
features: manifest.features ?? [],
|
||||
attachments: manifest.attachments ?? {},
|
||||
source: manifest.source,
|
||||
license: manifest.license,
|
||||
catalog_display: manifest.catalog_display,
|
||||
importer: manifest.importer,
|
||||
source_manifest_file: path.basename(manifestPath),
|
||||
generated_at: new Date().toISOString(),
|
||||
source_sha256: sourceSha,
|
||||
checks: manifest.checks,
|
||||
counts: {
|
||||
entries: entries.length,
|
||||
dictionary_entries: entries.length,
|
||||
hebrew_entries: hebrew.length,
|
||||
greek_entries: greek.length,
|
||||
},
|
||||
files,
|
||||
})
|
||||
|
||||
console.log(`${manifest.id}: imported ${entries.length} Strong's dictionary entries`)
|
||||
|
||||
async function findExtractedRoot(dir) {
|
||||
const entries = await readdir(dir, { withFileTypes: true })
|
||||
const dirs = entries.filter((entry) => entry.isDirectory())
|
||||
if (dirs.length !== 1) return dir
|
||||
return path.join(dir, dirs[0].name)
|
||||
}
|
||||
|
||||
function parseDictionary(source, variableName, language) {
|
||||
const match = new RegExp(`var\\s+${variableName}\\s*=\\s*(\\{[\\s\\S]*?\\})\\s*;\\s*(?:module\\.exports\\s*=\\s*${variableName}\\s*;)?\\s*$`).exec(source)
|
||||
if (!match) throw new Error(`Could not find ${variableName}`)
|
||||
const data = JSON.parse(match[1])
|
||||
return Object.entries(data).map(([number, entry]) => ({
|
||||
id: number,
|
||||
strongs_number: number,
|
||||
system: number.startsWith('H') ? 'hebrew' : 'greek',
|
||||
language,
|
||||
number_value: Number(number.slice(1)),
|
||||
lemma: entry.lemma ?? null,
|
||||
transliteration: entry.translit ?? entry.xlit ?? null,
|
||||
pronunciation: entry.pron ?? null,
|
||||
derivation: cleanText(entry.derivation),
|
||||
strongs_definition: cleanText(entry.strongs_def),
|
||||
kjv_definition: cleanText(entry.kjv_def),
|
||||
}))
|
||||
}
|
||||
|
||||
function cleanText(value) {
|
||||
if (value == null) return null
|
||||
return String(value).replace(/\s+/g, ' ').trim()
|
||||
}
|
||||
|
||||
async function writeJsonl(filePath, rows) {
|
||||
await writeFile(filePath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8')
|
||||
}
|
||||
|
||||
async function fileInfo(filePath) {
|
||||
const data = await readFile(filePath)
|
||||
return {
|
||||
path: path.basename(filePath),
|
||||
bytes: data.length,
|
||||
sha256: await sha256File(filePath),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user