import { codeToHtml, bundledLanguages } from 'shiki';
/**
* Map file extensions to Shiki language identifiers.
* Only extensions whose Shiki lang id differs from the extension itself need explicit entries.
*/
const EXT_OVERRIDE: Record = {
py: 'python',
js: 'javascript',
ts: 'typescript',
jsx: 'jsx',
tsx: 'tsx',
rb: 'ruby',
rs: 'rust',
kt: 'kotlin',
cs: 'csharp',
fs: 'fsharp',
sh: 'bash',
bash: 'bash',
zsh: 'bash',
yml: 'yaml',
md: 'markdown',
mdx: 'mdx',
dockerfile: 'dockerfile',
tf: 'terraform',
hcl: 'hcl',
ex: 'elixir',
exs: 'elixir',
erl: 'erlang',
hs: 'haskell',
ml: 'ocaml',
mli: 'ocaml',
pl: 'perl',
pm: 'perl',
r: 'r',
m: 'objective-c',
mm: 'objective-cpp',
h: 'c',
hpp: 'cpp',
cc: 'cpp',
cxx: 'cpp',
proto: 'proto',
nim: 'nim',
zig: 'zig',
v: 'v',
svelte: 'svelte',
vue: 'vue',
astro: 'astro',
prisma: 'prisma',
graphql: 'graphql',
gql: 'graphql',
jsonc: 'jsonc',
jsonl: 'jsonl'
};
const _langSet = new Set(Object.keys(bundledLanguages));
/**
* Resolve a file extension to a Shiki language id, or null if not supported.
*/
export function extToLang(ext: string): string | null {
const lower = ext.toLowerCase();
// explicit override first
if (EXT_OVERRIDE[lower]) return EXT_OVERRIDE[lower];
// if the extension itself is a bundled language id (e.g. 'go', 'rust', 'sql', 'toml', ...)
if (_langSet.has(lower)) return lower;
return null;
}
/**
* Returns true if the given file path has a code-file extension that Shiki can highlight.
*/
export function isCodeFile(path: string | null): boolean {
if (!path) return false;
const ext = path.split('.').pop()?.toLowerCase() ?? '';
return extToLang(ext) !== null;
}
/**
* Highlight code using Shiki with dual light/dark themes via CSS variables.
* Returns an HTML string. Throws on failure.
*/
export async function highlightCode(code: string, filePath: string): Promise {
const ext = filePath.split('.').pop()?.toLowerCase() ?? '';
const lang = extToLang(ext) ?? 'text';
return await codeToHtml(code, {
lang,
themes: {
light: 'github-light',
dark: 'github-dark'
},
defaultColor: 'light'
});
}