尽管 Markdown 看起来很简单,但实际上解析起来有些复杂。每个部分都建立在下一个部分的基础上,因此要涵盖所有边缘情况,即使您只想处理文档的一部分,您也需要一个完整的解析器。
例如,各种类型的块级元素可以嵌套在其他块级元素(列表、块引用等)中。大多数实现依赖于解析器中不同的特定事件顺序,以确保正确解析整个文档。如果您删除较早的部分之一,许多后期的部分会破裂。例如,代码块内的 Markdown 标记不会被解析为 Markdown,因为第一步是查找和识别代码块,以便解析中的后续步骤永远不会看到代码块。
因此,为了实现您的目标并涵盖所有可能的边缘情况,您需要一个完整的 Markdown 解析器。但是,由于您不想输出 HTML,因此您的选择有限,您需要做一些工作才能获得有效的解决方案。
Markdown 解析器基本上有三种风格(我在这里概括一下):
- 使用正则表达式字符串替换将 Markdown 标记换成源文档中的 HTML 标记。
- 使用解析器(在每个步骤中)调用的渲染,因为它解析输出新文档的文档。
- 生成树对象或令牌列表(具体情况因实现而异),在后续步骤中将其呈现(转换为字符串)到新文档。
原始参考实现(markdown.pl)属于第一种类型,可能对您无用。我只是为了完整性而提到它。
Marked属于第二种类型,虽然可以使用,但您需要编写自己的渲染器,并让渲染器在渲染文档的同时修改文档。虽然通常是一种性能解决方案,但当您需要修改文档时,它并不总是最好的方法,尤其是当您需要文档中其他地方的上下文时。但是,您应该能够使其工作。
例如,要改编文档中的示例,您可能会执行以下操作(从此处multiplyString
借用):
function multiplyString (str, num) {
return num ? Array(num + 1).join(str) : "";
}
renderer.heading = function (text, level) {
return multiplyString("#", level+1) + " " + text;
}
当然,您还需要为所有其他块级渲染器方法和输出 Markdown 语法的内联级渲染器方法创建渲染器。请参阅下面关于一般渲染器的评论。
Markdown-JS属于第三种类型(事实证明,Marked 还提供了一个较低级别的 API来访问令牌,因此它也可以以这种方式使用)。如其自述文件所述:
中间代表
在内部,将 Markdown 块转换为 HTML 块的过程分为三个步骤:
- 将 Markdown 解析为 JsonML 树。在解析中找到的任何引用都存储在 key 下的根节点的属性哈希中
references
。
- 将 Markdown 树转换为 HTML 树。重命名任何需要它的节点(
bulletlist
例如ul
)并查找链接或图像使用的任何引用。完成后删除引用属性。
- 对 HTML 树进行字符串化,注意不要破坏空白很重要的空白(例如,围绕内联元素)。
如果您需要在中间阶段对数据进行一些处理或修改,则可以单独调用此过程的每个步骤。
您可以在步骤 1 或步骤 2 中获取树对象并进行修改。但是,我建议第 1 步,因为 JsonML 树将更接近实际的 Markdown 文档,因为第 2 步中的 HTML 树是要输出的 HTML 的表示。请注意,在任何实现中,HTML 都会丢失有关原始 Markdown 的一些信息。例如,星号或下划线是否用于强调(*foo*
vs. _foo_
),或者星号、破折号(连字符)或加号是否用作列表项目符号?我不确定 JsonML 树包含多少细节(没有亲自使用过),但它肯定比第 2 步中的 HTML 树更多。
一旦您对 JsonML 树进行了修改(可能使用此处列出的工具之一,那么您可能希望跳过第 2 步并实施您自己的第 3 步,将 JsonML 树呈现(字符串化)回 Markdown 文档。
这就是困难的部分。Markdown 解析器很少输出 Markdown。事实上,Markdown 解析器很少输出除 HTML 之外的任何内容。最流行的例外是 Pandoc,它是用于多种输入和输出格式的文档转换器。但是,想要继续使用 JavaScript 解决方案,您选择的任何库都需要您编写自己的渲染器,该渲染器将输出 Markdown(除非搜索出现由其他第三方构建的渲染器)。当然,一旦你这样做了,如果你提供它,其他人将来可以从中受益。不幸的是,构建 Markdown 渲染器超出了这个答案的范围。
构建渲染器时一种可能的捷径是,如果您使用的 Markdown 库恰好将位置信息存储在其标记列表中(或以其他方式让您可以访问每个元素的原始原始 Markdown),您可以使用渲染器中的该信息以简单地复制和输出原始 Markdown 文本,除非您需要更改它。例如,markdown-it库提供关于Token.map
和/或Token.markup
属性的数据。您仍然需要创建自己的渲染器,但让 Markdown 看起来更像原始渲染器应该更容易。
最后,我没有亲自使用过,也不推荐上面提到的任何特定的 Markdown 解析器。它们只是各种类型解析器的流行示例,用于演示如何创建解决方案。您可能会找到更适合您需求的不同实现。一个冗长但不完整的列表在这里。