Service layer (createPage/updatePage/restoreRevision/setPageLockdown/ deletePage, slugification, template expansion, wikilink preprocessing, category constants) plus the client-safe markdown rendering pipeline (react-markdown + remark-gfm + remark-directive) shared by the server component and the editor's live preview. Adds the three markdown dependencies to package.json and bun.lock.
35 lines
No EOL
1.2 KiB
TypeScript
35 lines
No EOL
1.2 KiB
TypeScript
/**
|
|
* 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<string, string>;
|
|
|
|
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, []);
|
|
} |