safe-mdx 1.3.2 → 1.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -14
- package/dist/assets/HtmlToJsxConverter-Ds0bTjpw.js +24 -0
- package/dist/assets/_commonjsHelpers-CqkleIqs.js +1 -0
- package/dist/assets/index-B5fPOjPt.css +1 -0
- package/dist/assets/index-B7ATSoRE.js +9 -0
- package/dist/assets/index-BwZ2FTRd.js +146 -0
- package/dist/assets/index-R1UqLMGJ.js +1 -0
- package/dist/assets/index-c0qeY2gs.js +9 -0
- package/dist/assets/jsx-runtime-BhZZLbvw.js +9 -0
- package/dist/assets/jsx-runtime-NArryeSM.js +1 -0
- package/dist/assets/react-Ca6JzGpx.js +1 -0
- package/dist/assets/react-dom-BYRHYqYl.js +1 -0
- package/dist/html/attributes.d.ts +19 -0
- package/dist/html/attributes.d.ts.map +1 -0
- package/dist/html/attributes.js +289 -0
- package/dist/html/attributes.js.map +1 -0
- package/dist/html/convert-attributes.d.ts +6 -0
- package/dist/html/convert-attributes.d.ts.map +1 -0
- package/dist/html/convert-attributes.js +43 -0
- package/dist/html/convert-attributes.js.map +1 -0
- package/dist/html/domparser-browser.d.ts +4 -0
- package/dist/html/domparser-browser.d.ts.map +1 -0
- package/dist/html/domparser-browser.js +7 -0
- package/dist/html/domparser-browser.js.map +1 -0
- package/dist/html/domparser.d.ts +2 -0
- package/dist/html/domparser.d.ts.map +1 -0
- package/dist/html/domparser.js +5 -0
- package/dist/html/domparser.js.map +1 -0
- package/dist/html/html-to-mdx-ast.d.ts +25 -0
- package/dist/html/html-to-mdx-ast.d.ts.map +1 -0
- package/dist/html/html-to-mdx-ast.js +247 -0
- package/dist/html/html-to-mdx-ast.js.map +1 -0
- package/dist/html/html-to-mdx-ast.test.d.ts +2 -0
- package/dist/html/html-to-mdx-ast.test.d.ts.map +1 -0
- package/dist/html/html-to-mdx-ast.test.js +411 -0
- package/dist/html/html-to-mdx-ast.test.js.map +1 -0
- package/dist/html/remark-mdx-jsx-normalize.d.ts +10 -0
- package/dist/html/remark-mdx-jsx-normalize.d.ts.map +1 -0
- package/dist/html/remark-mdx-jsx-normalize.js +194 -0
- package/dist/html/remark-mdx-jsx-normalize.js.map +1 -0
- package/dist/html/valid-html-elements.d.ts +10 -0
- package/dist/html/valid-html-elements.d.ts.map +1 -0
- package/dist/html/valid-html-elements.js +50 -0
- package/dist/html/valid-html-elements.js.map +1 -0
- package/dist/index.html +19 -0
- package/dist/parse.d.ts +2 -0
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +2 -0
- package/dist/parse.js.map +1 -1
- package/dist/safe-mdx.d.ts +2 -2
- package/dist/safe-mdx.d.ts.map +1 -1
- package/dist/safe-mdx.js +24 -76
- package/dist/safe-mdx.js.map +1 -1
- package/dist/safe-mdx.test.js +161 -8
- package/dist/safe-mdx.test.js.map +1 -1
- package/package.json +28 -6
- package/src/html/README +17 -0
- package/src/html/attributes.ts +297 -0
- package/src/html/convert-attributes.ts +59 -0
- package/src/html/domparser-browser.ts +6 -0
- package/src/html/domparser.ts +5 -0
- package/src/html/html-to-mdx-ast.test.ts +459 -0
- package/src/html/html-to-mdx-ast.ts +327 -0
- package/src/html/remark-mdx-jsx-normalize.ts +209 -0
- package/src/html/valid-html-elements.ts +65 -0
- package/src/parse.ts +3 -0
- package/src/safe-mdx.test.tsx +178 -12
- package/src/safe-mdx.tsx +25 -84
- package/dist/HtmlToJsxConverter.d.ts +0 -10
- package/dist/HtmlToJsxConverter.d.ts.map +0 -1
- package/dist/HtmlToJsxConverter.js +0 -22
- package/dist/HtmlToJsxConverter.js.map +0 -1
- package/dist/plugins.d.ts +0 -12
- package/dist/plugins.d.ts.map +0 -1
- package/dist/plugins.js +0 -68
- package/dist/plugins.js.map +0 -1
- package/src/HtmlToJsxConverter.tsx +0 -37
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { test, expect, describe } from 'vitest'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
4
|
+
import {
|
|
5
|
+
parseHtmlToMdxAst,
|
|
6
|
+
htmlToMdxAst,
|
|
7
|
+
remarkMdxJsxNormalize,
|
|
8
|
+
} from './html-to-mdx-ast.js'
|
|
9
|
+
import { unified } from 'unified'
|
|
10
|
+
import remarkMdx from 'remark-mdx'
|
|
11
|
+
import remarkStringify from 'remark-stringify'
|
|
12
|
+
import remarkParse from 'remark-parse'
|
|
13
|
+
import type { RootContent } from 'mdast'
|
|
14
|
+
import { mdxParse } from '../parse.js'
|
|
15
|
+
import { MdastToJsx } from '../safe-mdx.js'
|
|
16
|
+
|
|
17
|
+
// Default components for testing
|
|
18
|
+
const components = {
|
|
19
|
+
Heading({ level, children, ...props }) {
|
|
20
|
+
return React.createElement('h1', props, children)
|
|
21
|
+
},
|
|
22
|
+
Cards({ level, children, ...props }) {
|
|
23
|
+
return React.createElement('div', props, children)
|
|
24
|
+
},
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Helper to convert HTML to MDX string and rendered HTML
|
|
28
|
+
async function htmlToMdxString({
|
|
29
|
+
html,
|
|
30
|
+
withProcessor = false,
|
|
31
|
+
onError,
|
|
32
|
+
convertTagName,
|
|
33
|
+
convertAttributeValue,
|
|
34
|
+
}: {
|
|
35
|
+
html: string
|
|
36
|
+
withProcessor?: boolean
|
|
37
|
+
onError?: (error: unknown, text: string) => void
|
|
38
|
+
convertTagName?: (args: { tagName: string }) => string
|
|
39
|
+
convertAttributeValue?: (args: {
|
|
40
|
+
name: string
|
|
41
|
+
value: string
|
|
42
|
+
tagName: string
|
|
43
|
+
}) => string
|
|
44
|
+
}): Promise<{ mdx: string; html: string }> {
|
|
45
|
+
// If withProcessor is true, create a textToMdast that parses markdown
|
|
46
|
+
const textToMdast = withProcessor
|
|
47
|
+
? ({ text }: { text: string }) => {
|
|
48
|
+
const markdownProcessor = unified()
|
|
49
|
+
.use(remarkParse)
|
|
50
|
+
.use(remarkMdx)
|
|
51
|
+
const mdast = markdownProcessor.parse(text) as any
|
|
52
|
+
markdownProcessor.runSync(mdast)
|
|
53
|
+
// Return the children of the root node
|
|
54
|
+
return mdast.children || []
|
|
55
|
+
}
|
|
56
|
+
: undefined
|
|
57
|
+
|
|
58
|
+
const mdxAst = parseHtmlToMdxAst({
|
|
59
|
+
html,
|
|
60
|
+
textToMdast,
|
|
61
|
+
onError,
|
|
62
|
+
convertTagName,
|
|
63
|
+
convertAttributeValue,
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Generate MDX string
|
|
67
|
+
const processor = unified().use(remarkMdx).use(remarkStringify, {
|
|
68
|
+
// bullet: '-',
|
|
69
|
+
// fence: '`',
|
|
70
|
+
// fences: true
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Create a root node with the content
|
|
74
|
+
const root = {
|
|
75
|
+
type: 'root',
|
|
76
|
+
children: mdxAst,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mdx = processor.stringify(root as any)
|
|
80
|
+
|
|
81
|
+
// Generate HTML using MdastToJsx like in safe-mdx.test.tsx
|
|
82
|
+
// First parse the MDX to get the full AST
|
|
83
|
+
const mdast = mdxParse(mdx)
|
|
84
|
+
const visitor = new MdastToJsx({ markdown: mdx, mdast, components })
|
|
85
|
+
const jsx = visitor.run()
|
|
86
|
+
const renderedHtml = renderToStaticMarkup(jsx)
|
|
87
|
+
|
|
88
|
+
return { mdx, html: renderedHtml }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe('parseHtmlToMdxAst', () => {
|
|
92
|
+
test('parses simple HTML element', () => {
|
|
93
|
+
const result = parseHtmlToMdxAst({ html: '<div>Hello</div>' })
|
|
94
|
+
expect(result).toMatchInlineSnapshot(`
|
|
95
|
+
[
|
|
96
|
+
{
|
|
97
|
+
"attributes": [],
|
|
98
|
+
"children": [
|
|
99
|
+
{
|
|
100
|
+
"type": "text",
|
|
101
|
+
"value": "Hello",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
"name": "div",
|
|
105
|
+
"type": "mdxJsxTextElement",
|
|
106
|
+
},
|
|
107
|
+
]
|
|
108
|
+
`)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('filters out non-HTML elements when convertTagName returns empty string', () => {
|
|
112
|
+
const result = parseHtmlToMdxAst({
|
|
113
|
+
html: '<custom-element>Hello <span>world</span></custom-element>',
|
|
114
|
+
convertTagName: ({ tagName }) => {
|
|
115
|
+
// Only keep span, filter out custom-element
|
|
116
|
+
if (tagName === 'span') return 'span'
|
|
117
|
+
return tagName
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
expect(result).toMatchInlineSnapshot(`
|
|
121
|
+
[
|
|
122
|
+
{
|
|
123
|
+
"attributes": [],
|
|
124
|
+
"children": [
|
|
125
|
+
{
|
|
126
|
+
"type": "text",
|
|
127
|
+
"value": "Hello ",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"attributes": [],
|
|
131
|
+
"children": [
|
|
132
|
+
{
|
|
133
|
+
"type": "text",
|
|
134
|
+
"value": "world",
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
"name": "span",
|
|
138
|
+
"type": "mdxJsxTextElement",
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
"name": "custom-element",
|
|
142
|
+
"type": "mdxJsxTextElement",
|
|
143
|
+
},
|
|
144
|
+
]
|
|
145
|
+
`)
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('handles self-closing tags', () => {
|
|
149
|
+
const result = parseHtmlToMdxAst({
|
|
150
|
+
html: '<img src="https://example.com/img.jpg" />',
|
|
151
|
+
})
|
|
152
|
+
expect(result).toMatchInlineSnapshot(`
|
|
153
|
+
[
|
|
154
|
+
{
|
|
155
|
+
"attributes": [
|
|
156
|
+
{
|
|
157
|
+
"name": "src",
|
|
158
|
+
"type": "mdxJsxAttribute",
|
|
159
|
+
"value": "https://example.com/img.jpg",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
"children": [],
|
|
163
|
+
"name": "img",
|
|
164
|
+
"type": "mdxJsxTextElement",
|
|
165
|
+
},
|
|
166
|
+
]
|
|
167
|
+
`)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('handles span with attributes', () => {
|
|
171
|
+
const result = parseHtmlToMdxAst({
|
|
172
|
+
html: '<span color="blue">colored text</span>',
|
|
173
|
+
})
|
|
174
|
+
expect(result).toMatchInlineSnapshot(`
|
|
175
|
+
[
|
|
176
|
+
{
|
|
177
|
+
"attributes": [
|
|
178
|
+
{
|
|
179
|
+
"name": "color",
|
|
180
|
+
"type": "mdxJsxAttribute",
|
|
181
|
+
"value": "blue",
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
"children": [
|
|
185
|
+
{
|
|
186
|
+
"type": "text",
|
|
187
|
+
"value": "colored text",
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
"name": "span",
|
|
191
|
+
"type": "mdxJsxTextElement",
|
|
192
|
+
},
|
|
193
|
+
]
|
|
194
|
+
`)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test('handles mixed content', () => {
|
|
198
|
+
const result = parseHtmlToMdxAst({
|
|
199
|
+
html: 'Some text <strong>bold</strong> more text',
|
|
200
|
+
})
|
|
201
|
+
expect(result).toMatchInlineSnapshot(`
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
"type": "text",
|
|
205
|
+
"value": "Some text ",
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"attributes": [],
|
|
209
|
+
"children": [
|
|
210
|
+
{
|
|
211
|
+
"type": "text",
|
|
212
|
+
"value": "bold",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
"name": "strong",
|
|
216
|
+
"type": "mdxJsxTextElement",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"type": "text",
|
|
220
|
+
"value": " more text",
|
|
221
|
+
},
|
|
222
|
+
]
|
|
223
|
+
`)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('handles comments', () => {
|
|
227
|
+
const result = parseHtmlToMdxAst({ html: '<!-- This is a comment -->' })
|
|
228
|
+
expect(result).toMatchInlineSnapshot(`[]`)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('handles table with attributes', () => {
|
|
232
|
+
const result = parseHtmlToMdxAst({
|
|
233
|
+
html: '<table class="data-table"><tr><td>Cell</td></tr></table>',
|
|
234
|
+
})
|
|
235
|
+
expect(result).toMatchInlineSnapshot(`
|
|
236
|
+
[
|
|
237
|
+
{
|
|
238
|
+
"attributes": [
|
|
239
|
+
{
|
|
240
|
+
"name": "className",
|
|
241
|
+
"type": "mdxJsxAttribute",
|
|
242
|
+
"value": "data-table",
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
"children": [
|
|
246
|
+
{
|
|
247
|
+
"attributes": [],
|
|
248
|
+
"children": [
|
|
249
|
+
{
|
|
250
|
+
"attributes": [],
|
|
251
|
+
"children": [
|
|
252
|
+
{
|
|
253
|
+
"type": "text",
|
|
254
|
+
"value": "Cell",
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
"name": "td",
|
|
258
|
+
"type": "mdxJsxTextElement",
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
"name": "tr",
|
|
262
|
+
"type": "mdxJsxTextElement",
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
"name": "table",
|
|
266
|
+
"type": "mdxJsxTextElement",
|
|
267
|
+
},
|
|
268
|
+
]
|
|
269
|
+
`)
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
describe('parseHtmlToMdxAst with markdown processor', () => {
|
|
274
|
+
test('parses markdown inside HTML tags', async () => {
|
|
275
|
+
const htmlToConvert = '<div>This is **bold** text</div>'
|
|
276
|
+
const result = await htmlToMdxString({
|
|
277
|
+
html: htmlToConvert,
|
|
278
|
+
withProcessor: true,
|
|
279
|
+
onError: (e) => {
|
|
280
|
+
throw e
|
|
281
|
+
},
|
|
282
|
+
})
|
|
283
|
+
expect(result).toMatchInlineSnapshot(`
|
|
284
|
+
{
|
|
285
|
+
"html": "<div>This is <strong>bold</strong> text</div>",
|
|
286
|
+
"mdx": "<div>This is **bold** text</div>
|
|
287
|
+
",
|
|
288
|
+
}
|
|
289
|
+
`)
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
test('handles mixed markdown and HTML inside tags', async () => {
|
|
293
|
+
const htmlToConvert = '<div>**Bold text:** <a href="#">link</a></div>'
|
|
294
|
+
const result = await htmlToMdxString({
|
|
295
|
+
html: htmlToConvert,
|
|
296
|
+
withProcessor: true,
|
|
297
|
+
onError: (e) => {
|
|
298
|
+
throw e
|
|
299
|
+
},
|
|
300
|
+
})
|
|
301
|
+
expect(result).toMatchInlineSnapshot(`
|
|
302
|
+
{
|
|
303
|
+
"html": "<div><strong>Bold text:</strong><a href="#">link</a></div>",
|
|
304
|
+
"mdx": "<div>**Bold text:**<a href="#">link</a></div>
|
|
305
|
+
",
|
|
306
|
+
}
|
|
307
|
+
`)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
test('handles markdown inside table cells', async () => {
|
|
311
|
+
const htmlToConvert =
|
|
312
|
+
'<table><tr><td>**Bold** text and [link](http://example.com)</td></tr></table>'
|
|
313
|
+
const result = await htmlToMdxString({
|
|
314
|
+
html: htmlToConvert,
|
|
315
|
+
withProcessor: true,
|
|
316
|
+
onError: (e) => {
|
|
317
|
+
throw e
|
|
318
|
+
},
|
|
319
|
+
})
|
|
320
|
+
expect(result).toMatchInlineSnapshot(`
|
|
321
|
+
{
|
|
322
|
+
"html": "<table><tr><td><strong>Bold</strong> text and <a href="http://example.com" title="">link</a></td></tr></table>",
|
|
323
|
+
"mdx": "<table><tr><td>**Bold** text and [link](http://example.com)</td></tr></table>
|
|
324
|
+
",
|
|
325
|
+
}
|
|
326
|
+
`)
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
test('preserves plain text when no markdown', async () => {
|
|
330
|
+
const htmlToConvert = '<div>Plain text without markdown</div>'
|
|
331
|
+
const result = await htmlToMdxString({
|
|
332
|
+
html: htmlToConvert,
|
|
333
|
+
withProcessor: true,
|
|
334
|
+
onError: (e) => {
|
|
335
|
+
throw e
|
|
336
|
+
},
|
|
337
|
+
})
|
|
338
|
+
expect(result).toMatchInlineSnapshot(`
|
|
339
|
+
{
|
|
340
|
+
"html": "<div>Plain text without markdown</div>",
|
|
341
|
+
"mdx": "<div>Plain text without markdown</div>
|
|
342
|
+
",
|
|
343
|
+
}
|
|
344
|
+
`)
|
|
345
|
+
})
|
|
346
|
+
|
|
347
|
+
test('handles nested HTML tags with markdown', async () => {
|
|
348
|
+
const htmlToConvert =
|
|
349
|
+
'<div><span>**Bold** and <a href="#">link</a></span></div>'
|
|
350
|
+
const result = await htmlToMdxString({
|
|
351
|
+
html: htmlToConvert,
|
|
352
|
+
withProcessor: true,
|
|
353
|
+
onError: (e) => {
|
|
354
|
+
throw e
|
|
355
|
+
},
|
|
356
|
+
})
|
|
357
|
+
expect(result).toMatchInlineSnapshot(`
|
|
358
|
+
{
|
|
359
|
+
"html": "<div><span><strong>Bold</strong> and<a href="#">link</a></span></div>",
|
|
360
|
+
"mdx": "<div><span>**Bold** and<a href="#">link</a></span></div>
|
|
361
|
+
",
|
|
362
|
+
}
|
|
363
|
+
`)
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
test('normalize plugin without parentType does not apply', () => {
|
|
367
|
+
// Without parentType, normalization should not happen
|
|
368
|
+
const withoutParent = htmlToMdxAst({
|
|
369
|
+
html: '<span>Text</span>'
|
|
370
|
+
})
|
|
371
|
+
// Without normalization, elements remain as initially created (text elements)
|
|
372
|
+
expect(withoutParent).toHaveLength(1)
|
|
373
|
+
expect(withoutParent[0]).toMatchObject({
|
|
374
|
+
type: 'mdxJsxTextElement',
|
|
375
|
+
name: 'span'
|
|
376
|
+
})
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
test('applies normalize plugin with parentType', () => {
|
|
380
|
+
// Test with a block element inside a paragraph (phrasing context)
|
|
381
|
+
const blockInParagraph = htmlToMdxAst({
|
|
382
|
+
html: '<div>Block in paragraph</div>',
|
|
383
|
+
parentType: 'paragraph'
|
|
384
|
+
})
|
|
385
|
+
// Even though div is a block element, it should remain mdxJsxFlowElement
|
|
386
|
+
// because block-level tags have priority
|
|
387
|
+
expect(blockInParagraph).toHaveLength(1)
|
|
388
|
+
expect(blockInParagraph[0]).toMatchObject({
|
|
389
|
+
type: 'mdxJsxFlowElement',
|
|
390
|
+
name: 'div'
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
// Test with inline element in paragraph (should be text element)
|
|
394
|
+
const inlineInParagraph = htmlToMdxAst({
|
|
395
|
+
html: '<span>Inline in paragraph</span>',
|
|
396
|
+
parentType: 'paragraph'
|
|
397
|
+
})
|
|
398
|
+
expect(inlineInParagraph).toHaveLength(1)
|
|
399
|
+
expect(inlineInParagraph[0]).toMatchObject({
|
|
400
|
+
type: 'mdxJsxTextElement',
|
|
401
|
+
name: 'span'
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
// Test with inline element in root (should be flow element)
|
|
405
|
+
const inlineInRoot = htmlToMdxAst({
|
|
406
|
+
html: '<span>Inline in root</span>',
|
|
407
|
+
parentType: 'root'
|
|
408
|
+
})
|
|
409
|
+
expect(inlineInRoot).toHaveLength(1)
|
|
410
|
+
expect(inlineInRoot[0]).toMatchObject({
|
|
411
|
+
type: 'mdxJsxFlowElement',
|
|
412
|
+
name: 'span'
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
// Test with multiple elements
|
|
416
|
+
const multipleElements = htmlToMdxAst({
|
|
417
|
+
html: '<span>First</span><div>Second</div>',
|
|
418
|
+
parentType: 'paragraph'
|
|
419
|
+
})
|
|
420
|
+
expect(multipleElements).toHaveLength(2)
|
|
421
|
+
expect(multipleElements[0]).toMatchObject({
|
|
422
|
+
type: 'mdxJsxTextElement',
|
|
423
|
+
name: 'span'
|
|
424
|
+
})
|
|
425
|
+
expect(multipleElements[1]).toMatchObject({
|
|
426
|
+
type: 'mdxJsxFlowElement', // div is block-level
|
|
427
|
+
name: 'div'
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
// Test with nested elements - the normalize plugin should handle nested context correctly
|
|
431
|
+
const nestedElements = htmlToMdxAst({
|
|
432
|
+
html: '<div><span>Nested span</span><p><em>Emphasis</em></p></div>',
|
|
433
|
+
parentType: 'root'
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
// The plugin normalizes based on the entire tree structure
|
|
437
|
+
expect(nestedElements).toHaveLength(1)
|
|
438
|
+
expect(nestedElements[0]).toMatchObject({
|
|
439
|
+
type: 'mdxJsxFlowElement',
|
|
440
|
+
name: 'div',
|
|
441
|
+
children: expect.arrayContaining([
|
|
442
|
+
expect.objectContaining({
|
|
443
|
+
type: 'mdxJsxFlowElement', // span in div (flow container) becomes flow
|
|
444
|
+
name: 'span'
|
|
445
|
+
}),
|
|
446
|
+
expect.objectContaining({
|
|
447
|
+
type: 'mdxJsxFlowElement',
|
|
448
|
+
name: 'p',
|
|
449
|
+
children: expect.arrayContaining([
|
|
450
|
+
expect.objectContaining({
|
|
451
|
+
type: 'mdxJsxTextElement', // em in paragraph (phrasing container) becomes text
|
|
452
|
+
name: 'em'
|
|
453
|
+
})
|
|
454
|
+
])
|
|
455
|
+
})
|
|
456
|
+
])
|
|
457
|
+
})
|
|
458
|
+
})
|
|
459
|
+
})
|