File size: 3,982 Bytes
21ad36a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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<Record<string, SupportedCodeLanguage>> = {
  '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<CodeTheme, 'github-light' | 'github-dark'> = {
  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<SupportedCodeLanguage, Promise<void>>();

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;
}