Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 58 additions & 37 deletions src/lib/docs-content/remark-code-blocks.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,52 @@
// @ts-check
import { createHighlighter, createCssVariablesTheme } from 'shiki';
import { visit } from 'unist-util-visit';
import { createHighlighter, createCssVariablesTheme } from "shiki";
import { visit } from "unist-util-visit";

const cssVarsTheme = createCssVariablesTheme({ name: 'css-variables', variablePrefix: '--shiki-', fontStyle: true });
const cssVarsTheme = createCssVariablesTheme({
name: "css-variables",
variablePrefix: "--shiki-",
fontStyle: true,
});

/** @type {import('shiki').Highlighter | undefined} */
let hl;
// 1. Cast globalThis to include your custom property type
/** @type {any} */
const globalObj = globalThis;

async function getHighlighter() {
if (!hl) {
hl = await createHighlighter({
themes: [cssVarsTheme],
langs: [
'bash', 'sh', 'shell',
'json', 'jsonc', 'yaml', 'toml',
'typescript', 'javascript', 'jsx', 'tsx',
'svelte',
'markdown',
'rust', 'go',
'sql',
'python',
'html', 'css',
'diff',
'text'
]
});
}
return hl;
if (!globalObj._shikiHighlighterPromise) {
globalObj._shikiHighlighterPromise = createHighlighter({
themes: [cssVarsTheme],
langs: [
"bash",
"sh",
"shell",
"json",
"jsonc",
"yaml",
"toml",
"typescript",
"javascript",
"jsx",
"tsx",
"svelte",
"markdown",
"rust",
"go",
"sql",
"python",
"html",
"css",
"diff",
"text",
],
});
}

/**
* Extract the promise for easier type annotation
* @type {Promise<import('shiki').Highlighter>}
*/
const highlighterPromise = globalObj._shikiHighlighterPromise;

/**
* Converts fenced code blocks to <CodeBlock> component tags with Shiki-highlighted
* HTML pre-rendered at build time using the css-variables theme.
Expand All @@ -37,41 +55,44 @@ async function getHighlighter() {
*/
export function remarkCodeBlocks() {
return async (tree) => {
const highlighter = await getHighlighter();
const highlighter = await highlighterPromise;
const supported = new Set(highlighter.getLoadedLanguages());

visit(tree, 'code', (node, index, parent) => {
visit(tree, "code", (node, index, parent) => {
if (!parent || index === undefined) return;

const lang = node.lang ?? '';
const fileMatch = /file="([^"]+)"/.exec(node.meta ?? '');
const file = fileMatch?.[1] ?? '';
const lang = node.lang ?? "";
const fileMatch = /file="([^"]+)"/.exec(node.meta ?? "");
const file = fileMatch?.[1] ?? "";
const code = JSON.stringify(node.value);

const resolvedLang = lang && supported.has(lang) ? lang : 'text';
const resolvedLang = lang && supported.has(lang) ? lang : "text";

let highlighted;
try {
highlighted = highlighter.codeToHtml(node.value, {
lang: resolvedLang,
theme: 'css-variables'
theme: "css-variables",
});
} catch {
highlighted = highlighter.codeToHtml(node.value, { lang: 'text', theme: 'css-variables' });
highlighted = highlighter.codeToHtml(node.value, {
lang: "text",
theme: "css-variables",
});
}

const props = [
`code={${code}}`,
`highlighted={${JSON.stringify(highlighted)}}`,
lang && `lang="${lang}"`,
file && `file="${file}"`
file && `file="${file}"`,
]
.filter(Boolean)
.join(' ');
.join(" ");

/** @type {any} */ (parent.children)[index] = {
type: 'html',
value: `<CodeBlock ${props} />`
type: "html",
value: `<CodeBlock ${props} />`,
};
});
};
Expand Down
Loading