diff options
Diffstat (limited to '')
-rw-r--r-- | packages/markdown/remark/package.json | 1 | ||||
-rw-r--r-- | packages/markdown/remark/src/index.ts | 10 | ||||
-rw-r--r-- | packages/markdown/remark/src/remark-shiki.ts | 23 | ||||
-rw-r--r-- | packages/markdown/remark/src/types.ts | 5 |
4 files changed, 37 insertions, 2 deletions
diff --git a/packages/markdown/remark/package.json b/packages/markdown/remark/package.json index 6a0a69b45..00a023d1d 100644 --- a/packages/markdown/remark/package.json +++ b/packages/markdown/remark/package.json @@ -38,6 +38,7 @@ "remark-parse": "^10.0.1", "remark-rehype": "^10.0.1", "remark-smartypants": "^2.0.0", + "shiki": "^0.10.0", "unified": "^10.1.1", "unist-util-map": "^3.0.0", "unist-util-visit": "^4.1.0" diff --git a/packages/markdown/remark/src/index.ts b/packages/markdown/remark/src/index.ts index e8242279a..78d645227 100644 --- a/packages/markdown/remark/src/index.ts +++ b/packages/markdown/remark/src/index.ts @@ -9,6 +9,7 @@ import { remarkJsx, loadRemarkJsx } from './remark-jsx.js'; import rehypeJsx from './rehype-jsx.js'; import rehypeEscape from './rehype-escape.js'; import remarkPrism from './remark-prism.js'; +import remarkShiki from './remark-shiki.js'; import remarkUnwrap from './remark-unwrap.js'; import { loadPlugins } from './load-plugins.js'; @@ -37,6 +38,8 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp let { remarkPlugins = [], rehypePlugins = [] } = opts ?? {}; const scopedClassName = opts?.$?.scopedClassName; const mode = opts?.mode ?? 'mdx'; + const syntaxHighlight = opts?.syntaxHighlight ?? 'prism'; + const shikiTheme = opts?.shikiTheme ?? 'github-dark'; const isMDX = mode === 'mdx'; const { headers, rehypeCollectHeaders } = createCollectHeaders(); @@ -64,7 +67,12 @@ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOp parser.use([scopedStyles(scopedClassName)]); } - parser.use([remarkPrism(scopedClassName)]); + if (syntaxHighlight === 'prism') { + parser.use([remarkPrism(scopedClassName)]); + } else if (syntaxHighlight === 'shiki') { + parser.use([await remarkShiki(shikiTheme)]); + } + parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement'] }]]); loadedRehypePlugins.forEach(([plugin, opts]) => { diff --git a/packages/markdown/remark/src/remark-shiki.ts b/packages/markdown/remark/src/remark-shiki.ts new file mode 100644 index 000000000..5becad76d --- /dev/null +++ b/packages/markdown/remark/src/remark-shiki.ts @@ -0,0 +1,23 @@ +import shiki from 'shiki'; +import { visit } from 'unist-util-visit'; + +const remarkShiki = async (theme: shiki.Theme) => { + const highlighter = await shiki.getHighlighter({ theme }); + + return () => (tree: any) => { + visit(tree, 'code', (node) => { + let html = highlighter.codeToHtml(node.value, { lang: node.lang ?? 'plaintext' }); + + // Replace "shiki" class naming with "astro". + html = html.replace('<pre class="shiki"', '<pre class="astro-code"'); + // Replace "shiki" css variable naming with "astro". + html = html.replace(/style="(background-)?color: var\(--shiki-/g, 'style="$1color: var(--astro-code-'); + + node.type = 'html'; + node.value = html; + node.children = []; + }); + }; +}; + +export default remarkShiki; diff --git a/packages/markdown/remark/src/types.ts b/packages/markdown/remark/src/types.ts index 541d3ff27..043594c9c 100644 --- a/packages/markdown/remark/src/types.ts +++ b/packages/markdown/remark/src/types.ts @@ -1,10 +1,13 @@ -import * as unified from 'unified'; +import type * as unified from 'unified'; +import type * as shiki from 'shiki'; export type UnifiedPluginImport = Promise<{ default: unified.Plugin }>; export type Plugin = string | [string, any] | UnifiedPluginImport | [UnifiedPluginImport, any]; export interface AstroMarkdownOptions { mode?: 'md' | 'mdx'; + syntaxHighlight?: 'prism' | 'shiki' | false; + shikiTheme?: shiki.Theme; remarkPlugins?: Plugin[]; rehypePlugins?: Plugin[]; } |