2022-10-28 14:17:40 +00:00
|
|
|
import { queryRegex } from "$sb/lib/query.ts";
|
2023-07-30 06:56:44 +00:00
|
|
|
import {
|
|
|
|
findNodeOfType,
|
|
|
|
ParseTree,
|
|
|
|
renderToText,
|
|
|
|
traverseTree,
|
|
|
|
} from "$sb/lib/tree.ts";
|
2022-10-28 14:17:40 +00:00
|
|
|
import { markdown, space } from "$sb/silverbullet-syscall/mod.ts";
|
|
|
|
import Handlebars from "handlebars";
|
|
|
|
|
|
|
|
import { replaceTemplateVars } from "../core/template.ts";
|
2022-11-24 11:04:00 +00:00
|
|
|
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
|
2022-12-14 19:04:20 +00:00
|
|
|
import { directiveRegex } from "./directives.ts";
|
2023-05-23 18:53:53 +00:00
|
|
|
import { updateDirectives } from "./command.ts";
|
2023-07-02 09:25:32 +00:00
|
|
|
import { buildHandebarOptions } from "./util.ts";
|
|
|
|
import { PageMeta } from "../../web/types.ts";
|
2023-07-30 09:30:01 +00:00
|
|
|
import { resolvePath, rewritePageRefs } from "$sb/lib/resolve.ts";
|
2022-10-28 14:17:40 +00:00
|
|
|
|
|
|
|
const templateRegex = /\[\[([^\]]+)\]\]\s*(.*)\s*/;
|
|
|
|
|
|
|
|
export async function templateDirectiveRenderer(
|
|
|
|
directive: string,
|
2023-07-02 09:25:32 +00:00
|
|
|
pageMeta: PageMeta,
|
2022-12-14 19:04:20 +00:00
|
|
|
arg: string | ParseTree,
|
2022-10-28 14:17:40 +00:00
|
|
|
): Promise<string> {
|
2022-12-14 19:04:20 +00:00
|
|
|
if (typeof arg !== "string") {
|
|
|
|
throw new Error("Template directives must be a string");
|
|
|
|
}
|
2022-10-28 14:17:40 +00:00
|
|
|
const match = arg.match(templateRegex);
|
|
|
|
if (!match) {
|
|
|
|
throw new Error(`Invalid template directive: ${arg}`);
|
|
|
|
}
|
2023-07-30 06:56:44 +00:00
|
|
|
let templatePath = match[1];
|
2022-10-28 14:17:40 +00:00
|
|
|
const args = match[2];
|
|
|
|
let parsedArgs = {};
|
|
|
|
if (args) {
|
|
|
|
try {
|
2023-07-02 09:25:32 +00:00
|
|
|
parsedArgs = JSON.parse(replaceTemplateVars(args, pageMeta));
|
2022-10-28 14:17:40 +00:00
|
|
|
} catch {
|
2023-07-02 09:25:32 +00:00
|
|
|
throw new Error(
|
|
|
|
`Failed to parse template instantiation arg: ${
|
|
|
|
replaceTemplateVars(args, pageMeta)
|
|
|
|
}`,
|
|
|
|
);
|
2022-10-28 14:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
let templateText = "";
|
2023-07-30 06:56:44 +00:00
|
|
|
if (
|
|
|
|
templatePath.startsWith("http://") || templatePath.startsWith("https://")
|
|
|
|
) {
|
2022-10-28 14:17:40 +00:00
|
|
|
try {
|
2023-07-30 06:56:44 +00:00
|
|
|
const req = await fetch(templatePath);
|
2022-10-28 14:17:40 +00:00
|
|
|
templateText = await req.text();
|
|
|
|
} catch (e: any) {
|
|
|
|
templateText = `ERROR: ${e.message}`;
|
|
|
|
}
|
|
|
|
} else {
|
2023-07-30 06:56:44 +00:00
|
|
|
templatePath = resolvePath(pageMeta.name, templatePath);
|
|
|
|
templateText = await space.readPage(templatePath);
|
2022-10-28 14:17:40 +00:00
|
|
|
}
|
2023-06-14 08:20:21 +00:00
|
|
|
const tree = await markdown.parseMarkdown(templateText);
|
|
|
|
await extractFrontmatter(tree, [], true); // Remove entire frontmatter section, if any
|
2023-07-30 06:56:44 +00:00
|
|
|
|
|
|
|
// Resolve paths in the template
|
|
|
|
rewritePageRefs(tree, templatePath);
|
|
|
|
|
2023-06-14 08:20:21 +00:00
|
|
|
let newBody = renderToText(tree);
|
|
|
|
|
2023-07-30 06:56:44 +00:00
|
|
|
// console.log("Rewritten template:", newBody);
|
|
|
|
|
2022-10-28 14:17:40 +00:00
|
|
|
// if it's a template injection (not a literal "include")
|
2022-12-14 19:04:20 +00:00
|
|
|
if (directive === "use") {
|
2022-10-28 14:17:40 +00:00
|
|
|
const templateFn = Handlebars.compile(
|
2023-07-02 09:25:32 +00:00
|
|
|
newBody,
|
2022-10-28 14:17:40 +00:00
|
|
|
{ noEscape: true },
|
|
|
|
);
|
2023-07-02 09:25:32 +00:00
|
|
|
newBody = templateFn(parsedArgs, buildHandebarOptions(pageMeta));
|
2022-10-28 14:17:40 +00:00
|
|
|
|
|
|
|
// Recursively render directives
|
2023-08-10 17:00:28 +00:00
|
|
|
const tree = await markdown.parseMarkdown(newBody);
|
|
|
|
newBody = await updateDirectives(pageMeta, tree, newBody);
|
2022-10-28 14:17:40 +00:00
|
|
|
}
|
|
|
|
return newBody.trim();
|
|
|
|
}
|
|
|
|
|
2023-07-06 14:47:50 +00:00
|
|
|
export function cleanTemplateInstantiations(text: string) {
|
|
|
|
return text.replaceAll(directiveRegex, (
|
|
|
|
_fullMatch,
|
|
|
|
startInst,
|
|
|
|
type,
|
|
|
|
_args,
|
|
|
|
body,
|
|
|
|
endInst,
|
|
|
|
): string => {
|
|
|
|
if (type === "use") {
|
|
|
|
body = body.replaceAll(
|
|
|
|
queryRegex,
|
|
|
|
(
|
|
|
|
_fullMatch: string,
|
|
|
|
_startQuery: string,
|
|
|
|
_query: string,
|
|
|
|
body: string,
|
|
|
|
) => {
|
|
|
|
return body.trim();
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return `${startInst}${body}${endInst}`;
|
|
|
|
});
|
2022-10-28 14:17:40 +00:00
|
|
|
}
|