Skip to content

Commit 5c1ea1b

Browse files
committed
fix(stringify): preserve children order
1 parent d426839 commit 5c1ea1b

File tree

2 files changed

+209
-1
lines changed

2 files changed

+209
-1
lines changed

src/runtime/stringify/mdc-remark.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const mdcRemarkNodeHandlers = {
173173
}
174174
}
175175

176-
const mdcRemarkHandlers: Record<string, (state: State, node: Parents) => unknown> = {
176+
const mdcRemarkHandlers: Record<string, (state: State, node: Parents, parent: Parents | undefined) => unknown> = {
177177
template: (state: State, node: Parents) => {
178178
const vSlot = Object.keys(node.properties || {}).find(prop => prop?.startsWith('v-slot:'))?.replace('v-slot:', '') || 'default'
179179

@@ -195,6 +195,20 @@ const mdcRemarkHandlers: Record<string, (state: State, node: Parents) => unknown
195195
children: state.toFlow(state.all(node))
196196
}
197197
},
198+
ul: (state: State, node: Parents, parent: Parents | undefined) => {
199+
const result = defaultHandlers.ul(state, node as Element)
200+
201+
return parent?.tagName === 'p'
202+
? result
203+
: { type: 'paragraph', children: [result] }
204+
},
205+
ol: (state: State, node: Parents, parent: Parents | undefined) => {
206+
const result = defaultHandlers.ol(state, node as Element)
207+
208+
return parent?.tagName === 'p'
209+
? result
210+
: { type: 'paragraph', children: [result] }
211+
},
198212
code: (state: State, node: Parents) => {
199213
const attributes = { ...node.properties }
200214
if ('style' in attributes && !attributes.style) {

test/stringify/ol.test.ts

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import { expect, it, describe } from 'vitest'
2+
import { stringifyMarkdown } from '../utils/parser'
3+
4+
const ast = {
5+
type: 'root',
6+
children: [
7+
{
8+
type: 'element',
9+
tag: 'steps',
10+
props: {},
11+
children: [
12+
{
13+
type: 'element',
14+
tag: 'h3',
15+
props: { id: 'create-your-docs-directory' },
16+
children: [{ type: 'text', value: 'Create your docs directory' }]
17+
},
18+
{
19+
type: 'element',
20+
tag: 'p',
21+
props: {},
22+
children: [
23+
{ type: 'text', value: 'Use the ' },
24+
{
25+
type: 'element',
26+
tag: 'code',
27+
props: {},
28+
children: [{ type: 'text', value: 'create-docus' }]
29+
},
30+
{ type: 'text', value: ' CLI to create a new Docus project:' }
31+
]
32+
},
33+
{
34+
type: 'element',
35+
tag: 'pre',
36+
props: {
37+
className: 'language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight',
38+
code: 'npx create-docus my-docs\n',
39+
filename: 'Terminal',
40+
language: 'bash',
41+
meta: '',
42+
style: ''
43+
},
44+
children: [
45+
{
46+
type: 'element',
47+
tag: 'code',
48+
props: { __ignoreMap: '' },
49+
children: [
50+
{
51+
type: 'element',
52+
tag: 'span',
53+
props: { class: 'line', line: 1 },
54+
children: [
55+
{
56+
type: 'element',
57+
tag: 'span',
58+
props: { class: 'sBMFI' },
59+
children: [{ type: 'text', value: 'npx' }]
60+
},
61+
{
62+
type: 'element',
63+
tag: 'span',
64+
props: { class: 'sfazB' },
65+
children: [{ type: 'text', value: ' create-docus' }]
66+
},
67+
{
68+
type: 'element',
69+
tag: 'span',
70+
props: { class: 'sfazB' },
71+
children: [{ type: 'text', value: ' my-docs\n' }]
72+
}
73+
]
74+
}
75+
]
76+
}
77+
]
78+
},
79+
{
80+
type: 'element',
81+
tag: 'p',
82+
props: {},
83+
children: [{ type: 'text', value: 'You can choose between two templates:' }]
84+
},
85+
{
86+
type: 'element',
87+
tag: 'ul',
88+
props: {},
89+
children: [
90+
{
91+
type: 'element',
92+
tag: 'li',
93+
props: {},
94+
children: [
95+
{
96+
type: 'element',
97+
tag: 'code',
98+
props: {},
99+
children: [{ type: 'text', value: 'default' }]
100+
},
101+
{ type: 'text', value: ': Basic Docus setup for single-language documentation' }
102+
]
103+
},
104+
{
105+
type: 'element',
106+
tag: 'li',
107+
props: {},
108+
children: [
109+
{
110+
type: 'element',
111+
tag: 'code',
112+
props: {},
113+
children: [{ type: 'text', value: 'i18n' }]
114+
},
115+
{ type: 'text', value: ': Includes internationalization support for multi-language documentation' }
116+
]
117+
}
118+
]
119+
}
120+
]
121+
}
122+
]
123+
}
124+
125+
describe('stringify ordered list (ol)', () => {
126+
it('should stringify complex AST with steps component', async () => {
127+
const markdown = await stringifyMarkdown(ast, {})
128+
129+
expect(markdown).toBeTruthy()
130+
expect(markdown).toContain('::steps')
131+
expect(markdown).toContain('### Create your docs directory')
132+
expect(markdown).toContain('Use the `create-docus` CLI')
133+
expect(markdown).toContain('```bash [Terminal]')
134+
expect(markdown).toContain('npx create-docus my-docs')
135+
expect(markdown).toContain('You can choose between two templates:')
136+
expect(markdown).toContain('- `default`: Basic Docus setup')
137+
expect(markdown).toContain('- `i18n`: Includes internationalization support')
138+
expect(markdown).toContain('::')
139+
})
140+
141+
it('should stringify nested elements correctly', async () => {
142+
const markdown = await stringifyMarkdown(ast, {})
143+
144+
// Check that inline code is preserved
145+
expect(markdown).toMatch(/`create-docus`/)
146+
expect(markdown).toMatch(/`default`/)
147+
expect(markdown).toMatch(/`i18n`/)
148+
149+
// Check that code block format is correct
150+
expect(markdown).toMatch(/```bash \[Terminal\]\s+npx create-docus my-docs/)
151+
})
152+
153+
it('should handle empty data parameter', async () => {
154+
const markdown = await stringifyMarkdown(ast)
155+
expect(markdown).toBeTruthy()
156+
expect(typeof markdown).toBe('string')
157+
})
158+
159+
it('should preserve list structure', async () => {
160+
const markdown = await stringifyMarkdown(ast, {})
161+
162+
// Count list items
163+
const listItemMatches = markdown.match(/^- /gm)
164+
expect(listItemMatches).toHaveLength(2)
165+
})
166+
167+
it('should preserve children order', async () => {
168+
const markdown = await stringifyMarkdown(ast, {})
169+
170+
// Extract the structure by finding key elements in order
171+
const h3Index = markdown.indexOf('### Create your docs directory')
172+
const firstParagraphIndex = markdown.indexOf('Use the `create-docus` CLI')
173+
const preIndex = markdown.indexOf('```bash [Terminal]')
174+
const secondParagraphIndex = markdown.indexOf('You can choose between two templates:')
175+
const listIndex = markdown.indexOf('- `default`')
176+
177+
// Verify all elements are present
178+
expect(h3Index).toBeGreaterThan(-1)
179+
expect(firstParagraphIndex).toBeGreaterThan(-1)
180+
expect(preIndex).toBeGreaterThan(-1)
181+
expect(secondParagraphIndex).toBeGreaterThan(-1)
182+
expect(listIndex).toBeGreaterThan(-1)
183+
184+
// Verify order: h3 -> paragraph -> pre -> paragraph -> paragraph(ul)
185+
expect(h3Index).toBeLessThan(firstParagraphIndex)
186+
expect(firstParagraphIndex).toBeLessThan(preIndex)
187+
expect(preIndex).toBeLessThan(secondParagraphIndex)
188+
expect(secondParagraphIndex).toBeLessThan(listIndex)
189+
190+
// Verify ul is wrapped in paragraph (list comes after paragraph text)
191+
const paragraphBeforeList = markdown.substring(secondParagraphIndex, listIndex)
192+
expect(paragraphBeforeList).toContain('You can choose between two templates:')
193+
})
194+
})

0 commit comments

Comments
 (0)