v4.0.0
Summary
This release is a complete rewrite of Asciidoctor.js — the Opal runtime and transpiled Ruby code have been replaced by a native JavaScript implementation.
The public API has been preserved as closely as possible from version 3, but there are breaking changes.
Please refer to the migration guide before upgrading.
Release meta
Released on: 2026-06-22
Released by: github-actions[bot]
Published by: GitHub
Logs: full diff
Changelog
Bug Fixes
- Fix registry reuse — extensions registered directly on a registry instance (e.g.
registry.preprocessor(fn)) now survive the internal reset and are preserved across multiple conversions, matching the behaviour of group-block registrations (Extensions.create(name, block)); both patterns are now safe to reuse
Improvements
- Document registry reuse behaviour — extensions registered via a group block (
Extensions.create(name, block)) survive the internal reset and are safe to reuse across multiple conversions; extensions registered directly on a registry instance are cleared on every activation and will be silently lost after the first conversion - Add
--extensionCLI option to load and register Asciidoctor extension files — the option calls theregister(registry)named export of the loaded module with a shared registry; can be repeated to load multiple extensions - Clarify
--requireCLI option — it now only loads the module as a side effect and no longer auto-calls any exported function; use--extensionto register Asciidoctor extensions, and--requirefor libraries that configure themselves on load (syntax highlighters, polyfills, etc.) - Improve JSDoc for
IncludeProcessorDsl#handles— add@overloadsignatures documenting the two setter forms (arity-1(target)and arity-2(doc, target)) and the invoker form; updateIncludeProcessorDslInterfacetypedef to expose both setter overloads
Breaking Changes
--requireno longer inspects or calls aregisterexport from the loaded module — this auto-call was already broken (it passed theExtensionsnamespace instead of a registry instance); any extension that relied on this behaviour must be updated to either export aregister(registry)function and be loaded via--extension, or self-register usingExtensions.register()at the top level to remain loadable via--require