frierena personal git archive

frieren

a self-hosted git server in one binary — everyone reads, only the owner writes

frieren / web / lib / shiki.tsmaster

1.7 KB · raw

import { createHighlighter, type Highlighter } from "shiki";

const LANGS = [
  "typescript", "tsx", "javascript", "jsx", "json", "go", "rust", "python",
  "c", "cpp", "css", "html", "yaml", "toml", "markdown", "bash", "sql",
  "java", "swift", "kotlin", "ruby", "php", "docker", "make", "diff",
];

const EXT_TO_LANG: Record<string, string> = {
  ts: "typescript", mts: "typescript", cts: "typescript", tsx: "tsx",
  js: "javascript", mjs: "javascript", cjs: "javascript", jsx: "jsx",
  json: "json", go: "go", rs: "rust", py: "python",
  c: "c", h: "c", cpp: "cpp", cc: "cpp", hpp: "cpp",
  css: "css", html: "html", htm: "html",
  yml: "yaml", yaml: "yaml", toml: "toml", md: "markdown",
  sh: "bash", bash: "bash", zsh: "bash", sql: "sql",
  java: "java", swift: "swift", kt: "kotlin", rb: "ruby", php: "php",
  dockerfile: "docker", patch: "diff", diff: "diff",
};

let highlighter: Promise<Highlighter> | null = null;

function getHighlighter(): Promise<Highlighter> {
  highlighter ??= createHighlighter({ themes: ["vitesse-dark"], langs: LANGS });
  return highlighter;
}

export function langForFile(name: string): string | null {
  const base = name.toLowerCase().split("/").pop() ?? "";
  if (base === "makefile") return "make";
  if (base === "dockerfile") return "docker";
  const ext = base.includes(".") ? base.split(".").pop()! : "";
  return EXT_TO_LANG[ext] ?? null;
}

// highlight returns shiki HTML for known languages, null otherwise
// (the caller renders a plain <pre> instead).
export async function highlight(code: string, lang: string | null): Promise<string | null> {
  if (!lang) return null;
  try {
    const hl = await getHighlighter();
    return hl.codeToHtml(code, { lang, theme: "vitesse-dark" });
  } catch {
    return null;
  }
}