1import MarkdownIt from "markdown-it";2import { type Html, trusted } from "./html.ts";3import { pathUrl } from "./layout.ts";456export type MarkdownBase = { repo: string; dir: string; at: string | null };78const SCHEME = /^([a-z][a-z0-9+.-]*):/i;9const ALLOWED_SCHEMES = ["http", "https", "mailto"];101112const md = new MarkdownIt({ html: false, linkify: true });13md.validateLink = (url) => {14 const scheme = SCHEME.exec(url.trim());15 return !scheme || ALLOWED_SCHEMES.includes((scheme[1] as string).toLowerCase());16};17md.core.ruler.push("kepr_raw_links", (state) => {18 const base = state.env as MarkdownBase;19 const visit = (tokens: typeof state.tokens) => {20 for (const token of tokens) {21 const attr = token.type === "link_open" ? "href" : token.type === "image" ? "src" : null;22 const url = attr && token.attrGet(attr);23 if (attr && typeof url === "string") token.attrSet(attr, rawUrl(url, base));24 if (token.children) visit(token.children);25 }26 };27 visit(state.tokens);28});2930function rawUrl(url: string, base: MarkdownBase): string {31 if (SCHEME.test(url) || url.startsWith("#") || url.startsWith("//")) return url;32 33 const resolved = new URL(url, `http://repo/${base.dir ? `${pathUrl(base.dir)}/` : ""}`);34 const at = base.at ? `?at=${base.at}` : "";35 return `/${base.repo}/raw${resolved.pathname}${at}${resolved.hash}`;36}3738export function renderMarkdown(source: string, base: MarkdownBase): Html {39 return trusted(md.render(source, base));40}414243export function firstParagraph(44 source: string,45 base: MarkdownBase,46): { html: Html; text: string } | null {47 const tokens = md.parse(source, base);48 const index = tokens.findIndex((token) => token.type === "paragraph_open");49 const inline = tokens[index + 1];50 if (index < 0 || !inline?.children) return null;51 const text = inline.children52 .filter((token) => token.type === "text" || token.type === "code_inline")53 .map((token) => token.content)54 .join("");55 return { html: trusted(md.renderer.renderInline(inline.children, md.options, base)), text };56}