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}