/** * lang-detect.ts — Map file paths to highlight.js language identifiers. * * Pure function, no DOM or hljs dependency — fully unit-testable. * Used by blob.ts and diff.ts for client-side syntax highlighting. */ const EXT_MAP: Record = { py: 'python', pyw: 'python', ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript', mjs: 'javascript', cjs: 'javascript', rs: 'rust', go: 'go', swift: 'swift', kt: 'kotlin', kts: 'kotlin', java: 'java', rb: 'ruby', rake: 'ruby', cpp: 'cpp', cc: 'cpp', cxx: 'cpp', c: 'cpp', h: 'cpp', hpp: 'cpp', hs: 'haskell', json: 'json', jsonc: 'json', yaml: 'yaml', yml: 'yaml', toml: 'toml', bats: 'bash', sh: 'bash', bash: 'bash', zsh: 'bash', html: 'xml', htm: 'xml', xml: 'xml', svg: 'xml', css: 'css', scss: 'css', sass: 'css', sql: 'sql', md: 'markdown', mdx: 'markdown', txt: 'plaintext', }; /** Dotfiles with no extension, keyed by exact filename. */ const FILENAME_MAP: Record = { '.museattributes': 'toml', '.museignore': 'toml', }; /** * Return the highlight.js language identifier for a file path. * Falls back to `'plaintext'` for unknown extensions. */ export function extToLang(path: string): string { const filename = path.split('/').pop() ?? path; if (filename in FILENAME_MAP) return FILENAME_MAP[filename]!; // Zsh completion files: _ with no extension (e.g. completions/_muse) if (filename.startsWith('_') && !filename.includes('.')) return 'bash'; const ext = filename.split('.').pop()?.toLowerCase() ?? ''; return EXT_MAP[ext] ?? 'plaintext'; }