diff --git a/src/world/loader.test.ts b/src/world/loader.test.ts index 56b677d..11b882e 100644 --- a/src/world/loader.test.ts +++ b/src/world/loader.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { parseRoom, parseItem } from './loader' +import { parseRoom, parseItem, parseEnding } from './loader' const FOYER_MD = `--- id: foyer @@ -214,3 +214,35 @@ takeable: false expect(() => parseItem(md, 'items/x.md')).toThrow(/empty long description/i) }) }) + +const TRUE_ENDING_MD = `--- +id: true +whenFlags: + ratGone: true +--- + +You stand at the top of the stair. The thing below has settled. + +The door behind you opens, and outside, finally, is morning. +` + +describe('parseEnding', () => { + it('parses an ending with flags and prose', () => { + const result = parseEnding(TRUE_ENDING_MD, 'endings/true.md') + expect(result.id).toBe('true') + expect(result.ending.whenFlags).toEqual({ ratGone: true }) + expect(result.ending.narration).toBe( + 'You stand at the top of the stair. The thing below has settled.\n\nThe door behind you opens, and outside, finally, is morning.', + ) + }) + + it('accepts an empty body (unreachable ending stub)', () => { + const md = `--- +id: wrong +whenFlags: {} +--- +` + const result = parseEnding(md, 'endings/wrong.md') + expect(result.ending.narration).toBe('') + }) +}) diff --git a/src/world/loader.ts b/src/world/loader.ts index 86ff6e6..6e80081 100644 --- a/src/world/loader.ts +++ b/src/world/loader.ts @@ -1,7 +1,7 @@ import matter from 'gray-matter' import type { Room, RoomDescriptions, Item } from './types' import type { Direction } from '../engine/types' -import { roomFrontmatterSchema, itemFrontmatterSchema } from './schema' +import { roomFrontmatterSchema, itemFrontmatterSchema, endingFrontmatterSchema } from './schema' const WIKILINK = /^\[\[([^\]|]+)(?:\|[^\]]*)?\]\]$/ @@ -115,3 +115,19 @@ export function parseItem(raw: string, sourcePath: string): Item { takeable: fm.takeable, } } + +export interface ParsedEnding { + id: 'true' | 'wrong' | 'bad' + ending: { whenFlags: Record; narration: string } +} + +export function parseEnding(raw: string, _sourcePath: string): ParsedEnding { + const parsed = matter(raw) + // YAML parses bare `true` as boolean; coerce id to string before schema validation. + const data = { ...parsed.data, id: String(parsed.data.id) } + const fm = endingFrontmatterSchema.parse(data) + return { + id: fm.id, + ending: { whenFlags: fm.whenFlags, narration: parsed.content.trim() }, + } +}