Skip to content

Commit

Permalink
Add preserveLinkText option
Browse files Browse the repository at this point in the history
Resolves #2355
  • Loading branch information
Gerrit0 committed Aug 25, 2023
1 parent 3d8ff29 commit d4db571
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 14 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -13,6 +13,9 @@
### Features

- TypeDoc config files now support options default-exported from an ESM config file, #2268.
- TypeDoc config files may now export a promise containing configuration, #2268.
- Added `--preserveLinkText` option (defaults to true) which determines whether the reflection name or full link text is included
in the output when no override is specified, #2355.
- Added a no-results placeholder when no search results are available, #2347.
- Implemented several miscellaneous performance improvements to generate docs faster, this took the time to generate TypeDoc's
site from ~5.6 seconds to ~5.4 seconds.
Expand Down
31 changes: 25 additions & 6 deletions src/lib/converter/comments/linkResolver.ts
Expand Up @@ -32,21 +32,28 @@ export type ExternalSymbolResolver = (
symbolId: ReflectionSymbolId | undefined,
) => ExternalResolveResult | string | undefined;

export type LinkResolverOptions = {
preserveLinkText: boolean;
};

export function resolveLinks(
comment: Comment,
reflection: Reflection,
externalResolver: ExternalSymbolResolver,
options: LinkResolverOptions,
) {
comment.summary = resolvePartLinks(
reflection,
comment.summary,
externalResolver,
options,
);
for (const tag of comment.blockTags) {
tag.content = resolvePartLinks(
reflection,
tag.content,
externalResolver,
options,
);
}

Expand All @@ -55,6 +62,7 @@ export function resolveLinks(
reflection,
reflection.readme,
externalResolver,
options,
);
}
}
Expand All @@ -63,24 +71,26 @@ export function resolvePartLinks(
reflection: Reflection,
parts: readonly CommentDisplayPart[],
externalResolver: ExternalSymbolResolver,
options: LinkResolverOptions,
): CommentDisplayPart[] {
return parts.flatMap((part) =>
processPart(reflection, part, externalResolver),
processPart(reflection, part, externalResolver, options),
);
}

