summaryrefslogtreecommitdiff
path: root/packages/markdown-support/src/index.ts
diff options
context:
space:
mode:
authorGravatar Pavel Mineev <pavel@mineev.me> 2021-07-01 19:55:22 +0300
committerGravatar GitHub <noreply@github.com> 2021-07-01 11:55:22 -0500
commitd3969436dcbe40a3d41a036ff7c2761aed176109 (patch)
tree5b79d1ce306566bc53a71e2fe2e5b494adf20904 /packages/markdown-support/src/index.ts
parente773771b917d1d11e8a5647ccdc2d44c903f1f4c (diff)
downloadastro-d3969436dcbe40a3d41a036ff7c2761aed176109.tar.gz
astro-d3969436dcbe40a3d41a036ff7c2761aed176109.tar.zst
astro-d3969436dcbe40a3d41a036ff7c2761aed176109.zip
Remark and rehype plugins (#562)
* remark plugins * remove unused dependency * enable codeblocks * backward compatibility with remark-code-titles * add support for rehype plugins * add proper types for plugins * fixes after review - connect plugins by name - make plugins configurable - connect gfm and footnotes if no plugins provided from config - add more plugins to example * update and rename example * add documentation for markdown plugins * chore: rename with-markdown-plugins example * chore: restructure dependencies * feat: add back smartypants, fix mdx expressions * chore: remove log * test: add markdown plugin tests * chore: add changeset * docs: update markdown doc Co-authored-by: Nate Moore <nate@skypack.dev>
Diffstat (limited to 'packages/markdown-support/src/index.ts')
-rw-r--r--packages/markdown-support/src/index.ts61
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;