Skip to content

Commit 722c844

Browse files
hendrikheilclaudefarnabaz
authored
fix(stringify): render block component children of list items on their own line (#252)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Farnabaz <farnabaz@gmail.com>
1 parent 6539b91 commit 722c844

2 files changed

Lines changed: 98 additions & 8 deletions

File tree

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
## Input
2+
3+
```md
4+
- *item one*
5+
::block
6+
#body
7+
### Heading
8+
::
9+
```
10+
11+
## AST
12+
13+
```json
14+
{
15+
"frontmatter": {},
16+
"meta": {},
17+
"nodes": [
18+
[
19+
"ul",
20+
{},
21+
[
22+
"li",
23+
{},
24+
[
25+
"p",
26+
{},
27+
[
28+
"em",
29+
{},
30+
"item one"
31+
]
32+
],
33+
[
34+
"block",
35+
{},
36+
[
37+
"template",
38+
{
39+
"name": "body"
40+
},
41+
[
42+
"h3",
43+
{
44+
"id": "heading"
45+
},
46+
"Heading"
47+
]
48+
]
49+
]
50+
]
51+
]
52+
]
53+
}
54+
```
55+
56+
## HTML
57+
58+
```html
59+
<ul>
60+
<li>
61+
<p><em>item one</em></p>
62+
<block>
63+
<template name="body">
64+
<h3 id="heading">Heading</h3>
65+
</template>
66+
</block>
67+
</li>
68+
</ul>
69+
```
70+
71+
## Markdown
72+
73+
```md
74+
- *item one*
75+
::block
76+
#body
77+
### Heading
78+
::
79+
```

packages/comark/src/internal/stringify/handlers/li.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,36 @@ export async function li(node: ComarkElement, state: State) {
2626
}
2727

2828
const prefixWidth = prefix.length
29+
30+
// Direct text children render sibling components inline.
31+
const hasInlineContent = children.some((child) => typeof child === 'string')
32+
2933
let result = ''
3034

3135
for (const child of children) {
32-
const rendered = await state.one(child, state, node)
33-
if (result && Array.isArray(child)) {
34-
if (blockElements.has(child[0] as string)) {
35-
// Block-level child: put on its own line and indent to align with list prefix
36-
const indented = indent(rendered, { width: prefixWidth })
36+
if (Array.isArray(child)) {
37+
const tag = child[0] as string
38+
39+
if (result && blockElements.has(tag)) {
40+
const indented = indent(await state.one(child, state, node), { width: prefixWidth })
3741
result = result.trimEnd() + '\n' + indented.trimEnd() + '\n'
3842
continue
3943
}
4044

41-
if (child[0] === 'p') {
42-
const indented = indent(rendered, { width: prefixWidth })
45+
if (result && tag === 'p') {
46+
const indented = indent(await state.one(child, state, node), { width: prefixWidth })
4347
result = result.trimEnd() + '\n\n' + indented.trimEnd() + '\n'
4448
continue
4549
}
50+
51+
// No parent → mdc skips its own nesting indentation, so li owns it here.
52+
if (!hasInlineContent && !(tag in state.handlers)) {
53+
const indented = indent(await state.one(child, state), { width: prefixWidth, ignoreFirstLine: !result })
54+
result = result ? result.trimEnd() + '\n' + indented.trimEnd() + '\n' : indented.trimEnd() + '\n'
55+
continue
56+
}
4657
}
47-
result += rendered
58+
result += await state.one(child, state, node)
4859
}
4960
result = result.trim()
5061

0 commit comments

Comments
 (0)