Skip to content

Commit 33e4eb9

Browse files
committed
Fix externalSymbolLinkMappings with inherited methods
Fixes #3014
1 parent 3b84d2d commit 33e4eb9

File tree

17 files changed

+432
-38
lines changed

17 files changed

+432
-38
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ yarn-error.log
1313
/src/test/renderer/testProject/json.json
1414
**/node_modules/
1515
!src/test/converter2/behavior/node_modules/
16+
!src/test/converter2/renderer/node_modules/
1617
/coverage/
1718
/dist/
1819
/docs

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ title: Changelog
1414

1515
- Fixed bug introduced in 0.28.8 where TypeDoc could not render docs with some mixin classes, #3007.
1616
- `@inheritDoc` will now correctly overwrite `@remarks` and `@returns` blocks on the target comment, #3012.
17+
- The `externalSymbolLinkMappings` option now works properly on links pointing to inherited/overwritten signatures, #3014.
1718

1819
## v0.28.12 (2025-09-01)
1920

src/lib/converter/plugins/ImplementsPlugin.ts

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { ReferenceType, ReflectionType, type SomeType, type Type } from "../../m
1313
import { filterMap, type TranslatedString, zip } from "#utils";
1414
import { ConverterComponent } from "../components.js";
1515
import type { Context } from "../context.js";
16-
import { getHumanName } from "../../utils/index.js";
16+
import { findPackageForPath, getHumanName } from "../../utils/index.js";
1717
import { ConverterEvents } from "../converter-events.js";
1818
import type { Converter } from "../converter.js";
1919

@@ -182,18 +182,34 @@ export class ImplementsPlugin extends ConverterComponent {
182182

183183
for (const child of reflection.children || []) {
184184
if (child.inheritedFrom && !isValidRef(child.inheritedFrom)) {
185-
child.inheritedFrom = ReferenceType.createBrokenReference(child.inheritedFrom.name, project);
185+
child.inheritedFrom = ReferenceType.createBrokenReference(
186+
child.inheritedFrom.name,
187+
project,
188+
child.inheritedFrom.package,
189+
);
186190
}
187191
if (child.overwrites && !isValidRef(child.overwrites)) {
188-
child.overwrites = ReferenceType.createBrokenReference(child.overwrites.name, project);
192+
child.overwrites = ReferenceType.createBrokenReference(
193+
child.overwrites.name,
194+
project,
195+
child.overwrites.package,
196+
);
189197
}
190198

191199
for (const childSig of child.getAllSignatures()) {
192200
if (childSig.inheritedFrom && !isValidRef(childSig.inheritedFrom)) {
193-
childSig.inheritedFrom = ReferenceType.createBrokenReference(childSig.inheritedFrom.name, project);
201+
childSig.inheritedFrom = ReferenceType.createBrokenReference(
202+
childSig.inheritedFrom.name,
203+
project,
204+
childSig.inheritedFrom.package,
205+
);
194206
}
195207
if (childSig.overwrites && !isValidRef(childSig.overwrites)) {
196-
childSig.overwrites = ReferenceType.createBrokenReference(childSig.overwrites.name, project);
208+
childSig.overwrites = ReferenceType.createBrokenReference(
209+
childSig.overwrites.name,
210+
project,
211+
childSig.overwrites.package,
212+
);
197213
}
198214
}
199215
}
@@ -522,6 +538,18 @@ export class ImplementsPlugin extends ConverterComponent {
522538
}
523539
}
524540

