diff --git a/server/utils/docs/render.ts b/server/utils/docs/render.ts index eb3393b173..133d7d7676 100644 --- a/server/utils/docs/render.ts +++ b/server/utils/docs/render.ts @@ -191,9 +191,9 @@ async function renderJsDocTags(tags: JsDocTag[], symbolLookup: SymbolLookup): Pr : null const examplePromises = examples.map(async example => { if (!example.doc) return '' - const langMatch = example.doc.match(/```(\w+)?/) + const langMatch = example.doc.match(/```([\w-]+)?/) const lang = langMatch?.[1] || 'typescript' - const code = example.doc.replace(/```\w*\n?/g, '').trim() + const code = example.doc.replace(/```[\w-]*\n?/g, '').trim() return highlightCodeBlock(code, lang) }) diff --git a/server/utils/docs/text.ts b/server/utils/docs/text.ts index 706fbede38..f62a7773b7 100644 --- a/server/utils/docs/text.ts +++ b/server/utils/docs/text.ts @@ -106,7 +106,7 @@ export async function renderMarkdown(text: string, symbolLookup: SymbolLookup): // - \r\n, \n, or \r line endings const codeBlockData: Array<{ lang: string; code: string }> = [] let result = text.replace( - /```[ \t]*(\w*)[ \t]*(?:\r\n|\r|\n)([\s\S]*?)(?:\r\n|\r|\n)?```/g, + /```[ \t]*([\w-]*)[ \t]*(?:\r\n|\r|\n)([\s\S]*?)(?:\r\n|\r|\n)?```/g, (_, lang, code) => { const index = codeBlockData.length codeBlockData.push({ lang: lang || 'text', code: code.trim() }) diff --git a/test/unit/server/utils/docs/text.spec.ts b/test/unit/server/utils/docs/text.spec.ts index 5353b233df..4cb05b0a39 100644 --- a/test/unit/server/utils/docs/text.spec.ts +++ b/test/unit/server/utils/docs/text.spec.ts @@ -293,6 +293,17 @@ describe('renderMarkdown', () => { expect(result).not.toContain('```') }) + it('should handle fenced code blocks with hyphenated language (e.g. glimmer-ts)', async () => { + // Regression test for #2437: regex used \w which dropped the `-ts` suffix, + // leaving it at the start of the code body and breaking highlighting. + const input = '```glimmer-ts\nconst x = 1;\n```' + const result = await renderMarkdown(input, emptyLookup) + // Code must not leak the truncated language suffix into the body + expect(result).not.toContain('-ts') + expect(result).toContain('const') + expect(result).not.toContain('```') + }) + it('should escape HTML inside fenced code blocks', async () => { const input = '```ts\nconst arr: Array = [];\n```' const result = await renderMarkdown(input, emptyLookup)