mirror of
https://github.com/zoriya/v10.git
synced 2026-08-16 02:45:09 +00:00
fix(site): cache shiki highlighters during dev HMR (#1619)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import css from 'shiki/langs/css.mjs';
|
||||
import html from 'shiki/langs/html.mjs';
|
||||
import javascript from 'shiki/langs/javascript.mjs';
|
||||
import tsx from 'shiki/langs/tsx.mjs';
|
||||
import createHighlighter from './createHighlighter';
|
||||
import createHighlighter, { getOrCreateCachedHighlighter } from './createHighlighter';
|
||||
|
||||
// Eager module-level Promise — deliberately NOT a top-level `await`.
|
||||
//
|
||||
@@ -15,7 +15,9 @@ import createHighlighter from './createHighlighter';
|
||||
// avoiding the bug. The Promise starts resolving at import time, giving
|
||||
// it a head start before `client:idle` hydration kicks in.
|
||||
// ClientCode.tsx consumes this via React 19's `use()` hook + Suspense.
|
||||
const highlighterPromise = createHighlighter({ langs: [bash, html, tsx, css, javascript] });
|
||||
const highlighterPromise = getOrCreateCachedHighlighter('client', () =>
|
||||
createHighlighter({ langs: [bash, html, tsx, css, javascript] })
|
||||
);
|
||||
|
||||
export function getClientHighlighter() {
|
||||
return highlighterPromise;
|
||||
|
||||
@@ -1,7 +1,34 @@
|
||||
import { createHighlighter as libCreateHighlighter } from 'shiki';
|
||||
import { type Highlighter, createHighlighter as libCreateHighlighter } from 'shiki';
|
||||
import gruvboxDarkHard from 'shiki/themes/gruvbox-dark-hard.mjs';
|
||||
import gruvboxDarkSoft from 'shiki/themes/gruvbox-dark-soft.mjs';
|
||||
|
||||
const SHIKI_CACHE_SYMBOL = Symbol.for('@videojs/site/shiki');
|
||||
|
||||
type ShikiCacheKey = 'server' | 'client';
|
||||
|
||||
interface ShikiGlobalCache {
|
||||
server?: Promise<Highlighter>;
|
||||
client?: Promise<Highlighter>;
|
||||
}
|
||||
|
||||
function getShikiGlobalCache(): ShikiGlobalCache {
|
||||
const global = globalThis as typeof globalThis & {
|
||||
[SHIKI_CACHE_SYMBOL]?: ShikiGlobalCache;
|
||||
};
|
||||
global[SHIKI_CACHE_SYMBOL] ??= {};
|
||||
return global[SHIKI_CACHE_SYMBOL];
|
||||
}
|
||||
|
||||
/** Reuse one highlighter per role across HMR module re-evaluations in dev. */
|
||||
export function getOrCreateCachedHighlighter(
|
||||
key: ShikiCacheKey,
|
||||
factory: () => Promise<Highlighter>
|
||||
): Promise<Highlighter> {
|
||||
const cache = getShikiGlobalCache();
|
||||
cache[key] ??= factory();
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
export default function createHighlighter(config: Omit<Parameters<typeof libCreateHighlighter>[0], 'themes'>) {
|
||||
return libCreateHighlighter({
|
||||
...config,
|
||||
|
||||
@@ -4,15 +4,17 @@ import html from 'shiki/langs/html.mjs';
|
||||
import javascript from 'shiki/langs/javascript.mjs';
|
||||
import tsx from 'shiki/langs/tsx.mjs';
|
||||
import ts from 'shiki/langs/typescript.mjs';
|
||||
import createHighlighter from './createHighlighter';
|
||||
import createHighlighter, { getOrCreateCachedHighlighter } from './createHighlighter';
|
||||
|
||||
// The highlighter is a long-lived singleton at module scope; it is never
|
||||
// disposed, so every grammar and theme we hand it stays in memory for the
|
||||
// process lifetime. Only pre-load the languages `<ServerCode>` actually
|
||||
// renders — adding `bundledLanguages` here would drag ~300 grammars into the
|
||||
// build and dominate first-render cost on code-heavy pages.
|
||||
const serverHighlighter = await createHighlighter({
|
||||
langs: [bash, css, html, javascript, ts, tsx],
|
||||
});
|
||||
const serverHighlighter = await getOrCreateCachedHighlighter('server', () =>
|
||||
createHighlighter({
|
||||
langs: [bash, css, html, javascript, ts, tsx],
|
||||
})
|
||||
);
|
||||
|
||||
export default serverHighlighter;
|
||||
|
||||
Reference in New Issue
Block a user