如何使用 Astro 组件自定义 Markdown?

was*_*ila 11 markdown vite astrojs

md vs mdx

md import pipeline 渲染为 html,mdx import pipeline 渲染为 .js/.ts/.jsx...,这允许使用 Astro 组件自定义 html 标签。

目标

我想利用 Astro 的 .md 文件中的 mdx 功能

我尝试过什么

  • 尝试在 Astro 中配置 mdx 集成,但不幸的是它排除了 .md 扩展名以允许默认的 md rehype 管道

  • 我将所有 .md 文件重命名为 .mdx 的解决方法非常具有侵入性(更改文件元数据)我想找到一种不同的方法

  • 分叉 mdx 集成很难维护

  • 我启动了一个 vite 插件,它更改 .md ids 以添加 x 作为 .mdx,然后我必须编写自己的加载程序,然后它变得太复杂

  • astro-remote 仅采用一些默认组件,并且允许替换任何自定义组件

例子

我想避免

更确切地说

任何实现这一目标的最佳方法的想法,感觉就像缺少释放 Astro 对 Markdow 的力量的最后一步!

参考

was*_*ila 2

因此,经过长时间的搜索,我回答了我自己的问题,这确实可以以一种优雅的方式实现。

概念

  • 解析 Markdown 并获取抽象语法树。AST 已经提供了 json 格式的所有节点,可以用作 Astro 组件的属性

例如,也可以选择使用扩展,但让我们保持简单

import {fromMarkdown} from 'mdast-util-from-markdown'

const tree = fromMarkdown(content)
Run Code Online (Sandbox Code Playgroud)
  • 将树传递给递归组件,该组件处理需要使用特定组件呈现的所有自定义节点。
  • 使用通用 Markdown 渲染器处理默认的非自定义节点,例如toHtml(toHast(node))

执行

这是完整的自定义渲染递归组件的样子

---
import Heading from '../nodes/Heading.astro';
import Image from '../nodes/image/Image.astro'
import Code from '../nodes/code/Code.astro'
import {toHast} from 'mdast-util-to-hast'
import {toHtml} from 'hast-util-to-html'
export interface Props {
    node: object;
    data: object;
}

const {node, data} = Astro.props;
const handled_types = ["root","heading","paragraph","image","code"]
const other_type = !handled_types.includes(node.type)
---
{(node.type == "root") &&
    node.children.map((node)=>(
        <Astro.self node={node} data={data} />
    ))
}
{(node.type == "paragraph") &&
<p>
    {node.children.map((node)=>(
        <Astro.self node={node} data={data}/>
    ))}
</p>
}
{(node.type == "heading") &&
    <Heading node={node} headings={data.headings}/>
}
{(node.type == "image") &&
    <Image node={node}  filepath={data.path}/>
}
{(node.type == "code") &&
    <Code node={node}  filepath={data.path}/>
}
{other_type &&
    <Fragment set:html={toHtml(toHast(node))}></Fragment>
}
Run Code Online (Sandbox Code Playgroud)

我在模板项目上尝试过这个,它工作得很好,这是一个示例用法,例如来自动态路由文件,例如[...sid].astro

---
const {sid} = Astro.params;
import AstroMarkdown from '@/components/renderers/AstroMarkdown.astro'

const data_content = await load_json(`gen/documents/${sid}/content.json`)
const tree = await load_json(`gen/documents/${sid}/tree.json`)
---
<AstroMarkdown node={tree} data={data_content} />
Run Code Online (Sandbox Code Playgroud)

参考