Skip to content

Commit af01957

Browse files
committed
fix: protect backtick spans from HTML comment stripping and add regression tests
1 parent 06823e5 commit af01957

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

app/composables/useMarkdown.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ function stripAndEscapeHtml(text: string, packageName?: string): string {
4141
)
4242

4343
// Strip HTML comments: <!-- ... --> (including unclosed comments from truncation)
44-
stripped = stripped.replace(/<!--[\s\S]*?(-->|$)/g, '')
44+
// Same alternation trick: preserve backtick spans so comments inside code are kept
45+
stripped = stripped.replace(
46+
/(`[^`]*`)|<!--[\s\S]*?(-->|$)/g,
47+
(match, codeSpan: string | undefined) => codeSpan ?? '',
48+
)
4549

4650
if (packageName) {
4751
// Trim first to handle leading/trailing whitespace from stripped HTML

test/nuxt/composables/use-markdown.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,50 @@ describe('useMarkdown', () => {
350350
expect(processed.value).toBe('A library ')
351351
})
352352
})
353+
354+
describe('HTML tags inside backtick spans (regression #1478)', () => {
355+
it('preserves HTML tags inside backtick code spans', () => {
356+
const processed = useMarkdown({ text: 'Use `<div>` for layout' })
357+
expect(processed.value).toBe('Use <code>&lt;div&gt;</code> for layout')
358+
})
359+
360+
it('preserves multiple HTML tags inside one backtick span', () => {
361+
const processed = useMarkdown({ text: 'Use `<div><span>test</span></div>` element' })
362+
expect(processed.value).toBe(
363+
'Use <code>&lt;div&gt;&lt;span&gt;test&lt;/span&gt;&lt;/div&gt;</code> element',
364+
)
365+
})
366+
367+
it('preserves backtick spans while stripping bare HTML tags', () => {
368+
const processed = useMarkdown({ text: '`<a>` some <b>bold</b> text `<c>`' })
369+
expect(processed.value).toBe('<code>&lt;a&gt;</code> some bold text <code>&lt;c&gt;</code>')
370+
})
371+
372+
it('strips HTML tags outside backticks but keeps backtick content', () => {
373+
const processed = useMarkdown({ text: '<b>hello</b> and `<input type="text">` world' })
374+
expect(processed.value).toBe(
375+
'hello and <code>&lt;input type=&quot;text&quot;&gt;</code> world',
376+
)
377+
})
378+
379+
it('handles backtick span with self-closing tag', () => {
380+
const processed = useMarkdown({ text: 'Use `<br/>` for line breaks' })
381+
expect(processed.value).toBe('Use <code>&lt;br/&gt;</code> for line breaks')
382+
})
383+
384+
it('handles backtick spans without HTML inside', () => {
385+
const processed = useMarkdown({ text: '`code` and <b>stripped</b>' })
386+
expect(processed.value).toBe('<code>code</code> and stripped')
387+
})
388+
389+
it('preserves HTML comments inside backtick spans', () => {
390+
const processed = useMarkdown({ text: 'Use `<!-- comment -->` syntax' })
391+
expect(processed.value).toBe('Use <code>&lt;!-- comment --&gt;</code> syntax')
392+
})
393+
394+
it('strips HTML comments outside backtick spans', () => {
395+
const processed = useMarkdown({ text: '`<div>` <!-- badge --> is an element' })
396+
expect(processed.value).toBe('<code>&lt;div&gt;</code> is an element')
397+
})
398+
})
353399
})

0 commit comments

Comments
 (0)