Configure Using the API

Instead of relying on the built-in process that is set up by an exporter extension, you can use the Assembler API directly to generate exports, giving you full control over the configuration.

Overview

To use the programmatic configuration, you will need to create an Antora extension that acts as an entry point for Assembler. You can use one of two APIs provided by Assembler:

  • configure(context, converter, config, providers) (also named configureAssembler)

  • assembleContent(playbook, contentCatalog, converter, config)

The simplest approach is to the use configure function as that takes care of registering the correct event listeners. In this scenario, your extension has to do the following:

  1. Define or load a converter

  2. Define a configuration object (so Assembler does not try to use a configuration file)

  3. Optionally define a navigation catalog to control the navigation tree Assembler uses

  4. Invoke the configure function, passing the generator context (i.e., this), converter, config, and providers

The providers parameter accepts a configSource object to override the Assembler configuration and a navigationCatalog object to override the navigation resolver.

A more low-level approach is to use the assembleContent function. When using this function, your extension must handle the work of registering the event listeners that the configure function normally does. In this scenario, your extension has to do the following:

  1. Configure Antora’s AsciiDoc loader to keep the AsciiDoc source

  2. Define or load a converter

  3. Define a configuration object (so Assembler does not try to use a configuration file)

  4. Optionally define a navigation catalog to control the navigation tree Assembler uses

  5. Invoke the assembleContent function bound to the generator context (i.e., this)

When using the assembleContent function directly, you lose the profile-based navigation feature (unless you faithfully implement the same logic from the configure function). That’s why we always recommend using the configure function if you can.

We’ll look at some examples next.

Usage

configure

Example 1. use-configure-api-extension.js
const { configure } = require('@antora/assembler')
const converter = require('@antora/pdf-extension/converter')
const runCommand = require('@antora/run-command-helper')

module.exports.register = function () {
  const configSource = {
    componentVersionFilter: {
      names: ['component-name'],
    },
    rootLevel: 1,
  }
  const navigationCatalog = {
    getNavigation: (function (component, version) {
      return [
        {
          items: this.getVariables().contentCatalog
            .findBy({ component, version, family: 'page' })
            .filter((it) => it.out)
            .map((it) => ({
              content: it.asciidoc.navtitle,
              url: it.pub.url,
              urlType: 'internal',
            })),
        },
      ]
    }).bind(this),
  }
  configure(
    this,
    converter.bind(this, runCommand),
    {},
    { configSource, navigationCatalog },
  )
}

The trickiest part is defining the navigation catalog. It must be an object with a single method named getNavigation that returns a navigation tree for the specified component version. Since the content catalog isn’t yet available when the extension is registered, you have to bind the generator context to the getNavigation function so that it can look up the content catalog on demand.

Each entry in the navigation tree must define a content key, which is the visible text. It may also have a url key that’s either an internal (pub) URL in the Antora site or an external URL. An internal URL, which must be indicated using the urlType key, is a root-relative path to the resource in the site. For pages, you’ll likely want to use the content catalog to retrieve this information, as shown in the example.

Note that the configSource object will be populated with default values by Assembler, and is therefore modified.

Due to how the converter object for the built-in PDF exporter extension is defined, the converter must be bound to the generator context (this) and the runCommand helper function (i.e., converter.bind(this, runCommand)).

assembleContent

Let’s look at the previous example, except this time using the assembleContent function instead of the configure function.

Example 2. use-assemble-content-api-extension.js
const { configure } = require('@antora/assembler')
const converter = require('@antora/pdf-extension/converter')
const runCommand = require('@antora/run-command-helper')

module.exports.register = function () {
  const configSource = {
    componentVersionFilter: {
      names: ['component-name'],
    },
    rootLevel: 1,
  }
  const navigationCatalog = {
    getNavigation: (function (component, version) {
      return [
        {
          items: this.getVariables().contentCatalog
            .findBy({ component, version, family: 'page' })
            .filter((it) => it.out)
            .map((it) => ({
              content: it.asciidoc.navtitle,
              url: it.pub.url,
              urlType: 'internal',
            })),
        },
      ]
    }).bind(this),
  }
  configure(
    this,
    converter.bind(this, runCommand),
    {},
    { configSource, navigationCatalog },
  )
}

Notice that the extension now has to configure Antora to keep the AsciiDoc source. It also needs to add the navigationBuilt event listener in which to call the assembleContent function. Unlike the configure function, you must bind the generator context to the assembleContent function when invoking it. You also need to pass the playbook and content catalog as the first and second arguments, respectively. You don’t have to bind the generator context to the getNavigation function since the content catalog is available inside the event listener.

If possible, use the configure function. If you need more low-level control, only then should you use the assembleContent function. But keep in mind, it does create some limitations.

Postprocessing

In either scenario, since the export files are stored in the content catalog, you can manipulate the out and pub properties of those files during or after the navigationBuilt event if you want them to be published to a different URL. This works regardless of how you use Assembler, whether you run it directly or run it by way of an exporter extension.