memorium/lib/documents.ts

152 lines
3.9 KiB
TypeScript
Raw Normal View History

2023-07-26 13:47:01 +02:00
import { unified } from "npm:unified";
import remarkParse from "npm:remark-parse";
import remarkStringify from "https://esm.sh/remark-stringify@10.0.3";
import remarkFrontmatter, {
Root,
} from "https://esm.sh/remark-frontmatter@4.0.1";
import remarkRehype from "https://esm.sh/remark-rehype@10.1.0";
import rehypeSanitize from "https://esm.sh/rehype-sanitize@5.0.1";
import rehypeStringify from "https://esm.sh/rehype-stringify@9.0.3";
2023-07-26 13:47:01 +02:00
import { parse } from "https://deno.land/std@0.194.0/yaml/mod.ts";
import * as cache from "@lib/cache/documents.ts";
2023-07-26 13:47:01 +02:00
const SILVERBULLET_SERVER = Deno.env.get("SILVERBULLET_SERVER");
2023-07-26 13:47:01 +02:00
export type Document = {
name: string;
lastModified: number;
contentType: string;
size: number;
perm: string;
};
export function parseFrontmatter(yaml: string) {
return parse(yaml);
}
export async function getDocuments(): Promise<Document[]> {
const cachedDocuments = await cache.getDocuments();
if (cachedDocuments) return cachedDocuments;
2023-07-26 13:47:01 +02:00
const headers = new Headers();
headers.append("Accept", "application/json");
const response = await fetch(SILVERBULLET_SERVER + "/index.json", {
2023-07-26 13:47:01 +02:00
headers: headers,
});
const documents = await response.json();
cache.setDocuments(documents);
return documents;
2023-07-26 13:47:01 +02:00
}
export function createDocument(
2023-07-31 17:21:17 +02:00
name: string,
content: string | ArrayBuffer,
mediaType?: string,
) {
const headers = new Headers();
if (mediaType) {
headers.append("Content-Type", mediaType);
}
return fetch(SILVERBULLET_SERVER + "/" + name, {
2023-07-31 17:21:17 +02:00
body: content,
method: "PUT",
headers,
});
}
2023-07-26 13:47:01 +02:00
export async function getDocument(name: string): Promise<string> {
const cachedDocument = await cache.getDocument(name);
if (cachedDocument) return cachedDocument;
2023-07-26 13:58:38 +02:00
const response = await fetch(SILVERBULLET_SERVER + "/" + name);
const text = await response.text();
cache.setDocument(name, text);
return text;
2023-07-26 13:47:01 +02:00
}
export function transformDocument(input: string, cb: (r: Root) => Root) {
const out = unified()
.use(remarkParse)
.use(remarkFrontmatter, ["yaml"])
.use(() => (tree) => {
return cb(tree);
})
.use(remarkStringify)
.processSync(input);
return String(out)
.replace("***\n", "---")
.replace("----------------", "---")
.replace("\n---", "---")
.replace(/^(date:[^'\n]*)'|'/gm, (match, p1, p2) => {
if (p1) {
// This is a line starting with date: followed by single quotes
return p1.replace(/'/gm, "");
} else if (p2) {
return "";
} else {
// This is a line with single quotes, but not starting with date:
return match;
}
});
}
2023-07-26 13:47:01 +02:00
export function parseDocument(doc: string) {
return unified()
.use(remarkParse).use(remarkFrontmatter, ["yaml", "toml"])
.parse(doc);
}
2023-07-30 19:40:39 +02:00
export function renderMarkdown(doc: string) {
const out = unified()
.use(remarkParse)
.use(remarkRehype)
.use(rehypeSanitize)
.use(rehypeStringify)
.processSync(doc);
return String(out);
}
2023-07-26 13:47:01 +02:00
export type ParsedDocument = ReturnType<typeof parseDocument>;
export type DocumentChild = ParsedDocument["children"][number];
export function findRangeOfChildren(children: DocumentChild[]) {
const firstChild = children[0];
const lastChild = children.length > 1
? children[children.length - 1]
: firstChild;
const start = firstChild.position?.start.offset;
const end = lastChild.position?.end.offset;
if (typeof start !== "number" || typeof end !== "number") return;
return [start, end];
}
export function getTextOfRange(children: DocumentChild[], text: string) {
if (!children || children.length === 0) {
return;
}
const range = findRangeOfChildren(children);
if (!range) return;
return text.substring(range[0], range[1]);
}
export function getTextOfChild(child: DocumentChild): string | undefined {
if ("value" in child) return child.value;
if ("children" in child) {
return getTextOfChild(child.children[0]);
}
return;
}