Skip to content

Commit ea6020d

Browse files
authored
fix: separate block markdown children (#7)
## Summary - Backport block/transparent-wrapper child boundary spacing to the v18 branch. - Keep inline siblings, punctuation, raw Markdown text nodes, and fenced code content unchanged. - Add dedicated `src/react/render-spacing.test.tsx` coverage for sticky child-boundary cases. - Treat only existing blank-line boundaries as already separated. ## Validation - `pnpm install` - `pnpm exec biome check .` - `pnpm test` - `pnpm build` - 2000 transparent block sibling smoke via built output: 19ms
1 parent ecf2d0e commit ea6020d

3 files changed

Lines changed: 341 additions & 48 deletions

File tree

src/react/render-spacing.test.tsx

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import { describe, expect, it } from 'vitest';
2+
import { renderToMarkdownString } from './render';
3+
4+
describe('renderToMarkdownString - child boundary spacing', () => {
5+
it('separates adjacent transparent block wrappers', async () => {
6+
expect(
7+
await renderToMarkdownString(
8+
<>
9+
<div>Row 1</div>
10+
<div>Row 2</div>
11+
</>,
12+
),
13+
).toMatchInlineSnapshot(`
14+
"Row 1
15+
16+
Row 2"
17+
`);
18+
});
19+
20+
it('separates nested transparent block wrappers', async () => {
21+
expect(
22+
await renderToMarkdownString(
23+
<main>
24+
<section>
25+
<div>Intro</div>
26+
</section>
27+
<article>
28+
<div>Details</div>
29+
</article>
30+
</main>,
31+
),
32+
).toMatchInlineSnapshot(`
33+
"Intro
34+
35+
Details"
36+
`);
37+
});
38+
39+
it('separates text from adjacent block wrappers in flow containers', async () => {
40+
expect(
41+
await renderToMarkdownString(
42+
<>
43+
Lead
44+
<section>Section</section>
45+
<aside>Aside</aside>
46+
Tail
47+
</>,
48+
),
49+
).toMatchInlineSnapshot(`
50+
"Lead
51+
52+
Section
53+
54+
Aside
55+
56+
Tail"
57+
`);
58+
});
59+
60+
it('separates blocks even when the previous text ends with a space', async () => {
61+
expect(
62+
await renderToMarkdownString(
63+
<>
64+
{'Lead '}
65+
<section>Section</section>
66+
</>,
67+
),
68+
).toMatchInlineSnapshot(`
69+
"Lead
70+
71+
Section"
72+
`);
73+
});
74+
75+
it('completes block separation when the boundary already has one newline', async () => {
76+
expect(
77+
await renderToMarkdownString(
78+
<>
79+
{'Lead\n'}
80+
<section>Section</section>
81+
</>,
82+
),
83+
).toMatchInlineSnapshot(`
84+
"Lead
85+
86+
Section"
87+
`);
88+
});
89+
90+
it('does not over-separate blocks that already start with one newline', async () => {
91+
expect(
92+
await renderToMarkdownString(
93+
<>
94+
{'Lead'}
95+
<pre data-lang="ts">
96+
<code>{'const value = 1;\n'}</code>
97+
</pre>
98+
</>,
99+
),
100+
).toMatchInlineSnapshot(`
101+
"Lead
102+
103+
\`\`\`ts
104+
const value = 1;
105+
106+
\`\`\`
107+
"
108+
`);
109+
});
110+
111+
it('does not add duplicate spacing around markdown blocks that already delimit themselves', async () => {
112+
expect(
113+
await renderToMarkdownString(
114+
<div>
115+
<h2>Title</h2>
116+
<p>Paragraph</p>
117+
<div>Footer</div>
118+
</div>,
119+
),
120+
).toMatchInlineSnapshot(`
121+
"## Title
122+
123+
Paragraph
124+
125+
Footer"
126+
`);
127+
});
128+
129+
it('keeps inline siblings and punctuation joined exactly', async () => {
130+
expect(
131+
await renderToMarkdownString(
132+
<p>
133+
<strong>foo</strong>
134+
<span>bar</span>,<a href="/docs">docs</a>.
135+
</p>,
136+
),
137+
).toMatchInlineSnapshot(`
138+
"**foo**bar,[docs](/docs).
139+
140+
"
141+
`);
142+
});
143+
144+
it('does not insert spaces into comma-separated inline React children', async () => {
145+
expect(
146+
await renderToMarkdownString(<p>ref-value,memo-value,provided</p>),
147+
).toMatchInlineSnapshot(`
148+
"ref-value,memo-value,provided
149+
150+
"
151+
`);
152+
});
153+
154+
it('keeps raw markdown text nodes untouched', async () => {
155+
expect(
156+
await renderToMarkdownString(
157+
<>
158+
{'# Code Example\\n'}
159+
{'\n'}
160+
{'```tsx\nconsole.log("Hello, world!");\n```\n'}
161+
</>,
162+
),
163+
).toMatchInlineSnapshot(`
164+
"# Code Example\\n
165+
\`\`\`tsx
166+
console.log("Hello, world!");
167+
\`\`\`
168+
"
169+
`);
170+
});
171+
172+
it('keeps fenced code block content untouched', async () => {
173+
expect(
174+
await renderToMarkdownString(
175+
<pre data-lang="ts">
176+
<code>{'const values = ["a", "b"];\n'}</code>
177+
</pre>,
178+
),
179+
).toMatchInlineSnapshot(`
180+
"
181+
\`\`\`ts
182+
const values = ["a", "b"];
183+
184+
\`\`\`
185+
"
186+
`);
187+
});
188+
});

src/react/render.test.tsx

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -264,21 +264,6 @@ describe('renderToMarkdownString - styles', () => {
264264
"
265265
`);
266266
});
267-
268-
it('renders two row correctly', async () => {
269-
const Comp1 = () => {
270-
return (
271-
<>
272-
<div>Row 1</div>
273-
<div>Row 2</div>
274-
</>
275-
);
276-
};
277-
278-
expect(await renderToMarkdownString(<Comp1 />)).toMatchInlineSnapshot(
279-
`"Row 1Row 2"`,
280-
);
281-
});
282267
});
283268

284269
describe('renderToMarkdownString - effects never execute (SSR behavior)', () => {

0 commit comments

Comments
 (0)