Skip to content

GFM literal autolinks can leave adjacent text nodes that merge on round-trip #20

Description

@ChristianMurphy

Initial checklist

Affected package

mdast-util-gfm 3.1.0, mdast-util-to-markdown 2.1.2, mdast-util-from-markdown 2.0.3

Steps to reproduce

import {fromMarkdown} from 'mdast-util-from-markdown'
import {toMarkdown} from 'mdast-util-to-markdown'
import {gfm} from 'micromark-extension-gfm'
import {gfmFromMarkdown, gfmToMarkdown} from 'mdast-util-gfm'

const input = '[http://a.\n]'
const opts = {extensions: [gfm()], mdastExtensions: [gfmFromMarkdown()]}
const tree1 = fromMarkdown(input, opts)

tree₁'s paragraph has four children (note adjacent text nodes):

{
  "type": "paragraph",
  "children": [
    { "type": "text", "value": "[" },
    { "type": "link", "url": "http://a", "children": [{ "type": "text", "value": "http://a" }] },
    { "type": "text", "value": "." },
    { "type": "text", "value": "\n]" }
  ]
}

stringify and re-parse:

const text2 = toMarkdown(tree1, {extensions: [gfmToMarkdown()]})
// text2: '\\[<http://a>.\n]\n'
const tree2 = fromMarkdown(text2, opts)

tree₂'s paragraph has three children (the two trailing text nodes are
merged):

{
  "type": "paragraph",
  "children": [
    { "type": "text", "value": "[" },
    { "type": "link", "url": "http://a", "children": [{ "type": "text", "value": "http://a" }] },
    { "type": "text", "value": ".\n]" }
  ]
}

Actual behavior

Adjacent text siblings remain in tree₁ but not in tree₂, so
normalize(tree1) !== normalize(tree2) even though the rendered HTML is the
same.

Expected behavior

Either the transform that extracts the literal autolink should merge its
surrounding text splits (so tree₁ has the same three children as tree₂), or a
subsequent pass should merge adjacent text siblings.

Runtime

node v24.13.1

Package manager

No response

Operating system

Ubuntu (WSL2)

Build and bundle tools

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    🤞 phase/openPost is being triaged manually

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions