2022-04-21 11:57:45 +00:00
|
|
|
import {
|
|
|
|
getCurrentPage,
|
|
|
|
reloadPage,
|
2022-04-25 08:33:38 +00:00
|
|
|
save,
|
2022-04-25 09:24:13 +00:00
|
|
|
} from "@silverbulletmd/plugos-silverbullet-syscall/editor";
|
2022-06-08 13:58:43 +00:00
|
|
|
import Handlebars from "handlebars";
|
2022-04-01 15:07:08 +00:00
|
|
|
|
2022-04-25 09:24:13 +00:00
|
|
|
import {
|
|
|
|
readPage,
|
|
|
|
writePage,
|
|
|
|
} from "@silverbulletmd/plugos-silverbullet-syscall/space";
|
|
|
|
import { invokeFunction } from "@silverbulletmd/plugos-silverbullet-syscall/system";
|
2022-07-06 10:18:47 +00:00
|
|
|
import { renderQuery } from "./engine";
|
|
|
|
import { parseQuery } from "./parser";
|
2022-04-13 12:46:52 +00:00
|
|
|
import { replaceTemplateVars } from "../core/template";
|
2022-06-08 13:58:43 +00:00
|
|
|
import { jsonToMDTable, queryRegex } from "./util";
|
2022-04-25 08:33:38 +00:00
|
|
|
import { dispatch } from "@plugos/plugos-syscall/event";
|
2022-04-20 08:56:43 +00:00
|
|
|
import { replaceAsync } from "../lib/util";
|
2022-07-25 14:51:46 +00:00
|
|
|
import { parseMarkdown } from "@silverbulletmd/plugos-silverbullet-syscall/markdown";
|
2022-08-09 13:37:47 +00:00
|
|
|
import { nodeAtPos, renderToText } from "@silverbulletmd/common/tree";
|
|
|
|
import { extractMeta } from "./data";
|
2022-03-29 15:02:28 +00:00
|
|
|
|
|
|
|
export async function updateMaterializedQueriesCommand() {
|
2022-04-01 15:07:08 +00:00
|
|
|
const currentPage = await getCurrentPage();
|
|
|
|
await save();
|
2022-05-17 13:54:55 +00:00
|
|
|
if (
|
|
|
|
await invokeFunction(
|
|
|
|
"server",
|
|
|
|
"updateMaterializedQueriesOnPage",
|
|
|
|
currentPage
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
await reloadPage();
|
|
|
|
}
|
2022-04-20 08:56:43 +00:00
|
|
|
}
|
|
|
|
|
2022-06-08 13:58:43 +00:00
|
|
|
export const templateInstRegex =
|
2022-08-10 07:18:11 +00:00
|
|
|
/(<!--\s*#(use|use-verbose|include)\s+\[\[([^\]]+)\]\](.*?)-->)(.+?)(<!--\s*\/\2\s*-->)/gs;
|
2022-06-08 13:58:43 +00:00
|
|
|
|
|
|
|
async function updateTemplateInstantiations(
|
|
|
|
text: string,
|
|
|
|
pageName: string
|
|
|
|
): Promise<string> {
|
|
|
|
return replaceAsync(
|
|
|
|
text,
|
|
|
|
templateInstRegex,
|
2022-07-25 14:51:46 +00:00
|
|
|
async (fullMatch, startInst, type, template, args, body, endInst) => {
|
2022-06-08 13:58:43 +00:00
|
|
|
args = args.trim();
|
|
|
|
let parsedArgs = {};
|
|
|
|
if (args) {
|
|
|
|
try {
|
|
|
|
parsedArgs = JSON.parse(args);
|
|
|
|
} catch (e) {
|
|
|
|
console.error("Failed to parse template instantiation args", args);
|
|
|
|
return fullMatch;
|
|
|
|
}
|
|
|
|
}
|
2022-07-25 14:51:46 +00:00
|
|
|
let templateText = "";
|
|
|
|
if (template.startsWith("http://") || template.startsWith("https://")) {
|
|
|
|
try {
|
|
|
|
let req = await fetch(template);
|
|
|
|
templateText = await req.text();
|
|
|
|
} catch (e: any) {
|
|
|
|
templateText = `ERROR: ${e.message}`;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
templateText = (await readPage(template)).text;
|
|
|
|
}
|
|
|
|
let newBody = templateText;
|
2022-08-09 13:37:47 +00:00
|
|
|
// if it's a template injection (not a literal "include")
|
2022-08-10 07:18:11 +00:00
|
|
|
if (type === "use" || type === "use-verbose") {
|
2022-08-09 13:37:47 +00:00
|
|
|
let tree = await parseMarkdown(templateText);
|
|
|
|
extractMeta(tree, ["$disableDirectives"]);
|
|
|
|
templateText = renderToText(tree);
|
2022-07-25 14:51:46 +00:00
|
|
|
let templateFn = Handlebars.compile(
|
|
|
|
replaceTemplateVars(templateText, pageName),
|
|
|
|
{ noEscape: true }
|
|
|
|
);
|
|
|
|
newBody = templateFn(parsedArgs);
|
|
|
|
}
|
2022-06-08 13:58:43 +00:00
|
|
|
return `${startInst}\n${newBody.trim()}\n${endInst}`;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-09 13:37:47 +00:00
|
|
|
async function cleanTemplateInstantiations(text: string): Promise<string> {
|
|
|
|
return replaceAsync(
|
|
|
|
text,
|
|
|
|
templateInstRegex,
|
|
|
|
async (fullMatch, startInst, type, template, args, body, endInst) => {
|
2022-08-10 07:18:11 +00:00
|
|
|
if (type === "use") {
|
2022-08-09 13:37:47 +00:00
|
|
|
body = body.replaceAll(
|
|
|
|
queryRegex,
|
|
|
|
(
|
|
|
|
fullMatch: string,
|
|
|
|
startQuery: string,
|
|
|
|
query: string,
|
|
|
|
body: string
|
|
|
|
) => {
|
|
|
|
return body.trim();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return `${startInst}${body}${endInst}`;
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-03-29 15:02:28 +00:00
|
|
|
// Called from client, running on server
|
2022-05-17 13:54:55 +00:00
|
|
|
export async function updateMaterializedQueriesOnPage(
|
|
|
|
pageName: string
|
|
|
|
): Promise<boolean> {
|
2022-07-04 13:07:27 +00:00
|
|
|
let text = "";
|
|
|
|
try {
|
|
|
|
text = (await readPage(pageName)).text;
|
|
|
|
} catch {
|
|
|
|
console.warn(
|
|
|
|
"Could not read page",
|
|
|
|
pageName,
|
|
|
|
"perhaps it doesn't yet exist"
|
|
|
|
);
|
|
|
|
return false;
|
|
|
|
}
|
2022-06-08 13:58:43 +00:00
|
|
|
let newText = await updateTemplateInstantiations(text, pageName);
|
2022-07-25 14:51:46 +00:00
|
|
|
let tree = await parseMarkdown(newText);
|
2022-08-09 13:37:47 +00:00
|
|
|
let metaData = extractMeta(tree, ["$disableDirectives"]);
|
|
|
|
if (metaData.$disableDirectives) {
|
|
|
|
console.log("Directives disabled, skipping");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
newText = renderToText(tree);
|
2022-07-25 14:51:46 +00:00
|
|
|
|
2022-06-08 13:58:43 +00:00
|
|
|
newText = await replaceAsync(
|
|
|
|
newText,
|
2022-04-12 11:33:07 +00:00
|
|
|
queryRegex,
|
2022-07-25 14:51:46 +00:00
|
|
|
async (fullMatch, startQuery, query, body, endQuery, index) => {
|
|
|
|
let currentNode = nodeAtPos(tree, index + 1);
|
|
|
|
if (currentNode?.type !== "CommentBlock") {
|
|
|
|
// If not a comment block, it's likely a code block, ignore
|
|
|
|
return fullMatch;
|
|
|
|
}
|
|
|
|
|
2022-04-19 14:54:47 +00:00
|
|
|
let parsedQuery = parseQuery(replaceTemplateVars(query, pageName));
|
2022-04-06 13:39:20 +00:00
|
|
|
|
2022-08-10 07:18:11 +00:00
|
|
|
// console.log("Parsed query", parsedQuery);
|
2022-04-19 14:54:47 +00:00
|
|
|
// Let's dispatch an event and see what happens
|
|
|
|
let results = await dispatch(
|
|
|
|
`query:${parsedQuery.table}`,
|
|
|
|
{ query: parsedQuery, pageName: pageName },
|
2022-04-20 08:56:43 +00:00
|
|
|
10 * 1000
|
2022-04-19 14:54:47 +00:00
|
|
|
);
|
|
|
|
if (results.length === 0) {
|
|
|
|
return `${startQuery}\n${endQuery}`;
|
|
|
|
} else if (results.length === 1) {
|
2022-04-28 09:55:38 +00:00
|
|
|
if (parsedQuery.render) {
|
|
|
|
let rendered = await renderQuery(parsedQuery, results[0]);
|
|
|
|
return `${startQuery}\n${rendered.trim()}\n${endQuery}`;
|
|
|
|
} else {
|
|
|
|
return `${startQuery}\n${jsonToMDTable(results[0])}\n${endQuery}`;
|
|
|
|
}
|
2022-04-19 14:54:47 +00:00
|
|
|
} else {
|
|
|
|
console.error("Too many query results", results);
|
|
|
|
return fullMatch;
|
2022-04-12 11:33:07 +00:00
|
|
|
}
|
2022-03-29 15:02:28 +00:00
|
|
|
}
|
2022-04-12 11:33:07 +00:00
|
|
|
);
|
2022-08-09 13:37:47 +00:00
|
|
|
newText = await cleanTemplateInstantiations(newText);
|
2022-05-17 13:54:55 +00:00
|
|
|
if (text !== newText) {
|
|
|
|
await writePage(pageName, newText);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2022-03-29 15:02:28 +00:00
|
|
|
}
|