links.ts
2.6 kB · typescript · 77 lines
1import { dirname, isAbsolute, relative, resolve as under } from "node:path";2import { config as gitConfig, dirRoute, isGit, link as gitLink, owner } from "../git/git.ts";3import { label, type Site } from "./build.ts";45/* TYPES */67export type Index = { base: string; slug: string; branch: string; map: Map<string, string>; git: Set<string> };89/* INDEX */1011export function index(site: Site): Index {12 const git = gitConfig(site);13 const roots = new Set(Object.values(site.inputs ?? {}).map((one) => one.path));14 const map = new Map<string, string>();15 const put = (file: string, route: string) => {16 if (!file) return;17 const name = label(site, file);18 map.set(file, route);19 if (name.includes("/") || roots.has(file)) map.set(name, route);20 };21 for (const route of site.routes) {22 if (isGit(route)) continue;23 if (route.source) put(route.source, route.route);24 for (const one of route.urls ?? []) if (one.source) put(one.source, one.route);25 }26 return {27 base: git?.root ?? site.root,28 slug: git?.slug ?? "",29 branch: git?.branch ?? "main",30 map,31 git: new Set(site.routes.filter(isGit).map((route) => route.route)),32 };33}3435/* STAMP */3637export const stamp = (idx: Index) =>38 JSON.stringify([39 [...idx.map].filter(([key]) => key.startsWith(idx.base)).map(([key, route]) => [key.slice(idx.base.length), route]),40 [...idx.git],41 ]);4243/* RESOLVE */4445const OUT = /^(https?:|mailto:|tel:|#|\/)/;4647const keys = (path: string) => [path, `${path}.md`, path.replace(/\.md$/, "")];4849const known = (idx: Index, path: string) => {50 for (const key of keys(path)) {51 const hit = idx.map.get(key);52 if (hit) return hit;53 }54 return "";55};5657export function resolve(site: Site, from: string, url: string): string {58 const idx = site.index;59 if (!idx || OUT.test(url)) return url;60 const cut = url.search(/[#?]/);61 const tail = cut < 0 ? "" : url.slice(cut);62 const head = cut < 0 ? url : url.slice(0, cut);63 if (!head) return url;64 const file = under(idx.base, from);65 const target = under(dirname(file), head);66 const path = relative(idx.base, target);67 const hit = known(idx, target) || known(idx, path);68 if (hit) return hit + tail;69 if (!path || path.startsWith("..") || isAbsolute(path)) return url;70 if (idx.git.size) {71 const where = gitLink(relative(idx.base, dirname(file)), head);72 const page = where.startsWith("/raw/") ? (owner(where) ?? "") : where;73 if (idx.git.has(page)) return where + tail;74 return idx.git.has(dirRoute(path)) ? dirRoute(path) + tail : url;75 }76 return idx.slug ? `https://github.com/${idx.slug}/blob/${idx.branch}/${path}${tail}` : url;77}