Skip to content

Commit ce7b91f

Browse files
committed
Highlight code blocks again, and number multi-line ones
Dropping Docusaurus took Prism with it, so every fence rendered as flat text. Shiki now highlights at build time, which keeps the pages free of a runtime highlighter: the markdown pipeline emits the coloured HTML into the payload. Line numbers are new — the Docusaurus site never showed them. They are limited to blocks that actually span several lines, since a one-line install command reads worse with a "1" beside it. The counter hangs off the .line spans Shiki already emits, so no extra markup is needed.
1 parent a90cf71 commit ce7b91f

5 files changed

Lines changed: 394 additions & 1 deletion

File tree

website/content.mjs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,23 @@ import { fileURLToPath } from 'node:url';
55
import matter from 'gray-matter';
66
import MarkdownIt from 'markdown-it';
77
import anchor from 'markdown-it-anchor';
8+
import { createHighlighter } from 'shiki';
89

910
const contentRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), 'content');
1011

1112
/** Locale directory names; the default locale lives at the content root. */
1213
export const CONTENT_LOCALES = ['zh-Hans', 'zh-Hant', 'es', 'fr', 'de', 'ru', 'he', 'ar', 'ja', 'ko'];
1314

15+
// Highlighting happens at build time, so the pages carry no highlighter at
16+
// runtime. Shiki wraps each line in <span class="line">, which is also what the
17+
// line-number counter in the stylesheet hangs off.
18+
const highlighter = await createHighlighter({
19+
themes: ['github-dark-default'],
20+
langs: ['bash', 'json', 'go', 'javascript', 'typescript', 'yaml', 'toml', 'ini', 'text'],
21+
});
22+
23+
const HIGHLIGHT_LANGS = new Set(highlighter.getLoadedLanguages());
24+
1425
const markdown = new MarkdownIt({ html: true, linkify: true }).use(anchor, {
1526
slugify: (title) =>
1627
title
@@ -33,6 +44,14 @@ function tableOfContents(tokens) {
3344
return items;
3445
}
3546

47+
markdown.options.highlight = (code, language) => {
48+
const lang = HIGHLIGHT_LANGS.has(language) ? language : 'text';
49+
const html = highlighter.codeToHtml(code, { lang, theme: 'github-dark-default' });
50+
// Line numbers only earn their space in multi-line blocks; a one-line install
51+
// command reads worse with a "1" next to it.
52+
return code.trimEnd().includes('\n') ? html.replace('<pre ', '<pre data-multiline ') : html;
53+
};
54+
3655
async function readDocument(file) {
3756
const source = await readFile(file, 'utf8');
3857
const parsed = matter(source);

website/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"markdown-it": "^15.0.0",
2323
"markdown-it-anchor": "^9.2.1",
2424
"sass": "^1.103.0",
25+
"shiki": "^4.4.3",
2526
"typescript": "~5.6.2",
2627
"vite": "^8.2.2"
2728
},

0 commit comments

Comments
 (0)