Changelog#
Track user-facing changes, upgrade notes, and compatibility updates for Wenmode releases.
This page records notable changes for released versions. Add unreleased entries here while preparing a release, then move them under the final version heading.
0.13.2#
Released Jul 28, 2026.
Fix regex pattern for setext heading of markdown renderer.
0.13.1#
Released Jul 28, 2026.
Avoid over-escaping plain punctuation in Markdown renderer text output.
0.13.0#
Released Jul 22, 2026.
Breaking Changes
Change
InlineRule.parse()to receive anInlineCandidate; usecandidate.startandcandidate.match.Change
BlockRule.parse()to receive aBlockCandidate; usecandidate.lineandcandidate.match.Change
ContinueRuleto produce aContinueCandidatewithmatch_candidate()beforeparse_paragraph_continuation().Move rule dependencies from root transforms to
Rule.required_rules.
0.12.1#
Released Jul 22, 2026.
Fixed
Preserve links that appear after failed direct destinations with nested parentheses.
Performance
Improve edge-case parsing performance for malformed links, malformed images, and flat mixed emphasis delimiter runs.
0.12.0#
Released Jul 22, 2026.
Added
Allow
github_alert.configure()to add custom alert types.
Breaking Changes
Change
InlineRule.parse()to receive a candidatestartoffset instead of are.Match; inline rules now decide internally whether to use their compiled regex pattern.Replace
InlineRule.trigger_charswithInlineRule.opener, a single-character string or tuple of single-character strings used for inline candidate dispatch.
0.11.1#
Released Jul 19, 2026.
Harden HTML URL sanitization against percent-encoded unsafe schemes.
Fix escaped characters in link and reference destinations.
0.11.0#
Released Jul 13, 2026.
Added
Add
wenmode.plugins.github_alertfor top-level GitHub alert blockquotes.Add
wenmode.plugins.heading_idsto install generated heading IDs on the heading rules already enabled in a parser configuration.Re-export
BlockFenced,InlineDelimited, andInlineLiteralfromwenmode.pluginsas the public entry point for simple custom plugin rules.
Breaking Changes
Replace
AtxHeading(id_transform=True)andSetextHeading(id_transform=True)with explicit node transforms:AtxHeading(transforms=[HeadingIdTransform()])andSetextHeading(transforms=[HeadingIdTransform()]).
0.10.0#
Released Jul 11, 2026.
Breaking Changes
Remove
NodeSpecandNode.to_spec(). Usewenmode.nodes.BUILTIN_NODESand pluginnodeslists when iterating over available node classes.Expose built-in presets and parser rule collections as read-only sequences and mappings. Use
create_preset()orParser.register_rule(s)to derive or update rule configurations.Stop exporting Markdown renderer implementation helpers from
wenmode.renderers; use the documented renderer classes and extension hooks.Escape string values from unknown or custom literal nodes in HTML output, including with
HTMLRenderer(escape=False). Custom literal extensions that relied on implicit markup passthrough must register an HTML handler.Validate common structural fields when restoring serialized AST mappings and reject parser-internal escaping metadata on concrete
htmlandhtmlContainernodes by default. Trusted Wenmode AST round trips can opt in withallow_internal_metadata=True.Remove
wenmode.ast.node_from_ast(). Usewenmode.ast.from_ast()for AST restoration.Use a single plugin protocol: plugins must expose
setup(wen, /). Configure built-in plugins withplugin_module.configure(...)before passing them toWenmodeorWenmode.use().Remove
DeclarativePluginSpecandinstall_declarative(). Configurable rule experiments remain private underwenmode._declarativeswhile their API evolves.Rename the plugin module protocol to
PluginModule, with optionalname,nodes, and rendererhandlersmetadata next tosetup(wen, /).Move shared CJK, block, and parser utility helpers under
wenmode.utilsand keep parser-state-specific helpers out of generic utility modules.
Fixes
Bound
from_ast()restoration with default node-depth and node-count budgets, and reject active reference cycles before recursive restoration can hit Python recursion limits.Enforce
Parser.max_container_depthfor all nestedparse_blocks()callers, preserving boundary content as shallow paragraphs instead of recursing through directive or custom container rules without a shared limit.Validate restored ordered-list
startvalues and escape rendered list-start attributes through the shared HTML attribute renderer.Reject custom block and paragraph-continuation rules that return a node without advancing parser state, with a rule-specific
RuntimeErrorinstead of allowing an infinite parse loop.Reject custom paragraph-continuation rules that mutate parser state while returning
None, preserving the documented decline behavior of continuing the paragraph unchanged.Release completed iterable-stream source prefixes before yielding parsed blocks, while preserving absolute positions and clarifying that
StreamBlockState.linesis the active buffered window.Reject streaming before the first chunk when parser root transforms or renderer root hooks require complete-root work that incremental parsing would otherwise skip.
Performance
Avoid capturing trailing
.*regex groups in blockquote, list, reference, footnote, spoiler, and related block rules when the rest of the line can be derived from the match end.Improve source-position tracking for nested parses by coalescing contiguous collected source spans, adding single-segment
SourceMapfast paths, and avoiding repeated offset lookups for common collector paths.Reduce position overhead for nested directives, HTML containers, list continuations, reference titles, footnotes, lazy blockquotes, and tables in edge-case benchmarks.
Keep inline code delimiter stress cases near-linear, including long successful code spans and many wrong-length backtick runs.
0.9.1#
Released Jul 9, 2026.
Limit recursive inline parsing for emphasis, image alt text, text directives, and declarative inline children with
Parser.max_container_depth, avoidingRecursionErroron deeply nested or adversarial inline input.Avoid repeatedly scanning nested link labels when parsing inputs with many bracket openers and repeated link suffixes, improving pathological link and reference parsing from near-quadratic to near-linear behavior.
Cache declarative inline closer positions so invalid closer candidates, such as inline math delimiters before digits, do not rescan the same suffixes for each opener.
Respect container depth in the
html_containerplugin so deeply nested HTML containers fall back to raw HTML instead of recursing unboundedly.
0.9.0#
Released Jul 2, 2026.
Breaking Changes
Change
from_ast()andnode_from_ast()to acceptnodes=[MyNode]instead ofregistry={MyNode.type: MyNode}, and removeregistry_from_plugins().Rename
BUILTIN_NODE_REGISTRYtowenmode.nodes.BUILTIN_NODES, and change built-in pluginnodesexports to node class lists instead of{type: class}mappings.Split
wenmode.plugins.mathintowenmode.plugins.inline_mathandwenmode.plugins.block_math.Split
wenmode.plugins.spoilerintowenmode.plugins.inline_spoilerandwenmode.plugins.block_spoiler.Rename constructor-time plugin option containers from
PluginSpectoPluginConfig;plugin(target, **options)now returnsPluginConfig.Stop exporting plugin plumbing types
Plugin,PluginConfig, andPluginTargetfrom package facades; useplugin()and declarative spec APIs instead.Expose declarative plugin data as
DeclarativePluginSpec, and allow declarative plugins to omitsetup()when they exposespec.
Added
Add
AsciiDocRenderer, CLI--format=asciidocsupport, and built-in plugin handlers for best-effort serialization to AsciiDoc.Add
wenmode.plugins.cjk_friendlyfor opt-in CJK-friendly inline parsing, including emphasis delimiter matching and extended autolink trailing punctuation handling.Add
wenmode.plugins.smartypantsfor opt-in HTML smart punctuation rendering of quotes, dashes, and ellipses in plain text nodes.Add declarative plugin specs with
wenmode.plugins.install_declarative(), and convert simple delimiter plugins such asmark,insert, andinline_spoilerto use the new Python-side installer.Add declarative
InlineLiteral,BlockFenced, andRendererFallbackspecs for literal spans, fenced blocks, and wrapper-free renderer output; convertinline_mathandblock_mathparsing to those specs.Allow declarative plugins to expose renderer
handlersnext tospec, so plugins with custom renderer functions do not needsetup().Export
RenderHandlerfromwenmode.renderersfor custom renderer handler type annotations.Add
NodeSpecandNode.to_spec()for deriving static node shape metadata from Wenmode node classes.Document core and plugin AST node shape contracts and expand round-trip coverage for
from_ast()with plugin node classes.
0.8.0#
Released Jun 29, 2026.
Add constructor-time plugin setup options with
wenmode.plugins.plugin(), so configured plugins can be passed directly toWenmode(..., plugins=[...]).Add
wenmode.ast.registry_from_plugins()and exposenodesregistries on built-in plugins so stored AST JSON can be restored with plugin node classes.Add
wenmode.presets.create_preset()for deriving custom rule lists from existing presets by rule name.Add
Parser.supports_streaming,Parser.streaming_blockers(), and matchingWenmodeproxies to inspect custom streaming configurations before callingstream().
0.7.0#
Released Jun 27, 2026.
Add
wenmode.plugins.html_container, a non-CommonMark replacement forHtmlBlockthat parses standalone HTML tag pairs ashtmlContainernodes with Markdown block children while preserving raw HTML fallback behavior for raw-text tags, void/self-closing tags, inline HTML, and unclosed tag pairs.Add structured
attributesdata tohtmlContainernodes and document AST interoperability for plugin node registries and unknown node fallback.Fix emphasis flanking around Unicode combining marks and format characters, including NFD-decomposed accents and zero-width joiner cases.
0.6.1#
Released Jun 26, 2026.
Fix emphasis parsing for CommonMark’s multiple-of-3 delimiter rule when a delimiter run has already been partially consumed. Inputs such as
*a***a*now render as<em>a</em>*<em>a</em>instead of leaving the second emphasis span unparsed.Fix reStructuredText rendering so image directive
altandtitleoptions cannot emit embedded newlines, and inline math escapes backticks correctly.
0.6.0#
Released Jun 24, 2026.
Refactor parser internals into private
_parsermodules for rule-set compilation, block parsing, inline parsing, and paragraph-interruption decisions. Public parser entry points remain onParser.Change
Parser.parse_inlines()to require an explicitBlockState, and changeParser.inline_source()to receive that state when mapping nested inline source ranges.Store active inline source maps on
BlockStateinstead of the reusableParserinstance so nested inline source lookup is scoped to the active parse state.Clarify public extension API boundaries for parser helpers,
BlockState,StateKey,SourceMap, renderer hooks, rules, nodes, and plugins. Modules underwenmode._parserare private implementation details.Add regression coverage for parser rule-set rebuilds, nested source maps, deferred inline queues, streaming compatibility, HTML block performance, and nested disallowed HTML escaping.
Add
LiteralDirectivenodes for MyST-style fenced directives whose body should remain literal text. Thefenced_directiveplugin now emitsliteralDirectiveforcode-blockby default, with configurableliteral_names.Update the local
wenmode-mystexample to useLiteralDirectiveforcode-blockandsourcecodebodies instead of carrying a local custom directive node.Add configurable fence characters to
FencedDirectiveRuleand thefenced_directiveplugin. The localwenmode-mystexample now enables colon fences through the shared rule instead of a custom subclass.Move MyST
(label)=target support in the localwenmode-mystexample into thewenmode_myst.targetplugin module so it can be passed directly toWenmode(..., plugins=[target]).
0.5.0#
Released Jun 23, 2026.
Add
pluginsparameter toWenmodeso applications can install plugins during construction. The existingWenmode.use(plugin, **options)API remains supported for adding plugins after an instance exists or passing setup options.Update the CLI and local integration examples to use constructor-time plugin setup internally.
Add documentation entry points for the FastAPI streaming file-upload example and clarify which syntax the
streamingpreset supports or deliberately disables.
0.4.0#
Released Jun 22, 2026.
Add
wenmode.plugins.frontmatterfor top-level---front matter. The plugin stores metadata onroot.data["frontmatter"], supports customloadanddumpcallbacks, preserves source positions, keeps HTML output metadata-free by default, serializes metadata back to Markdown, and renders flat metadata as RST docinfo fields.Add renderer root hook pseudo handlers:
root:preandroot:post. Use these through normal renderer handler registration when a plugin needs document prefixes or suffixes without replacing the renderer’s built-inroothandler.Move HTML footnote sections and RST deferred image definitions to
root:posthooks, keeping root rendering composable for plugins such as front matter.Add CLI
--pluginsupport for built-in plugins on bothrenderandast.Update rule base classes to prefer class attributes such as
name,pattern, andtrigger_chars, while keeping configured rule instances supported.Add CI coverage for local examples and use locked
uvdependency resolution for reproducible development and CI tasks.Add PyPy test coverage and publish PyPy support in package metadata.
Update the MkDocs and Sphinx examples to use built-in plugins; Wenmode’s own documentation now builds through the local
wenmode_mystexample instead ofmyst_parser.Expand stability and security regression coverage for renderer isolation, HTML attribute escaping, URL sanitization, streaming compatibility, and front matter rendering.
0.3.1#
Released Jun 21, 2026.
Harden
HTMLRendererattribute output by escaping generated attribute values, dropping unsafe attribute names such as event handlers andstyleby default, and sanitizing link and image URLs.Fix HTML block parsing so nested tags are preserved correctly.
Fix normalized Markdown output for list rendering and RST output for literal code and links.
Fix table parsing in custom rule lists so body lines without an unescaped pipe end the table instead of being padded into table rows.
Add
Table(require_body_pipe=False)and configure thegithubpreset with it to preserve GFM-compatible short table body rows.Enforce UTF-8 output in the CLI.
0.3.0#
Released Jun 21, 2026.
Add a
wenmodecommand line interface, also available withpython -m wenmode, for rendering Markdown and printing AST JSON.Add
wenmode.asthelpers for walking node trees, finding nodes by type or predicate, and extracting plain text from nodes.Add an introduction page and streamline the documentation index for users evaluating Wenmode.
Improve stability coverage for source positions, plugin state isolation, streaming preset compatibility, and
parse_iter()plugin nodes.
0.2.0#
Released Jun 20, 2026.
Add the
Wenmode.use(plugin, **options)plugin API and built-inwenmode.pluginsmodules for non-standard syntax such as math, definition lists, abbreviations, spoilers, ruby text, inline roles, and extra inline formatting.Add opt-in source positions with
Wenmode(..., positions=True)andParser(..., positions=True).Root.to_ast()includes unist-styleposition.startandposition.endobjects when positions are enabled, while internalPositionobjects store offsets for cheaper parsing and simpler custom rule code.Add source mapping helpers for custom rules that recursively parse nested inline or block content.
Require custom
InlineRule.parse()implementations to receive a concreteBlockState;Parser.parse_inlines()remains the standalone inline parsing entry point.Move non-standard extension syntax out of
wenmode.rulesand into explicit plugins. Code using those extension rules directly should migrate toWenmode().use(wenmode.plugins.<name>).
0.1.1#
Released Jun 19, 2026.
Avoid potential regex DoS cases in ATX heading parsing, inline directives, spoiler spans, and extended autolink trimming.
Change
RootTransformfrom a protocol to a base class with default no-op hooks, making custom root transforms easier to define without requiring boilerplateprepare()methods.Improve
StateKeytype hints for typed parser extension state.
0.1.0#
Released Jun 18, 2026.
Initial beta release.
CommonMark-style parser and HTML renderer.
GitHub-flavored Markdown preset with tables, task list items, strikethrough, extended autolinks, footnotes, and GFM disallowed HTML tag handling.
Streaming preset for incremental HTML output.
mdast-style AST nodes with
Node.to_ast().Pluggable renderers for HTML, normalized Markdown, and reStructuredText.
Custom rule APIs for block, continuation, inline, and root-transform rules.
Directive parsing and built-in HTML directive renderers.