website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


src/_helpers/highlightCode.js
3c79a68 1
import Prism from "prismjs";
3c79a68 2
import loadPrismLanguage from "prismjs/components/index.js";
3c79a68 3
3c79a68 4
// Avoid "Language does not exist" console noise for extensions we don't
3c79a68 5
// have a mapping for — those just fall back to plain, unhighlighted text.
3c79a68 6
loadPrismLanguage.silent = true;
3c79a68 7
3c79a68 8
// File-extension -> Prism language name, for the cases where they don't
3c79a68 9
// already match (or where Prism has no component at all, mapped to null
3c79a68 10
// so we skip straight to plain text instead of a failed load attempt).
3c79a68 11
const EXT_TO_LANG = {
3c79a68 12
  h: "c",
3c79a68 13
  html: "markup",
3c79a68 14
  jsonc: "json",
3c79a68 15
  kt: "kotlin",
3c79a68 16
  lock: "toml",
3c79a68 17
  md: "markdown",
3c79a68 18
  mdx: "markdown",
3c79a68 19
  mjs: "javascript",
3c79a68 20
  plist: "markup",
3c79a68 21
  rs: "rust",
3c79a68 22
  scm: "scheme",
3c79a68 23
  sh: "bash",
3c79a68 24
  ts: "typescript",
3c79a68 25
  mi: null,
3c79a68 26
  nu: null,
3c79a68 27
  plum: null,
3c79a68 28
  txt: null,
3c79a68 29
  typ: null,
3c79a68 30
};
3c79a68 31
44991dd 32
// Fenced-code-block language identifiers (the "rust" in ```rust) mostly
44991dd 33
// already match Prism's own component ids, unlike file extensions —
44991dd 34
// except for a handful of common short forms people actually type in
44991dd 35
// READMEs, which need the same kind of aliasing EXT_TO_LANG does above.
44991dd 36
const FENCE_LANG_ALIASES = {
44991dd 37
  js: "javascript",
44991dd 38
  sh: "bash",
44991dd 39
  rs: "rust",
44991dd 40
  ts: "typescript",
44991dd 41
  yml: "yaml",
44991dd 42
  html: "markup",
44991dd 43
  htm: "markup",
44991dd 44
};
3c79a68 45
44991dd 46
const grammarCache = new Map();
3c79a68 47
44991dd 48
/** Load (and cache) a Prism grammar by its canonical component id. */
44991dd 49
const loadGrammar = (lang) => {
44991dd 50
  if (grammarCache.has(lang)) return grammarCache.get(lang);
44991dd 51
  if (!Prism.languages[lang]) loadPrismLanguage(lang);
44991dd 52
  const grammar = Prism.languages[lang] ?? null;
3c79a68 53
  const result = grammar ? { lang, grammar } : null;
44991dd 54
  grammarCache.set(lang, result);
3c79a68 55
  return result;
3c79a68 56
};
3c79a68 57
44991dd 58
const getLanguage = (ext) => {
44991dd 59
  const lang = Object.prototype.hasOwnProperty.call(EXT_TO_LANG, ext) ? EXT_TO_LANG[ext] : ext;
44991dd 60
  return lang ? loadGrammar(lang) : null;
44991dd 61
};
44991dd 62
3c79a68 63
/**
3c79a68 64
 * Highlight source text for a given file extension using Prism, at build
3c79a68 65
 * time — no client-side highlighter ships to the browser. Falls back to
3c79a68 66
 * (still-escaped) plain text when there's no Prism grammar for the
3c79a68 67
 * extension.
3c79a68 68
 *
3c79a68 69
 * @returns {{ html: string, lang: string }}
3c79a68 70
 */
3c79a68 71
export const highlightCode = (text, ext) => {
3c79a68 72
  const found = getLanguage(ext);
3c79a68 73
  if (!found) {
3c79a68 74
    return { html: escapeHtml(text), lang: ext || "text" };
3c79a68 75
  }
3c79a68 76
  return { html: Prism.highlight(text, found.grammar, found.lang), lang: found.lang };
3c79a68 77
};
3c79a68 78
44991dd 79
/**
44991dd 80
 * Highlight a fenced markdown code block by its declared language (the
44991dd 81
 * "rust" in ```rust), for markdown-it's `highlight` option — used for
44991dd 82
 * repo READMEs, which render through a separate MarkdownIt instance
44991dd 83
 * rather than highlightCode()'s extension-keyed lookup. markdown-it
44991dd 84
 * wraps whatever this returns in <pre><code class="language-{lang}">,
44991dd 85
 * matching the shared pre/code styling and .token.* colors in
44991dd 86
 * shared.css. Falls back to plain escaped text, same as highlightCode.
44991dd 87
 *
44991dd 88
 * @returns {string} inner HTML for markdown-it's fence renderer
44991dd 89
 */
44991dd 90
export const highlightFenceCode = (text, lang) => {
44991dd 91
  const normalized = lang ? (FENCE_LANG_ALIASES[lang.toLowerCase()] ?? lang.toLowerCase()) : null;
44991dd 92
  const found = normalized ? loadGrammar(normalized) : null;
44991dd 93
  return found ? Prism.highlight(text, found.grammar, found.lang) : escapeHtml(text);
44991dd 94
};
44991dd 95
3c79a68 96
const escapeHtml = (str) =>
3c79a68 97
  String(str).replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);