From 2e7abe418e40671b0123c29ab260af5f8fcdc164 Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Fri, 10 Apr 2026 12:31:24 -0500 Subject: [PATCH] Normalize React language ids for syntax highlighting - Map `typescriptreact` and `javascriptreact` to Shiki-supported languages - Use normalized cache keys and fall back cleanly when highlighting fails --- apps/web/src/lib/syntaxHighlighting.ts | 31 ++++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/apps/web/src/lib/syntaxHighlighting.ts b/apps/web/src/lib/syntaxHighlighting.ts index 20ad44a01..df475043b 100644 --- a/apps/web/src/lib/syntaxHighlighting.ts +++ b/apps/web/src/lib/syntaxHighlighting.ts @@ -4,6 +4,21 @@ import { LRUCache } from "./lruCache"; import { fnv1a32, resolveDiffThemeName, type DiffThemeName } from "./diffRendering"; const CODE_FENCE_LANGUAGE_REGEX = /(?:^|\s)language-([^\s]+)/; + +/** + * Map VSCode language identifiers that don't match Shiki's bundled language names. + * VSCode uses e.g. "typescriptreact" / "javascriptreact" while Shiki expects "tsx" / "jsx". + */ +const VSCODE_TO_SHIKI_LANG: Record = { + typescriptreact: "tsx", + javascriptreact: "jsx", +}; + +/** Normalise a language identifier so Shiki can resolve it. */ +function normalizeLanguage(language: string): string { + return VSCODE_TO_SHIKI_LANG[language] ?? language; +} + const MAX_HIGHLIGHT_CACHE_ENTRIES = 500; const MAX_HIGHLIGHT_CACHE_MEMORY_BYTES = 50 * 1024 * 1024; const highlightedCodeCache = new LRUCache( @@ -61,16 +76,17 @@ export function setCachedHighlightedHtml( } export function getHighlighterPromise(language: string): Promise { - const cached = highlighterPromiseCache.get(language); + const normalized = normalizeLanguage(language); + const cached = highlighterPromiseCache.get(normalized); if (cached) return cached; const promise = getSharedHighlighter({ themes: [resolveDiffThemeName("dark"), resolveDiffThemeName("light")], - langs: [language as SupportedLanguages], + langs: [normalized as SupportedLanguages], preferredHighlighter: "shiki-js", }).catch((err) => { - highlighterPromiseCache.delete(language); - if (language === "text") { + highlighterPromiseCache.delete(normalized); + if (normalized === "text") { // "text" itself failed — Shiki cannot initialize at all, surface the error throw err; } @@ -78,7 +94,7 @@ export function getHighlighterPromise(language: string): Promise