541+
function getConstructorPackagePath(context: Context, clause: ts.ExpressionWithTypeArguments): string | undefined {
542+
const symbol = context.getSymbolAtLocation(clause.expression);
543+
if (!symbol) return undefined;
544+
545+
const resolvedSymbol = context.resolveAliasedSymbol(symbol);
546+
547+
const symbolPath = resolvedSymbol?.declarations?.[0]?.getSourceFile().fileName;
548+
if (!symbolPath) return undefined;
549+
550+
return findPackageForPath(symbolPath)?.[0];
551+
}
552+
525553
function constructorInheritance(
526554
context: Context,
527555
reflection: DeclarationReflection,
@@ -533,17 +561,21 @@ function constructorInheritance(
533561
);
534562

535563
if (!extendsClause) return;
536-
const name = `${extendsClause.types[0].getText()}.constructor`;
564+
const extendsType = extendsClause.types[0];
565+
const refPackage = getConstructorPackagePath(context, extendsType);
566+
567+
const name = `${extendsType.getText()}.constructor`;
537568

538569
const key = constructorDecl ? "overwrites" : "inheritedFrom";
539570

540571
reflection[key] ??= ReferenceType.createBrokenReference(
541572
name,
542573
context.project,
574+
refPackage,
543575
);
544576

545577
for (const sig of reflection.signatures ?? []) {
546-
sig[key] ??= ReferenceType.createBrokenReference(name, context.project);
578+
sig[key] ??= ReferenceType.createBrokenReference(name, context.project, refPackage);
547579
}
548580
}
549581

@@ -576,7 +608,7 @@ function createLink(
576608
const rootSymbols = context.checker.getRootSymbols(symbol);
577609
const ref = rootSymbols.length && rootSymbols[0] != symbol
578610
? context.createSymbolReference(rootSymbols[0], context, name)
579-
: ReferenceType.createBrokenReference(name, context.project);
611+
: ReferenceType.createBrokenReference(name, context.project, undefined);
580612

581613
link(reflection);
582614
link(reflection.getSignature);

src/lib/converter/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ const queryConverter: TypeConverter<ts.TypeQueryNode> = {
705705
ReferenceType.createBrokenReference(
706706
node.exprName.getText(),
707707
context.project,
708+
undefined,
708709
),
709710
);
710711
}
@@ -786,6 +787,7 @@ const referenceConverter: TypeConverter<
786787
const ref = ReferenceType.createBrokenReference(
787788
context.checker.typeToString(type),
788789
context.project,
790+
undefined,
789791
);
790792
ref.refersToTypeParameter = true;
791793
return ref;

