扩展教程
如果您了解 Antora 扩展的工作原理,并已牢固掌握相关概念,本页将提供一个端到端的教程,通过一个深入的示例让您全面了解并帮助您充分利用 Antora 的这一功能。
在本教程中,我们将创建一个扩展,用于定位未列出(unlisted)的页面,即无法从导航中访问的页面。扩展将首先检索每个组件版本的导航树。然后,它将遍历该组件版本中的页面,并查找导航树中未找到的任何页面。如果发现任何未列出的页面,它将逐一记录警告。如果进行了配置,它还会将这些页面添加到导航中的一个专门类别下。
本示例让您有机会使用扩展的大部分功能。我们将创建扩展,在 playbook 中注册它,配置它,最后启用它运行 Antora。让我们开始吧。
创建扩展
首先,你需要创建扩展。我们将扩展文件命名为 unlisted-pages-extension.js,并将其放置在 playbook 相邻的 lib/ 文件夹中,这样它就能整齐地组织在 playbook 资源库中。
用 Example 1 中的源代码填充扩展文件。下一节将分析这些代码的作用。
module.exports.register = function ({ config }) {
const { addToNavigation, unlistedPagesHeading = 'Unlisted Pages' } = config
const logger = this.getLogger('unlisted-pagesAlian-extension')
this
.on('navigationBuilt', ({ contentCatalog }) => {
contentCatalog.getComponents().forEach(({ versions }) => {
versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
const navEntriesByUrl = getNavEntriesByUrl(nav)
const unlistedPages = contentCatalog
.findBy({ component, version, family: 'page' })
.filter((page) => page.out)
.reduce((collector, page) => {
if ((page.pub.url in navEntriesByUrl) || page.pub.url === defaultUrl) return collector
logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page')
return collector.concat(page)
}, [])
if (unlistedPages.length && addToNavigation) {
nav.push({
content: unlistedPagesHeading,
items: unlistedPages.map((page) => {
return { content: page.asciidoc.navtitle, url: page.pub.url, urlType: 'internal' }
}),
root: true,
})
}
})
})
})
}
function getNavEntriesByUrl (items = [], accum = {}) {
items.forEach((item) => {
if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item
getNavEntriesByUrl(item.items, accum)
})
return accum
}
js
让我们暂停一下,逐步分析一下这个扩展的作用。
扩展如何工作
扩展首先要导出注册函数,Antora 在需要扩展文件后会立即调用该函数。register 函数与生成器上下文绑定,可用于添加监听器。该函数接受扩展的配置对象(通过对象重构)作为唯一参数。它还会从配置对象中提取几个配置键,以自定义其行为。
module.exports.register = function ({ config }) {
const { addToNavigation, unlistedPagesHeading = 'Unlisted Pages' } = config
}
js
接下来,扩展会创建一个命名日志记录器,用于报告未列出的页面。为此,扩展在上下文中调用 getLogger
方法创建一个命名日志记录器。该方法需要使用 Antora 提供的 @antora/logger
模块,然后将名称传递给默认函数以创建子日志记录器。
const logger = this.getLogger('unlisted-pagesAlian-extension')
js
然后,扩展就会为 navigationBuilt
事件添加一个监听器。由于扩展需要访问导航,这正是生成器检查导航树的好机会。为了访问导航和页面,监听器使用对象重构从上下文变量中检索 contentCatalog
对象。页面转换完成后,就会触发 navigationBuilt
事件,从而访问每个页面的导航标题。
this
.on('navigationBuilt', ({ contentCatalog }) => {
})
js
调用时,navigationBuilt
事件的监听器会从内容目录中检索每个组件版本的导航树,以及一些有关组件版本的信息,以便定位其页面。
contentCatalog.getComponents().forEach(({ versions }) => {
versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
})
})
js
为便于在导航中查找页面,该扩展提供了一个辅助工具,可按 URL 为导航中的每个条目创建一个查找表,并忽略任何重复内容。
function getNavEntriesByUrl (items = [], accum = {}) {
items.forEach((item) => {
if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item
getNavEntriesByUrl(item.items, accum)
})
return accum
}
js
然后,扩展会使用该辅助程序为每个导航创建查找表:
const navEntriesByUrl = getNavEntriesByUrl(nav)
js
现在真正的工作开始了。扩展程序会返回内容目录,查找当前组件版本中的所有页面,并对该列表进行过滤,只查找可发布的页面(即具有 out
属性的页面)。然后,它会通过比较资源 URL 来检查导航中是否有该页面。如果找不到匹配的页面,就会使用日志记录器记录警告,并将该页面添加到返回的收集器中。
const unlistedPages = contentCatalog
.findBy({ component, version, family: 'page' })
.filter((page) => page.out)
.reduce((collector, page) => {
if ((page.pub.url in navEntriesByUrl) || page.pub.url === defaultUrl) return collector
logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page')
return collector.concat(page)
}, [])
js
让我们仔细看看这条警告信息。
logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page')
js
请注意,我们传递的第一个参数是对象,第二个参数是信息。作为第一个参数传递的对象的键会被合并到结构化日志信息中。在输出漂亮的日志信息时,Antora 的日志程序会为 file
和 source
键提供自定义格式。file
关键字应指向带有路径关键字的对象,如果适用,还应带 abspath
关键字。提供此内容的最简单方法是传递虚拟文件的 src
属性,该属性包含文件所在位置的所有必要信息。origin
关键字应指向虚拟文件的 src.origin
属性,该属性提供了有关内容来源的信息。您还可以使用 line
键输入可选的行号。自定义格式器会将所有这些信息编译成格式化信息,以帮助用户找到相关文件。
最后,如果扩展发现未列出的页面,它会将其添加到导航中的新类别中,并在配置了特殊标题的情况下将其添加到特殊标题中。
if (unlistedPages.length && addToNavigation) {
nav.push({
content: unlistedPagesHeading,
items: unlistedPages.map((page) => {
return { content: page.asciidoc.navtitle, url: page.pub.url, urlType: 'internal' }
}),
root: true,
})
}
js
addToNavigation
变量来自扩展项上的配置键 add_to_navigation
。Antora 会自动将配置键名转换为 camelCase,以便与 JavaScript 中的变量命名规则保持一致。
现在扩展程序已经编写完成,并且您也了解了它的作用,是时候注册它了。
注册扩展
要注册扩展,需要在 playbook 的 antora.extensions
关键字中为其添加 require request 条目。在我们的例子中,require request 是指从 playbook 文件到扩展文件的相对路径。
antora:
extensions:
- ./lib/unlisted-pages-extension.js
yaml
下次运行 Antora 时将调用该扩展。不过,由于该扩展是可配置的,我们希望使用更正式的输入格式,以便为这些配置键留出空间。
配置扩展
要在配置中注册扩展,需要在 playbook 的 antora.extensions
关键字中添加一个映射项。在此过程中,你将在 require
关键字中定义 require 请求,为其他配置关键字让路。
antora:
extensions:
- require: ./lib/unlisted-pages-extension.js
add_to_navigation: true
unlisted_pages_heading: Orphans
yaml
如果希望扩展只在使用 --extension
CLI 选项指定时使用,则还需要设置 id
和 enabled
键。
antora:
extensions:
- id: unlisted-pages
enabled: false
require: ./lib/unlisted-pages-extension.js
add_to_navigation: true
unlisted_pages_heading: Orphans
yaml
现在,只有在运行 Antora 时通过 --extension=unlisted-pages
时,扩展才会运行。
当扩展接受配置时,即使你不想默认启用它,在游戏本中注册它也是明智之举。 |
使用扩展
剩下的就是在运行 Antora 时使用扩展了。如果扩展已启用(默认情况下已启用),您只需运行 Antora 并像平常一样传递 playbook 文件即可:
$ antora antora-playbook
如果未启用扩展,则需要在使用 --extension
CLI 选项运行 Antora 时启用它:
$ antora --extension=unlisted-pages antora-playbook.yml
如果你的播放列表中有未列出的页面,你会看到类似下面的警告信息:
[12:02:02.532] WARN (unlisted-pages-extension): detected unlisted page source: /path/to/worktree (refname: main <worktree>, start path: docs) file: modules/ROOT/pages/name-of-page.adoc
如果 add_too_navigation
密钥为真,则该页面也会列在导航树底部的未列出页面类别中。
要解决未列出页面的问题,请找到相应的导航文件并为未列出页面添加一个条目,然后再次运行 Antora 检查您的工作。
恭喜您!您已经完成了第一个 Antora 扩展。