Core: use internal store for Shiki highlighter instances

This commit is contained in:
Fuma Nama
2025-03-19 20:50:59 +08:00
parent 7c8a690e7d
commit c5add28e46
3 changed files with 70 additions and 26 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'fumadocs-core': patch
---
use internal store for Shiki highlighter instances
+47 -8
View File
@@ -6,6 +6,8 @@ import {
type CodeToHastOptionsCommon,
type RegexEngine,
type Awaitable,
type Highlighter,
type BundledHighlighterOptions,
} from 'shiki';
import type { BundledTheme } from 'shiki/themes';
import { type Components, toJsxRuntime } from 'hast-util-to-jsx-runtime';
@@ -40,9 +42,9 @@ export type HighlightOptions = CodeToHastOptionsCommon<BundledLanguage> &
components?: Partial<Components>;
};
let defaultEngine: RegexEngine | undefined;
const highlighters = new Map<string, Promise<Highlighter>>();
export async function _highlight(code: string, options: HighlightOptions) {
const { getSingletonHighlighter } = await import('shiki');
const { lang, components: _, engine, ...rest } = options;
let themes: CodeOptionsThemes<BundledTheme> = { themes: defaultThemes };
@@ -52,13 +54,9 @@ export async function _highlight(code: string, options: HighlightOptions) {
themes = { themes: options.themes };
}
const highlighter = await getSingletonHighlighter({
const highlighter = await getHighlighter('custom', {
engine,
langs: [lang],
engine:
engine ??
(defaultEngine ??= await import('shiki/engine/oniguruma').then((res) =>
res.createOnigurumaEngine(import('shiki/wasm')),
)),
themes:
'theme' in themes
? [themes.theme]
@@ -84,6 +82,47 @@ export function _renderHighlight(hast: Root, options?: HighlightOptions) {
});
}
export async function getHighlighter(
engineType: 'js' | 'oniguruma' | 'custom',
options: BundledHighlighterOptions<BundledLanguage, BundledTheme>,
) {
const { createHighlighter } = await import('shiki');
let highlighter = highlighters.get(engineType);
if (!highlighter) {
let engine = options.engine;
if (engineType === 'js') {
const { createJavaScriptRegexEngine } = await import(
'shiki/engine/javascript'
);
engine = createJavaScriptRegexEngine();
}
if (engineType === 'oniguruma' || !engine) {
const { createOnigurumaEngine } = await import('shiki/engine/oniguruma');
engine = createOnigurumaEngine(import('shiki/wasm'));
}
highlighter = createHighlighter({
...options,
engine,
});
}
highlighters.set(engineType, highlighter);
return highlighter.then(async (instance) => {
await Promise.all([
// @ts-expect-error unknown
instance.loadLanguage(options.langs),
// @ts-expect-error unknown
instance.loadTheme(options.themes),
]);
return instance;
});
}
export async function highlight(
code: string,
options: HighlightOptions,
+18 -18
View File
@@ -8,17 +8,18 @@ import {
} from '@shikijs/transformers';
import type { Processor, Transformer } from 'unified';
import {
getSingletonHighlighter,
type ShikiTransformer,
type BuiltinTheme,
bundledLanguages,
} from 'shiki';
import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx';
import { createOnigurumaEngine } from 'shiki/engine/oniguruma';
import { createJavaScriptRegexEngine } from 'shiki/engine/javascript';
import type { IconOptions, CodeBlockIcon } from './transformer-icon';
import { transformerIcon } from './transformer-icon';
import { createStyleTransformer, defaultThemes } from '@/highlight/shiki';
import {
createStyleTransformer,
defaultThemes,
getHighlighter,
} from '@/highlight/shiki';
interface MetaValue {
name: string;
@@ -145,21 +146,20 @@ export function rehypeCode(
transformers.push(transformerTab());
}
const highlighter = getSingletonHighlighter({
engine: options.experimentalJSEngine
? createJavaScriptRegexEngine()
: createOnigurumaEngine(() => import('shiki/wasm')),
themes:
'themes' in options
? (Object.values(options.themes).filter(Boolean) as BuiltinTheme[])
: [options.theme],
langs:
options.langs ??
(options.lazy ? undefined : Object.keys(bundledLanguages)),
});
const highlighter = getHighlighter(
options.experimentalJSEngine ? 'js' : 'oniguruma',
{
themes:
'themes' in options
? (Object.values(options.themes).filter(Boolean) as BuiltinTheme[])
: [options.theme],
langs:
options.langs ?? (options.lazy ? [] : Object.keys(bundledLanguages)),
},
);
const transformer = highlighter.then((instance) =>
rehypeShikiFromHighlighter(instance, {
const transformer = highlighter.then((loaded) =>
rehypeShikiFromHighlighter(loaded, {
...options,
transformers,
}),