在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 – >降价。
我把这两个软件包放在一起:
- 用于Markdown – > HTML的https://www.npmjs.com/package/markdown-it
- 为HTML – > Markdown https://www.npmjs.com/package/h2m
我不知道你是否有任何性能要求(阅读:这是缓慢的…),但这是一个非常低的投资解决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器(我在这里概括):
- 使用正则expression式stringreplacereplace源文档中HTML标记的Markdown标记。
- 使用由parsing器(在每个步骤中)调用的渲染器,因为它parsing输出新文档的文档。
- 生成一个树对象或一个令牌列表(具体细节因实现而异),在稍后的步骤中将其呈现(转换为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的过程有三个步骤:
- 将Markdownparsing到JsonML树中。 在parsing中find的任何引用都存储在关键
references
下的根节点的属性哈希中。- 将Markdown树转换为HTML树。 重命名任何需要它的节点(例如,
bulletlist
到ul
),并查找链接或图像使用的任何引用。 完成后删除参考属性。- 将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
-
##space
与##space
-
带
space##