Files
supabase/apps/docs/features/ui/CodeBlock/CodeBlock.highlight.ts
Anthony Lio 6d08a747f1 fix(docs): guide reference perf enhancements (#50239)
## What kind of change does this PR introduce?

follow-up to #50235 to reduce reference page payloads and cold rendering
overhead

## What is the current behavior?

reference pages ship a large rsc payload inside the html _ most of it is
duplication rather than content along with shiki that writes ~30
character css variable name for every syntax token making the page heavy
in some cases

## What is the new behavior?

- moves repeated styles into shared css and uses compact, namespaced
token classes
- renders details icons inside the client trigger
- follows shiki’s guidance to [reuse one
highlighter](https://shiki.style/guide/best-performance#cache-the-highlighter-instance)
and [load languages on
demand](https://shiki.style/guide/best-performance#use-shorthands)

`page size`
page | before | after | change
-- | -- | -- | --
javascript | 10.61 mb | 8.28 mb | -21.9%
dart | 4.59 mb | 4.13 mb | -10.1%
python | 4.38 mb | 3.73 mb | -14.9%
swift | 2.87 mb | 2.56 mb | -10.9%
server | 1.59 mb | 1.35 mb | -15.2%
kotlin | 3.42 mb | 3.17 mb | -7.2%

`cold initialization`
language | before | after | reduction
-- | -- | -- | --
bash | 2,180 ms | 23 ms | 98.95%
javascript | 2,245 ms | 38 ms | 98.29%

## Additional context

measured on a local production build which uses the checked in generated
content _ production has larger sdk data, so absolute sizes there will
be higher

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added reusable expand/collapse controls for API reference details,
with updated icons, labels, and styling.
* Improved code block rendering with class-based syntax highlighting,
wrapped-code support, responsive layouts, and lazy language loading.

* **Style**
* Added theme-aware syntax-token colors, line-number styling, and
configurable code-block shadows.
  * Consolidated expandable reference panel and item styling.

* **Tests**
* Added coverage for syntax highlighting, code block rendering, language
support, token stability, and reference details.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-15 23:55:49 +03:00

55 lines
1.7 KiB
TypeScript

import {
bundledLanguages,
createHighlighter,
makeSingletonHighlighter,
type BundledLanguage,
} from 'shiki'
import theme from './supabase-2.json' with { type: 'json' }
const getHighlighter = makeSingletonHighlighter(() =>
createHighlighter({ themes: [structuredClone(theme)], langs: [] })
)
// keep the eager highlighter's tagged templates and component syntax intact
const INJECTED_LANGUAGES: Record<string, Array<BundledLanguage>> = {
'source.js': ['ts-tags'],
'source.ts': ['ts-tags'],
'text.html.markdown': ['vue'],
'text.html.derivative': ['angular-html', 'vue'],
'text.pug': ['vue'],
}
export async function highlightCode(code: string, lang: BundledLanguage | null) {
const highlighter = await getHighlighter()
if (lang && !highlighter.getLoadedLanguages().includes(lang)) {
const languages = new Set<BundledLanguage>()
async function collectLanguages(language: BundledLanguage) {
if (languages.has(language)) return
languages.add(language)
const { default: grammars } = await bundledLanguages[language]()
await Promise.all(
grammars.flatMap(({ embeddedLangsLazy = [], scopeName }) => {
const injected = Object.entries(INJECTED_LANGUAGES).flatMap(([scope, languages]) =>
scopeName === scope || scopeName.startsWith(`${scope}.`) ? languages : []
)
return [...embeddedLangsLazy, ...injected].map((embedded) =>
collectLanguages(embedded as BundledLanguage)
)
})
)
}
await collectLanguages(lang)
await highlighter.loadLanguage(...languages)
}
return highlighter.codeToTokens(code, {
lang: lang || undefined,
theme: 'Supabase Theme',
tokenizeTimeLimit: 0,
tokenizeMaxLineLength: 100_000,
})
}