Skip to content

Fix MarkdownRenderer corrupting email autolinks on round-trip#269

Closed
gaoflow wants to merge 1 commit into
frostming:masterfrom
gaoflow:fix-md-renderer-email-autolink
Closed

Fix MarkdownRenderer corrupting email autolinks on round-trip#269
gaoflow wants to merge 1 commit into
frostming:masterfrom
gaoflow:fix-md-renderer-email-autolink

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 20, 2026

Copy link
Copy Markdown

Summary

MarkdownRenderer corrupts email autolinks when re-rendering Markdown:

import marko
from marko.md_renderer import MarkdownRenderer

md = marko.Markdown(renderer=MarkdownRenderer)
md("<mail@x.com>")   # -> '<mailto:mail@x.com>\n'

Re-parsing that output no longer yields the original email link — <mailto:mail@x.com> is parsed as a URI autolink, so the rendered HTML link text becomes mailto:mail@x.com instead of mail@x.com. The MarkdownRenderer round-trip is broken for any email autolink. URI autolinks (<http://example.org>) round-trip correctly.

Cause

AutoLink.__init__ prepends "mailto:" to dest for emails, but preserves the original address in the children:

self.dest = match.group(1)
if re.match(patterns.email, self.dest):
    self.dest = "mailto:" + self.dest
self.children = [RawText(match.group(1))]

render_auto_link emitted the synthetic dest:

return f"<{element.dest}>"

so the mailto: prefix leaked into the Markdown output. (The HTML renderer correctly uses the children for the display text.)

Fix

Render the children — the original text — instead of dest. For URI autolinks the children equal dest, so they are unchanged:

return f"<{self.render_children(element)}>"

Verification

  • Added test_markdown_renderer_email_autolink: asserts <mail@x.com> re-renders unchanged and that both an email and a URI autolink round-trip to equivalent HTML. It fails before the fix and passes after.
  • Verified email autolinks with subdomains / +/-/. in the local part, explicit <mailto:...> URIs, and https autolinks all round-trip and are idempotent under re-rendering.
  • Full suite: 1397 passed (was 1396). ruff and mypy clean on the changed files.

This pull request was prepared with the assistance of AI, under my direction and review.

AutoLink prepends "mailto:" to an email autolink's dest while keeping
the original address in its children. MarkdownRenderer.render_auto_link
emitted the synthetic dest, so re-rendering <mail@x.com> produced
<mailto:mail@x.com>, which parses back with the wrong link text (and is
no longer an email autolink).

Render the children (the original text) instead. URI autolinks have
children equal to dest, so they are unaffected.
@frostming

Copy link
Copy Markdown
Owner

MarkdownRenderer never guarantees round-trip consistency. It works more like a normalizer and SHOULD only ensure that the Markdown before and after conversion renders to the same HTML.

This can be verified:

before: https://spec.commonmark.org/dingus/?text=%3Cmail%40x.com%3E
after: https://spec.commonmark.org/dingus/?text=%3Cmailto%3Amail%40x.com%3E

@frostming frostming closed this Jun 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants