扩展教程
如果您了解Antora扩展的工作原理,并对相关概念有扎实的掌握,本页面提供了一个端到端教程,通过深入示例引导您全面了解并充分利用Antora的这一功能。
在本教程中,我们将创建一个定位未列出页面的扩展,这些页面无法从导航中访问。该扩展将首先检索每个组件版本的导航树。然后,它将遍历该组件版本中的页面,并定位任何未在该导航树中找到的页面。如果找到任何未列出的页面,它将为每个页面记录警告。如果配置为这样做,它还将在导航中的专用类别下添加这些页面。
这个示例让您有机会使用扩展可用的大部分功能。我们将创建扩展,在playbook中注册它,配置它,最后启用Antora运行。让我们开始吧。
创建扩展
首先,您需要创建扩展。让我们将扩展文件命名为 unlisted-pages-extension.js,并将其放在playbook旁边的 lib/ 文件夹中,以便在playbook存储库中整洁地组织。
使用示例1中显示的源代码填充扩展文件。接下来的部分将分析此代码的功能。
module.exports.register = function ({ config }) {
const { addToNavigation, unlistedPagesHeading = '未列出的页面' } = config
const logger = this.getLogger('unlisted-pages-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 }, '检测到未列出的页面')
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
}
让我们暂停一下,逐步分解这个扩展的功能。
扩展如何工作
扩展首先通过导出注册函数开始,Antora在需要扩展文件后立即调用该函数。注册函数绑定到生成器上下文,可以使用该上下文添加监听器。该函数通过对象解构接受扩展的配置对象作为唯一参数。然后,它继续从配置对象中提取几个配置键,以自定义其行为。
module.exports.register = function ({ config }) {
const { addToNavigation, unlistedPagesHeading = '未列出的页面' } = config
}
接下来,扩展创建一个命名记录器,用于报告未列出的页面。它通过在上下文上调用getLogger
来创建一个命名记录器。这个方法反过来需要Antora提供的@antora/logger
模块,然后将名称传递给其默认函数以创建子记录器。
const logger = this.getLogger('unlisted-pages-extension')
然后,扩展为navigationBuilt
事件添加了一个监听器。由于扩展需要访问导航,这是生成器中检查导航树的正确机会。为了访问导航和页面,监听器使用对象解构从上下文变量中检索contentCatalog
对象。在页面转换后,会发出navigationBuilt
事件,这样可以访问每个页面的导航标题。
this
.on('navigationBuilt', ({ contentCatalog }) => {
})
当调用navigationBuilt
事件的监听器时,它从内容目录中检索每个组件版本的导航树,以及有关组件版本的一些信息,以定位其页面。
contentCatalog.getComponents().forEach(({ versions }) => {
versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => {
})
})
为了更容易在导航中找到页面,扩展提供了一个辅助函数,通过URL为每个导航条目创建查找表,忽略任何重复项。
function getNavEntriesByUrl (items = [], accum = {}) {
items.forEach((item) => {
if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item
getNavEntriesByUrl(item.items, accum)
})
return accum
}
然后,扩展使用此辅助函数为每个导航创建查找表:
const navEntriesByUrl = getNavEntriesByUrl(nav)
现在真正的工作开始了。扩展返回到内容目录中,查找当前组件版本中的所有页面,将该列表过滤,仅查找可发布的页面(即具有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 }, '检测到未列出的页面')
return collector.concat(page)
}, [])
让我们更仔细地看一下这个警告消息。
logger.warn({ file: page.src, source: page.src.origin }, '检测到未列出的页面')
请注意,我们将一个对象作为第一个参数传递,将消息作为第二个参数。作为第一个参数传递的对象的键将合并到结构化日志消息中。Antora的记录器为file
和source
键提供了自定义格式化程序,用于输出漂亮的日志消息。file
键应指向具有path
键的对象,如果适用,还应具有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,
})
}
addToNavigation
变量来自扩展条目上的配置键add_to_navigation
。Antora会自动将配置键名称转换为驼峰命名法,以使其与JavaScript中的变量命名约定一致。
现在扩展已编写,并且您了解它的功能,是时候注册它了。
注册扩展
要注册扩展,您需要在playbook的antora.extensions
键中添加一个对其的require请求条目。在我们的情况下,require请求是从playbook文件到扩展文件的相对路径。
antora:
extensions:
- ./lib/unlisted-pages-extension.js
扩展将在下次运行Antora时被调用。但是,由于此扩展是可配置的,我们将使用更正式的条目格式,以便为这些配置键腾出空间。
配置扩展
要注册带有配置的扩展,您需要在playbook的antora.extensions
键中为其添加一个映射条目。这样做时,您将在require
键中定义require请求,为其他配置键腾出空间。
antora:
extensions:
- require: ./lib/unlisted-pages-extension.js
add_to_navigation: true
unlisted_pages_heading: 孤立页面
如果希望扩展仅在使用--extension
CLI选项指定时才使用,还需要设置id
和enabled
键。
antora:
extensions:
- id: unlisted-pages
enabled: false
require: ./lib/unlisted-pages-extension.js
add_to_navigation: true
unlisted_pages_heading: 孤立页面
现在,只有在运行Antora时通过--extension=unlisted-pages
传递扩展时,扩展才会运行。
当扩展接受配置时,将其注册到playbook中总是明智的,即使您不希望默认启用它。 |
使用扩展
剩下的就是在运行Antora时使用扩展。如果扩展已启用(默认情况下是启用的),您只需要像往常一样运行Antora并传递playbook文件即可:
$ antora antora-playbook
如果扩展未启用,您需要在运行Antora时使用--extension
CLI选项来启用它:
$ antora --extension=unlisted-pages antora-playbook.yml
如果playbook中有未列出的页面,您将看到类似于以下警告消息:
[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_to_navigation
键为true,则您还会在导航树底部的未列出页面类别中找到该页面。
要解决未列出页面的问题,请找到适当的导航文件并为未列出的页面添加条目,然后再次运行Antora以检查您的工作。
恭喜!您已经创建了您的第一个Antora扩展。