import { findNodeOfType, type ParseTree, traverseTree } from "./tree.ts"; // [[Wikilinks]] use absolute paths and should pass pathToResolve with a leading / to this function // [Markdown links]() are relative unless it has a leading / export function resolvePath( currentPage: string, pathToResolve: string, fullUrl = false, ): string { // [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); } } else { pathToResolve = relativeToAbsolutePath(currentPage, pathToResolve); if (isFederationPath(currentPage) && !isFederationPath(pathToResolve)) { const domainPart = currentPage.split("/")[0]; pathToResolve = domainPart + "/" + pathToResolve; } } if (fullUrl) { pathToResolve = federatedPathToUrl(pathToResolve); } return pathToResolve; } export function federatedPathToUrl(path: string): string { if (!isFederationPath(path)) { return path; } path = path.substring(1); if (path.startsWith("localhost")) { path = "http://" + path; } else { path = "https://" + path; } return path; } export function isFederationPath(path: string): boolean { return path.startsWith("!"); } export function isLocalPath(path: string): boolean { return !path.includes("://") && !path.startsWith("mailto:"); } export function rewritePageRefs(tree: ParseTree, containerPageName: string) { 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; } if (n.type === "WikiLinkPage") { n.children![0].text = resolvePath( containerPageName, "/" + n.children![0].text!, ); return true; } return false; }); } export function rewritePageRefsInString( bodyText: string, containerPageName: string, ): string { return bodyText.replaceAll(/\[\[(.+)\]\]/g, (_match, pageRefName) => { return `[[${resolvePath(containerPageName, "/" + pageRefName)}]]`; }); } export function cleanPageRef(pageRef: string): string { if (pageRef.startsWith("[[") && pageRef.endsWith("]]")) { return pageRef.slice(2, -2); } else { return pageRef; } } export function folderName(path: string): string { return path.split("/").slice(0, -1).join("/"); } export function absoluteToRelativePath(page: string, linkTo: string): string { // 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 { // 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("/"); }