v4.0.1
Summary
Release meta
Released on: 2026-07-01
Released by: github-actions[bot]
Published by: GitHub
Logs: full diff
Changelog
Bug Fixes
- Fix reassigned document-body attributes all resolving to their final value — a custom attribute redefined in the body (e.g.
:reassigned: one…:reassigned: two) must resolve to the value in scope at each reference (Ruby rendersonethentwo), but every reference rendered the last value when any content preceded the entries (including a document header). The parser reset the shared block-attributes object between blocks withObject.keys(...), which skips theSymbol-keyedATTR_ENTRIES_KEY, so the array ofAttributeEntryobjects leaked and accumulated across blocks; each block’s attribute-entry playback then replayed every assignment. The reset now usesReflect.ownKeys(...)so the entries are cleared too - Fix reassigned attributes not resolving correctly when referenced from list item or table cell text (e.g.
:x: 1…* item {x}…:x: 2…* item {x}) — unlike paragraph content (resolved lazily during conversion with attribute-entry playback), list item and table cell text is pre-computed eagerly afterrestoreAttributes()reverted the document to its header state, so body-level attributes were out of scope and rendered as unresolved ({x}) or with the wrong value._resolveAllTextsnow replays each block’s attribute entries in document order (mirroringAbstractBlock#convert) while pre-computing, then restores the header state. In addition, a block carrying onlySymbol-keyed attribute entries (an:attr:entry immediately preceding a list or table) now still receives them — the parser’s transfer guard usedObject.keys(...).length(0 for entry-only attributes) instead ofReflect.ownKeys(...).length, matching Ruby’sattributes.empty?where:attribute_entriesis counted - Fix loss of the structured
source_locationon log messages emitted through the reader/preprocessor path (e.g.include file not found,unterminated <type> block, unterminated preprocessor conditionals) — these were logged as a plain string with the cursor baked into the text as a"<path>: line <N>: "prefix, somessage.getSourceLocation()returnednull(regression from 2.x). The reader now logs an auto-formatting message that keeps the cursor as a structuredsource_location, sogetSourceLocation()(getFile()/getLineNumber()) is populated again andgetText()stays clean; the stderrLoggerstill renders the"<path>: line <N>: "prefix. This restores line-anchored diagnostics for downstream tooling (IDE integrations, linters, CI annotations) - Fix
allow-uri-readnot being recognised when declared with an empty value (e.g.allow-uri-read=orattributes: { 'allow-uri-read': '' }) — include resolution checked the attribute’s truthiness viagetAttribute, but ’'is falsy in JavaScript while Asciidoctor treats the mere presence of an attribute as enabled; the include reader (Node and browser modes) now checks presence viahasAttribute, matching Ruby’sattr?` semantics - Fix incorrect generated type declarations for
asyncsubstitutor methods —applySubs,subQuotes,subMacros,subPostReplacements,subSource,subCallouts,highlightSource,restorePassthroughsandparseAttributesare allasyncbut their JSDoc@returnsdeclared the unwrapped type (e.g.string), so the emitted.d.tsadvertisedstringinstead ofPromise<string>; callers following the types could concatenate the returned Promise and produce[object Promise]. The JSDoc now declaresPromise<…>and the.d.tswas regenerated
Improvements
- Improve typing of
MemoryLogger#getMessages()— it previously returnedany[]; theLogMessagewrapper class is now exported andgetMessages()is declared to returnLogMessage[], so consumers get typedgetSeverity()(string),getText()(string) andgetSourceLocation()(Cursor | undefined) accessors. The wrapper’s internal_text/_sourceLocationfields were renamed to publictext/sourceLocationproperties (consistent withseverity), providing dual property/getter access