使用节点脚本为pandoc创建过滤器

问题描述 投票:0回答:1

我有一个可行的解决方案,但感觉很脆弱。我想让它变得更强大。

我想使用 pandoc 将 Markdown 文件转换为 HTML。我想在我的 Markdown 文件中使用

$title$
$content$
等变量,并让 pandoc 用它们的值替换这些占位符字符串。

使用 HTML 模板文件时,这是开箱即用的。您的模板文件将包含您需要的所有样板文件,但不包含实际内容。您可以在 template.html 文件的顶部创建一个 YAML 块,如下所示:

---
title: Page Title
...

您的 HTML 可能包含如下标签:

<title>$title$</title>

当 pandoc 将 Markdown 渲染为 HTML 时,输出的 HTML 文件将包含:

<title>Page Title</title>

但是,在包含 HTML 页面实际内容的 Markdown 文档中,有两个问题需要解决:

  1. pandoc 将任何
    $text surrounded by dollar signs$
    解释为数学。
  2. pandoc 不会自动用变量值替换任何变量名称。

我对第一个问题的解决方案是使用

\$
来逃避 Markdown 中的美元符号。这是一个示例。

输入.md

---
header: Section Header
content: This is content.
...

# \$header\$

\$content\$

我对第二个问题的解决方案是使用filter。这是一个 JavaScript 文件,我可以用它来替换像

$header$
这样的变量,并使用顶部 YAML 块中为其指定的值(包括用于调试的代码):

替换.js

#!/usr/bin/env node

const { createWriteStream } = require('fs')
const logger = new console.Console(createWriteStream("./output.log"));

// Pandoc filter to convert all text to uppercase

var pandoc = require("pandoc-filter");
var Str = pandoc.Str;

function action({ t: type, c: value }, format, meta) {
  log(arguments)
  const keys =  Object.keys(meta).reduce(keyMap, {})

    if (type === "Str"){
    const key = keys[value]
    if (key) {
      const replace = meta[key].c.reduce(replacer, "")
      logKeys(keys, key, replace)
      return Str(replace)
    }
  }
}

function keyMap( map, key ){
  const cue = `$${key}$`
  map[cue] = key
  return map
}

function replacer(result, { t, c }) {
  switch (t) {
    case "Space":
      return result + " "
    case "Emph":
      return result + `<em>${c[0].c}</em>`
    default: 
      return result + c
  }
}

pandoc.stdio(action);

// Useful for debugging

function log(args) {
  const [{t: type, c: content}, format, meta] = args
  logger.log(`type:   "${type}"`)
  if (content) {
    logger.log(`typeof  content: ${typeof content}
        content: ${JSON.stringify(content)}`)
  }
  logger.log(`format: ${format}`)
  logger.log(`meta:   ${JSON.stringify(meta)}`)
  logger.log("")
}

function logKeys(keys, key, replace) {
  logger.log("keys:", keys)
  logger.log(`${key} ==> ${replace}`)
  logger.log("---")
}

这需要安装 pandoc-filter 模块。

npm install -g pandoc-filter

我的代码创建了一个名为

output.log
的文件,以便我可以看到
replace.js
中发生的情况。

当我跑步时...

pandoc input.md --filter replace.js -o output.html

...我在

output.html
文件中得到了我期望的 HTML:

<h1 id="header">Section Header</h1>
<p>This is content.</p>

但是,如果我将

content
的值更改为
This is *content*.
(“内容”用星号括起来以表示斜体),
output.log
显示我期望的值...

content ==> This is <em>content</em>.

...但是在

output.html
中,尖括号已被字符实体取代。

<p>This is &lt;em&gt;content&lt;/em&gt;.</p>

不仅如此,我的整个

replacer()
switch{}
声明也很笨拙。它只处理我特别选择的案例。

如何使我的

replacer.js
脚本更加通用和健壮?

node.js filter pandoc
1个回答
0
投票

发送到

value
函数的
action
是一个“抽象语法树”(AST),而不是字符串。
action
函数不应返回字符串,而应返回:

  • undefined
    ,意思是“保持 AST 不变”
  • 修改后的 AST

pandoc 将使用 AST 的修改版本来生成输出。

这是

action
函数的更正版本。

function action({ t: type, c: value }, format, meta) {
  // log(arguments)
  const substitutes =  Object.keys(meta).reduce(keyMap, {})

    if (type === "Str"){
    // value may be a key in substitutes like "$title$"
    return substitutes[value] // undefined if no substitute
  }

  function keyMap( map, key ){
    const cue = `$${key}$`
    map[cue] = meta[key].c
    return map
  }
}

每次调用我的

input.md
meta
的值为:

{
    "content": {
        "t": "MetaInlines",
        "c": [
            {
                "t": "Str",
                "c": "This"
            },
            {
                "t": "Space"
            },
            {
                "t": "Str",
                "c": "is"
            },
            {
                "t": "Space"
            },
            {
                "t": "Emph",
                "c": [
                    {
                        "t": "Str",
                        "c": "content"
                    }
                ]
            },
            {
                "t": "Str",
                "c": "."
            }
        ]
    },
    "header": {
        "t": "MetaInlines",
        "c": [
            {
                "t": "Str",
                "c": "Section"
            },
            {
                "t": "Space"
            },
            {
                "t": "Str",
                "c": "Header"
            }
        ]
    }
}

substitutes
对象将
$content$
映射到
meta.content.c

处的数组

当使用

action()
等于
value
调用
$content$
时,
action()
返回此 AST 数组。

© www.soinside.com 2019 - 2024. All rights reserved.