astro/packages/markdown-support/src/index.ts
2021-06-04 18:20:12 +00:00

69 lines
2.3 KiB
TypeScript

import type { AstroMarkdownOptions } from './types';
import createCollectHeaders from './rehype-collect-headers.js';
import scopedStyles from './remark-scoped-styles.js';
import { remarkCodeBlock, rehypeCodeBlock } from './codeblock.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';
}
/** 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' });
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 { headers, rehypeCollectHeaders } = createCollectHeaders();
let parser = unified().use(markdown).use(remarkCodeBlock());
if (scopedClassName) {
parser = parser.use(scopedStyles(scopedClassName));
}
if (useGfm) {
const { default: gfm } = await import('remark-gfm');
parser = parser.use(gfm);
}
if (useFootnotes) {
const { default: footnotes } = await import('remark-footnotes');
parser = parser.use(footnotes);
}
let result: string;
try {
const vfile = await parser
.use(markdownToHtml, { allowDangerousHtml: true, passThrough: ['raw'] })
.use(raw)
.use(rehypeCollectHeaders)
.use(rehypeCodeBlock())
.use(rehypeStringify)
.process(content);
result = vfile.contents.toString();
} catch (err) {
throw err;
}
return {
astro: { headers, source: content },
content: result.toString(),
};
}