import type { Topic } from '@bogam/models'; interface TransclusionOutput { combinedContent: string; } type TopicContentReader = (topicName: string) => Promise; const TRANSCLUSION_PATTERN = /(^|[^<])<<([^>]+?)>>(?!>)/g; const MAX_TRANSCLUSION_DEPTH = 10; export async function handleTransclusion( content: string, skipRendering: boolean = false, readTopic: TopicContentReader ): Promise { if (skipRendering) { return { combinedContent: content }; } const combinedContent = await processTransclusions(content, readTopic); return { combinedContent }; } async function processTransclusions( content: string, readTopic: TopicContentReader, depth = 0, includedTopics = new Set() ): Promise { if (depth > MAX_TRANSCLUSION_DEPTH) return content; TRANSCLUSION_PATTERN.lastIndex = 0; if (!TRANSCLUSION_PATTERN.test(content)) return content; TRANSCLUSION_PATTERN.lastIndex = 0; const matches = Array.from(content.matchAll(TRANSCLUSION_PATTERN)); if (matches.length === 0) return content; let result = content; for (const m of matches) { const marker = m[0]; const prefix = m[1] || ''; const topicName = (m[2] || '').trim(); // Skip TOC and NOTOC markers (case-insensitive) // TOC will be inserted after transclusion. if (/^(no)?toc$/i.test(topicName)) { continue; } if (includedTopics.has(topicName)) { const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); result = result.replace( new RegExp(escaped, 'g'), `${prefix}
Circular transclusion detected: ${topicName}
` ); continue; } try { const topicData = await readTopic(topicName); const processedContent = await processTransclusions( topicData.article || '', readTopic, depth + 1, new Set(includedTopics).add(topicName) ); // Escape special regex characters when replacing const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); result = result.replace(new RegExp(escaped, 'g'), `${prefix}${processedContent}`); } catch (error) { console.error(`Transclusion error for "${topicName}":`, error); const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); result = result.replace( new RegExp(escaped, 'g'), `${prefix}
Failed to load content from ${topicName}
` ); } } return result; }