import type { Root, Paragraph, PhrasingContent } from 'mdast'; import type { VFile } from 'vfile'; import { visit } from 'unist-util-visit'; import { toString } from 'mdast-util-to-string'; export function remarkDescription() { return (tree: Root, vfile: VFile) => { let description: string | null = null; visit(tree, 'paragraph', (node: Paragraph) => { if (description !== null) return; description = summarize(node); }); // Astro exposes this as `remarkPluginFrontmatter` from render() const astro = (vfile.data.astro ??= { frontmatter: {} }) as { frontmatter: Record }; astro.frontmatter.description = description; }; } /** * Convert a paragraph node to a plain-text string, stripping any * MDX JSX elements (e.g. ) so their content doesn't * leak into the summary. */ function summarize(par: Paragraph): string { const filtered = par.children.filter( (child: PhrasingContent) => !(child.type as string).startsWith('mdxJsx') ); return toString({ type: 'paragraph', children: filtered }); }