Skip to content

Commit cd59c32

Browse files
committed
Add aliasing for languages used in code block headers
1 parent ab50a77 commit cd59c32

4 files changed

Lines changed: 84 additions & 4 deletions

File tree

‎_TODO.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,13 @@ See comp images in Breadcrumbs component directory
147147

148148
## Code Block Problems
149149

150-
- I aliased 'promql' to 'go'. When a code fence using the alias is rendered with the language set to 'promql', it shows as 'go' incorrectly because of the alias. Also we need custom handling for all language names that are displayed: html should be uppercase, typescript as TypeScript, etc. Also we don't want all aliased names to show the alias - for example using the aliases 'ts', 'js', and 'md' would be better to show the full language names.
150+
- I aliased 'promql' to 'go'. When a code fence using the alias is rendered with the language set to 'promql', it shows as 'go' incorrectly because of the alias.
151151

152-
- We're getting random highlighting in code blocks, like the word "app" in the first code block in `argocd-sync-failures-gitops-debugging-troubleshooting/pdf.astro`. If the problem is not straight forward, let's discuss the fix first. Here's outerhtml of the block from chrome inspector:
152+
- We need custom handling for all language names that are displayed:
153+
1. `html` should be displayed uppercase as `HTML`, `yaml` as `YAML`
154+
2. `typescript` and its alias `ts` as `TypeScript`, `javascript` and its alias `js` as `JavaScript`
155+
3. The alias `md` as `Markdown
156+
4. All languages with their first names uppercased
153157

154158
- The "go" language code block in `src/content/articles/api-versioning-deprecation-sunset-headers-migration/index.mdx` is not being combined with the "typescript", "python", and "ruby" tabbed code block. Same with "python" block in "### Admission Control Strategies" section of `src/content/articles/backpressure-load-shedding-admission-control-overload/index.mdx`, plus the line numbering is weird in the code block.
155159

‎src/components/Code/CodeTabs/client/index.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ function getCodeText(pre: HTMLPreElement): string {
2020

2121
const EXCLUDED_SINGLE_TAB_LANGUAGES = new Set(['text', 'mermaid', 'math'])
2222

23-
function formatLanguageLabel(language: string): string {
23+
/**
24+
* Fallback label formatter for cases where `data-language-label` is not set
25+
* (e.g. cached HTML from before the server-side label was introduced).
26+
* The canonical display name config lives in `src/lib/config/codeBlocks.ts`.
27+
*/
28+
function formatLanguageLabelFallback(language: string): string {
2429
const normalized = language.trim().toLowerCase()
2530
const labelMap: Record<string, string> = {
2631
js: 'JavaScript',
@@ -47,11 +52,15 @@ function getTabLabel(pre: HTMLPreElement): string | null {
4752
const codeTitle = pre.getAttribute('data-code-title')
4853
if (codeTitle && codeTitle.trim()) return codeTitle.trim()
4954

55+
// Use server-computed display label from the centralized config.
56+
const label = pre.getAttribute('data-language-label')
57+
if (label && label.trim()) return label.trim()
58+
5059
const language = pre.getAttribute('data-language')
5160
if (!language || !language.trim()) return null
5261
if (EXCLUDED_SINGLE_TAB_LANGUAGES.has(language.trim().toLowerCase())) return null
5362

54-
return formatLanguageLabel(language)
63+
return formatLanguageLabelFallback(language)
5564
}
5665

5766
async function writeToClipboard(text: string): Promise<boolean> {

‎src/lib/config/codeBlocks.ts‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Centralized configuration for how code block language names are displayed.
3+
*
4+
* The `languageDisplayNames` map controls the label shown in code block headers.
5+
* Keys are language identifiers as written in Markdown code fences (lowercase).
6+
* Values are the display strings shown in the UI.
7+
*
8+
* Languages not listed here fall back to capitalizing the first letter
9+
* (e.g. "python" → "Python", "ruby" → "Ruby").
10+
*
11+
* When a code fence uses a Shiki language alias (e.g. `ts` → `typescript`),
12+
* the display label is resolved from the **original** identifier the author
13+
* wrote, not the resolved Shiki language. This allows `promql` (aliased to
14+
* `go` for highlighting) to display as "PromQL" while real `go` fences
15+
* display as "Go".
16+
*/
17+
export const languageDisplayNames: Record<string, string> = {
18+
// All-uppercase acronym languages
19+
css: 'CSS',
20+
hcl: 'HCL',
21+
html: 'HTML',
22+
http: 'HTTP',
23+
json: 'JSON',
24+
sql: 'SQL',
25+
toml: 'TOML',
26+
xml: 'XML',
27+
yaml: 'YAML',
28+
yml: 'YAML',
29+
30+
// Mixed-case languages
31+
graphql: 'GraphQL',
32+
javascript: 'JavaScript',
33+
js: 'JavaScript',
34+
jsx: 'JSX',
35+
markdown: 'Markdown',
36+
md: 'Markdown',
37+
promql: 'PromQL',
38+
ts: 'TypeScript',
39+
tsx: 'TSX',
40+
typescript: 'TypeScript',
41+
}
42+
43+
/**
44+
* Returns the display label for a code block language.
45+
*
46+
* Should be called with the **original** language identifier from the Markdown
47+
* source (before Shiki alias resolution) so that aliases like `promql` display
48+
* correctly instead of showing the resolved target (`go`).
49+
*
50+
* Lookup order:
51+
* 1. Exact match in `languageDisplayNames`
52+
* 2. Capitalize first letter (default)
53+
*/
54+
export function formatLanguageLabel(language: string): string {
55+
const key = language.trim().toLowerCase()
56+
if (!key) return language
57+
58+
const explicit = languageDisplayNames[key]
59+
if (explicit) return explicit
60+
61+
// Default: capitalize first letter
62+
return key.charAt(0).toUpperCase() + key.slice(1)
63+
}

‎src/lib/markdown/plugins/rehype-shiki/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Root, Element, Parent, Text } from 'hast'
22
import type { Plugin } from 'unified'
33
import { visit } from 'unist-util-visit'
44
import { createHighlighter } from 'shiki'
5+
import { formatLanguageLabel } from '@lib/config/codeBlocks'
56

67
type ShikiThemes =
78
| string
@@ -269,6 +270,7 @@ const rehypeShiki: Plugin<[RehypeShikiOptions], Root> = (options: RehypeShikiOpt
269270
index: number
270271
original: Element
271272
lang: string
273+
rawLang: string
272274
codeText: string
273275
metaRaw: string | undefined
274276
title: string | undefined
@@ -310,6 +312,7 @@ const rehypeShiki: Plugin<[RehypeShikiOptions], Root> = (options: RehypeShikiOpt
310312
index,
311313
original: node,
312314
lang,
315+
rawLang,
313316
codeText: stripTrailingFenceNewline(codeText),
314317
metaRaw: metaForShiki || undefined,
315318
title,
@@ -377,6 +380,7 @@ const rehypeShiki: Plugin<[RehypeShikiOptions], Root> = (options: RehypeShikiOpt
377380
delete (highlightedPre.properties as Record<string, unknown>)['data-shiki-meta']
378381

379382
highlightedPre.properties['data-language'] = replacement.lang
383+
highlightedPre.properties['data-language-label'] = formatLanguageLabel(replacement.rawLang)
380384

381385
if (replacement.title) {
382386
highlightedPre.properties['data-code-title'] = replacement.title

0 commit comments

Comments
 (0)