diff options
author | 2023-11-14 23:00:17 +0800 | |
---|---|---|
committer | 2023-11-14 23:00:17 +0800 | |
commit | 4537ecf0d060f89cb8c000338a7fc5f4197a88c8 (patch) | |
tree | 1ef855fc2197749001d202c7e8fee133d9cbc120 /packages/integrations/markdoc/src | |
parent | 6f5de8dfba021cacc8f58140671a98bcbb0936a3 (diff) | |
download | astro-4537ecf0d060f89cb8c000338a7fc5f4197a88c8.tar.gz astro-4537ecf0d060f89cb8c000338a7fc5f4197a88c8.tar.zst astro-4537ecf0d060f89cb8c000338a7fc5f4197a88c8.zip |
Refactor shikiji syntax highlighting code (#9083)
Diffstat (limited to 'packages/integrations/markdoc/src')
-rw-r--r-- | packages/integrations/markdoc/src/extensions/shiki.ts | 105 |
1 files changed, 5 insertions, 100 deletions
diff --git a/packages/integrations/markdoc/src/extensions/shiki.ts b/packages/integrations/markdoc/src/extensions/shiki.ts index 9ca894245..c70ef7f08 100644 --- a/packages/integrations/markdoc/src/extensions/shiki.ts +++ b/packages/integrations/markdoc/src/extensions/shiki.ts @@ -1,107 +1,19 @@ import Markdoc from '@markdoc/markdoc'; +import { createShikiHighlighter } from '@astrojs/markdown-remark'; import type { ShikiConfig } from 'astro'; import { unescapeHTML } from 'astro/runtime/server/index.js'; -import { bundledLanguages, getHighlighter, type Highlighter } from 'shikiji'; import type { AstroMarkdocConfig } from '../config.js'; -const ASTRO_COLOR_REPLACEMENTS: Record<string, string> = { - '#000001': 'var(--astro-code-color-text)', - '#000002': 'var(--astro-code-color-background)', - '#000004': 'var(--astro-code-token-constant)', - '#000005': 'var(--astro-code-token-string)', - '#000006': 'var(--astro-code-token-comment)', - '#000007': 'var(--astro-code-token-keyword)', - '#000008': 'var(--astro-code-token-parameter)', - '#000009': 'var(--astro-code-token-function)', - '#000010': 'var(--astro-code-token-string-expression)', - '#000011': 'var(--astro-code-token-punctuation)', - '#000012': 'var(--astro-code-token-link)', -}; -const COLOR_REPLACEMENT_REGEX = new RegExp( - `(${Object.keys(ASTRO_COLOR_REPLACEMENTS).join('|')})`, - 'g' -); - -const PRE_SELECTOR = /<pre class="(.*?)shiki(.*?)"/; -const LINE_SELECTOR = /<span class="line"><span style="(.*?)">([\+|\-])/g; -const INLINE_STYLE_SELECTOR = /style="(.*?)"/; -const INLINE_STYLE_SELECTOR_GLOBAL = /style="(.*?)"/g; - -/** - * Note: cache only needed for dev server reloads, internal test suites, and manual calls to `Markdoc.transform` by the user. - * Otherwise, `shiki()` is only called once per build, NOT once per page, so a cache isn't needed! - */ -const highlighterCache = new Map<string, Highlighter>(); - -export default async function shiki({ - langs = [], - theme = 'github-dark', - wrap = false, -}: ShikiConfig = {}): Promise<AstroMarkdocConfig> { - const cacheId = typeof theme === 'string' ? theme : theme.name || ''; - let highlighter = highlighterCache.get(cacheId)!; - if (!highlighter) { - highlighter = await getHighlighter({ - langs: langs.length ? langs : Object.keys(bundledLanguages), - themes: [theme], - }); - highlighterCache.set(cacheId, highlighter); - } +export default async function shiki(config?: ShikiConfig): Promise<AstroMarkdocConfig> { + const highlighter = await createShikiHighlighter(config); return { nodes: { fence: { attributes: Markdoc.nodes.fence.attributes!, transform({ attributes }) { - let lang: string; - - if (typeof attributes.language === 'string') { - const langExists = highlighter - .getLoadedLanguages() - .includes(attributes.language as any); - if (langExists) { - lang = attributes.language; - } else { - console.warn( - `[Shiki highlighter] The language "${attributes.language}" doesn't exist, falling back to plaintext.` - ); - lang = 'plaintext'; - } - } else { - lang = 'plaintext'; - } - - let html = highlighter.codeToHtml(attributes.content, { lang, theme }); - - // Q: Could these regexes match on a user's inputted code blocks? - // A: Nope! All rendered HTML is properly escaped. - // Ex. If a user typed `<span class="line"` into a code block, - // It would become this before hitting our regexes: - // <span class="line" - - html = html.replace(PRE_SELECTOR, `<pre class="$1astro-code$2"`); - // Add "user-select: none;" for "+"/"-" diff symbols - if (attributes.language === 'diff') { - html = html.replace( - LINE_SELECTOR, - '<span class="line"><span style="$1"><span style="user-select: none;">$2</span>' - ); - } - - if (wrap === false) { - html = html.replace(INLINE_STYLE_SELECTOR, 'style="$1; overflow-x: auto;"'); - } else if (wrap === true) { - html = html.replace( - INLINE_STYLE_SELECTOR, - 'style="$1; overflow-x: auto; white-space: pre-wrap; word-wrap: break-word;"' - ); - } - - // theme.id for shiki -> shikiji compat - const themeName = typeof theme === 'string' ? theme : theme.name; - if (themeName === 'css-variables') { - html = html.replace(INLINE_STYLE_SELECTOR_GLOBAL, (m) => replaceCssVariables(m)); - } + const lang = typeof attributes.language === 'string' ? attributes.language : 'plaintext'; + const html = highlighter.highlight(attributes.content, lang); // Use `unescapeHTML` to return `HTMLString` for Astro renderer to inline as HTML return unescapeHTML(html) as any; @@ -110,10 +22,3 @@ export default async function shiki({ }, }; } - -/** - * shiki -> shikiji compat as we need to manually replace it - */ -function replaceCssVariables(str: string) { - return str.replace(COLOR_REPLACEMENT_REGEX, (match) => ASTRO_COLOR_REPLACEMENTS[match] || match); -} |