Files
shadcn-ui/apps/v4/lib/highlight-code.ts
shadcn 47c47eaed2 feat: add docs for base-ui components (#9304)
* feat: add base and radix docs

* feat: transform code for display

* fix

* fix

* fix

* fix

* fix

* chore: remove claude files

* fix

* fix

* fix

* chore: run format:write

* fix

* feat: add more examples

* fix

* feat: add aspect-ratio

* feat: add avatar

* feat: add badge

* feat: add breadcrumb

* fix

* feat: add button

* fix

* fix

* fix

* feat: add calendar and card

* feat: add carousel

* fix: chart

* feat: add checkbox

* feat: add collapsible

* feat: add combobox

* feat: add command

* feat: add context menu

* feat: add data-table dialog and drawer

* feat: dropdown-menu

* feat: add date-picker

* feat: add empty

* feat: add field and hover-card

* fix: input

* feat: add input

* feat: add input-group

* feat: add input-otp

* feat: add item

* feat: add kbd and label

* feat: add menubar

* feat: add native-select

* feat: add more components

* feat: more components

* feat: more components

* feat: add skeleton, slider and sonner

* feat: add spinner and switch

* feat: add more components

* fix: tabs

* fix: tabs

* feat: add docs for sidebar

* fix

* fix

* fi

* docs: update

* fix: create page

* fix

* fix

* chore: add changelog

* fix
2026-01-20 19:31:38 +04:00

107 lines
3.3 KiB
TypeScript

import { createHash } from "crypto"
import { LRUCache } from "lru-cache"
import { codeToHtml } from "shiki"
import type { ShikiTransformer } from "shiki"
// LRU cache for cross-request caching of highlighted code.
// Shiki highlighting is CPU-intensive and deterministic, so caching is safe.
const highlightCache = new LRUCache<string, string>({
max: 500,
ttl: 1000 * 60 * 60, // 1 hour.
})
export const transformers = [
{
code(node) {
if (node.tagName === "code") {
const raw = this.source
node.properties["__raw__"] = raw
if (raw.startsWith("npm install")) {
node.properties["__npm__"] = raw
node.properties["__yarn__"] = raw.replace("npm install", "yarn add")
node.properties["__pnpm__"] = raw.replace("npm install", "pnpm add")
node.properties["__bun__"] = raw.replace("npm install", "bun add")
}
if (raw.startsWith("npx create-")) {
node.properties["__npm__"] = raw
node.properties["__yarn__"] = raw.replace(
"npx create-",
"yarn create "
)
node.properties["__pnpm__"] = raw.replace(
"npx create-",
"pnpm create "
)
node.properties["__bun__"] = raw.replace("npx", "bunx --bun")
}
// npm create.
if (raw.startsWith("npm create")) {
node.properties["__npm__"] = raw
node.properties["__yarn__"] = raw.replace("npm create", "yarn create")
node.properties["__pnpm__"] = raw.replace("npm create", "pnpm create")
node.properties["__bun__"] = raw.replace("npm create", "bun create")
}
// npx.
if (raw.startsWith("npx")) {
node.properties["__npm__"] = raw
node.properties["__yarn__"] = raw.replace("npx", "yarn")
node.properties["__pnpm__"] = raw.replace("npx", "pnpm dlx")
node.properties["__bun__"] = raw.replace("npx", "bunx --bun")
}
// npm run.
if (raw.startsWith("npm run")) {
node.properties["__npm__"] = raw
node.properties["__yarn__"] = raw.replace("npm run", "yarn")
node.properties["__pnpm__"] = raw.replace("npm run", "pnpm")
node.properties["__bun__"] = raw.replace("npm run", "bun")
}
}
},
},
] as ShikiTransformer[]
export async function highlightCode(code: string, language: string = "tsx") {
// Create cache key from code content and language.
const cacheKey = createHash("sha256")
.update(`${language}:${code}`)
.digest("hex")
// Check cache first.
const cached = highlightCache.get(cacheKey)
if (cached) {
return cached
}
const html = await codeToHtml(code, {
lang: language,
themes: {
dark: "github-dark",
light: "github-light",
},
transformers: [
{
pre(node) {
node.properties["class"] =
"no-scrollbar min-w-0 overflow-x-auto overscroll-none px-4 py-3.5 outline-none has-[[data-highlighted-line]]:px-0 has-[[data-line-numbers]]:px-0 has-[[data-slot=tabs]]:p-0 !bg-transparent"
},
code(node) {
node.properties["data-line-numbers"] = ""
},
line(node) {
node.properties["data-line"] = ""
},
},
],
})
// Cache the result.
highlightCache.set(cacheKey, html)
return html
}