Skip to content

Commit 020ebee

Browse files
More control delegated for external link resolvers (#2066)
* more control delegated for external resolve * new exported types added to index * code formatting * ExternalResolveResult.caption was made optional and its value now used as default. * addUnknownSymbolResolver signature was updated + naming update. * Redundant ExternalResolveAttempt type was removed * Prettier * Prettier with newer version * Revert bad test changes * Always provide reflection to link resolver Also swap argument order so that the possibly undefined parameter is last. * Fix lint --------- Co-authored-by: Gerrit Birkeland <[email protected]>
1 parent c565bc6 commit 020ebee

File tree

5 files changed

+70
-28
lines changed

5 files changed

+70
-28
lines changed

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export {
1313
type ComponentPath,
1414
type Meaning,
1515
type MeaningKeyword,
16+
type ExternalResolveResult,
1617
} from "./lib/converter";
1718

1819
export {

src/lib/converter/comments/linkResolver.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,19 @@ import { resolveDeclarationReference } from "./declarationReferenceResolver";
1616
const urlPrefix = /^(http|ftp)s?:\/\//;
1717
const brackets = /\[\[(?!include:)([^\]]+)\]\]/g;
1818

19+
export type ExternalResolveResult = { target: string; caption?: string };
20+
export type ExternalSymbolResolver = (
21+
ref: DeclarationReference,
22+
refl: Reflection,
23+
part: Readonly<CommentDisplayPart> | undefined
24+
) => ExternalResolveResult | string | undefined;
25+
1926
export function resolveLinks(
2027
comment: Comment,
2128
reflection: Reflection,
2229
validation: ValidationOptions,
2330
logger: Logger,
24-
attemptExternalResolve: (ref: DeclarationReference) => string | undefined
31+
externalResolver: ExternalSymbolResolver
2532
) {
2633
let warned = false;
2734
const warn = () => {
@@ -39,7 +46,7 @@ export function resolveLinks(
3946
warn,
4047
validation,
4148
logger,
42-
attemptExternalResolve
49+
externalResolver
4350
);
4451
for (const tag of comment.blockTags) {
4552
tag.content = resolvePartLinks(
@@ -48,7 +55,7 @@ export function resolveLinks(
4855
warn,
4956
validation,
5057
logger,
51-
attemptExternalResolve
58+
externalResolver
5259
);
5360
}
5461

@@ -59,7 +66,7 @@ export function resolveLinks(
5966
warn,
6067
validation,
6168
logger,
62-
attemptExternalResolve
69+
externalResolver
6370
);
6471
}
6572
}
@@ -70,7 +77,7 @@ export function resolvePartLinks(
7077
warn: () => void,
7178
validation: ValidationOptions,
7279
logger: Logger,
73-
attemptExternalResolve: (ref: DeclarationReference) => string | undefined
80+
externalResolver: ExternalSymbolResolver
7481
): CommentDisplayPart[] {
7582
return parts.flatMap((part) =>
7683
processPart(
@@ -79,7 +86,7 @@ export function resolvePartLinks(
7986
warn,
8087
validation,
8188
logger,
82-
attemptExternalResolve
89+
externalResolver
8390
)
8491
);
8592
}
@@ -90,7 +97,7 @@ function processPart(
9097
warn: () => void,
9198
validation: ValidationOptions,
9299
logger: Logger,
93-
attemptExternalResolve: (ref: DeclarationReference) => string | undefined
100+
externalResolver: ExternalSymbolResolver
94101
): CommentDisplayPart | CommentDisplayPart[] {
95102
if (part.kind === "text" && brackets.test(part.text)) {
96103
warn();
@@ -106,7 +113,7 @@ function processPart(
106113
return resolveLinkTag(
107114
reflection,
108115
part,
109-
attemptExternalResolve,
116+
externalResolver,
110117
(msg: string) => {
111118
if (validation.invalidLink) {
112119
logger.warn(msg);
@@ -122,7 +129,7 @@ function processPart(
122129
function resolveLinkTag(
123130
reflection: Reflection,
124131
part: InlineTagDisplayPart,
125-
attemptExternalResolve: (ref: DeclarationReference) => string | undefined,
132+
externalResolver: ExternalSymbolResolver,
126133
warn: (message: string) => void
127134
) {
128135
let pos = 0;
@@ -146,9 +153,22 @@ function resolveLinkTag(
146153
defaultDisplayText = target.name;
147154
} else {
148155
// If we didn't find a link, it might be a @link tag to an external symbol, check that next.
149-
target = attemptExternalResolve(declRef[0]);
150-
if (target) {
151-
defaultDisplayText = part.text.substring(0, pos);
156+
const externalResolveResult = externalResolver(
157+
declRef[0],
158+
reflection,
159+
part
160+
);
161+
162+
defaultDisplayText = part.text.substring(0, pos);
163+
164+
switch (typeof externalResolveResult) {
165+
case "string":
166+
target = externalResolveResult;
167+
break;
168+
case "object":
169+
target = externalResolveResult.target;
170+
defaultDisplayText =
171+
externalResolveResult.caption || defaultDisplayText;
152172
}
153173
}
154174
}

src/lib/converter/converter.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ import type {
2727
} from "../utils/options/declaration";
2828
import { parseComment } from "./comments/parser";
2929
import { lexCommentString } from "./comments/rawLexer";
30-
import { resolvePartLinks, resolveLinks } from "./comments/linkResolver";
30+
import {
31+
resolvePartLinks,
32+
resolveLinks,
33+
ExternalSymbolResolver,
34+
ExternalResolveResult,
35+
} from "./comments/linkResolver";
3136
import type { DeclarationReference } from "./comments/declarationReference";
3237

3338
/**
@@ -77,9 +82,7 @@ export class Converter extends ChildableComponent<
7782
externalSymbolLinkMappings!: Record<string, Record<string, string>>;
7883

7984
private _config?: CommentParserConfig;
80-
private _externalSymbolResolvers: Array<
81-
(ref: DeclarationReference) => string | undefined
82-
> = [];
85+
private _externalSymbolResolvers: Array<ExternalSymbolResolver> = [];
8386

8487
get config(): CommentParserConfig {
8588
return this._config || this._buildCommentParserConfig();
@@ -268,19 +271,22 @@ export class Converter extends ChildableComponent<
268271
*
269272
* Note: This will be used for both references to types declared in node_modules (in which case the
270273
* reference passed will have the `moduleSource` set and the `symbolReference` will navigate via `.`)
271-
* and user defined \{\@link\} tags which cannot be resolved.
274+
* and user defined \{\@link\} tags which cannot be resolved. If the link being resolved is inferred
275+
* from a type, then no `part` will be passed to the resolver function.
272276
* @since 0.22.14
273277
*/
274-
addUnknownSymbolResolver(
275-
resolver: (ref: DeclarationReference) => string | undefined
276-
): void {
278+
addUnknownSymbolResolver(resolver: ExternalSymbolResolver): void {
277279
this._externalSymbolResolvers.push(resolver);
278280
}
279281

280282
/** @internal */
281-
resolveExternalLink(ref: DeclarationReference): string | undefined {
283+
resolveExternalLink(
284+
ref: DeclarationReference,
285+
refl: Reflection,
286+
part?: CommentDisplayPart
287+
): ExternalResolveResult | string | undefined {
282288
for (const resolver of this._externalSymbolResolvers) {
283-
const resolved = resolver(ref);
289+
const resolved = resolver(ref, refl, part);
284290
if (resolved) return resolved;
285291
}
286292
}
@@ -300,7 +306,7 @@ export class Converter extends ChildableComponent<
300306
owner,
301307
this.validation,
302308
this.owner.logger,
303-
(ref) => this.resolveExternalLink(ref)
309+
(ref, part, refl) => this.resolveExternalLink(ref, part, refl)
304310
);
305311
} else {
306312
let warned = false;
@@ -319,7 +325,7 @@ export class Converter extends ChildableComponent<
319325
warn,
320326
this.validation,
321327
this.owner.logger,
322-
(ref) => this.resolveExternalLink(ref)
328+
(ref, part, refl) => this.resolveExternalLink(ref, part, refl)
323329
);
324330
}
325331
}

src/lib/converter/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,9 @@ export type {
99
Meaning,
1010
MeaningKeyword,
1111
} from "./comments/declarationReference";
12+
export type {
13+
ExternalSymbolResolver,
14+
ExternalResolveResult,
15+
} from "./comments/linkResolver";
1216

1317
import "./plugins/index";

src/lib/converter/plugins/LinkResolverPlugin.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Component, ConverterComponent } from "../components";
2-
import type { Context } from "../../converter";
2+
import type { Context, ExternalResolveResult } from "../../converter";
33
import { ConverterEvents } from "../converter-events";
44
import { BindOption, ValidationOptions } from "../../utils";
55
import { DeclarationReflection } from "../../models";
@@ -45,14 +45,25 @@ export class LinkResolverPlugin extends ConverterComponent {
4545
);
4646
}
4747

48-
for (const { type } of discoverAllReferenceTypes(
48+
for (const { type, owner } of discoverAllReferenceTypes(
4949
context.project,
5050
false
5151
)) {
5252
if (!type.reflection) {
53-
type.externalUrl = context.converter.resolveExternalLink(
54-
type.toDeclarationReference()
53+
const resolveResult = context.converter.resolveExternalLink(
54+
type.toDeclarationReference(),
55+
owner
5556
);
57+
switch (typeof resolveResult) {
58+
case "string":
59+
type.externalUrl = resolveResult as string;
60+
break;
61+
case "object":
62+
type.externalUrl = (
63+
resolveResult as ExternalResolveResult
64+
).target;
65+
break;
66+
}
5667
}
5768
}
5869
}

0 commit comments

Comments
 (0)