diff options
Diffstat (limited to 'packages/markdown-support/src/index.ts')
-rw-r--r-- | packages/markdown-support/src/index.ts | 61 |
1 files changed, 33 insertions, 28 deletions
diff --git a/packages/markdown-support/src/index.ts b/packages/markdown-support/src/index.ts index 08f171c3c..f311efa7c 100644 --- a/packages/markdown-support/src/index.ts +++ b/packages/markdown-support/src/index.ts @@ -1,62 +1,67 @@ -import type { AstroMarkdownOptions } from './types'; +import type { AstroMarkdownOptions, MarkdownRenderingOptions } from './types'; import createCollectHeaders from './rehype-collect-headers.js'; import scopedStyles from './remark-scoped-styles.js'; -import { remarkCodeBlock, rehypeCodeBlock } from './codeblock.js'; +import remarkExpressions from './remark-expressions.js'; +import rehypeExpressions from './rehype-expressions.js'; +import { rehypeCodeBlock } from './codeblock.js'; +import { loadPlugins } from './load-plugins.js'; import raw from 'rehype-raw'; import unified from 'unified'; import markdown from 'remark-parse'; import markdownToHtml from 'remark-rehype'; -// import smartypants from '@silvenon/remark-smartypants'; import rehypeStringify from 'rehype-stringify'; -export interface MarkdownRenderingOptions extends Partial<AstroMarkdownOptions> { - $?: { - scopedClassName: string | null; - }; - mode: 'md' | 'astro-md'; -} +export { AstroMarkdownOptions, MarkdownRenderingOptions }; /** Internal utility for rendering a full markdown file and extracting Frontmatter data */ export async function renderMarkdownWithFrontmatter(contents: string, opts?: MarkdownRenderingOptions | null) { // Dynamic import to ensure that "gray-matter" isn't built by Snowpack const { default: matter } = await import('gray-matter'); const { data: frontmatter, content } = matter(contents); - const value = await renderMarkdown(content, { ...opts, mode: 'md' }); + const value = await renderMarkdown(content, opts); return { ...value, frontmatter }; } /** Shared utility for rendering markdown */ export async function renderMarkdown(content: string, opts?: MarkdownRenderingOptions | null) { - const { $: { scopedClassName = null } = {}, mode = 'astro-md', footnotes: useFootnotes = true, gfm: useGfm = true } = opts ?? {}; + const { $: { scopedClassName = null } = {}, footnotes: useFootnotes = true, gfm: useGfm = true, remarkPlugins = [], rehypePlugins = [] } = opts ?? {}; const { headers, rehypeCollectHeaders } = createCollectHeaders(); + let parser = unified().use(markdown).use([remarkExpressions, { addResult: true }]); - let parser = unified().use(markdown).use(remarkCodeBlock()); + if (remarkPlugins.length === 0) { + if (useGfm) { + remarkPlugins.push('remark-gfm'); + } - if (scopedClassName) { - parser = parser.use(scopedStyles(scopedClassName)); - } + if (useFootnotes) { + remarkPlugins.push('remark-footnotes'); + } - if (useGfm) { - const { default: gfm } = await import('remark-gfm'); - parser = parser.use(gfm); + remarkPlugins.push('@silvenon/remark-smartypants'); } + const loadedRemarkPlugins = await Promise.all(loadPlugins(remarkPlugins)); + const loadedRehypePlugins = await Promise.all(loadPlugins(rehypePlugins)); - if (useFootnotes) { - const { default: footnotes } = await import('remark-footnotes'); - parser = parser.use(footnotes); + loadedRemarkPlugins.forEach(([plugin, opts]) => { + parser.use(plugin, opts); + }); + + if (scopedClassName) { + parser.use(scopedStyles(scopedClassName)); } + parser.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw', 'mdxTextExpression'] }); + parser.use(rehypeExpressions); + + loadedRehypePlugins.forEach(([plugin, opts]) => { + parser.use(plugin, opts); + }); + let result: string; try { - const vfile = await parser - .use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw'] }) - .use(raw) - .use(rehypeCollectHeaders) - .use(rehypeCodeBlock()) - .use(rehypeStringify) - .process(content); + const vfile = await parser.use(raw).use(rehypeCollectHeaders).use(rehypeCodeBlock()).use(rehypeStringify, { entities: { useNamedReferences: true }}).process(content); result = vfile.contents.toString(); } catch (err) { throw err; |