自定义转换器

在本页中,您将学习如何创建和注册自定义转换器。

数据结构

首先,让我们简要解释一下 Asciidoctor.js 处理器的工作原理。当您使用 Asciidoctor.js 转换文档时,处理器会创建一个文档的树形表示,称为抽象语法树(AST)。

这个树或数据结构由节点组成。让我们通过以下文档举个具体的例子:

= Title

== Section 1

== Section 2

在上面的例子中,我们有一个包含两个章节的文档。很自然地,我们的树形表示将由一个 Document 节点和两个 Section 节点组成。正如你所看到的,结构是层次化的,两个 Section 节点是 Document 节点的子节点,而 Document 节点本身是树的根节点。

如果我们转换这个文档,转换器将负责转换文档中的每一个节点。在上面的例子中,转换器的任务就是转换一个 Document 节点和两个 Section 节点。

自定义转换器类

接下来,我们来看一下如何创建一个自定义转换器。

一个自定义转换器是一个 JavaScript ,包含一个 convert 方法:

class CustomConverter {
  convert (node, transform) { (1) (2)
    return node.getContent()
  }
}
1 node 是一个继承自 AbstractNode 的节点。
2 transform 只有在节点是 Document 时才会定义。 当转换为嵌入式文档时,transform 的值为 embedded; 当转换为独立文档时(即 standalonetrue),transform 的值为 document

要注册自定义转换器,我们可以使用 ConverterFactory 上的 register 方法:

asciidoctor.ConverterFactory.register(new CustomConverter(), ['html5']) (1)
1 实例化 CustomConverter 并将其注册为 html5 后端。

html5 是默认的后端,因此上述代码将有效地替代 Asciidoctor.js 提供的内置 HTML5 转换器。

接下来让我们创建一个稍微复杂一些的自定义转换器:

class CustomConverter {
  convert (node, transform) {
    const nodeName = transform || node.getNodeName()
    if (nodeName === 'embedded') {
      return `<embedded>
${node.getContent()}
</embedded>` (1)
    } else if (nodeName === 'document') {
      return `<document>
${node.getContent()}
</document>` (2)
    } else if (nodeName === 'section') {
      return `${node.getTitle()}` (3)
    }
    return '' (4)
  }
}
1 如果节点是嵌入式文档,我们将文档内容放在 <embedded> 标签内。
2 如果节点是独立文档,我们将文档内容放在 <document> 标签内。
3 如果节点是章节,我们返回章节的标题。
4 否则,我们返回一个空字符串。

注册自定义转换器后,我们可以转换文档:

const doc = asciidoctor.load(`= Title

== Section 1

== Section 2`)

console.log(doc.convert())
// Prints:
// <embedded>
// Section 1
// Section 2
// </embedded>

节点名称完整列表:

  • document

  • embedded

  • outline

  • section

  • admonition

  • audio

  • colist

  • dlist

  • example

  • floating-title

  • image

  • listing

  • literal

  • stem

  • olist

  • open

  • page_break

  • paragraph

  • preamble

  • quote

  • thematic_break

  • sidebar

  • table

  • toc

  • ulist

  • verse

  • video

  • inline_anchor

  • inline_break

  • inline_button

  • inline_callout

  • inline_footnote

  • inline_image

  • inline_indexterm

  • inline_kbd

  • inline_menu

  • inline_quoted

组合模式

在前面的部分中,我们展示了如何创建和注册一个独立的自定义转换器。但是您可能只想在某些节点上使用自定义转换器,其他节点则委托给另一个转换器(例如,内置转换器)。

在下面的示例中,我们将使用自定义转换器来转换段落,而其他节点将使用内置的 HTML5 转换器:

const asciidoctor = require('asciidoctor')()

class SemanticParagraphConverter {
  constructor () {
    this.baseConverter = asciidoctor.Html5Converter.$new() (1)
  }

  convert (node, transform) {
    if (node.getNodeName() === 'paragraph') {
      return `<p>${node.getContent()}</p>` (2)
    }
    return this.baseConverter.convert(node, transform) (3)
  }
}
1 实例化内置的 HTML5 转换器。
2 定义如何转换 paragraph 节点。
3 默认情况下,调用内置的 HTML5 转换器处理其他节点。

这样,您就可以灵活地控制不同节点的转换方式。