Skip to content

Commit 42e21cc

Browse files
committed
feat: shiki fully support
Signed-off-by: Innei <[email protected]>
1 parent 2b7f89f commit 42e21cc

File tree

4 files changed

+66
-59
lines changed

4 files changed

+66
-59
lines changed

src/components/modules/shared/CodeBlock.tsx

+4-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import type { ReactNode } from 'react'
66

77
import { HighLighterPrismCdn } from '~/components/ui/code-highlighter'
88
import { ShikiHighLighterWrapper } from '~/components/ui/code-highlighter/shiki/ShikiWrapper'
9-
import { isSupportedShikiLang } from '~/components/ui/code-highlighter/shiki/utils'
109
import { ExcalidrawLoading } from '~/components/ui/excalidraw/ExcalidrawLoading'
1110
import { isClientSide } from '~/lib/env'
1211

@@ -65,15 +64,13 @@ export const CodeBlockRender = (props: {
6564
const lang = props.lang
6665
const nextProps = { ...props }
6766
nextProps.content = formatCode(props.content)
68-
if (lang && isSupportedShikiLang(lang)) {
67+
if (lang) {
6968
const ShikiHighLighter =
7069
shikiImport ??
7170
lazy(() =>
72-
import('~/components/ui/code-highlighter/shiki/Shiki').then(
73-
(mod) => ({
74-
default: mod.ShikiHighLighter,
75-
}),
76-
),
71+
import('~/components/ui/code-highlighter').then((mod) => ({
72+
default: mod.ShikiFallback,
73+
})),
7774
)
7875
if (isClientSide) {
7976
shikiImport = ShikiHighLighter

src/components/ui/code-highlighter/CodeHighlighter.tsx

+25
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import React, {
2+
use,
23
useCallback,
34
useEffect,
45
useInsertionEffect,
6+
useMemo,
57
useRef,
68
} from 'react'
79
import type { FC } from 'react'
10+
import type { ShikiProps } from './shiki/Shiki'
811

912
import { useIsPrintMode } from '~/atoms/css-media'
1013
import { useIsDark } from '~/hooks/common/use-is-dark'
@@ -14,6 +17,7 @@ import { loadScript, loadStyleSheet } from '~/lib/load-script'
1417
import { toast } from '~/lib/toast'
1518

1619
import styles from './CodeHighlighter.module.css'
20+
import { ShikiHighLighter } from './shiki/Shiki'
1721

1822
declare global {
1923
interface Window {
@@ -146,3 +150,24 @@ const useLoadHighlighter = (ref: React.RefObject<HTMLElement>) => {
146150
})
147151
}, [])
148152
}
153+
let bundledLanguagesKeysSet: Set<string> | null = null
154+
export const ShikiFallback: FC<ShikiProps> = (props) => {
155+
const { lang } = props
156+
const shikiSupported = use(
157+
useMemo(async () => {
158+
if (!lang) return false
159+
160+
if (!bundledLanguagesKeysSet) {
161+
const { bundledLanguages } = await import('shiki/langs')
162+
bundledLanguagesKeysSet = new Set(Object.keys(bundledLanguages))
163+
}
164+
165+
return bundledLanguagesKeysSet.has(lang)
166+
}, [lang]),
167+
)
168+
169+
if (!shikiSupported) {
170+
return <HighLighterPrismCdn {...props} />
171+
}
172+
return <ShikiHighLighter {...props} />
173+
}

src/components/ui/code-highlighter/shiki/Shiki.tsx

+30-25
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { isServerSide } from '~/lib/env'
55

66
import { ShikiHighLighterWrapper } from './ShikiWrapper'
77

8-
interface Props {
8+
export interface ShikiProps {
99
lang: string | undefined
1010
content: string
1111

@@ -21,42 +21,47 @@ const codeHighlighterPromise = (async () => {
2121
import('./core'),
2222
])
2323

24-
const loaded = await getHighlighterCore({
24+
const core = await getHighlighterCore({
2525
themes: [
2626
import('shiki/themes/github-light.mjs'),
2727
import('shiki/themes/github-dark.mjs'),
2828
],
29-
langs: [
30-
() => import('shiki/langs/javascript.mjs'),
31-
() => import('shiki/langs/typescript.mjs'),
32-
() => import('shiki/langs/css.mjs'),
33-
() => import('shiki/langs/tsx.mjs'),
34-
() => import('shiki/langs/jsx.mjs'),
35-
() => import('shiki/langs/json.mjs'),
36-
() => import('shiki/langs/sql.mjs'),
37-
() => import('shiki/langs/rust.mjs'),
38-
() => import('shiki/langs/go.mjs'),
39-
() => import('shiki/langs/cpp.mjs'),
40-
() => import('shiki/langs/c.mjs'),
41-
() => import('shiki/langs/markdown.mjs'),
42-
() => import('shiki/langs/vue.mjs'),
43-
() => import('shiki/langs/html.mjs'),
44-
() => import('shiki/langs/asm.mjs'),
45-
() => import('shiki/langs/shell.mjs'),
46-
() => import('shiki/langs/ps.mjs'),
47-
],
29+
langs: [],
4830
loadWasm: getWasm,
4931
})
5032

51-
return (o: { lang: string; attrs: string; code: string }) =>
52-
codeHighlighter(loaded, o)
33+
return {
34+
codeHighlighter: core,
35+
fn: (o: { lang: string; attrs: string; code: string }) => {
36+
return codeHighlighter(core, o)
37+
},
38+
}
5339
})()
5440

55-
export const ShikiHighLighter: FC<Props> = (props) => {
41+
export const ShikiHighLighter: FC<ShikiProps> = (props) => {
5642
const { lang: language, content: value, attrs } = props
5743
const codeHighlighter = use(codeHighlighterPromise)
44+
45+
use(
46+
useMemo(async () => {
47+
async function loadShikiLanguage(language: string, languageModule: any) {
48+
const shiki = codeHighlighter?.codeHighlighter
49+
if (!shiki) return
50+
if (!shiki.getLoadedLanguages().includes(language)) {
51+
await shiki.loadLanguage(await languageModule())
52+
}
53+
}
54+
55+
const { bundledLanguages } = await import('shiki/langs')
56+
57+
if (!language) return
58+
const importFn = (bundledLanguages as any)[language]
59+
if (!importFn) return
60+
return loadShikiLanguage(language || '', importFn)
61+
}, [codeHighlighter?.codeHighlighter, language]),
62+
)
5863
const highlightedHtml = useMemo(() => {
59-
return codeHighlighter?.({
64+
return codeHighlighter?.fn?.({
6065
attrs: attrs || '',
6166
// code: `${value.split('\n')[0].repeat(10)} // [!code highlight]\n${value}`,
6267
code: value,
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// import { bundledLanguages } from 'shiki/langs'
2+
13
export const parseFilenameFromAttrs = (attrs: string) => {
24
// filename=""
35

@@ -8,32 +10,10 @@ export const parseFilenameFromAttrs = (attrs: string) => {
810
return null
911
}
1012

13+
// const shikiSupportLangSet = new Set(Object.keys(bundledLanguages))
1114
export const isSupportedShikiLang = (lang: string) => {
12-
return [
13-
'javascript',
14-
'typescript',
15-
'ts',
16-
'js',
17-
'css',
18-
'tsx',
19-
'jsx',
20-
'json',
21-
'sql',
22-
'markdown',
23-
'vue',
24-
'rust',
25-
'go',
26-
'cpp',
27-
'c',
28-
'html',
29-
'asm',
30-
'bash',
31-
'ps',
32-
'ps1',
33-
// plain text
34-
'text',
35-
'plaintext',
36-
'txt',
37-
'plain',
38-
].includes(lang.toLowerCase())
15+
// require esm error, fuck nextjs 14.12.x
16+
// @see https://github.com/vercel/next.js/issues/64434
17+
// return shikiSupportLangSet.has(lang)
18+
return true
3919
}

0 commit comments

Comments
 (0)