Skip to content

Commit 458ba1a

Browse files
committed
refactor: extract highlightCodeSync for shared code highlighting
- Add synchronous highlightCodeSync() function to shiki.ts - Update readme.ts to use shared function instead of duplicating logic - Reduces code duplication between readme and docs rendering
1 parent d7aacaa commit 458ba1a

2 files changed

Lines changed: 26 additions & 22 deletions

File tree

server/utils/readme.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import sanitizeHtml from 'sanitize-html'
33
import { hasProtocol } from 'ufo'
44
import type { ReadmeResponse } from '#shared/types/readme'
55
import { convertBlobToRawUrl, type RepositoryInfo } from '#shared/utils/git-providers'
6+
import { highlightCodeSync } from './shiki'
67

78
/**
89
* Playground provider configuration
@@ -266,26 +267,7 @@ export async function renderReadmeHtml(
266267

267268
// Syntax highlighting for code blocks (uses shared highlighter)
268269
renderer.code = ({ text, lang }: Tokens.Code) => {
269-
const language = lang || 'text'
270-
const loadedLangs = shiki.getLoadedLanguages()
271-
272-
// Use Shiki if language is loaded, otherwise fall back to plain
273-
if (loadedLangs.includes(language as never)) {
274-
try {
275-
const html = shiki.codeToHtml(text, {
276-
lang: language,
277-
theme: 'github-dark',
278-
})
279-
// Shiki doesn't encode > in text content (e.g., arrow functions)
280-
return escapeRawGt(html)
281-
} catch {
282-
// Fall back to plain code block
283-
}
284-
}
285-
286-
// Plain code block for unknown languages
287-
const escaped = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
288-
return `<pre><code class="language-${language}">${escaped}</code></pre>\n`
270+
return highlightCodeSync(shiki, text, lang || 'text')
289271
}
290272

291273
// Resolve image URLs (with GitHub blob → raw conversion)

server/utils/shiki.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,17 @@ export async function getShikiHighlighter(): Promise<HighlighterCore> {
4949
return highlighter
5050
}
5151

52-
export async function highlightCodeBlock(code: string, language: string): Promise<string> {
53-
const shiki = await getShikiHighlighter()
52+
/**
53+
* Synchronously highlight a code block using an already-initialized highlighter.
54+
* Use this when you have already awaited getShikiHighlighter() and need to
55+
* highlight multiple blocks without async overhead (e.g., in marked renderers).
56+
*
57+
* @param shiki - The initialized Shiki highlighter instance
58+
* @param code - The code to highlight
59+
* @param language - The language identifier (e.g., 'typescript', 'bash')
60+
* @returns HTML string with syntax highlighting
61+
*/
62+
export function highlightCodeSync(shiki: HighlighterCore, code: string, language: string): string {
5463
const loadedLangs = shiki.getLoadedLanguages()
5564

5665
if (loadedLangs.includes(language as never)) {
@@ -74,6 +83,19 @@ export async function highlightCodeBlock(code: string, language: string): Promis
7483
return `<pre><code class="language-${language}">${escaped}</code></pre>\n`
7584
}
7685

86+
/**
87+
* Highlight a code block with syntax highlighting (async convenience wrapper).
88+
* Initializes the highlighter if needed, then delegates to highlightCodeSync.
89+
*
90+
* @param code - The code to highlight
91+
* @param language - The language identifier (e.g., 'typescript', 'bash')
92+
* @returns HTML string with syntax highlighting
93+
*/
94+
export async function highlightCodeBlock(code: string, language: string): Promise<string> {
95+
const shiki = await getShikiHighlighter()
96+
return highlightCodeSync(shiki, code, language)
97+
}
98+
7799
/**
78100
* Escape raw > characters in HTML text content.
79101
* Shiki outputs > without encoding in constructs like arrow functions (=>).

0 commit comments

Comments
 (0)