扩展教程

如果您了解 Antora 扩展的工作原理,并已牢固掌握相关概念,本页将提供一个端到端的教程,通过一个深入的示例让您全面了解并帮助您充分利用 Antora 的这一功能。

在本教程中,我们将创建一个扩展,用于定位未列出(unlisted)的页面,即无法从导航中访问的页面。扩展将首先检索每个组件版本的导航树。然后,它将遍历该组件版本中的页面,并查找导航树中未找到的任何页面。如果发现任何未列出的页面,它将逐一记录警告。如果进行了配置,它还会将这些页面添加到导航中的一个专门类别下。

本示例让您有机会使用扩展的大部分功能。我们将创建扩展,在 playbook 中注册它,配置它,最后启用它运行 Antora。让我们开始吧。

创建扩展

首先,你需要创建扩展。我们将扩展文件命名为 unlisted-pages-extension.js,并将其放置在 playbook 相邻的 lib/ 文件夹中,这样它就能整齐地组织在 playbook 资源库中。

Example 1 中的源代码填充扩展文件。下一节将分析这些代码的作用。

Example 1. lib/unlisted-pages-extension.js
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 的日志程序会为 filesource 键提供自定义格式。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 中的变量命名规则保持一致。

删除未列出的页面

与其将未列出的页面添加到导航中,不如将它们从网站上删除。这也是限制网页发布的一种方法。

unlistedPages.forEach((page) => contentCatalog.removeFile(page))
js

如果采用这种方法,可能需要删除对未列出页面的警告,或将其降级为信息或调试严重性级别。

现在扩展程序已经编写完成,并且您也了解了它的作用,是时候注册它了。

注册扩展

要注册扩展,需要在 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 选项指定时使用,则还需要设置 idenabled 键。

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 扩展。