/** * Wiki template expansion. * * Pure module — safe to import client-side. Replaces `{{Name}}` placeholders * (name trimmed, exact match on map keys) with the template body. Nested * placeholders expand up to at most 2 levels deep: the page body is level 0, * template bodies expanded from the page are level 1, templates inside those * are level 2, and any `{{...}}` found at level 2+ is left as literal text. * If a template name repeats anywhere in the current expansion chain, that * `{{Name}}` is left literal (cycle guard). */ export type TemplateMap = Record; const PLACEHOLDER_RE = /\{\{\s*([^{}]+?)\s*\}\}/g; function expandLevel( text: string, templates: TemplateMap, depth: number, chain: string[], ): string { if (depth >= 2) return text; return text.replace(PLACEHOLDER_RE, (match, rawName: string) => { const name = rawName.trim(); const body = templates[name]; if (body === undefined) return match; if (chain.includes(name)) return match; return expandLevel(body, templates, depth + 1, [...chain, name]); }); } export function expandTemplates(source: string, templates: TemplateMap): string { return expandLevel(source, templates, 0, []); }