astro-loader-obsidian
Version:
Use your Obsidian Vault in Astro projects
129 lines (98 loc) • 3.18 kB
text/typescript
import path from "node:path";
import { slugify } from "./utils/slugify";
const fileName = (str: string) => path.basename(str, path.extname(str));
// biome-ignore lint/suspicious/noMisleadingCharacterClass: <explanation>
const ascii = (s: string) => s.normalize("NFKD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
const asciiCompare = (str: string, that: string) => {
return ascii(str) === (ascii(that));
}
const asciiInclude = (str: string, that: string) => {
return ascii(str).includes(ascii(that));
}
const nameEquals = (str: string, that: string) => {
return asciiCompare(
fileName(str),
fileName(that),
);
}
const localeContains = (str: string, sub: string) => {
if(sub==="") return true;
if(!sub || !str.length) return false;
const subStr = `${sub}`;
if(subStr.length>str.length) return false;
return asciiInclude(str, subStr);
}
export const matches = (id: string, thatId: string) => {
return localeContains(id, thatId) && nameEquals(id, thatId);
}
const closest = (link: string) => (a: string, b: string) => {
const aMismatch = link.replace(a.replace('.md', ''), "").length;
const bMismatch = link.replace(b.replace('.md', ''), "").length;
if (aMismatch === 0) {
return -1;
}
if (bMismatch === 0) {
return 1;
}
return bMismatch - aMismatch;
}
export const getDocumentFromLink = (
link: string,
files: string[]
): string|undefined => {
// return the most precise match
const entries = files.filter(id => matches(id, link));
// sort results by the length of the mismatch. The closest the match, the first
return entries.sort(closest(link))?.[0];
};
export const getAssetFromLink = (
link: string,
assets: string[]
): string => {
let linkStr = link;
const regex = /(!)?\[\[([^\]]+?)\]\]/g // /\[\[(!)?([\w/]+)\]\]/g;
const match = linkStr.matchAll(regex).next();
if (match.value) {
linkStr = match.value[2] as string;
}
// return the most precise match
const matches = assets.filter((id) => id.includes(linkStr));
return matches.sort((a, b) => {
const aMismatch = linkStr.replace(a, "").length;
const bMismatch = linkStr.replace(b, "").length;
return bMismatch - aMismatch;
})[0] as string;
};
export const toSlug = (
entry: string,
i18n?: boolean,
permalink?: string
): [string, string|undefined] => {
let entrySlug = slugify(entry);
let language: string | undefined;
if (i18n) {
const [entryLanguage, ...entryPath] = entry.split(path.sep);
language = entryLanguage;
entrySlug = slugify(entryPath.join("/"));
}
const slug = entrySlug;
return [slug, language];
};
export const toUrl = (
entry: string,
baseUrl: string,
i18n?: boolean,
defaultLocale?: string,
permalink?: string
): string => {
const [slug, language] = toSlug(entry, i18n, permalink);
const base = baseUrl.replace(/^\/+|\/+$/g, '');
const urlParts = [base, slug].filter(p => p.length > 0 && p !== '/');
if (i18n && language !== defaultLocale) {
urlParts.unshift(language as string);
}
const pathname = urlParts.join("/");
return pathname.startsWith("/")
? pathname
: `/${pathname}`;
};