Markdown元素之间的转换

时间:2016-02-07 19:54:40

标签: javascript node.js markdown javascript-marked

有哪些选项可以解析Markdown文档并处理其元素以输出另一个Markdown文档?

让我们说吧

```
# unaffected #
```

# H1 #

H1
==

## H2 ##

H2
--

### H3 ###

应转换为

```
# unaffected #
```

## H1 ##

H1
--

### H2 ###

### H2 ###

#### H3 ####

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

该文档可能包含其他不受影响的标记元素。

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

5 个答案:

答案 0 :(得分:5)

以下是使用外部降价解析器pandoc的解决方案。它允许haskell或python中的custom filters修改输入(还有一个node.js port)。这是一个python过滤器,每个标题增加一个级别。我们将其另存为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-style headers(使用===---)转换为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);
});

答案 1 :(得分:3)

您是否考虑过将HTML用作中间格式?一旦进入HTML,标题类型之间的差异将无法区分,因此Markdown - &gt; HTML转换将为您有效地规范化它们。有降价 - &gt; HTML转换器很多,还有很多HTML - &gt;降价。

我使用这两个包组合了一个例子:

我不知道你是否有任何性能要求(读:这很慢......)但这是一个非常低的投资解决方案。看看:

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 ###

答案 2 :(得分:2)

尽管显而易见,Markdown实际上解析起来有点复杂。每个部分都构建在下一个部分之上,这样即使您只想处理文档的一部分,要覆盖所有边缘情况,您需要一个完整的解析器。

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

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

基本上有三种Markdown解析器样式(我在这里概括):

  1. 使用正则表达式字符串替换替换源文档中HTML标记的Markdown标记。
  2. 使用由解析器调用的渲染器(在每个步骤中),因为它解析输出新文档的文档。
  3. 生成一个树对象或令牌列表(具体因实现而异),在后续步骤中将其呈现(转换为字符串)为新文档。
  4. 原始参考实现(markdown.pl)是第一种类型,对您来说可能没用。我只是提到它的完整性。

    Marked属于第二种,虽然可以使用它,但您需要编写自己的渲染器并让渲染器在渲染时同时修改文档。虽然通常是一种performat解决方案,但当您需要修改文档时,它并不总是最好的方法,尤其是当您需要文档中其他位置的上下文时。但是,你应该能够使它发挥作用。

    例如,要调整文档中的example,您可以执行以下操作(multiplyStringhere借用):

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

    当然,您还需要为输出Markdown语法的所有其他block level renderer methodsinline level renderer methods创建渲染器。请参阅下面关于渲染器的一般评论。

    Markdown-JS是第三种(因为事实证明Marked还提供了lower level API访问令牌的权限,因此它也可以这样使用)。正如其README中所述:

      

    中级代表

         

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

         
        
    1. 将Markdown解析为JsonML树。在中找到的任何参考文献   解析存储在根节点的属性哈希下   key references
    2.   
    3. 将Markdown树转换为HTML树。重命名任何节点   需要它(例如bulletlistul)并查找任何引用   链接或图像使用。完成后删除引用属性。
    4.   
    5. 对HTML树进行字符串化,小心不要破坏空格   空格很重要(例如,周围的内联元素)。
    6.         

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

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

    一旦你对JsonML树进行了修改(使用tools listed here之一进行修改),你可能会想跳过第2步并实现你自己的第3步,它将JsonML树呈现(字符串化)回来Markdown文档。

    其中有困难的部分。 Markdown解析器很少输出Markdown。事实上,Markdown解析器很少输出除HTML之外的任何内容。最受欢迎的例外是Pandoc,它是一种用于多种输入和输出格式的文档转换器。但是,希望继续使用JavaScript解决方案,您选择的任何库都需要您编写自己的渲染器,该渲染器将输出Markdown(除非搜索出现由其他第三方构建的渲染器)。当然,一旦你这样做,如果你可以使用,其他人可以在将来从中受益。不幸的是,构建Markdown渲染器超出了这个答案的范围。

    构建渲染器时的一个可能的快捷方式是,如果您使用的Markdown lib恰好将位置信息存储在其标记列表中(或者以其他方式使您可以基于每个元素访问原始的Markdown),您可以在渲染器中使用该信息来简单地复制和输出原始Markdown文本,除非您需要更改它。例如,markdown-it lib在Token.map和/或Token.markup属性上提供该数据。您仍然需要创建自己的渲染器,但应该更容易让Markdown看起来更像原始渲染器。

    最后,我没有亲自使用过,也没有推荐上面提到的任何特定Markdown解析器。它们只是各种类型解析器的流行示例,用于演示如何创建解决方案。您可以找到更符合您需求的不同实施方案。虽然不完整,但list is here。{/ p>

答案 3 :(得分:0)

您必须使用正则表达式。 marked本身使用Regexp来解析文档。为什么不帮你?

这是您需要的一些正则表达式,来自marked.js source code on github

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]+/
};

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

在github上标记分为两个组件。一个用于解析,一个用于渲染。您可以将渲染更改为您自己的渲染。 (编译程序)

Example of one function in Render.js

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

答案 4 :(得分:0)

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

然后全部替换

  1. #space##space

  2. space#space##