function processPart(
reflection: Reflection,
part: CommentDisplayPart,
externalResolver: ExternalSymbolResolver,
options: LinkResolverOptions,
): CommentDisplayPart | CommentDisplayPart[] {
if (part.kind === "inline-tag") {
if (
part.tag === "@link" ||
part.tag === "@linkcode" ||
part.tag === "@linkplain"
) {
return resolveLinkTag(reflection, part, externalResolver);
return resolveLinkTag(reflection, part, externalResolver, options);
}
}

Expand All @@ -91,6 +101,7 @@ function resolveLinkTag(
reflection: Reflection,
part: InlineTagDisplayPart,
externalResolver: ExternalSymbolResolver,
options: LinkResolverOptions,
): InlineTagDisplayPart {
let defaultDisplayText = "";
let pos = 0;
Expand All @@ -112,7 +123,9 @@ function resolveLinkTag(
if (tsTarget) {
target = tsTarget;
pos = end;
defaultDisplayText = part.tsLinkText || target.name;
defaultDisplayText =
part.tsLinkText ||
(options.preserveLinkText ? part.text : target.name);
} else if (declRef) {
// If we didn't find a target, we might be pointing to a symbol in another project that will be merged in
// or some external symbol, so ask external resolvers to try resolution. Don't use regular declaration ref
Expand All @@ -127,7 +140,9 @@ function resolveLinkTag(
: undefined,
);

defaultDisplayText = part.text.substring(0, pos);
defaultDisplayText = options.preserveLinkText
? part.text
: part.text.substring(0, pos);

switch (typeof externalResolveResult) {
case "string":
Expand All @@ -147,7 +162,9 @@ function resolveLinkTag(
pos = declRef[1];

if (target) {
defaultDisplayText = target.name;
defaultDisplayText = options.preserveLinkText
? part.text
: target.name;
} else {
// If we didn't find a link, it might be a @link tag to an external symbol, check that next.
const externalResolveResult = externalResolver(
Expand All @@ -159,7 +176,9 @@ function resolveLinkTag(
: undefined,
);

defaultDisplayText = part.text.substring(0, pos);
defaultDisplayText = options.preserveLinkText
? part.text
: part.text.substring(0, pos);

switch (typeof externalResolveResult) {
case "string":
Expand Down
20 changes: 16 additions & 4 deletions src/lib/converter/converter.ts
Expand Up @@ -90,6 +90,10 @@ export class Converter extends ChildableComponent<
@BindOption("useTsLinkResolution")
useTsLinkResolution!: boolean;

/** @internal */
@BindOption("preserveLinkText")
preserveLinkText!: boolean;

private _config?: CommentParserConfig;
private _externalSymbolResolvers: Array<ExternalSymbolResolver> = [];

Expand Down Expand Up @@ -310,12 +314,20 @@ export class Converter extends ChildableComponent<
owner: Reflection,
): CommentDisplayPart[] | undefined {
if (comment instanceof Comment) {
resolveLinks(comment, owner, (ref, part, refl, id) =>
this.resolveExternalLink(ref, part, refl, id),
resolveLinks(
comment,
owner,
(ref, part, refl, id) =>
this.resolveExternalLink(ref, part, refl, id),
{ preserveLinkText: this.preserveLinkText },
);
} else {
return resolvePartLinks(owner, comment, (ref, part, refl, id) =>
this.resolveExternalLink(ref, part, refl, id),
return resolvePartLinks(
owner,
comment,
(ref, part, refl, id) =>
this.resolveExternalLink(ref, part, refl, id),
{ preserveLinkText: this.preserveLinkText },
);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/lib/utils/options/declaration.ts
Expand Up @@ -155,6 +155,7 @@ export interface TypeDocOptionMap {
// Comment
commentStyle: typeof CommentStyle;
useTsLinkResolution: boolean;
preserveLinkText: boolean;
jsDocCompatibility: JsDocCompatibility;
blockTags: `@${string}`[];
inlineTags: `@${string}`[];
Expand Down
4 changes: 2 additions & 2 deletions src/lib/utils/options/readers/typedoc.ts
Expand Up @@ -89,13 +89,13 @@ export class TypeDocReader implements OptionsReader {
try {
try {
// eslint-disable-next-line @typescript-eslint/no-var-requires
fileContent = require(file);
fileContent = await require(file);
} catch (error: any) {
if (error?.code === "ERR_REQUIRE_ESM") {
// On Windows, we need to ensure this path is a file path.
// Or we'll get ERR_UNSUPPORTED_ESM_URL_SCHEME
const esmPath = pathToFileURL(file).toString();
fileContent = (await import(esmPath)).default;
fileContent = await (await import(esmPath)).default;
} else {
throw error;
}
Expand Down
6 changes: 6 additions & 0 deletions src/lib/utils/options/sources/typedoc.ts
Expand Up @@ -554,6 +554,12 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
type: ParameterType.Boolean,
defaultValue: true,
});
options.addDeclaration({
name: "preserveLinkText",
help: "If set, @link tags without link text will use the text content as the link. If not set, will use the target reflection name.",
type: ParameterType.Boolean,
defaultValue: true,
});

options.addDeclaration({
name: "blockTags",
Expand Down
2 changes: 1 addition & 1 deletion src/test/behavior.c2.test.ts
Expand Up @@ -752,7 +752,7 @@ describe("Behavior Tests", () => {
[ReflectionKind.Variable, "A"],
[ReflectionKind.Variable, "A"],
]);
equal(getLinkTexts(localSymbolRef), ["A!", "A2!", "A"]);
equal(getLinkTexts(localSymbolRef), ["A!", "A2!", "AnotherName"]);

equal(getLinks(query(project, "scoped")), [
[ReflectionKind.Property, "Meanings.B.prop"],
Expand Down
2 changes: 1 addition & 1 deletion src/test/issues.c2.test.ts
Expand Up @@ -471,7 +471,7 @@ describe("Issue Tests", () => {
| InlineTagDisplayPart
| undefined;
equal(tag?.kind, "inline-tag");
equal(tag.text, "method");
equal(tag.text, "Test2.method");
ok(
tag.target === query(project, "Test.method"),
"Incorrect resolution",
Expand Down

0 comments on commit d4db571

Please sign in to comment.