silverbullet/plug-api/lib/resolve.ts

146 lines
4.0 KiB
TypeScript
Raw Normal View History

2024-07-30 23:33:33 +08:00
import { findNodeOfType, type ParseTree, traverseTree } from "./tree.ts";
2023-07-30 17:30:01 +08:00
2024-05-28 02:33:41 +08:00
// [[Wikilinks]] use absolute paths and should pass pathToResolve with a leading / to this function
// [Markdown links]() are relative unless it has a leading /
2023-07-30 05:41:37 +08:00
export function resolvePath(
currentPage: string,
pathToResolve: string,
fullUrl = false,
): string {
2024-05-28 02:33:41 +08:00
// [Markdown links]() with spaces in the url need to be uri encoded or wrapped in <>
if (pathToResolve.startsWith("<") && pathToResolve.endsWith(">")) {
pathToResolve = pathToResolve.slice(1, -1);
}
if (isFederationPath(pathToResolve)) {
return pathToResolve;
} else if (pathToResolve.startsWith("/")) {
if (isFederationPath(currentPage)) {
const domainPart = currentPage.split("/")[0];
pathToResolve = domainPart + pathToResolve;
} else {
pathToResolve = pathToResolve.slice(1);
2023-07-30 05:41:37 +08:00
}
} else {
2024-05-28 02:33:41 +08:00
pathToResolve = relativeToAbsolutePath(currentPage, pathToResolve);
2023-07-30 14:56:44 +08:00
2024-05-28 02:33:41 +08:00
if (isFederationPath(currentPage) && !isFederationPath(pathToResolve)) {
const domainPart = currentPage.split("/")[0];
pathToResolve = domainPart + "/" + pathToResolve;
}
2023-12-20 00:55:11 +08:00
}
2024-05-28 02:33:41 +08:00
if (fullUrl) {
pathToResolve = federatedPathToUrl(pathToResolve);
2023-12-20 00:55:11 +08:00
}
2024-05-28 02:33:41 +08:00
return pathToResolve;
2023-12-20 00:55:11 +08:00
}
2023-07-30 17:30:01 +08:00
export function federatedPathToUrl(path: string): string {
2024-05-28 02:33:41 +08:00
if (!isFederationPath(path)) {
2023-12-20 00:55:11 +08:00
return path;
}
2023-07-30 17:30:01 +08:00
path = path.substring(1);
if (path.startsWith("localhost")) {
path = "http://" + path;
} else {
path = "https://" + path;
}
return path;
}
export function isFederationPath(path: string): boolean {
2023-07-30 14:56:44 +08:00
return path.startsWith("!");
}
2023-07-30 17:30:01 +08:00
export function isLocalPath(path: string): boolean {
2024-05-28 02:33:41 +08:00
return !path.includes("://") && !path.startsWith("mailto:");
}
export function rewritePageRefs(tree: ParseTree, containerPageName: string) {
2023-07-30 17:30:01 +08:00
traverseTree(tree, (n): boolean => {
if (n.type === "FencedCode") {
const codeInfo = findNodeOfType(n, "CodeInfo");
if (!codeInfo) {
return true;
}
if (!["query", "template"].includes(codeInfo.children![0].text!)) {
return true;
}
const codeText = findNodeOfType(n, "CodeText");
if (!codeText) {
return true;
}
let bodyText = codeText.children![0].text!;
bodyText = rewritePageRefsInString(bodyText, containerPageName);
codeText.children![0].text = bodyText;
return true;
}
2023-07-30 17:30:01 +08:00
if (n.type === "WikiLinkPage") {
n.children![0].text = resolvePath(
containerPageName,
2024-05-28 02:33:41 +08:00
"/" + n.children![0].text!,
);
2023-07-30 17:30:01 +08:00
return true;
}
return false;
});
}
export function rewritePageRefsInString(
bodyText: string,
containerPageName: string,
): string {
return bodyText.replaceAll(/\[\[(.+)\]\]/g, (_match, pageRefName) => {
2024-05-28 02:33:41 +08:00
return `[[${resolvePath(containerPageName, "/" + pageRefName)}]]`;
});
}
export function cleanPageRef(pageRef: string): string {
if (pageRef.startsWith("[[") && pageRef.endsWith("]]")) {
return pageRef.slice(2, -2);
} else {
return pageRef;
}
}
2023-12-20 00:55:11 +08:00
export function folderName(path: string): string {
2023-12-20 00:55:11 +08:00
return path.split("/").slice(0, -1).join("/");
}
2024-05-28 02:33:41 +08:00
export function absoluteToRelativePath(page: string, linkTo: string): string {
2024-05-28 02:33:41 +08:00
// Remove leading /
page = page.startsWith("/") ? page.slice(1) : page;
linkTo = linkTo.startsWith("/") ? linkTo.slice(1) : linkTo;
const splitLink = linkTo.split("/");
const splitPage = page.split("/");
splitPage.pop();
while (splitPage && splitPage[0] === splitLink[0]) {
splitPage.shift();
splitLink.shift();
}
splitPage.fill("..");
return [...splitPage, ...splitLink].join("/");
}
export function relativeToAbsolutePath(page: string, linkTo: string): string {
2024-05-28 02:33:41 +08:00
// Remove leading /
page = page.startsWith("/") ? page.slice(1) : page;
linkTo = linkTo.startsWith("/") ? linkTo.slice(1) : linkTo;
const splitPage = page.split("/").slice(0, -1);
const splitLink = linkTo.split("/");
while (splitLink && splitLink[0] === "..") {
splitPage.pop();
splitLink.shift();
}
return [...splitPage, ...splitLink].join("/");
}