Asciidoctor.js Documentation

repository·main·Indexed 21 days ago

https://github.com/asciidoctor/asciidoctor.js

A native JavaScript implementation of Asciidoctor providing AsciiDoc parsing and conversion capabilities. Includes the @asciidoctor/core engine and a CLI tool. Features include an asynchronous API, a pluggable HTTP cache system, and support for custom converters and extensions via a Registry instance. Requires Node.js ≥ 24.

Tokens
45.3K
Snippets
176
Records
235
Agent score
74%

What's inside Asciidoctor.js

  1. Overview of Asciidoctor.js customization levels

    main

    Asciidoctor.js offers nine levels of customization, ranging from simple attribute toggles to building a complete custom converter. Use the following guide to choose the appropriate level based on your required skills and desired outcome:

    LevelApproachSkills RequiredHTML5 Only
    1Built-in attributesAsciiDocNo
    2Use a theme (stylesheet)AsciiDocYes
    3Override the default stylesheetCSSYes
    4Create your own stylesheetCSSYes
    5Docinfo processorJavaScript, HTMLNo
    6PostprocessorJavaScriptNo
    7Custom block or macro extensionJavaScript, AsciiDocNo
    8Custom templatesJavaScript, template engineNo
    9Custom converterJavaScript, Asciidoctor ASTNo
  2. Overview of Asciidoctor.js

    main

    Asciidoctor.js is a native JavaScript implementation of the Asciidoctor AsciiDoc processor. It is designed to be fast and lightweight, running in both Node.js and browser environments without requiring Ruby or Opal.

    Key characteristics include:

    • Pure ESM: Supports full tree-shaking; you can import only the specific modules you need.
    • Async API: All primary entry points (such as convert, load, loadFile, and convertFile) are asynchronous and return Promises.
    • Zero Runtime Dependencies: No Ruby or Opal required.
    • Environment Support: Works in both Node.js and the browser.
    • Node.js Requirement: Requires Node.js version 22 (LTS) or later.
  3. Extend Asciidoctor using the Extension API

    main

    Asciidoctor provides an extension API that allows you to expand the language for new use cases. Extensions are designed to be written using a full programming language and can be distributed via standard packaging mechanisms like npm.

    Note that while the extension API is generally stable, the behavior for inline macros is subject to change. Currently, inline macro processors must return converted text (e.g., HTML) rather than an AST node. Future versions may require inline macro processors to return an inline node instead.

  4. Register extensions via the Registry instance

    main

    When using a Registry instance directly, be aware of its lifecycle:

    • Group-block registrations: Extensions registered via Extensions.create(name, block) survive internal resets and are safe to reuse across multiple conversions.
    • Direct registrations: Extensions registered directly on a registry instance (e.g., registry.preprocessor(fn)) are cleared on every activation and will be lost after the first conversion. To persist them, use the group-block pattern.
  5. Create a Preprocessor Extension

    main

    A preprocessor extension allows you to manipulate the raw document text before the main parsing process begins. This is useful for detecting specific patterns (like custom comments) and performing transformations, such as setting document attributes or injecting content.

    In the provided example, a preprocessor detects a // draft: comment, sets a status attribute to DRAFT, and injects a warning banner at the top of the document.

    // Example logic within a preprocessor extension
    // Detects '// draft:' and sets attribute 'status' to 'DRAFT'
  6. Enable HTTP caching for remote URIs

    main

    Asciidoctor.js supports a pluggable HTTP cache system. You can activate it by setting the cache-uri document attribute.

    By default, an ephemeral in-memory cache is used per conversion. You can register a custom implementation (e.g., for persistent or file-system-backed caching) using HttpCacheManager.setCache(cacheImplementation).

  7. Use the composition pattern to partially override a converter

    main

    If you only want to change how specific nodes are handled while letting the built-in converter handle everything else, use the composition pattern. You can instantiate the built-in converter (e.g., Html5Converter.create()) and call its convert method for any nodes you do not wish to customize.

    import { Html5Converter } from '@asciidoctor/core'
    
    class SemanticParagraphConverter {
      constructor () {
        this.baseConverter = Html5Converter.create()
      }
    
      async convert (node, transform, opts) {
        if (node.getNodeName() === 'paragraph') {
          return `<p>${await node.getContent()}</p>`
        }
        // Delegate all other nodes to the built-in converter
        return this.baseConverter.convert(node, transform, opts)
      }
    }
  8. Available Extension Points in Asciidoctor

    main

    Asciidoctor provides several extension points to hook into different stages of the document processing lifecycle:

    • Preprocessor: Processes raw source lines before they reach the parser.
    • Tree processor: Processes the Asciidoctor.Document (AST) after parsing is complete.
    • Postprocessor: Processes the output after conversion but before it is written to disk.
    • Docinfo Processor: Adds content to the header or footer regions of the generated document.
    • Block processor: Processes content marked with a custom block style (e.g., [custom]).
    • Block macro processor: Registers and processes custom block macros (e.g., gist::12345[]).
    • Inline macro processor: Registers and processes custom inline macros (e.g., btn:[Save]).
    • Include processor: Processes the include::<filename>[] directive.
  9. Handle attribute changes in v4

    main

    Property Naming

    Properties that were snake_case in v3 are now camelCase in v4:

    • nodeName (was node_name)
    • sourceLocation (was source_location)
    • contentModel (was content_model)
    • defaultSubs (was default_subs)

    getAttribute return value

    In v4, node.getAttribute(name) returns null instead of undefined when an attribute is absent. Update checks accordingly:

    // v3 style (will fail in v4)
    if (node.getAttribute('language') === undefined) { ... }
    
    // v4 style
    if (node.getAttribute('language') === null) { ... }
    // or
    if (!node.getAttribute('language')) { ... }
  10. Implement a custom HTTP cache strategy

    main

    You can implement custom caching strategies (such as file-system or Redis-backed caching) by extending the HttpCache class and overriding the read(uri) method.

    The read(uri) method must return a Promise<Response> that is compatible with the Fetch API.

    import { HttpCache, HttpCacheManager } from '@asciidoctor/core'
    
    class FileSystemHttpCache extends HttpCache {
      #cacheDir
    
      constructor(cacheDir) {
        super()
        this.#cacheDir = cacheDir
      }
    
      async read(uri) {
        // Implement logic to check cache, fetch from network on miss, and persist
        // Must return a Promise<Response>
      }
    }
    
    // Register the custom cache before any conversions run
    HttpCacheManager.setCache(new FileSystemHttpCache('./cache'))