自定义转换器
在本页中,您将学习如何创建和注册自定义转换器。
数据结构
首先,让我们简要解释一下 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 ;
当转换为独立文档时(即 standalone 为 true ),transform 的值为 document 。 |
要注册自定义转换器,我们可以使用 ConverterFactory
上的 register
方法:
asciidoctor.ConverterFactory.register(new CustomConverter(), ['html5']) (1)
1 | 实例化 CustomConverter 并将其注册为 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 转换器处理其他节点。 |
这样,您就可以灵活地控制不同节点的转换方式。