SilverBullet pivot to become an offline-first PWA (#403)

This commit is contained in:
Zef Hemel
2023-05-23 20:53:53 +02:00
committed by GitHub
parent b256269897
commit 5f484bed57
389 changed files with 4484 additions and 291129 deletions
+15 -15
View File
@@ -1,24 +1,24 @@
import * as YAML from "yaml";
import { YAML } from "$sb/plugos-syscall/mod.ts";
import {
addParentPointers,
findNodeOfType,
ParseTree,
renderToText,
replaceNodesMatching,
traverseTree,
replaceNodesMatchingAsync,
traverseTreeAsync,
} from "$sb/lib/tree.ts";
// Extracts front matter (or legacy "meta" code blocks) from a markdown document
// optionally removes certain keys from the front matter
export function extractFrontmatter(
export async function extractFrontmatter(
tree: ParseTree,
removeKeys: string[] = [],
): any {
): Promise<any> {
let data: any = {};
addParentPointers(tree);
replaceNodesMatching(tree, (t) => {
await replaceNodesMatchingAsync(tree, async (t) => {
// Find top-level hash tags
if (t.type === "Hashtag") {
// Check if if nested directly into a Paragraph
@@ -38,7 +38,7 @@ export function extractFrontmatter(
const yamlNode = t.children![1].children![0];
const yamlText = renderToText(yamlNode);
try {
const parsedData: any = YAML.parse(yamlText);
const parsedData: any = await YAML.parse(yamlText);
const newData = { ...parsedData };
data = { ...data, ...parsedData };
if (removeKeys.length > 0) {
@@ -51,7 +51,7 @@ export function extractFrontmatter(
}
}
if (removedOne) {
yamlNode.text = YAML.stringify(newData);
yamlNode.text = await YAML.stringify(newData);
}
}
// If nothing is left, let's just delete this whole block
@@ -92,7 +92,7 @@ export function extractFrontmatter(
}
}
if (removedOne) {
codeTextNode.children![0].text = YAML.stringify(newData).trim();
codeTextNode.children![0].text = (await YAML.stringify(newData)).trim();
}
}
// If nothing is left, let's just delete this whole block
@@ -112,26 +112,26 @@ export function extractFrontmatter(
}
// Updates the front matter of a markdown document and returns the text as a rendered string
export function prepareFrontmatterDispatch(
export async function prepareFrontmatterDispatch(
tree: ParseTree,
data: Record<string, any>,
): any {
): Promise<any> {
let dispatchData: any = null;
traverseTree(tree, (t) => {
await traverseTreeAsync(tree, async (t) => {
// Find FrontMatter and parse it
if (t.type === "FrontMatter") {
const bodyNode = t.children![1].children![0];
const yamlText = renderToText(bodyNode);
try {
const parsedYaml = YAML.parse(yamlText) as any;
const parsedYaml = await YAML.parse(yamlText) as any;
const newData = { ...parsedYaml, ...data };
// Patch inline
dispatchData = {
changes: {
from: bodyNode.from,
to: bodyNode.to,
insert: YAML.stringify(newData, { noArrayIndent: true }),
insert: await YAML.stringify(newData),
},
};
} catch (e: any) {
@@ -147,7 +147,7 @@ export function prepareFrontmatterDispatch(
changes: {
from: 0,
to: 0,
insert: "---\n" + YAML.stringify(data, { noArrayIndent: true }) +
insert: "---\n" + await YAML.stringify(data) +
"---\n",
},
};
+2 -2
View File
@@ -17,7 +17,7 @@ export async function readSecrets(keys: string[]): Promise<any[]> {
}
return collectedSecrets;
} catch (e: any) {
if (e.message === "Page not found") {
if (e.message === "Not found") {
throw new Error(`No such secret: ${keys[0]}`);
}
throw e;
@@ -34,7 +34,7 @@ export async function readSecret(key: string): Promise<any> {
}
return val;
} catch (e: any) {
if (e.message === "Page not found") {
if (e.message === "Not found") {
throw new Error(`No such secret: ${key}`);
}
throw e;
+6 -7
View File
@@ -1,6 +1,6 @@
import { readYamlPage } from "./yaml_page.ts";
import { notifyUser } from "./util.ts";
import * as YAML from "yaml";
import { YAML } from "$sb/plugos-syscall/mod.ts";
import { space } from "$sb/silverbullet-syscall/mod.ts";
@@ -30,7 +30,7 @@ export async function readSettings<T extends object>(settings: T): Promise<T> {
}
return collectedSettings as T;
} catch (e: any) {
if (e.message === "Page not found") {
if (e.message === "Not found") {
// No settings yet, return default values for all
return settings;
}
@@ -47,7 +47,7 @@ export async function readSetting(
const val = allSettings[key];
return val === undefined ? defaultValue : val;
} catch (e: any) {
if (e.message === "Page not found") {
if (e.message === "Not found") {
// No settings yet, return default values for all
return defaultValue;
}
@@ -71,10 +71,9 @@ export async function writeSettings<T extends object>(settings: T) {
// const doc = new YAML.Document();
// doc.contents = writeSettings;
const contents =
`This page contains settings for configuring SilverBullet and its Plugs.\nAny changes outside of the yaml block will be overwritten.\n\`\`\`yaml\n${
YAML.stringify(
`This page contains settings for configuring SilverBullet and its Plugs.\nAny changes outside of the yaml block will be overwritten.\n\`\`\`yaml\n${await YAML
.stringify(
writeSettings,
)
}\n\`\`\``; // might need \r\n for windows?
)}\n\`\`\``; // might need \r\n for windows?
await space.writePage(SETTINGS_PAGE, contents);
}
+2 -2
View File
@@ -73,7 +73,7 @@ Deno.test("Test parsing", () => {
};
}
});
console.log(JSON.stringify(mdTree, null, 2));
// console.log(JSON.stringify(mdTree, null, 2));
let mdTree3 = parse(lang, mdTest3);
console.log(JSON.stringify(mdTree3, null, 2));
// console.log(JSON.stringify(mdTree3, null, 2));
});
+51
View File
@@ -69,6 +69,25 @@ export function collectNodesMatching(
return results;
}
export async function collectNodesMatchingAsync(
tree: ParseTree,
matchFn: (tree: ParseTree) => Promise<boolean>,
): Promise<ParseTree[]> {
if (await matchFn(tree)) {
return [tree];
}
let results: ParseTree[] = [];
if (tree.children) {
for (const child of tree.children) {
results = [
...results,
...await collectNodesMatchingAsync(child, matchFn),
];
}
}
return results;
}
// return value: returning undefined = not matched, continue, null = delete, new node = replace
export function replaceNodesMatching(
tree: ParseTree,
@@ -93,6 +112,29 @@ export function replaceNodesMatching(
}
}
export async function replaceNodesMatchingAsync(
tree: ParseTree,
substituteFn: (tree: ParseTree) => Promise<ParseTree | null | undefined>,
) {
if (tree.children) {
const children = tree.children.slice();
for (const child of children) {
const subst = await substituteFn(child);
if (subst !== undefined) {
const pos = tree.children.indexOf(child);
if (subst) {
tree.children.splice(pos, 1, subst);
} else {
// null = delete
tree.children.splice(pos, 1);
}
} else {
replaceNodesMatchingAsync(child, substituteFn);
}
}
}
}
export function findNodeMatching(
tree: ParseTree,
matchFn: (tree: ParseTree) => boolean,
@@ -116,6 +158,15 @@ export function traverseTree(
collectNodesMatching(tree, matchFn);
}
export async function traverseTreeAsync(
tree: ParseTree,
// Return value = should stop traversal?
matchFn: (tree: ParseTree) => Promise<boolean>,
): Promise<void> {
// Do a collect, but ignore the result
await collectNodesMatchingAsync(tree, matchFn);
}
// Finds non-text node at position
export function nodeAtPos(tree: ParseTree, pos: number): ParseTree | null {
if (pos < tree.from! || pos >= tree.to!) {
+2 -4
View File
@@ -1,6 +1,6 @@
import { findNodeOfType, traverseTree } from "$sb/lib/tree.ts";
import { markdown, space } from "$sb/silverbullet-syscall/mod.ts";
import * as YAML from "yaml";
import { YAML } from "$sb/plugos-syscall/mod.ts";
export async function readCodeBlockPage(
pageName: string,
@@ -58,8 +58,6 @@ export async function writeYamlPage(
data: any,
prelude = "",
): Promise<void> {
const text = YAML.stringify(data, {
noCompatMode: true,
});
const text = await YAML.stringify(data);
await space.writePage(pageName, prelude + "```yaml\n" + text + "\n```");
}