diff --git a/src/lib/utils/codeHighlight.ts b/src/lib/utils/codeHighlight.ts index fcd8443530..0e33dd3872 100644 --- a/src/lib/utils/codeHighlight.ts +++ b/src/lib/utils/codeHighlight.ts @@ -1,5 +1,3 @@ -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. @@ -53,7 +51,97 @@ const EXT_OVERRIDE: Record = { jsonl: 'jsonl' }; -const _langSet = new Set(Object.keys(bundledLanguages)); +// Common extensions that exactly match their Shiki language ID. +// This replaces the runtime `bundledLanguages` import from shiki, which +// pulled ~5-10MB of JavaScript into the initial page load just so +// isCodeFile() could check extension support. +const KNOWN_LANG_IDS = new Set([ + 'ada', + 'awk', + 'bat', + 'c', + 'cmake', + 'clojure', + 'cpp', + 'crystal', + 'css', + 'd', + 'dart', + 'diff', + 'elixir', + 'elm', + 'erlang', + 'fish', + 'gleam', + 'glsl', + 'go', + 'groovy', + 'haml', + 'haskell', + 'hlsl', + 'html', + 'ini', + 'java', + 'javascript', + 'json', + 'json5', + 'jsonc', + 'jsx', + 'julia', + 'kotlin', + 'latex', + 'less', + 'lisp', + 'log', + 'lua', + 'make', + 'markdown', + 'matlab', + 'mdx', + 'mojo', + 'nim', + 'nix', + 'nushell', + 'ocaml', + 'pascal', + 'perl', + 'php', + 'postcss', + 'powershell', + 'prisma', + 'prolog', + 'proto', + 'pug', + 'python', + 'r', + 'ruby', + 'rust', + 'sass', + 'scala', + 'scheme', + 'scss', + 'solidity', + 'sql', + 'svelte', + 'swift', + 'tcl', + 'terraform', + 'tex', + 'toml', + 'tsx', + 'typescript', + 'typst', + 'v', + 'vb', + 'verilog', + 'vhdl', + 'vue', + 'wasm', + 'wgsl', + 'xml', + 'yaml', + 'zig' +]); /** * Resolve a file extension to a Shiki language id, or null if not supported. @@ -62,8 +150,8 @@ 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; + // if the extension itself is a known language id (e.g. 'go', 'rust', 'sql', 'toml', ...) + if (KNOWN_LANG_IDS.has(lower)) return lower; return null; } @@ -79,11 +167,16 @@ export function isCodeFile(path: string | null): boolean { /** * Highlight code using Shiki with dual light/dark themes via CSS variables. * Returns an HTML string. Throws on failure. + * + * Shiki is loaded on demand (dynamic import) to avoid pulling ~5-10MB of + * JavaScript into the initial page bundle. Since this function is already + * async, callers are completely unaffected by the change. */ export async function highlightCode(code: string, filePath: string): Promise { const ext = filePath.split('.').pop()?.toLowerCase() ?? ''; const lang = extToLang(ext) ?? 'text'; + const { codeToHtml } = await import('shiki'); return await codeToHtml(code, { lang, themes: { @@ -93,3 +186,4 @@ export async function highlightCode(code: string, filePath: string): Promise