1import {2 type BundledLanguage,3 bundledLanguages,4 createCssVariablesTheme,5 createHighlighter,6 type Highlighter,7} from "shiki";8import { type Html, html } from "./html.ts";9101112const theme = createCssVariablesTheme({ name: "kepr", variablePrefix: "--tk-" });13const ROLE = /^var\(--tk-token-([a-z-]+)\)$/;14const ITALIC = 1;1516let highlighter: Promise<Highlighter> | undefined;1718function languageOf(path: string): BundledLanguage | null {19 const name = path.split("/").pop()?.toLowerCase() ?? "";20 const ext = name.includes(".") ? (name.split(".").pop() as string) : name;21 return ext in bundledLanguages ? (ext as BundledLanguage) : null;22}232425export async function highlightLines(code: string, path: string): Promise<Html[]> {26 const lines = code.replace(/\n$/, "").split("\n");27 const lang = languageOf(path);28 if (!lang) return lines.map((line) => html`${line}`);29 highlighter ??= createHighlighter({ themes: [theme], langs: [] });30 const shiki = await highlighter;31 await shiki.loadLanguage(lang);32 return shiki.codeToTokensBase(code.replace(/\n$/, ""), { lang, theme }).map(33 (tokens) =>34 html`${tokens.map((token) => {35 const role = ROLE.exec(token.color ?? "")?.[1];36 const classes = [role && `tk-${role}`, (token.fontStyle ?? 0) & ITALIC && "tk-i"].filter(37 Boolean,38 );39 return classes.length40 ? html`<span class="${classes.join(" ")}">${token.content}</span>`41 : token.content;42 })}`,43 );44}