Skip to content

Commit bb2cca3

Browse files
authored
fix(api-markdown-documenter): Escape child content of HTML headings (#25494)
Fixed an issue where HTML headings were being generated with unescaped child content. This caused headings for signatures with generic type parameters to be interpreted like HTML rather than as plain text. See updated test collateral of example issues.
1 parent 8d3818c commit bb2cca3

File tree

4 files changed

+40
-17
lines changed

4 files changed

+40
-17
lines changed

tools/api-markdown-documenter/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This aligns the behavior of this function with other section creation helpers.
77

88
### 🐞 Bug Fixes
99

10+
- Fixed an issue where HTML headings were being generated with unescaped child content.
11+
This caused headings for signatures with generic type parameters to be interpreted like HTML rather than as plain text.
1012
- Fixed an issue where HTML table cell content was generated with line breaks for formatting.
1113
This would break Markdown table syntax.
1214
Line breaks are now omitted in this context.

tools/api-markdown-documenter/src/mdast/Normalize.ts

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Licensed under the MIT License.
44
*/
55

6+
import type { Element } from "hast";
7+
import { toHtml } from "hast-util-to-html";
68
import type { BlockContent, Break, Html, Nodes, Root, RootContent, Strong } from "mdast";
79

810
import type { Section } from "./Section.js";
@@ -130,25 +132,44 @@ function transformAsHeading(
130132
// Markdown headings don't natively support anchor IDs.
131133
// If the heading has an ID set, we will render it as an HTML element.
132134
// While there are extended syntax options for Markdown that do support IDs, none of them are widely supported.
133-
return headingNode.id === undefined
134-
? [
135+
if (headingNode.id !== undefined) {
136+
// Create hast representation of HTML heading, then convert to string representation.
137+
// This ensures that contents are escaped properly.
138+
const htmlHeadingNode: Element = {
139+
type: "element",
140+
tagName: `h${headingLevel}`,
141+
properties: {
142+
id: headingNode.id,
143+
},
144+
children: [
135145
{
136-
type: "heading",
137-
depth: headingLevel,
138-
children: [
139-
{
140-
type: "text",
141-
value: headingNode.title,
142-
},
143-
],
146+
type: "text",
147+
value: headingNode.title,
144148
},
145-
]
146-
: [
149+
],
150+
};
151+
const htmlHeadingString = toHtml(htmlHeadingNode, {});
152+
153+
return [
154+
{
155+
type: "html",
156+
value: htmlHeadingString,
157+
},
158+
];
159+
}
160+
161+
return [
162+
{
163+
type: "heading",
164+
depth: headingLevel,
165+
children: [
147166
{
148-
type: "html",
149-
value: `<h${headingLevel} id="${headingNode.id}">${headingNode.title}</h${headingLevel}>`,
167+
type: "text",
168+
value: headingNode.title,
150169
},
151-
];
170+
],
171+
},
172+
];
152173
}
153174

154175
function transformAsBoldText(headingNode: SectionHeading): BlockContent[] {

tools/api-markdown-documenter/src/test/snapshots/markdown/simple-suite-test/default-config/test-suite-a/testinterfacewithcallsignature-interface.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface TestInterfaceWithCallSignature
1818

1919
## Call Signature Details
2020

21-
<h3 id="_call_-callsignature"><T extends string>(foo: T): number & T</h3>
21+
<h3 id="_call_-callsignature">&#x3C;T extends string>(foo: T): number &#x26; T</h3>
2222

2323
Test call signature.
2424

tools/api-markdown-documenter/src/test/snapshots/markdown/simple-suite-test/flat-config/test-suite-a.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ export interface TestInterfaceWithCallSignature
382382

383383
### Call Signature Details
384384

385-
<h4 id="testinterfacewithcallsignature-_call_-callsignature"><T extends string>(foo: T): number & T</h4>
385+
<h4 id="testinterfacewithcallsignature-_call_-callsignature">&#x3C;T extends string>(foo: T): number &#x26; T</h4>
386386

387387
Test call signature.
388388

0 commit comments

Comments
 (0)