0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-23 21:53:55 -05:00
astro/packages/markdown/remark/src/index.ts
Juan Martín Seery 2bc91543ce
feat: Added the ability to add custom themes/languages to Shiki (#2518)
* Replaced `shikiTheme` with `shikiConfig`

* Code.astro now accepts custom themes/langs

* Updated docs

* Updated tests

* Fixed language loading

* Added customization examples

* Updated documentation

* Added more tests

* Changelogs

* Changed some spaces to tabs

* Fixed typo in changesets

* Moved tests fixtures

* Rolled back changes to with-markdown-shiki

* Removed lang example in docs

* Optimized Code component

* Try to fix windows errors

* Try to see if this new tests work
2022-02-07 08:31:02 -08:00

104 lines
3.7 KiB
TypeScript

import type { AstroMarkdownOptions, MarkdownRenderingOptions } from './types';
import createCollectHeaders from './rehype-collect-headers.js';
import scopedStyles from './remark-scoped-styles.js';
import { remarkExpressions, loadRemarkExpressions } from './remark-expressions.js';
import rehypeExpressions from './rehype-expressions.js';
import rehypeIslands from './rehype-islands.js';
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';
import { unified } from 'unified';
import markdown from 'remark-parse';
import markdownToHtml from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeRaw from 'rehype-raw';
import matter from 'gray-matter';
export { AstroMarkdownOptions, MarkdownRenderingOptions };
/** Internal utility for rendering a full markdown file and extracting Frontmatter data */
export async function renderMarkdownWithFrontmatter(contents: string, opts?: MarkdownRenderingOptions | null) {
const { data: frontmatter, content } = matter(contents);
const value = await renderMarkdown(content, opts);
return { ...value, frontmatter };
}
export const DEFAULT_REMARK_PLUGINS = ['remark-gfm', 'remark-smartypants'];
export const DEFAULT_REHYPE_PLUGINS = ['rehype-slug'];
/** Shared utility for rendering markdown */
export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) {
let { remarkPlugins = [], rehypePlugins = [] } = opts ?? {};
const scopedClassName = opts?.$?.scopedClassName;
const mode = opts?.mode ?? 'mdx';
const syntaxHighlight = opts?.syntaxHighlight ?? 'prism';
const shikiConfig = opts?.shikiConfig ?? {};
const isMDX = mode === 'mdx';
const { headers, rehypeCollectHeaders } = createCollectHeaders();
await Promise.all([loadRemarkExpressions(), loadRemarkJsx()]); // Vite bug: dynamically import() these because of CJS interop (this will cache)
let parser = unified()
.use(markdown)
.use(isMDX ? [remarkJsx] : [])
.use(isMDX ? [remarkExpressions] : [])
.use([remarkUnwrap]);
if (remarkPlugins.length === 0 && rehypePlugins.length === 0) {
remarkPlugins = [...DEFAULT_REMARK_PLUGINS];
rehypePlugins = [...DEFAULT_REHYPE_PLUGINS];
}
const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins));
const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins));
loadedRemarkPlugins.forEach(([plugin, opts]) => {
parser.use([[plugin, opts]]);
});
if (scopedClassName) {
parser.use([scopedStyles(scopedClassName)]);
}
if (syntaxHighlight === 'prism') {
parser.use([remarkPrism(scopedClassName)]);
} else if (syntaxHighlight === 'shiki') {
parser.use([await remarkShiki(shikiConfig)]);
}
parser.use([[markdownToHtml as any, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression', 'mdxJsxTextElement', 'mdxJsxFlowElement'] }]]);
loadedRehypePlugins.forEach(([plugin, opts]) => {
parser.use([[plugin, opts]]);
});
parser
.use(isMDX ? [rehypeJsx] : [])
.use(isMDX ? [rehypeExpressions] : [])
.use(isMDX ? [] : [rehypeRaw])
.use(isMDX ? [rehypeEscape] : [])
.use(rehypeIslands);
let result: string;
try {
const vfile = await parser.use([rehypeCollectHeaders]).use(rehypeStringify, { allowDangerousHtml: true }).process(content);
result = vfile.toString();
} catch (err) {
console.error(err);
throw err;
}
return {
metadata: { headers, source: content, html: result.toString() },
code: result.toString(),
};
}
export default renderMarkdownWithFrontmatter;