Commit 397bdb23 authored by Dan Allen's avatar Dan Allen
Browse files

merge !1100

resolves #1190 allow CLI to control order of extensions declared in playbook
parents 154e170f 66851083
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ This project utilizes semantic versioning.
=== Added

* *playbook-builder*: Allow extension to be switched off from CLI by negating value (#976)
* *playbook-builder*: Allow CLI to control order of extensions declared in playbook (#1190)
* *content-aggregator*: Allow content source to be filtered by commits (playbook model only) (#831)

=== Changed
+30 −0
Original line number Diff line number Diff line
@@ -55,3 +55,33 @@ If you don't assign an ID to the extension entry, you can reference the value of
In this case, Antora looks for the first entry in the list of extensions whose `require` key matches the value of the CLI option.

If Antora can't locate an entry that matches the value of the `--extension` CLI option, it falls back to treating the value as a require request.

== Load order

By default, declaring and enabling an extension in the playbook also determines its load order.
Any enabled extensions declared in the playbook are loaded first in playbook order, followed by any extensions enabled through the CLI.

However, the CLI still has an opportunity to influence this load order.
If the value of the `--extension` option matches an extension declared in the playbook, the load order of the extension is updated.
That extension acts as though it's not declared in the playbook and enabled by the CLI instead.
Yet, the configuration from the playbook is still used.

Let's consider an example:

.An extension declared in the playbook
[,yaml]
----
antora:
  extensions:
  - require: ./from-playbook.js
    custom: value
----

Now, if we want to load an extension before this extension, we can do so using the CLI:

 $ antora --extension ./from-cli.js --extension ./from-playbook.js antora-playbook.yml

The from-cli.js extension will be loaded first, followed by the from-playbook.js extension.
Had we not referenced the from-playbook.js extension using the `--extension` option, it would have been loaded first.

This technique also works for an extension declared in the playbook as not enabled (i.e., `enabled: false`).
+10 −4
Original line number Diff line number Diff line
@@ -132,21 +132,27 @@ function registerFormats (convict) {
      const accum = config?.has(name) ? config.get(name) : []
      const byId = {}
      const byRequire = {}
      let orderIdx = 0
      const order = new Map()
      for (const [idx, it] of accum.entries()) {
        const ext = it.constructor === Object ? it : (accum[idx] = { require: it })
        byRequire[ext.require] ??= ext
        if ('id' in ext) byId[ext.id] ??= ext
        order.set(it, orderIdx++)
      }
      for (let request of val.split(',')) {
        const enable = request.charAt() === '!' ? (request = request.slice(1)) == null : true
        const match = byId[request] ?? byRequire[request]
        if (match) {
        let match
        if ((match = byId[request] ?? byRequire[request])) {
          if ((match.enabled ?? true) !== enable) match.enabled = enable
        } else {
          accum.push((byRequire[request] = { require: request }))
          accum.push((match = byRequire[request] = { require: request }))
        }
        order.set(match, orderIdx++)
      }
      return accum.map((it) => (Object.keys(it).length === 1 && 'require' in it ? it.require : it))
      return accum
        .sort((a, b) => order.get(a) - order.get(b))
        .map((it) => (Object.keys(it).length === 1 && 'require' in it ? it.require : it))
    },
  })
  convict.addFormat({
+23 −4
Original line number Diff line number Diff line
@@ -607,10 +607,10 @@ describe('buildPlaybook()', () => {
    const args = ['--playbook', duplicateExtensionIdSchemaSpec, '--extension', '@antora/pdf-extension']
    const playbook = buildPlaybook(args, {})
    expect(playbook.antora.extensions).to.have.lengthOf(2)
    expect(playbook.antora.extensions[0].enabled).to.be.true()
    expect(playbook.antora.extensions[0].foo).to.equal('bar')
    expect(playbook.antora.extensions[1].enabled).to.be.false()
    expect(playbook.antora.extensions[1].foo).to.equal('baz')
    expect(playbook.antora.extensions[0].enabled).to.be.false()
    expect(playbook.antora.extensions[0].foo).to.equal('baz')
    expect(playbook.antora.extensions[1].enabled).to.be.true()
    expect(playbook.antora.extensions[1].foo).to.equal('bar')
  })

  it('should not add duplicate entry to require-array', () => {
@@ -620,6 +620,25 @@ describe('buildPlaybook()', () => {
    expect(num).to.equal(1)
  })

  it('should allow CLI to control load order of extension enabled in playbook', () => {
    const args = [
      '--playbook',
      defaultSchemaSpec,
      '--extension',
      '@antora/pdf-extension',
      '--extension',
      'antora-lunr',
      '--extension',
      '@namespace/the-extension',
    ]
    const playbook = buildPlaybook(args, {})
    expect(playbook.antora.extensions).to.have.lengthOf(4)
    expect(playbook.antora.extensions[1]).to.equal('@antora/pdf-extension')
    expect(playbook.antora.extensions[2]).to.equal('antora-lunr')
    expect(playbook.antora.extensions[3].require).to.equal('@namespace/the-extension')
    expect(playbook.antora.extensions[3].enabled).to.be.true()
  })

  it('should throw error if dir-or-virtual-files key is not a string or array', () => {
    Object.keys(schema).forEach((key) => {
      if (key !== 'playbook') delete schema[key]