From d4db57192c057945b31b8c07072cbd9c5aff0dfc Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 25 Aug 2023 14:14:09 -0600 Subject: [PATCH] Add preserveLinkText option Resolves #2355 --- CHANGELOG.md | 3 +++ src/lib/converter/comments/linkResolver.ts | 31 +++++++++++++++++----- src/lib/converter/converter.ts | 20 +++++++++++--- src/lib/utils/options/declaration.ts | 1 + src/lib/utils/options/readers/typedoc.ts | 4 +-- src/lib/utils/options/sources/typedoc.ts | 6 +++++ src/test/behavior.c2.test.ts | 2 +- src/test/issues.c2.test.ts | 2 +- 8 files changed, 55 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 088b47238..8ff8cbbc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/src/lib/converter/comments/linkResolver.ts b/src/lib/converter/comments/linkResolver.ts index 1283a5436..ea43f65ce 100644 --- a/src/lib/converter/comments/linkResolver.ts +++ b/src/lib/converter/comments/linkResolver.ts @@ -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, ); } @@ -55,6 +62,7 @@ export function resolveLinks( reflection, reflection.readme, externalResolver, + options, ); } } @@ -63,9 +71,10 @@ 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), ); } @@ -73,6 +82,7 @@ function processPart( reflection: Reflection, part: CommentDisplayPart, externalResolver: ExternalSymbolResolver, + options: LinkResolverOptions, ): CommentDisplayPart | CommentDisplayPart[] { if (part.kind === "inline-tag") { if ( @@ -80,7 +90,7 @@ function processPart( part.tag === "@linkcode" || part.tag === "@linkplain" ) { - return resolveLinkTag(reflection, part, externalResolver); + return resolveLinkTag(reflection, part, externalResolver, options); } } @@ -91,6 +101,7 @@ function resolveLinkTag( reflection: Reflection, part: InlineTagDisplayPart, externalResolver: ExternalSymbolResolver, + options: LinkResolverOptions, ): InlineTagDisplayPart { let defaultDisplayText = ""; let pos = 0; @@ -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 @@ -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": @@ -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( @@ -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": diff --git a/src/lib/converter/converter.ts b/src/lib/converter/converter.ts index 931f46b0d..4ffc9e80b 100644 --- a/src/lib/converter/converter.ts +++ b/src/lib/converter/converter.ts @@ -90,6 +90,10 @@ export class Converter extends ChildableComponent< @BindOption("useTsLinkResolution") useTsLinkResolution!: boolean; + /** @internal */ + @BindOption("preserveLinkText") + preserveLinkText!: boolean; + private _config?: CommentParserConfig; private _externalSymbolResolvers: Array = []; @@ -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 }, ); } } diff --git a/src/lib/utils/options/declaration.ts b/src/lib/utils/options/declaration.ts index 5b7d061d0..e31e81a9b 100644 --- a/src/lib/utils/options/declaration.ts +++ b/src/lib/utils/options/declaration.ts @@ -155,6 +155,7 @@ export interface TypeDocOptionMap { // Comment commentStyle: typeof CommentStyle; useTsLinkResolution: boolean; + preserveLinkText: boolean; jsDocCompatibility: JsDocCompatibility; blockTags: `@${string}`[]; inlineTags: `@${string}`[]; diff --git a/src/lib/utils/options/readers/typedoc.ts b/src/lib/utils/options/readers/typedoc.ts index 17bbe011d..f47ec57bc 100644 --- a/src/lib/utils/options/readers/typedoc.ts +++ b/src/lib/utils/options/readers/typedoc.ts @@ -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; } diff --git a/src/lib/utils/options/sources/typedoc.ts b/src/lib/utils/options/sources/typedoc.ts index a2fce5f81..ad3513eae 100644 --- a/src/lib/utils/options/sources/typedoc.ts +++ b/src/lib/utils/options/sources/typedoc.ts @@ -554,6 +554,12 @@ export function addTypeDocOptions(options: Pick) { 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", diff --git a/src/test/behavior.c2.test.ts b/src/test/behavior.c2.test.ts index 181802eb3..df8250791 100644 --- a/src/test/behavior.c2.test.ts +++ b/src/test/behavior.c2.test.ts @@ -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"], diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 421e5a929..4d2598b43 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -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",