2022-02-22 16:46:04 +00:00
|
|
|
import type * as shiki from 'shiki';
|
|
|
|
import { getHighlighter } from 'shiki';
|
2022-01-31 22:14:07 +00:00
|
|
|
import { visit } from 'unist-util-visit';
|
|
|
|
|
2022-02-07 16:31:02 +00:00
|
|
|
export interface ShikiConfig {
|
|
|
|
/**
|
|
|
|
* The languages loaded to Shiki.
|
|
|
|
* Supports all languages listed here: https://github.com/shikijs/shiki/blob/main/docs/languages.md#all-languages
|
|
|
|
* Instructions for loading a custom language: https://github.com/shikijs/shiki/blob/main/docs/languages.md#supporting-your-own-languages-with-shiki
|
|
|
|
*
|
|
|
|
* @default []
|
|
|
|
*/
|
|
|
|
langs?: shiki.ILanguageRegistration[];
|
|
|
|
/**
|
|
|
|
* The styling theme.
|
|
|
|
* Supports all themes listed here: https://github.com/shikijs/shiki/blob/main/docs/themes.md#all-themes
|
|
|
|
* Instructions for loading a custom theme: https://github.com/shikijs/shiki/blob/main/docs/themes.md#loading-theme
|
|
|
|
*
|
|
|
|
* @default "github-dark"
|
|
|
|
*/
|
|
|
|
theme?: shiki.IThemeRegistration;
|
|
|
|
/**
|
|
|
|
* Enable word wrapping.
|
|
|
|
* - true: enabled.
|
|
|
|
* - false: enabled.
|
|
|
|
* - null: All overflow styling removed. Code will overflow the element by default.
|
|
|
|
*
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
wrap?: boolean | null;
|
|
|
|
}
|
|
|
|
|
2022-03-02 22:09:18 +00:00
|
|
|
/**
|
|
|
|
* getHighlighter() is the most expensive step of Shiki. Instead of calling it on every page,
|
|
|
|
* cache it here as much as possible. Make sure that your highlighters can be cached, state-free.
|
|
|
|
*/
|
|
|
|
const highlighterCache = new Map<string, shiki.Highlighter>();
|
2022-01-31 22:14:07 +00:00
|
|
|
|
2022-03-02 22:09:18 +00:00
|
|
|
const remarkShiki = async ({ langs = [], theme = 'github-dark', wrap = false }: ShikiConfig) => {
|
|
|
|
const cacheID: string = typeof theme === 'string' ? theme : theme.name;
|
|
|
|
let highlighter = highlighterCache.get(cacheID);
|
|
|
|
if (!highlighter) {
|
|
|
|
highlighter = await getHighlighter({ theme });
|
|
|
|
highlighterCache.set(cacheID, highlighter);
|
|
|
|
}
|
2022-02-07 16:31:02 +00:00
|
|
|
for (const lang of langs) {
|
|
|
|
await highlighter.loadLanguage(lang);
|
|
|
|
}
|
2022-01-31 22:14:07 +00:00
|
|
|
return () => (tree: any) => {
|
|
|
|
visit(tree, 'code', (node) => {
|
2022-03-02 22:09:18 +00:00
|
|
|
let html = highlighter!.codeToHtml(node.value, { lang: node.lang ?? 'plaintext' });
|
2022-01-31 22:14:07 +00:00
|
|
|
|
2022-02-08 05:48:00 +00:00
|
|
|
// Replace "shiki" class naming with "astro" and add "data-astro-raw".
|
|
|
|
html = html.replace('<pre class="shiki"', '<pre data-astro-raw class="astro-code"');
|
2022-01-31 22:14:07 +00:00
|
|
|
// Replace "shiki" css variable naming with "astro".
|
|
|
|
html = html.replace(/style="(background-)?color: var\(--shiki-/g, 'style="$1color: var(--astro-code-');
|
2022-02-07 16:31:02 +00:00
|
|
|
// Handle code wrapping
|
|
|
|
// if wrap=null, do nothing.
|
|
|
|
if (wrap === false) {
|
|
|
|
html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto;"');
|
|
|
|
} else if (wrap === true) {
|
|
|
|
html = html.replace(/style="(.*?)"/, 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"');
|
|
|
|
}
|
2022-01-31 22:14:07 +00:00
|
|
|
|
|
|
|
node.type = 'html';
|
|
|
|
node.value = html;
|
|
|
|
node.children = [];
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export default remarkShiki;
|