Skip to content

Commit 44a1288

Browse files
kentcdoddsCopilot
andauthored
fix: properly extract titles with backticks in MDX headings (#350)
Co-authored-by: copilot-pull-request-reviewer[bot] <175728472+copilot-pull-request-reviewer[bot]@users.noreply.github.com>
1 parent 4572c3f commit 44a1288

2 files changed

Lines changed: 87 additions & 4 deletions

File tree

packages/workshop-utils/src/compile-mdx.server.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import path from 'path'
55
import { rehypeCodeBlocksShiki } from '@kentcdodds/md-temp'
66
import { type Element, type Root as HastRoot } from 'hast'
77
import lz from 'lz-string'
8-
import { type Root as MdastRoot } from 'mdast'
8+
import { type Root as MdastRoot, type PhrasingContent } from 'mdast'
99
import { type MdxJsxFlowElement } from 'mdast-util-mdx-jsx'
1010
import { bundleMDX } from 'mdx-bundler'
1111
import PQueue from 'p-queue'
@@ -231,9 +231,25 @@ async function compileMdxImpl(file: string): Promise<{
231231
visit(tree, 'heading', (node) => {
232232
if (title) return
233233
if (node.depth === 1) {
234-
visit(node, 'text', (textNode) => {
235-
title = textNode.value.trim()
236-
})
234+
// Extract plain text content, preserving inline code but stripping other formatting
235+
const extractText = (nodes: Array<PhrasingContent>): string => {
236+
return nodes.map((childNode: PhrasingContent) => {
237+
if (childNode.type === 'text') {
238+
return childNode.value
239+
} else if (childNode.type === 'inlineCode') {
240+
return `\`${childNode.value}\``
241+
} else if (childNode.type === 'strong' || childNode.type === 'emphasis') {
242+
// For formatting like bold/italic, just extract the text content
243+
return extractText(childNode.children || [])
244+
} else if ('children' in childNode && childNode.children) {
245+
// For other nodes with children, recursively extract text
246+
return extractText(childNode.children || [])
247+
}
248+
return ''
249+
}).join('')
250+
}
251+
252+
title = extractText(node.children || []).trim()
237253
}
238254
})
239255
title = title ? title.replace(/^\d+\. /, '').trim() : null
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import fs from 'fs'
2+
import os from 'os'
3+
import path from 'path'
4+
import { describe, it, expect } from 'vitest'
5+
import { compileMdx } from './compile-mdx.server.js'
6+
7+
// Disposable object for temporary files
8+
function createTempFile(name: string, content: string) {
9+
const tempDir = os.tmpdir()
10+
const testFile = path.join(tempDir, name)
11+
fs.writeFileSync(testFile, content)
12+
13+
return {
14+
path: testFile,
15+
[Symbol.dispose]() {
16+
try {
17+
fs.unlinkSync(testFile)
18+
} catch {
19+
// Ignore cleanup errors
20+
}
21+
}
22+
}
23+
}
24+
25+
describe('compileMdx title parsing', () => {
26+
it('should extract title with backticks correctly', async () => {
27+
// Create a temporary MDX file with backticks in title
28+
const testMdxContent = `# Title with \`something\` highlighted
29+
30+
This is some content.
31+
`
32+
33+
using tempFile = createTempFile('test-backtick-title.mdx', testMdxContent)
34+
35+
const result = await compileMdx(tempFile.path)
36+
37+
// The title should be extracted correctly, preserving the full text
38+
expect(result.title).toBe('Title with `something` highlighted')
39+
})
40+
41+
it('should extract title with multiple backticks correctly', async () => {
42+
const testMdxContent = `# \`Code\` and \`more code\` in title
43+
44+
This is some content.
45+
`
46+
47+
using tempFile = createTempFile('test-multiple-backticks.mdx', testMdxContent)
48+
49+
const result = await compileMdx(tempFile.path)
50+
51+
expect(result.title).toBe('`Code` and `more code` in title')
52+
})
53+
54+
it('should extract title with mixed markdown correctly', async () => {
55+
const testMdxContent = `# Title with \`code\` and **bold** text
56+
57+
This is some content.
58+
`
59+
60+
using tempFile = createTempFile('test-mixed-markdown.mdx', testMdxContent)
61+
62+
const result = await compileMdx(tempFile.path)
63+
64+
// Bold formatting should be stripped, but backticks preserved
65+
expect(result.title).toBe('Title with `code` and bold text')
66+
})
67+
})

0 commit comments

Comments
 (0)