Skip to content

Commit 28c28f2

Browse files
committed
Resolving broken anchors in API when nested reflections (e.g. agRegistry#TagGroup#getAllWhere) appears
1 parent 075befa commit 28c28f2

4 files changed

Lines changed: 79 additions & 55 deletions

File tree

api/docusaurus-plugin/src/components/Index.tsx

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,26 @@ import type { TSDDeclarationReflection } from '../types';
55
import { AnchorLink } from './AnchorLink';
66
import { Icon } from './Icon';
77
import { Name } from './Name';
8+
import { resolveAnchorLink } from '../utils/links';
89

910
export interface IndexChildProps {
1011
id: number;
1112
}
1213

1314
function IndexChild({ id }: IndexChildProps) {
14-
const reflection = useRequiredReflection(id);
15+
const reflection = useRequiredReflection(id);
1516

16-
return (
17-
<li>
18-
<Link className="tsd-kind-icon" to={reflection.permalink ?? `#${reflection.name}`}>
19-
<Icon reflection={reflection} />
20-
<Name reflection={reflection} colorful={true} />
21-
</Link>
22-
</li>
23-
);
17+
return (
18+
<li>
19+
<Link
20+
className="tsd-kind-icon"
21+
to={reflection.permalink ? resolveAnchorLink(reflection.permalink) : resolveAnchorLink('', reflection.name)}
22+
>
23+
<Icon reflection={reflection} />
24+
<Name reflection={reflection} colorful={true} />
25+
</Link>
26+
</li>
27+
);
2428
}
2529

2630
export interface IndexProps {

api/docusaurus-plugin/src/plugin/data.ts

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {
1414
} from '../types';
1515
import { migrateToVersion0230 } from './structure/0.23';
1616
import { getKindSlug, getPackageSlug, joinUrl } from './url';
17+
import { resolveAnchorLink } from '../utils/links';
1718

1819
function shouldEmit(projectRoot: string, tsconfigPath: string) {
1920
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -176,42 +177,43 @@ export function loadPackageJsonAndDocs(
176177
}
177178

178179
export function addMetadataToReflections(
179-
project: JSONOutput.DeclarationReflection,
180-
packageSlug: string,
181-
urlPrefix: string,
180+
project: JSONOutput.DeclarationReflection,
181+
packageSlug: string,
182+
urlPrefix: string,
182183
): TSDDeclarationReflection {
183-
const permalink = `/${joinUrl(urlPrefix, packageSlug)}`;
184-
185-
if (project.children) {
186-
// eslint-disable-next-line no-param-reassign
187-
project.children = project.children.map((child) => {
188-
migrateToVersion0230(child);
189-
190-
const kindSlugPart = getKindSlug(child);
191-
const childSlug = kindSlugPart ? `/${kindSlugPart}/${child.name}` : `#${child.name}`;
192-
const childPermalink = permalink + childSlug;
193-
194-
// We need to go another level deeper and only use fragments
195-
if (child.kind === ReflectionKind.Namespace && child.children) {
196-
// eslint-disable-next-line no-param-reassign
197-
child.children = child.children.map((grandChild) => ({
198-
...grandChild,
199-
permalink: normalizeUrl([`${childPermalink}#${grandChild.name}`]),
200-
}));
201-
}
202-
203-
return {
204-
...child,
205-
permalink: normalizeUrl([childPermalink]),
206-
};
207-
});
208-
}
209-
210-
// @ts-expect-error Not sure why this fails
211-
return {
212-
...project,
213-
permalink: normalizeUrl([permalink]),
214-
};
184+
const permalink = `/${joinUrl(urlPrefix, packageSlug)}`;
185+
186+
if (project.children) {
187+
// eslint-disable-next-line no-param-reassign
188+
project.children = project.children.map((child) => {
189+
migrateToVersion0230(child);
190+
191+
const kindSlugPart = getKindSlug(child);
192+
const childPermalink = kindSlugPart
193+
? `${permalink}/${kindSlugPart}/${child.name}`
194+
: resolveAnchorLink(permalink, child.name);
195+
196+
// We need to go another level deeper and only use fragments
197+
if (child.kind === ReflectionKind.Namespace && child.children) {
198+
// eslint-disable-next-line no-param-reassign
199+
child.children = child.children.map((grandChild) => ({
200+
...grandChild,
201+
permalink: normalizeUrl([resolveAnchorLink(childPermalink, grandChild.name)]),
202+
}));
203+
}
204+
205+
return {
206+
...child,
207+
permalink: normalizeUrl([childPermalink]),
208+
};
209+
});
210+
}
211+
212+
// @ts-expect-error Not sure why this fails
213+
return {
214+
...project,
215+
permalink: normalizeUrl([permalink]),
216+
};
215217
}
216218

217219
function mergeReflections(base: TSDDeclarationReflection, next: TSDDeclarationReflection) {

api/docusaurus-plugin/src/utils/links.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,20 @@ export function removeScopes(text: string, scopes: string[]): string {
88
text,
99
);
1010
}
11+
12+
export function resolveAnchorLink(base: string, anchor?: string): string {
13+
let fullPath = base || '';
14+
if (anchor) {
15+
fullPath = fullPath ? `${fullPath}#${anchor}` : `#${anchor}`;
16+
}
17+
18+
const parts = fullPath.split('#');
19+
if (parts.length <= 2) {
20+
return fullPath;
21+
}
22+
23+
const urlPath = parts[0];
24+
const combinedAnchors = parts.slice(1).join('.');
25+
26+
return `${urlPath}#${combinedAnchors}`;
27+
}

api/docusaurus-plugin/src/utils/markdown.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { PropVersionMetadata } from '@docusaurus/plugin-content-docs';
22
import type { TSDDeclarationReflectionMap } from '../types';
3+
import { resolveAnchorLink } from './links';
34

45
function splitLinkText(text: string): { caption: string; target: string } {
56
let splitIndex = text.indexOf('|');
@@ -39,20 +40,20 @@ function findReflectionWithMatchingTarget(
3940
}
4041

4142
function replaceApiLinks(
42-
reflections: TSDDeclarationReflectionMap,
43+
reflections: TSDDeclarationReflectionMap,
4344
): (match: string, tagName: string, content: string) => string {
44-
return (match: string, tagName: string, content: string) => {
45-
const { caption, target } = splitLinkText(content);
46-
const [symbol, member] = target.split('.');
47-
const reflection = findReflectionWithMatchingTarget(reflections, symbol, member);
48-
const label = tagName === 'linkcode' ? `\`${caption}\`` : caption;
45+
return (match: string, tagName: string, content: string) => {
46+
const { caption, target } = splitLinkText(content);
47+
const [symbol, member] = target.split('.');
48+
const reflection = findReflectionWithMatchingTarget(reflections, symbol, member);
49+
const label = tagName === 'linkcode' ? `\`${caption}\`` : caption;
4950

50-
if (!reflection?.permalink) {
51-
return label;
52-
}
51+
if (!reflection?.permalink) {
52+
return label;
53+
}
5354

54-
return `[${label}](${reflection.permalink}${member ? `#${member}` : ''})`;
55-
};
55+
return `[${label}](${resolveAnchorLink(reflection.permalink, member)})`;
56+
};
5657
}
5758

5859
function replaceDocLinks(

0 commit comments

Comments
 (0)