aboutsummaryrefslogtreecommitdiff
path: root/packages/markdown
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--packages/markdown/remark/package.json1
-rw-r--r--packages/markdown/remark/src/index.ts10
-rw-r--r--packages/markdown/remark/src/remark-shiki.ts23
-rw-r--r--packages/markdown/remark/src/types.ts5
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[];
}