import { createHighlighterCore } from 'shiki/core'; import { createJavaScriptRegexEngine } from 'shiki/engine/javascript'; export type CodeTheme = 'light' | 'dark'; const LANGUAGE_LOADERS = { bash: () => import('@shikijs/langs/bash'), c: () => import('@shikijs/langs/c'), cpp: () => import('@shikijs/langs/cpp'), csharp: () => import('@shikijs/langs/csharp'), css: () => import('@shikijs/langs/css'), dart: () => import('@shikijs/langs/dart'), diff: () => import('@shikijs/langs/diff'), dockerfile: () => import('@shikijs/langs/dockerfile'), go: () => import('@shikijs/langs/go'), graphql: () => import('@shikijs/langs/graphql'), html: () => import('@shikijs/langs/html'), java: () => import('@shikijs/langs/java'), javascript: () => import('@shikijs/langs/javascript'), json: () => import('@shikijs/langs/json'), jsonc: () => import('@shikijs/langs/jsonc'), jsx: () => import('@shikijs/langs/jsx'), kotlin: () => import('@shikijs/langs/kotlin'), less: () => import('@shikijs/langs/less'), lua: () => import('@shikijs/langs/lua'), markdown: () => import('@shikijs/langs/markdown'), mdx: () => import('@shikijs/langs/mdx'), nginx: () => import('@shikijs/langs/nginx'), perl: () => import('@shikijs/langs/perl'), php: () => import('@shikijs/langs/php'), powershell: () => import('@shikijs/langs/powershell'), python: () => import('@shikijs/langs/python'), r: () => import('@shikijs/langs/r'), regex: () => import('@shikijs/langs/regex'), ruby: () => import('@shikijs/langs/ruby'), rust: () => import('@shikijs/langs/rust'), scala: () => import('@shikijs/langs/scala'), scss: () => import('@shikijs/langs/scss'), shellscript: () => import('@shikijs/langs/shellscript'), sql: () => import('@shikijs/langs/sql'), swift: () => import('@shikijs/langs/swift'), toml: () => import('@shikijs/langs/toml'), tsx: () => import('@shikijs/langs/tsx'), typescript: () => import('@shikijs/langs/typescript'), xml: () => import('@shikijs/langs/xml'), yaml: () => import('@shikijs/langs/yaml'), } as const; type SupportedCodeLanguage = keyof typeof LANGUAGE_LOADERS; export type HighlightLanguage = SupportedCodeLanguage | 'text'; const LANGUAGE_ALIASES: Readonly> = { 'c#': 'csharp', 'c++': 'cpp', bat: 'shellscript', cmd: 'shellscript', cs: 'csharp', docker: 'dockerfile', gql: 'graphql', htm: 'html', js: 'javascript', md: 'markdown', ps1: 'powershell', py: 'python', rb: 'ruby', rs: 'rust', sh: 'bash', shell: 'shellscript', ts: 'typescript', yml: 'yaml', }; const THEME_NAMES: Record = { light: 'github-light', dark: 'github-dark', }; const highlighterPromise = createHighlighterCore({ themes: [ import('@shikijs/themes/github-light'), import('@shikijs/themes/github-dark'), ], langs: [], engine: createJavaScriptRegexEngine(), }); const languagePromises = new Map>(); export function normalizeCodeLanguage(value: string): HighlightLanguage { const candidate = value.trim().toLowerCase(); if (!candidate || ['text', 'txt', 'plain', 'plaintext'].includes(candidate)) return 'text'; const aliased = LANGUAGE_ALIASES[candidate] ?? candidate; return Object.hasOwn(LANGUAGE_LOADERS, aliased) ? aliased as SupportedCodeLanguage : 'text'; } export function codeThemeName(theme: CodeTheme): 'github-light' | 'github-dark' { return THEME_NAMES[theme]; } export async function getCodeHighlighter(language: HighlightLanguage) { const highlighter = await highlighterPromise; if (language === 'text' || highlighter.getLoadedLanguages().includes(language)) return highlighter; let loading = languagePromises.get(language); if (!loading) { loading = highlighter.loadLanguage(LANGUAGE_LOADERS[language]()).then(() => undefined); languagePromises.set(language, loading); } await loading; return highlighter; }