在Markdown元素之间转换

有什么select来parsingMarkdown文档并处理其元素以输出另一个Markdown文档?

我们来说吧

``` # unaffected # ``` # H1 # H1 == ## H2 ## H2 -- ### H3 ### 

应该转换成

 ``` # unaffected # ``` ## H1 ## H1 -- ### H2 ### ### H2 ### #### H3 #### 

在Node环境中。 目标元素可能会有所不同(例如,####可能会转换为**)。

该文件可能包含其他标记元素应保持不受影响。

如何获得? 显然,不与正则expression式(使用正则expression式代替完整的词法分析器会影响# unaffected # )。 我希望使用marked但它似乎只能够输出HTML,而不是Markdown。

你有没有考虑过使用HTML作为中间格式? 一旦在HTML中,标题types之间的区别将是难以区分的,所以Markdown – > HTML转换将有效地标准化它们。 有markdown – > HTML转换器,还有一些HTML – >降价。

我把这两个软件包放在一起:

我不知道你是否有任何性能要求(阅读:这是缓慢的…),但这是一个非常低的投资解决scheme。 看一看:

 var md = require('markdown-it')(), h2m = require('h2m'); var mdContent = ` \`\`\` # unaffected # \`\`\` # H1 # H1 == ## H2 ## H2 -- ### H3 ### `; var htmlContent = md.render(mdContent); var newMdContent = h2m(htmlContent, {converter: 'MarkdownExtra'}); console.log(newMdContent); 

您可能需要使用混合的组件才能获得正确的方言支持。 我试了一堆,不能完全匹配你的输出。 我想可能是--正在被解释不同? 这是输出,我会让你决定是否足够好:

 ``` # unaffected # ``` # H1 # # H1 # ## H2 ## ## H2 ## ### H3 ### 

这是一个外部降价parsing器, pandoc解决scheme。 它允许在haskell或python 自定义filter修改input(也有一个node.js端口 )。 这里是一个pythonfilter,每个标题增加一个级别。 我们把它保存为header_increase.py

 from pandocfilters import toJSONFilter, Header def header_increase(key, value, format, meta): if key == 'Header' and value[0] < 7: value[0] = value[0] + 1 return Header(value[0], value[1], value[2]) if __name__ == "__main__": toJSONFilter(header_increase) 

它不会影响代码块。 但是,它可能会将 h1和h2元素的setex样式头 (使用===--- )转换为atx样式头(使用# ),反之亦然。

要使用脚本,可以从命令行调用pandoc:

 pandoc input.md --filter header_increase.py -o output.md -t markdown 

使用node.js,您可以使用pdc来调用pandoc。

 var pdc = require('pdc'); pdc(input_md, 'markdown', 'markdown', [ '--filter', './header_increase.py' ], function(err, result) { if (err) throw err; console.log(result); }); 

尽pipeMarkdown显然简单,但实际上parsing起来有些复杂。 每个部分都build立在下一个部分上,即使您只想处理文档的一部分,为了覆盖所有边缘情况,您也需要一个完整的parsing器。

例如,各种块级元素可以嵌套在其他块级元素(列表,块引用等)中。 大多数实现依赖于parsing器中各种特定的事件顺序,以确保正确parsing整个文档。 如果你删除了一个较早的作品,很多较后的作品将会被打破。 例如,代码块内的Markdown标记不会被parsing为Markdown,因为其中一个步骤是查找和标识代码块,以便后面的parsing步骤不会看到代码块。

因此,为了实现您的目标并涵盖所有可能的边缘情况,您需要一个完整的Markdownparsing器。 但是,由于您不想输出HTML,因此您的选项有所限制,您需要做一些工作才能获得可行的解决scheme。

基本上有三种Markdownparsing器(我在这里概括):

  1. 使用正则expression式stringreplacereplace源文档中HTML标记的Markdown标记。
  2. 使用由parsing器(在每个步骤中)调用的渲染器,因为它parsing输出新文档的文档。
  3. 生成一个树对象或一个令牌列表(具体细节因实现而异),在稍后的步骤中将其呈现(转换为string)为新文档。

原始参考实现(markdown.pl)是第一种types,可能对您无用。 我只是提到完整性。

标记是第二种,虽然可以使用,但您需要编写自己的渲染器,并让渲染器在渲染文件的同时修改文档。 尽pipe通常是一种性能良好的解决scheme,但当您需要修改文档时,并不总是最好的方法,特别是如果您需要文档中其他地方的上下文。 但是,你应该能够使其工作。

例如,为了适应文档中的示例 ,您可以执行如下操作(从此处借用的multiplyString ):

 function multiplyString (str, num) { return num ? Array(num + 1).join(str) : ""; } renderer.heading = function (text, level) { return multiplyString("#", level+1) + " " + text; } 

当然,您还需要为输出Markdown语法的所有其他块级渲染器方法和内联级别渲染器方法创build渲染器。 请参阅以下关于渲染器的一般意见。

Markdown-JS是第三种(因为Marked也提供了一个访问令牌的较低级别的API ,所以它也可以这样使用)。 正如其自述文件所述 :

中间表示

在内部,将一大块Markdown转换成一大块HTML的过程有三个步骤:

  1. 将Markdownparsing到JsonML树中。 在parsing中find的任何引用都存储在关键references下的根节点的属性哈希中。
  2. 将Markdown树转换为HTML树。 重命名任何需要它的节点(例如, bulletlistul ),并查找链接或图像使用的任何引用。 完成后删除参考属性。
  3. 将HTML树string化,注意不要破坏空白是重要的空白(例如,包围内联元素)。

如果您需要在中间阶段对数据进行一些处理或修改,则可以单独调用此过程的每个步骤。

您可以在步骤1或步骤2中采取树对象并进行修改。 不过,我会推荐第1步,因为JsonML树会更贴近实际的Markdown文档,因为第2步中的HTML树是要输出的HTML的表示forms。 请注意,在任何实现中,HTML都会丢失关于原始Markdown的一些信息。 例如,星号或下划线是用于强调( *foo*还是_foo_ ),还是星号,短划线(连字符)或加号用作列表项目符号? 我不确定有多less细节的JsonML树(没有亲自使用过),但它肯定应该比第2步中的HTML树更多。

一旦你对JsonML树进行了修改(perhpas使用这里列出的工具之一,那么你可能会想要跳过第2步并实现你自己的第3步,将JsonML树渲染(string化)回到Markdown文档。

其中最困难的部分就在于此。 Markdownparsing器输出Markdown非常罕见。 事实上,Markdownparsing器很难输出除HTML以外的任何内容。 Pandoc是最受欢迎的例外,它是许多格式input和输出的文档转换器。 但是,如果希望继续使用JavaScript解决scheme,那么您select的任何库都需要您编写自己的渲染器,该渲染器将输出Markdown(除非search出现由其他第三方构build的渲染器)。 当然,一旦你这样做了,如果你可以使用,其他人可以从中受益。 不幸的是,构build一个Markdown渲染器已经超出了这个答案的范围。

构build渲染器时,一个可能的捷径是,如果您使用的Markdown库碰巧将位置信息存储在其标记列表中(或者以某种方式使您可以访问每个元素的原始Markdown),则可以使用渲染器中的信息只需复制并输出原始Markdown文本,除非需要对其进行更改。 例如, markdown-it lib在Token.map和/或Token.markup属性中提供了这些数据。 您仍然需要创build自己的渲染器,但让Markdown看起来更像原始渲染应该更容易。

最后,我还没有亲自使用过,也不推荐上面提到的任何特定的Markdownparsing器。 它们是各种parsing器的stream行示例,用于演示如何创build解决scheme。 您可能会发现一个不同的实现更适合您的需求。 一个很长的,虽然不完整的列表在这里 。

你必须使用正则expression式。 marked为使用Regexp来parsing文档。 你为什么不呢?

这是你需要的一些正则expression式,来自github上的marked.js源代码 :

 var block = { newline: /^\n+/, code: /^( {4}[^\n]+\n*)+/, fences: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, nptable: noop, lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/, def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, table: noop, paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, text: /^[^\n]+/ }; 

如果你真的不想使用正则expression式,你可以叉marked对象。 并覆盖Renderer对象。

在github上标记的是分成两个部分。 一个用于parsing,一个用于渲染。 你可以很容易地改变渲染到你自己的渲染。 (编译器)

Render.js中一个函数的例子 :

 Renderer.prototype.blockquote = function(quote) { return '<blockquote>\n' + quote + '</blockquote>\n'; };) 

也许这是不完整的答案。 不受影响地复制到其他文件。

然后全部replace

  1. ##space##space

  2. space##