Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,17 +246,20 @@ function detectRenderMode(file?: VFile): RenderMode {

// Removed: Using mdast-util-to-string instead of custom extractTextContent function

function parseIconHtml(iconHtml: string): Array<{ type: 'html'; value: string }> {
function parseIconHtml(iconHtml: string): ElementContent[] {
if (!iconHtml || !iconHtml.trim()) {
return []
}

try {
// Validate HTML structure but return as raw HTML node for component mode compatibility
// Parse HTML string into proper HAST nodes
const hastTree = fromHtml(iconHtml, { fragment: true })

if (hastTree.type === 'root' && hastTree.children && hastTree.children.length > 0) {
return [{ type: 'html', value: iconHtml }]
// Return the actual HAST elements, not raw HTML nodes
return hastTree.children.filter(
(child): child is ElementContent => child.type === 'element' || child.type === 'text'
)
}

return []
Expand Down Expand Up @@ -308,18 +311,15 @@ function createAlertComponent(
},
},
[
u(
config.tags.icon,
{
data: {
hName: config.tags.icon,
hProperties: {
className: config.classNames.icon,
},
u(config.tags.icon, {
data: {
hName: config.tags.icon,
hProperties: {
className: config.classNames.icon,
},
hChildren: iconChildren, // Place HAST nodes in data.hChildren
},
iconChildren
),
}),
u('text', title),
]
),
Expand Down
35 changes: 33 additions & 2 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,7 +570,7 @@ describe('remarkGitHubAlerts', () => {
expect(result).toContain('viewBox="0 0 16 16"')
expect(result).toContain('data-alert-type="tip"')
expect(result).toContain(
'<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.5-10a.5.5 0 0 0-1 0v4a.5.5 0 0 0 1 0V6zm0 6a.5.5 0 0 0-1 0v1a.5.5 0 0 0 1 0v-1z"/>'
'<path d="M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16zm.5-10a.5.5 0 0 0-1 0v4a.5.5 0 0 0 1 0V6zm0 6a.5.5 0 0 0-1 0v1a.5.5 0 0 0 1 0v-1z"'
)
expect(result).toContain('</svg>')
})
Expand Down Expand Up @@ -1220,7 +1220,8 @@ More regular text.
// Verify special characters are properly handled
expect(result).toContain('<svg>')
expect(result).toContain('<text>')
expect(result).toContain('Test &amp; "quotes" &lt;tags&gt;')
// HTML entities can be rendered as named or numeric entities
expect(result).toMatch(/Test (?:&amp;|&#x26;) "quotes" (?:&lt;|&#x3C;)tags(?:&gt;|>)/)
expect(result).toContain('</text>')
expect(result).toContain('</svg>')
expect(result).toContain('data-alert-type="warning"')
Expand Down Expand Up @@ -1407,5 +1408,35 @@ More regular text.
expect(result).not.toContain('<svg>')
}
})

it('should properly render SVG icons as DOM elements not text in component mode', async () => {
// This test specifically addresses the user's issue where SVG was being rendered as text
const userProvidedSvg =
'<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 16 16"><path d="M8 16A8 8 0 1 1 8 0a8 8 0 0 1 0 16zm.93-9.412-1 4.705c-.07.34.029.533.304.533.194 0 .487-.07.686-.246l-.088.416c-.287.346-.92.598-1.465.598-.703 0-1.002-.422-.808-1.319l.738-3.468c.064-.293.006-.399-.287-.47l-.451-.081.082-.381 2.29-.287zM8 5.5a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/></svg>'
const markdown = `> [!NOTE]
> User test case for SVG rendering.`

const result = await processMarkdownComponent(markdown, {
alerts: {
note: {
iconElementHtml: userProvidedSvg,
},
},
})

// Verify the SVG is properly included as DOM elements
expect(result).toContain('<svg class="w-4 h-4"')
expect(result).toContain('fill="currentColor"')
expect(result).toContain('viewBox="0 0 16 16"')
expect(result).toContain('<path d=')

// Ensure the SVG is not rendered as escaped text
expect(result).not.toContain('&lt;svg')
expect(result).not.toContain('&gt;')

// Verify it's inside the proper icon container
expect(result).toContain('<span class="markdown-alert-icon">')
expect(result).toContain('</svg></span>')
})
})
})