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 namedconfigureAssembler
) -
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:
-
Define or load a converter
-
Define a configuration object (so Assembler does not try to use a configuration file)
-
Optionally define a navigation catalog to control the navigation tree Assembler uses
-
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:
-
Configure Antora’s AsciiDoc loader to keep the AsciiDoc source
-
Define or load a converter
-
Define a configuration object (so Assembler does not try to use a configuration file)
-
Optionally define a navigation catalog to control the navigation tree Assembler uses
-
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
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.
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.