Edge Markdown
Edge Markdown 构建于 Remark 和 MDC 之上,用于将 Markdown 文档转换为 HTML。
MDC(Markdown Components) 是一种扩展的 Markdown 格式,允许你直接在 Markdown 中使用组件。这让你可以轻松地将散文与交互式元素组合在同一文档中。
以下是 Edge Markdown 的一些关键特性。
-
GitHub Flavored Markdown (GFM):支持 GFM 特性,例如表格、任务列表、自动链接和删除线。
-
嵌入 Edge 组件:使用 MDC 语法在 Markdown 文件中内联使用 Edge 组件。
-
增强的代码块:代码示例使用 Shiki 渲染,提供语法高亮、代码块标题、行高亮以及内联 diff。
-
自定义散文渲染:标准的 Markdown 元素(图片、标题、列表、段落等)可以使用你自己的自定义组件来渲染。
-
AST 检查工具:由于 Markdown 会被编译为 AST,你可以分析节点以检测失效链接、校验引用,或重写图片源。
安装
从 npm 包仓库安装 edge-markdown 插件。
npm i edge-markdown
接下来是将该插件注册到 Edge。
import edge from 'edge.js'
import { edgeMarkdown } from 'edge-markdown'
edge.use(edgeMarkdown())
基本用法
配置好插件后,你可以使用 @markdown 标签渲染 Markdown 内容和文件。你可以指定 markdown 文件的绝对路径,或者使用 content 属性指定原始内容。
@markdown({
file: absolutePathToMdFile,
})
@markdown({
content: contentAsString,
})
@markdown 标签会将生成的 HTML 写入输出。如果你想访问额外的属性,例如文档的 frontmatter 和目录(table of contents),请考虑使用 $markdown.render 方法。
@let(doc = await $markdown.render({
file: absolutePathToMdFile,
}))
<h1>{{{ doc.frontmatter.title }}}</h1>
<div>{{{ doc.content }}}</div>
<div>{{{ doc.toc }}}</div>
配置选项
edgeMarkdown 函数接受多种配置选项,用于自定义 Markdown 处理行为。你可以控制组件加载、语法高亮、HTML 处理、目录生成以及插件集成。
edge.use(edgeMarkdown({
prefix: 'markdown',
highlight: true,
allowHTML: true,
toc: {
enabled: true,
maxDepth: 2,
},
remarkPlugins: [],
rehypePlugins: [],
components: {},
allowedTags: [],
hooks: [
(node) => {},
]
}))
- prefix
-
prefix选项定义了用于在components目录中查找自定义组件的前缀。例如,默认情况下note组件会从components/markdown/note.edge文件加载。 - highlight
-
启用/禁用默认的 Shiki 配置,用于高亮代码块。如果你决定禁用它,则需要使用自定义的 rehype 插件来处理代码块。
- allowHTML
-
启用/禁用在 Markdown 中使用 HTML 标签。当你渲染用户编写的内容时,应将其禁用。
- toc
-
启用/禁用目录的创建。
toc选项接受与mdast-util-toc包相同的选项集合。 - allowedTags
-
你可以指定一个要渲染的 HTML 标签数组,一旦定义,所有其他标签都将被跳过。如果你要将 Markdown 内容转换为邮件,并希望限制为特定的 HTML 标签,此设置会很有用。
- remarkPlugins
-
注册一组 remark 插件。
- rehypePlugins
-
注册一组 rehype 插件。
- hooks
-
hooks属性是一个函数数组,会对每个 AST 节点执行。AST 节点将来自 HAST 语法树的Element、Text或Comment。
生成 TOC
目录的 HTML 可以通过 $markdown.render 方法的返回值访问。
默认情况下,使用 level 2 及以下的标题来生成目录。不过,你可以通过 toc 配置选项来配置目录生成设置。
@let(doc = await $markdown.render({
file: absolutePathToMdFile,
toc: {
maxDepth: 2
}
}))
<div>
{{{ doc.content }}}
</div>
<div>
{{{ doc.toc }}}
</div>
配置 Shiki
默认情况下,Edge Markdown 使用 Shiki 进行语法高亮,并带有合理的默认值。你可以通过传入自定义主题和语言来自定义 Shiki 配置。
import { edgeMarkdown } from 'edge-markdown'
edge.use(edgeMarkdown({
shiki: {
theme: 'github-dark',
langs: ['javascript', 'typescript', 'json']
}
}))
你也可以完全禁用 Shiki,并使用自己的 rehype 插件来进行代码高亮。
edge.use(edgeMarkdown({
highlight: false,
rehypePlugins: [yourCustomCodePlugin]
}))
已启用的转换器
默认情况下启用了来自 Shiki 的以下 transformers。
| Transformer | 用途 |
|---|---|
transformerNotationDiff | 使用 [!code --] 和 [!code ++] 内联注释显示 diff 标记。 |
transformerNotationHighlight | 使用 [!code highlight] 内联注释高亮当前行。 |
transformerNotationWordHighlight | 在该词所在行之前使用 [!code word:Hello] 注释高亮给定的词。 |
此外,代码块反引号中的元数据可通过 pre 标签的 node.properties 属性获取。
```ts title=start/routes.ts
console.log('Highlighted') // [!code highlight]
console.log('Not highlighted')
console.log('hewwo') // [!code --]
console.log('hello') // [!code ++]
// [!code word:Hello]
const message = 'Hello World'
```
显示错误与警告
Markdown 渲染过程中产生的错误和警告,可以通过 $markdown.render 方法返回的 messages 属性访问。
这些可能包括通过 remark 和 rehype 插件报告的默认和自定义错误消息。每条消息都是 VFileMessage 类的一个实例。
@let(doc = await $markdown.render({
file: absolutePathToMdFile
}))
@each(message in doc.messages)
{{ message.line }}:{{ message.column }} {{ message.reason }}
@end
解析 front-matter
所有 Markdown 文件的 front-matter 都会被自动提取,你可以通过 $markdown.render 方法返回的 frontmatter 属性访问它。
@let(doc = await $markdown.render({
file: absolutePathToMdFile
}))
{{ doc.frontmatter.title }}
{{ doc.frontmatter.summary }}
如果你只需要 frontmatter 而不渲染 Markdown 内容,请使用 $markdown.frontmatter 方法。这是一个轻量操作,只解析 YAML frontmatter 并跳过整个 remark/rehype 管道。
@let(meta = await $markdown.frontmatter({
file: absolutePathToMdFile
}))
{{ meta.title }}
{{ meta.summary }}
$markdown.frontmatter 方法接受 file 或 content 属性,就像 $markdown.render 一样。
@let(meta = await $markdown.frontmatter({
content: rawMarkdownString
}))
渲染预览
你可以使用 $markdown.preview 方法渲染 Markdown 文档的预览。该方法只渲染第一个 h2 标题之前的内容,这对于在列表页生成文章摘要或内容预览很有用。
@let(doc = await $markdown.preview({
file: absolutePathToMdFile
}))
<article>
<div>{{{ doc.content }}}</div>
</article>
$markdown.preview 方法接受与 $markdown.render 相同的选项,并返回 content、frontmatter 和 messages 属性。
如果文档没有 h2 标题,则会渲染完整内容。
编写组件
你可以创建可复用的组件,通过交互式元素和自定义样式来增强 Markdown 内容。Edge Markdown 使用 MDC(Markdown Components)语法,将 Edge 组件直接嵌入 Markdown 文件中。
组件会自动从 components/[prefix] 目录加载,其中 prefix 默认为 markdown。这让你可以将 Markdown 专用的组件与常规 Edge 组件分开组织。
另请参阅:MDC 组件语法参考
在 components/markdown/alert.edge 创建一个组件文件:
<div class="alert alert-{{ type ?? 'note' }}">
<h3>{{ title }}</h3>
@markdownSlot()
</div>
然后在你的 Markdown 文件中使用它:
:alert{title="Important" type="warning"}
This is a warning alert with **markdown content** inside.
:
:alert{title="Tip"}
You can also use components with slots.
:
使用插槽
MDC 允许在 Markdown 组件中使用插槽。插槽通过 # 标签后跟插槽名来定义,你可以使用 @markdownSlot 标签渲染其内容。
:hero
Default slot text
#description
This will be rendered inside the `description` slot.
:
<div>
<h1>
@markdownSlot()
</h1>
<div>
@markdownSlot('description')
</div>
</div>
将散文元素渲染为组件
你可以创建具有特定名称的组件,来覆盖标准 Markdown 元素的渲染方式。这让你可以自定义标题、段落、图片以及其他元素的输出。
在下面的示例中,我们在 components/markdown 目录中创建自定义组件,用于渲染 img 和 h2 标签。
<figure>
<img src="{{ src }}" alt="{{ alt }}" />
@if(title)
<figcaption>{{ title }}</figcaption>
@end
</figure>
<h2 id="{{ id }}" class="section-heading">
@markdownSlot()
</h2>