Replace Generator Functions

Antora’s generator is a sequence of steps, where each step is performed by a function. These functions are known as generator functions. Generator functions also include functions for key substeps a generator function may perform, such as loadAsciiDoc. The generator retrieves these functions from the generator context. As such, these generator functions are designed to be replaceable, providing a more powerful way to extend Antora. Specifically, these functions can be retrieved and replaced programmatically by an extension.

Replacing functions in an extension does put the code at greater risk of breaking between major versions of Antora.

Get the functions

Like with context variables, generator functions are available on the generator context. The generator functions can be retrieved by calling the getFunctions method. This method returns an object of key-value pairs, where the keys are the function names and the values are the function objects. The built-in generator functions are not available until the contentStarted event is emitted.

Here’s an example that shows how to retrieve the aggregateContent function in an extension, assuming this is bound to the generator content.

const { aggregateContent } = this.getFunctions()

You may want to retrieve a function so you can wrap it. Regardless, the interesting part happens when you replace a function.

Replace a function

The generator functions can be replaced by passing functions to the replaceFunctions method. This method accepts the same object signature that the getFunctions method returns, where the keys are the function names and the values are the function objects.

You only have to specify the functions you want to replace. The generator will fall back to using its own functions for any function that’s not replaced by an extension.

Here’s an example that shows how to replace the publishFiles function in an extension:

module.exports.register = function () {
  this.replaceFunctions({
    async publishFiles () {
      console.log('Not publishing today')
      return []
    }
  })
}

When the functions are replaced matters. If you replace functions directly in the register method, it will prevent Antora from requiring and registering the corresponding built-in functions. If you replace functions in the contextStarted listener function, the function will replace the corresponding built-in function which has already been registered. This event gives you the opportunity to delegate to (i.e., wrap) the built-in function, as shown here:

module.exports.register = function () {
  this.on('contextStarted', () => {
    const delegate = this.getFunctions().publishFiles
    this.replaceFunctions({
      async publishFiles (playbook, catalogs) {
        console.log('It\'s publish time!')
        return delegate.call(this, playbook, catalogs)
      }
    })
  })
}

When replacing a function, you must adhere to the function’s signature. Like with the register function and event listener functions, the generator functions are automatically bound to the generator context.

Function reference

The list of functions that can be replaced by an extension, shown along with their signatures, are as follows:

  • aggregateContent(playbook): Promise<Object>

  • buildNavigation(contentCatalog, siteAsciiDocConfig): NavigationCatalog

  • classifyContent(playbook, contentAggregate, siteAsciiDocConfig): ContentCatalog

  • convertDocument(file, contentCatalog, siteAsciiDocConfig): File

  • convertDocuments(contentCatalog, siteAsciiDocConfig): void

  • createPageComposer(playbook, contentCatalog, uiCatalog, env): Function

  • extractAsciiDocMetadata(doc): Object

  • loadAsciiDoc(file, contentCatalog, config): Document

  • loadUi(uiCatalog): Promise<UiCatalog>

  • mapSite(playbook, publishablePages): File[]

  • produceRedirects(playbook, contentCatalog): File[]

  • publishFiles(playbook, catalogs): Promise<Object[]>

  • resolveAsciiDocConfig(playbook): Object

To learn more about these functions, consult the Antora source code.