feat: add local hexagram content for offline reading
Load a versioned Wikisource jing plus project-authored plain drafts so results can show labeled original and vernacular texts without unauthorized modern translations. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { kingWenEntries, validateContentPackage } from "./content-contract.mjs";
|
||||
import { repositoryRoot } from "./repository-files.mjs";
|
||||
import { lingjiPlain } from "../content/plain/lingji-plain.mjs";
|
||||
|
||||
const names = [
|
||||
"乾", "坤", "屯", "蒙", "需", "讼", "师", "比",
|
||||
"小畜", "履", "泰", "否", "同人", "大有", "谦", "豫",
|
||||
"随", "蛊", "临", "观", "噬嗑", "贲", "剥", "复",
|
||||
"无妄", "大畜", "颐", "大过", "坎", "离", "咸", "恒",
|
||||
"遁", "大壮", "晋", "明夷", "家人", "睽", "蹇", "解",
|
||||
"损", "益", "夬", "姤", "萃", "升", "困", "井",
|
||||
"革", "鼎", "震", "艮", "渐", "归妹", "丰", "旅",
|
||||
"巽", "兑", "涣", "节", "中孚", "小过", "既济", "未济",
|
||||
];
|
||||
|
||||
if (lingjiPlain.length !== 64) {
|
||||
throw new Error(`lingji plain table must contain 64 hexagrams, got ${lingjiPlain.length}`);
|
||||
}
|
||||
|
||||
const jingPath = path.join(repositoryRoot, "content", "raw", "zhouyi-wikisource-jing.json");
|
||||
const jing = JSON.parse(await readFile(jingPath, "utf8"));
|
||||
if (!Array.isArray(jing) || jing.length !== 64) {
|
||||
throw new Error("Wikisource jing import must contain 64 hexagrams; run scripts/import-zhouyi-wikisource.mjs");
|
||||
}
|
||||
|
||||
const jingById = new Map(jing.map((item) => [item.kingWenNumber, item]));
|
||||
const entries = kingWenEntries();
|
||||
const originalSource = "wikisource-zhouyi-jing-zh-Hans";
|
||||
const plainSource = "lingji-plain-zh-Hans-2026.1";
|
||||
|
||||
const contentPackage = {
|
||||
schemaVersion: 1,
|
||||
contentVersion: "zh-Hans-2026.1",
|
||||
specialUsageTexts: { qian: true, kun: true },
|
||||
sources: [
|
||||
{
|
||||
id: originalSource,
|
||||
title: "维基文库《周易》易经",
|
||||
edition: "仅卦辞、爻辞与乾用九、坤用六,不含十翼;由 zh.wikisource.org 导入并经 Wikimedia zh-hans 转换",
|
||||
license: "公版(Wikimedia PD-old)",
|
||||
url: "https://zh.wikisource.org/wiki/周易",
|
||||
},
|
||||
{
|
||||
id: plainSource,
|
||||
title: "灵机本地白话",
|
||||
edition: "2026.1 项目自撰草稿,待内容负责人审校后视为可再分发",
|
||||
license: "项目自撰;审校完成前仅用于本机构建,不以现代译注冒充公版",
|
||||
url: "https://creativecommons.org/licenses/by/4.0/deed.zh-Hans",
|
||||
},
|
||||
],
|
||||
hexagrams: entries.map((entry) => {
|
||||
const original = jingById.get(entry.kingWenNumber);
|
||||
if (!original) {
|
||||
throw new Error(`Missing jing text for King Wen ${entry.kingWenNumber}`);
|
||||
}
|
||||
const plain = lingjiPlain[entry.kingWenNumber - 1];
|
||||
if (!Array.isArray(plain) || plain.length !== 7 || plain.some((text) => !String(text).trim())) {
|
||||
throw new Error(`Plain text for King Wen ${entry.kingWenNumber} must contain 7 non-blank strings`);
|
||||
}
|
||||
return {
|
||||
...entry,
|
||||
name: names[entry.kingWenNumber - 1],
|
||||
symbol: String.fromCodePoint(0x4DC0 + entry.kingWenNumber - 1),
|
||||
judgmentOriginal: original.judgmentOriginal,
|
||||
judgmentPlain: plain[0],
|
||||
lineTextsBottomUp: original.lineTextsBottomUp,
|
||||
linePlainBottomUp: plain.slice(1),
|
||||
specialUsageText: original.specialUsageText ?? null,
|
||||
sourceRefs: [originalSource, plainSource],
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const errors = validateContentPackage(contentPackage);
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Built content package failed validation:\n${errors.join("\n")}`);
|
||||
}
|
||||
|
||||
const outputDirectory = path.join(repositoryRoot, "content", "packages");
|
||||
await mkdir(outputDirectory, { recursive: true });
|
||||
const outputPath = path.join(outputDirectory, "hexagram-content.json");
|
||||
await writeFile(outputPath, `${JSON.stringify(contentPackage, null, 2)}\n`);
|
||||
process.stdout.write(`wrote ${outputPath}\n`);
|
||||
Reference in New Issue
Block a user