This documentation covers a prerelease version of the software. Follow this link to view the documentation for the stable version (3.0) instead.

Register Extensions

These extension points are currently available.

Preprocessor

Processes the raw source lines before they are passed to the parser. See Preprocessor Example.

Tree processor

Processes the Asciidoctor.Document (AST) once parsing is complete. See Tree Processor Example.

Postprocessor

Processes the output after the document has been converted, but before it’s written to disk. See Postprocessor Example.

Docinfo Processor

Adds additional content to the header or footer regions of the generated document. See Docinfo Processor Example.

Block processor

Processes a block of content marked with a custom block style (i.e., [custom]). (similar to an AsciiDoc filter) See Block Processor Example.

Block macro processor

Registers a custom block macro and processes it (e.g., gist::12345[]). See Block Macro Processor Example.

Inline macro processor

Registers a custom inline macro and processes it (e.g., Save). See Inline Macro Processor Example.

Include processor

Processes the include::<filename>[] directive. See Include Processor Example.

Register one or more extensions

You can register an extension globally as follows:

import { Extensions, convert } from '@asciidoctor/core'

Extensions.register(function () {
  this.block(function () {
    const self = this
    self.named('callout')
    self.onContext('paragraph')
    self.process(function (parent, reader) {
      const lines = reader.getLines()
      return self.createBlock(parent, 'paragraph', lines, { role: 'callout' })
    })
  })
})

const text = `[callout]
Deploy to production only after all tests pass.`

const html = await convert(text)
console.log(html)

// <div class="paragraph callout">
// <p>Deploy to production only after all tests pass.</p>
// </div>

You can register more than one processor of each type, though you can only have one processor per custom block or macro. Each registered class is instantiated when the Asciidoctor.Document is created.

There is currently no extension point for processing a built-in block, such as a normal paragraph. Look for that feature in a future Asciidoctor release.

You can also create one or more registries. It can be useful when you want to convert the same text with different extensions enabled.

import { Extensions, convert } from '@asciidoctor/core'

const registryA = Extensions.create()
const registryB = Extensions.create()

registryA.block(function () {
  const self = this
  self.named('callout')
  self.onContext('paragraph')
  self.process(function (parent, reader) {
    // Render as a paragraph with a CSS role
    const lines = reader.getLines()
    return self.createBlock(parent, 'paragraph', lines, { role: 'callout' })
  })
})

registryB.block(function () {
  const self = this
  self.named('callout')
  self.onContext('paragraph')
  self.process(function (parent, reader) {
    // Render as a semantic <aside> element
    const lines = reader.getLines()
    const html = `<aside class="callout">${lines.join('\n')}</aside>`
    return self.createBlock(parent, 'pass', html)
  })
})

const text = `[callout]
Deploy to production only after all tests pass.`

console.log(await convert(text, { extension_registry: registryA }))
console.log('')
console.log(await convert(text, { extension_registry: registryB }))

// <div class="paragraph callout">
// <p>Deploy to production only after all tests pass.</p>
// </div>
//
// <aside class="callout">Deploy to production only after all tests pass.</aside>

In the example above, we’ve created two registries:

  • registryA

  • registryB

Both registries have a [callout] block extension registered with a specific implementation.

The first block extension is registered in registryA and renders the content as a paragraph with a callout CSS role. The other is registered in registryB and wraps the content in a semantic <aside> element.