Skip to content

Commit fdd8369

Browse files
committed
Improve slugger for headers containing HTML
Resolves #3023
1 parent efd06b1 commit fdd8369

29 files changed

+376
-48
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ yarn-error.log
1717
/coverage/
1818
/dist/
1919
/docs
20-
/docs-site
2120
/docs2
21+
/docs-*
2222
/td*.json
2323

2424
typedoc*.tgz

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ title: Changelog
1313
## Bug Fixes
1414

1515
- Fixed conversion of auto-accessor types on properties with the `accessor` keyword, #3019.
16+
- Improved handling of HTML tags within headers for anchor generation, #3023.
1617

1718
## v0.28.13 (2025-09-14)
1819

scripts/rebuild_specs.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ const conversions = [
8181
*/
8282
function rebuildConverterTests(dirs) {
8383
const program = ts.createProgram(app.options.getFileNames(), {
84-
...app.options.getCompilerOptions(),
84+
...app.options.getCompilerOptions(app.logger),
8585
noEmit: true,
8686
});
8787

src/lib/output/themes/MarkedPlugin.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,10 +409,19 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
409409
}
410410

411411
function getTokenTextContent(token: md.Token): string {
412+
// If there are children, we want their text content, not the full text content
412413
if (token.children) {
413414
return token.children.map(getTokenTextContent).join("");
414415
}
415-
return token.content;
416+
417+
// If this is a simple text fragment, use its content
418+
if (token.type === "text") {
419+
return token.content;
420+
}
421+
422+
// Otherwise this is some type of metadata token (e.g. header_open)
423+
// or a HTML tag token. Don't include it in the text content.
424+
return "";
416425
}
417426

418427
const kindNames = ["note", "tip", "important", "warning", "caution"];

src/lib/output/themes/default/Slugger.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getSimilarValues } from "#utils";
2-
import type { TypeDocOptionMap } from "../../../utils/index.js";
2+
import type { TypeDocOptionMap } from "#node-utils";
33

44
/**
55
* Responsible for getting a unique anchor for elements within a page.
@@ -8,20 +8,28 @@ export class Slugger {
88
private seen = new Map<string, number>();
99

1010
private serialize(value: string) {
11-
// Notes:
12-
// There are quite a few trade-offs here.
11+
// There are quite a few trade-offs here. We used to remove HTML tags here,
12+
// but TypeDoc now removes the HTML tags before passing text into the slug
13+
// method, which allows us to skip doing that here. This improves the slugger
14+
// generation for headers which look like the following:
15+
// (html allowed in markdown)
16+
// # test &lt;t&gt;
17+
// (html disallowed in markdown)
18+
// # test <t>
19+
// both of the above should slug to test-t
1320

1421
return (
1522
value
1623
.trim()
17-
// remove html tags
18-
.replace(/<[!/a-z].*?>/gi, "")
1924
// remove unwanted chars
2025
.replace(
2126
/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,
2227
"",
2328
)
29+
// change whitespace to dash
2430
.replace(/\s/g, "-")
31+
// combine adjacent dashes
32+
.replace(/--+/, "-")
2533
);
2634
}
2735

src/lib/utils-common/general.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export function assertNever(x: never): never {
4545

4646
export function assert(x: unknown, message = "Assertion failed"): asserts x {
4747
if (!x) {
48+
debugger;
4849
throw new Error(message);
4950
}
5051
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# gh3023 &lt;test&gt;
2+
3+
Anchor for above heading should be `gh3023-test`

src/test/output/Slugger.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,15 @@ describe("Slugger", () => {
1313
equal(slugger.slug("model"), "model");
1414
equal(slugger.slug("Model"), "Model-1");
1515
});
16+
17+
it("Handles embedded html characters", () => {
18+
const slugger = new Slugger({ lowercase: true });
19+
equal(slugger.slug("test <T>"), "test-t");
20+
equal(slugger.slug("test <T>"), "test-t-1");
21+
});
22+
23+
it("Handles adjacent whitespace", () => {
24+
const slugger = new Slugger({ lowercase: true });
25+
equal(slugger.slug("test test2"), "test-test2");
26+
});
1627
});

src/test/renderer/specs/classes/BaseClass.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,9 @@
401401
{
402402
"div.tsd-description": [
403403
{
404-
"div.tsd-comment.tsd-typography": "<p>Base class method</p>\n"
404+
"div.tsd-comment.tsd-typography": {
405+
"p": "Base class method"
406+
}
405407
},
406408
{
407409
"div.tsd-parameters": [

src/test/renderer/specs/classes/GenericClass.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
},
2828
{
2929
"section.tsd-panel.tsd-comment": {
30-
"div.tsd-comment.tsd-typography": "<p>Generic class</p>\n"
30+
"div.tsd-comment.tsd-typography": {
31+
"p": "Generic class"
32+
}
3133
}
3234
},
3335
{

0 commit comments

Comments
 (0)