Skip to content

renderMarkdown inserts blank lines inside raw-HTML elements, splitting them on reparse #301

Description

@sandros94

Environment

  • Operating System: Linux (WSL2)
  • Node Version: v24.17.0
  • Package Manager: pnpm@11.11.0
  • Comark Version: 0.5.1 (reproduces on current main)
  • Framework: None

Comark Version

v0.5.1

Reproduction

import { parse } from 'comark'
import { renderMarkdown } from 'comark/render'

const md = '<picture>\n<source srcset="a.avif">\n<img src="a.jpg">\n</picture>'
const rendered = await renderMarkdown(await parse(md))
// '<picture>\n<source srcset="a.avif" />\n\n<img src="a.jpg" />\n</picture>'
//                                        ^^ blank line

console.log(JSON.stringify((await parse(rendered)).nodes))
// [["picture",{…}, ["source",…]], ["img",…]]   ← img split out of the element

Description

The child-join loop in the html handler joins a raw-HTML element's children with blockSeparator whenever a child classifies as block (the catch-all arm classifies any unlisted tag as block, so source, figcaption, option… all hit it). A blank line terminates the HTML block per CommonMark, so the rendered markdown can't reparse to the original tree — parse → renderMarkdown → parse splits the element.

Anything with ≥2 children where one is not a known inline tag fails: figure>img+figcaption, details>summary+p, dl, select, audio with two sources, nested divs. Single-child elements escape it.

The inline variant is worse: text <picture><source…><img…></picture> tail gets the separator too (the loop runs regardless of oneLiner), which splits the containing paragraph in two on reparse.

Since the parser never nests markdown blocks inside a $.html element (blank-line-separated markdown is parsed outside the element), a blank line inside one is never legitimate in markdown output — children should be joined with \n, or nothing at all in inline position.

Fix + tests ready locally, PR incoming

Additional context

Parse-side relative: #235 / #236 are about the parser tolerating hand-written blank lines inside <details>; this one is about the stringifier introducing them. No overlap in touched files.

Filing two more round-trip issues alongside this one, all independent, will cross-link once up

Logs

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions