Rebuild split-rulebook journal parsing (vault sync 2026-08-18 to 08-23)
The bab5a62 vault commit split 9 rulebook monoliths into hub/ToC pages plus per-topic-file subfolders (rules/NN. <Book>/<Chapter>/<Topic>.md). The existing chapter-slicing parser (parseJournalBook) relied on finding a duplicate TOC-vs-body heading pair per chapter; after the split each hub heading only occurs once (in the ToC), so it was silently treating the ToC's link-list body as if it were real chapter content. Added parseSplitBookFolder (journal-parser.ts): walks a book's split subfolder directly, one JournalDoc per Rozdzial/Aneks subfolder-or-file, one JournalPage per topic file (or a single page for a standalone chapter file). The hub's own Wstep intro paragraph is unaffected by the split (it's still real prose, not a link) and continues to come from the existing prefaceTitle preface-extraction logic. Wired into build-journal-packs.ts for the 6 affected packs: core-rules-lore, bestiary-lore, magic-book-lore, abyss, blood-magic-history, economy. crimson-cult's rules source was never split into a subfolder, so it's left on the old code path unchanged. Also fixed a pre-existing derivePageTitle bug (page titles kept a leading markdown heading marker, e.g. "## Wstep" instead of "Wstep") to match the already-correct behavior in topical-parser.ts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ import { mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, statSync, re
|
||||
import { dirname, resolve, basename, extname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { compilePack } from '@foundryvtt/foundryvtt-cli';
|
||||
import { parseJournalBook, type JournalDoc, type JournalPage } from './parsers/journal-parser';
|
||||
import { parseJournalBook, parseSplitBookFolder, type JournalDoc, type JournalPage } from './parsers/journal-parser';
|
||||
import { parseTopicalFolder } from './parsers/topical-parser';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
@@ -37,6 +37,14 @@ interface SourceFile {
|
||||
* as a synthetic journal entry with this title (e.g. "Wstęp").
|
||||
*/
|
||||
prefaceTitle?: string;
|
||||
/**
|
||||
* Since `bab5a62`, `path`'s chapter bodies were replaced with links into
|
||||
* this per-topic subfolder (relative to repoRoot). When set, chapters are
|
||||
* read from that folder instead of sliced out of `path` - `path` is then
|
||||
* used only to extract `prefaceTitle`'s intro paragraph, which is still
|
||||
* real prose in the hub file.
|
||||
*/
|
||||
splitFolder?: string;
|
||||
}
|
||||
|
||||
interface PackConfig {
|
||||
@@ -57,13 +65,17 @@ const PACK_CONFIGS: PackConfig[] = [
|
||||
{
|
||||
pack: 'core-rules-lore',
|
||||
sourceBook: 'core-rules',
|
||||
sources: [{ path: 'ObsidianNotes/rules/00. Podręcznik Gry.md' }],
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/rules/00. Podręcznik Gry.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/00. Podręcznik Gry' },
|
||||
],
|
||||
skip: /^(Talenty|Zaklęcia|Atrybuty|Umiejętności|Wyposażenie)/i,
|
||||
},
|
||||
{
|
||||
pack: 'bestiary-lore',
|
||||
sourceBook: 'bestiary',
|
||||
sources: [{ path: 'ObsidianNotes/rules/06. Bestiariusz.md' }],
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/rules/06. Bestiariusz.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/06. Bestiariusz' },
|
||||
],
|
||||
skip: /^Talenty/i,
|
||||
},
|
||||
|
||||
@@ -73,9 +85,9 @@ const PACK_CONFIGS: PackConfig[] = [
|
||||
sourceBook: 'magic-book',
|
||||
sources: [
|
||||
// Lore intro lives inside the rules file (no dedicated lore/ file).
|
||||
{ path: 'ObsidianNotes/rules/01. Księga Magii.md', prefaceTitle: 'Wstęp' },
|
||||
{ path: 'ObsidianNotes/rules/01. Księga Magii.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/01. Księga Magii' },
|
||||
],
|
||||
skip: /^(Zaklęcia|Lista zaklęć|Rozdział V)/i,
|
||||
skip: /^(Zaklęcia|Lista zaklęć)/i,
|
||||
},
|
||||
{
|
||||
pack: 'abyss',
|
||||
@@ -84,17 +96,17 @@ const PACK_CONFIGS: PackConfig[] = [
|
||||
// Pure lore narrative.
|
||||
{ path: 'ObsidianNotes/lore/02. Otchłań i Magia.md' },
|
||||
// Rules intro (Wstęp only - mechanics chapters are skipped).
|
||||
{ path: 'ObsidianNotes/rules/02. Klątwa Otchłani.md', prefaceTitle: 'Wstęp' },
|
||||
{ path: 'ObsidianNotes/rules/02. Klątwa Otchłani.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/02. Klątwa Otchłani' },
|
||||
],
|
||||
skip: /^(Rozdział II - Talenty|Rozdział III - Dary|Rozdział IV - Choroby|Rozdział V - Zaklęcia|Rozdział X - Artefakty)/i,
|
||||
skip: /^(Talenty|Dary|Choroby|Zaklęcia|Artefakty)/i,
|
||||
},
|
||||
{
|
||||
pack: 'blood-magic-history',
|
||||
sourceBook: 'arcanum-sanguinis',
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/rules/04. Arcanum Sanguinis.md', prefaceTitle: 'Wstęp' },
|
||||
{ path: 'ObsidianNotes/rules/04. Arcanum Sanguinis.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/04. Arcanum Sanguinis' },
|
||||
],
|
||||
skip: /^Rozdział IV - Talenty/i,
|
||||
skip: /^Talenty/i,
|
||||
},
|
||||
{
|
||||
pack: 'crimson-cult',
|
||||
@@ -102,7 +114,9 @@ const PACK_CONFIGS: PackConfig[] = [
|
||||
sources: [
|
||||
// Lore narrative (former Chwała Szkarłatnemu Kultowi lore section).
|
||||
{ path: 'ObsidianNotes/lore/05. Szkarłatny Kult.md' },
|
||||
// Rules intro only (spells/artifacts skipped).
|
||||
// Rules intro only (spells/artifacts skipped). This book was NOT split
|
||||
// into a per-topic subfolder (bab5a62) - it stayed a monolith, so the
|
||||
// old heading-slicing approach still applies unmodified.
|
||||
{ path: 'ObsidianNotes/rules/05. Vivat Patriarcha coccineus!.md', prefaceTitle: 'Wstęp' },
|
||||
],
|
||||
skip: /^(Rozdział I - Zaklęcia|Rozdział II - Artefakty)/i,
|
||||
@@ -112,9 +126,9 @@ const PACK_CONFIGS: PackConfig[] = [
|
||||
sourceBook: 'gold-steel-magic',
|
||||
sources: [
|
||||
{ path: 'ObsidianNotes/lore/03. Ekonomia Magicznego Świata.md' },
|
||||
{ path: 'ObsidianNotes/rules/03. Złoto, Stal i Magia.md', prefaceTitle: 'Wstęp' },
|
||||
{ path: 'ObsidianNotes/rules/03. Złoto, Stal i Magia.md', prefaceTitle: 'Wstęp', splitFolder: 'ObsidianNotes/rules/03. Złoto, Stal i Magia' },
|
||||
],
|
||||
skip: /^(Aneks A - Nowe Talenty|Aneks B - Przedmioty)/i,
|
||||
skip: /^(Nowe Talenty|Przedmioty)/i,
|
||||
},
|
||||
|
||||
// ── New: Humanity Guide ──
|
||||
@@ -166,6 +180,35 @@ for (const cfg of PACK_CONFIGS) {
|
||||
console.warn(` · skip source ${src.path}: file not found`);
|
||||
continue;
|
||||
}
|
||||
if (src.splitFolder) {
|
||||
const folderPath = resolve(repoRoot, src.splitFolder);
|
||||
if (!existsSync(folderPath)) {
|
||||
console.warn(` · skip splitFolder ${src.splitFolder}: folder not found`);
|
||||
} else {
|
||||
if (src.prefaceTitle) {
|
||||
// Preface only - skip every real chapter (they come from the folder walk below).
|
||||
packDocs.push(
|
||||
...parseJournalBook({
|
||||
bookPath,
|
||||
pack: cfg.pack,
|
||||
sourceBook: cfg.sourceBook,
|
||||
prefaceTitle: src.prefaceTitle,
|
||||
chapterSkip: /./,
|
||||
}),
|
||||
);
|
||||
}
|
||||
packDocs.push(
|
||||
...parseSplitBookFolder({
|
||||
folderPath,
|
||||
pack: cfg.pack,
|
||||
sourceBook: cfg.sourceBook,
|
||||
chapterSkip: cfg.skip,
|
||||
}),
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const docs = parseJournalBook({
|
||||
bookPath,
|
||||
pack: cfg.pack,
|
||||
|
||||
Reference in New Issue
Block a user