Files
plainleaf/web/markdown.ts
T

96 lines
2.7 KiB
TypeScript

import {
findNodeOfType,
type ParseTree,
renderToText,
replaceNodesMatchingAsync,
} from "@silverbulletmd/silverbullet/lib/tree";
import {
parseRef,
type Ref,
validatePageName,
} from "@silverbulletmd/silverbullet/lib/page_ref";
import { parseMarkdown } from "./markdown_parser/parser.ts";
import type { LuaExpression } from "./space_lua/ast.ts";
import { evalExpression } from "./space_lua/eval.ts";
import type { LuaEnv, LuaStackFrame } from "./space_lua/runtime.ts";
import { parseExpressionString } from "./space_lua/parse.ts";
import { renderExpressionResult } from "./markdown_util.ts";
import type { Client } from "../web/client.ts";
/**
* Expands custom markdown Lua directives and transclusions into plain markdown
* @param mdTree parsed markdown tree
* @returns modified mdTree
*/
export async function expandMarkdown(
client: Client,
mdTree: ParseTree,
env: LuaEnv,
sf: LuaStackFrame,
): Promise<ParseTree> {
await replaceNodesMatchingAsync(mdTree, async (n) => {
if (n.type === "Image") {
// Let's scan for ![[embeds]] that are codified as Images, confusingly
const wikiLinkMark = findNodeOfType(n, "WikiLinkMark");
if (!wikiLinkMark) {
return;
}
const wikiLinkPage = findNodeOfType(n, "WikiLinkPage");
if (!wikiLinkPage) {
return;
}
const page = wikiLinkPage.children![0].text!;
// Check if this is likely a page link (based on the path format, e.g. if it contains an extension, it's probably not a page link)
let ref: Ref | undefined;
try {
ref = parseRef(page);
validatePageName(ref.page);
} catch {
// Not a valid page name, so not a page reference
return;
}
// Read the page
const { text } = await client.space.readPage(ref.page);
const parsedBody = parseMarkdown(text);
// Recursively process
return expandMarkdown(
client,
parsedBody,
env,
sf,
);
} else if (n.type === "LuaDirective") {
const expr = findNodeOfType(n, "LuaExpressionDirective") as
| LuaExpression
| null;
if (!expr) {
return;
}
const exprText = renderToText(expr);
try {
let result = await evalExpression(
parseExpressionString(exprText),
env,
sf,
);
if (result?.markdown) {
result = result.markdown;
}
const markdown = await renderExpressionResult(result);
return parseMarkdown(markdown);
} catch (e: any) {
// Reduce blast radius and give useful error message
console.error("Error evaluating Lua directive", exprText, e);
return parseMarkdown(`**Error:** ${e.message}`);
}
}
});
return mdTree;
}