src/lib/models/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -960,8 +960,10 @@ export class ReferenceType extends Type {
960960
* later during conversion.
961961
* @internal
962962
*/
963-
static createBrokenReference(name: string, project: ProjectReflection) {
964-
return new ReferenceType(name, -1 as ReflectionId, project, name);
963+
static createBrokenReference(name: string, project: ProjectReflection, packageName: string | undefined) {
964+
const ref = new ReferenceType(name, -1 as ReflectionId, project, name);
965+
ref.package = packageName;
966+
return ref;
965967
}
966968

967969
protected override getTypeString() {

src/lib/output/themes/default/partials/typeAndParent.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { DefaultThemeRenderContext } from "../DefaultThemeRenderContext.js";
2-
import { ArrayType, ReferenceType, SignatureReflection, type Type } from "../../../../models/index.js";
2+
import { ArrayType, ReferenceType, SignatureReflection, type Type } from "#models";
33
import { JSX } from "#utils";
44

55
export const typeAndParent = (context: DefaultThemeRenderContext, props: Type): JSX.Element => {
@@ -12,15 +12,23 @@ export const typeAndParent = (context: DefaultThemeRenderContext, props: Type):
1212
);
1313
}
1414

15-
if (props instanceof ReferenceType && props.reflection) {
16-
const refl = props.reflection instanceof SignatureReflection ? props.reflection.parent : props.reflection;
17-
const parent = refl.parent!;
15+
if (props instanceof ReferenceType) {
16+
if (props.reflection) {
17+
const refl = props.reflection instanceof SignatureReflection ? props.reflection.parent : props.reflection;
18+
const parent = refl.parent!;
1819

19-
return (
20-
<>
21-
{<a href={context.urlTo(parent)}>{parent.name}</a>}.{<a href={context.urlTo(refl)}>{refl.name}</a>}
22-
</>
23-
);
20+
return (
21+
<>
22+
{<a href={context.urlTo(parent)}>{parent.name}</a>}.{<a href={context.urlTo(refl)}>{refl.name}</a>}
23+
</>
24+
);
25+
} else if (props.externalUrl) {
26+
if (props.externalUrl === "#") {
27+
return <>{props.toString()}</>;
28+
} else {
29+
return <a href={props.externalUrl} class="external" target="_blank">{props.name}</a>;
30+
}
31+
}
2432
}
2533

2634
return <>{props.toString()}</>;

src/test/converter/inheritance/specs.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -671,14 +671,16 @@
671671
"inheritedFrom": {
672672
"type": "reference",
673673
"target": -1,
674-
"name": "My.constructor"
674+
"name": "My.constructor",
675+
"package": "typedoc"
675676
}
676677
}
677678
],
678679
"inheritedFrom": {
679680
"type": "reference",
680681
"target": -1,
681-
"name": "My.constructor"
682+
"name": "My.constructor",
683+
"package": "typedoc"
682684
}
683685
},
684686
{

src/test/converter/mixin/specs.json

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -426,14 +426,16 @@
426426
"inheritedFrom": {
427427
"type": "reference",
428428
"target": -1,
429-
"name": "Mixin2(Mixin1Func(Base)).method1"
429+
"name": "Mixin2(Mixin1Func(Base)).method1",
430+
"package": "typedoc"
430431
}
431432
}
432433
],
433434
"inheritedFrom": {
434435
"type": "reference",
435436
"target": -1,
436-
"name": "Mixin2(Mixin1Func(Base)).method1"
437+
"name": "Mixin2(Mixin1Func(Base)).method1",
438+
"package": "typedoc"
437439
}
438440
},
439441
{
@@ -493,14 +495,16 @@
493495
"inheritedFrom": {
494496
"type": "reference",
495497
"target": -1,
496-
"name": "Mixin2(Mixin1Func(Base)).method2"
498+
"name": "Mixin2(Mixin1Func(Base)).method2",
499+
"package": "typedoc"
497500
}
498501
}
499502
],
500503
"inheritedFrom": {
501504
"type": "reference",
502505
"target": -1,
503-
"name": "Mixin2(Mixin1Func(Base)).method2"
506+
"name": "Mixin2(Mixin1Func(Base)).method2",
507+
"package": "typedoc"
504508
}
505509
}
506510
],
@@ -875,14 +879,16 @@
875879
"inheritedFrom": {
876880
"type": "reference",
877881
"target": -1,
878-
"name": "Mixin.method1"
882+
"name": "Mixin.method1",
883+
"package": "typedoc"
879884
}
880885
}
881886
],
882887
"inheritedFrom": {
883888
"type": "reference",
884889
"target": -1,
885-
"name": "Mixin.method1"
890+
"name": "Mixin.method1",
891+
"package": "typedoc"
886892
}
887893
}
888894
],
@@ -1139,14 +1145,16 @@
11391145
"inheritedFrom": {
11401146
"type": "reference",
11411147
"target": -1,
1142-
"name": "Mixin.method1"
1148+
"name": "Mixin.method1",
1149+
"package": "typedoc"
11431150
}
11441151
}
11451152
],
11461153
"inheritedFrom": {
11471154
"type": "reference",
11481155
"target": -1,
1149-
"name": "Mixin.method1"
1156+
"name": "Mixin.method1",
1157+
"package": "typedoc"
11501158
}
11511159
},
11521160
{
@@ -1206,14 +1214,16 @@
12061214
"inheritedFrom": {
12071215
"type": "reference",
12081216
"target": -1,
1209-
"name": "Mixin.method2"
1217+
"name": "Mixin.method2",
1218+
"package": "typedoc"
12101219
}
12111220
}
12121221
],
12131222
"inheritedFrom": {
12141223
"type": "reference",
12151224
"target": -1,
1216-
"name": "Mixin.method2"
1225+
"name": "Mixin.method2",
1226+
"package": "typedoc"
12171227
}
12181228
}
12191229
],
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { GH3014Base } from "@typedoc/gh3014";
2+
3+
export class GH3014 extends GH3014Base {
4+
constructor() {
5+
super();
6+
}
7+
}

src/test/converter2/renderer/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,5 @@ export * as ExpandType from "./expandType";
139139
export * as GH2982 from "./gh2982";
140140
export { gh2995 } from "./gh2995";
141141
export * as GH3007 from "./gh3007";
142+
export { GH3014 } from "./gh3014";
142143
export { box as boxAlias };

0 commit comments

Comments
 (0)