From 23008f617c34144c7e7a963a5aca1b2b04104c3e Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 20 Dec 2024 15:35:09 -0700 Subject: [PATCH] Improve link resolution prioritization Ref: gerrit0/typedoc-plugin-zod#8 --- CHANGELOG.md | 2 ++ src/lib/converter/comments/linkResolver.ts | 35 ++++++++++++++++----- src/lib/utils/array.ts | 22 +++++++++++++ src/test/converter/exports/specs.json | 4 +-- src/test/converter/exports/specs.nodoc.json | 2 +- src/test/utils/array.test.ts | 21 +++++++++++++ 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f27a53844..a6d75277b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ title: Changelog - `@include` and `@includeCode` now work in the readme file, #2814. - TypeDoc will now avoid making references to references, #2811. +- Improved link resolution logic to prioritize type alias properties with the + same symbol over type literal properties within function parameters. ## v0.27.5 (2024-12-14) diff --git a/src/lib/converter/comments/linkResolver.ts b/src/lib/converter/comments/linkResolver.ts index e2e082b34..c893c217c 100644 --- a/src/lib/converter/comments/linkResolver.ts +++ b/src/lib/converter/comments/linkResolver.ts @@ -13,6 +13,7 @@ import { parseDeclarationReference, } from "./declarationReference.js"; import { resolveDeclarationReference } from "./declarationReferenceResolver.js"; +import { maxElementByScore } from "../../utils/array.js"; const urlPrefix = /^(http|ftp)s?:\/\//; @@ -138,14 +139,32 @@ function resolveLinkTag( if (tsTargets.length) { // Find the target most likely to have a real url in the generated documentation - target = - tsTargets.find((r) => r.kindOf(ReflectionKind.SomeExport)) || - tsTargets.find( - (r) => - r.kindOf(ReflectionKind.SomeMember) && - r.parent?.kindOf(ReflectionKind.SomeExport), - ) || - tsTargets[0]; + // 1. A direct export (class, interface, variable) + // 2. A property of a direct export (class/interface property) + // 3. A property of a type of an export (property on type alias) + // 4. Whatever the first symbol found was + target = maxElementByScore(tsTargets, (r) => { + if (r.kindOf(ReflectionKind.SomeExport)) { + return 4; + } + if ( + r.kindOf(ReflectionKind.SomeMember) && + r.parent?.kindOf(ReflectionKind.SomeExport) + ) { + return 3; + } + if ( + r.kindOf(ReflectionKind.SomeMember) && + r.parent?.kindOf(ReflectionKind.TypeLiteral) && + r.parent.parent?.kindOf( + ReflectionKind.TypeAlias | ReflectionKind.Variable, + ) + ) { + return 2; + } + + return 1; + })!; pos = end; defaultDisplayText = part.tsLinkText || diff --git a/src/lib/utils/array.ts b/src/lib/utils/array.ts index 5a92f8542..82e85bea9 100644 --- a/src/lib/utils/array.ts +++ b/src/lib/utils/array.ts @@ -196,3 +196,25 @@ export function joinArray( } return ""; } + +export function maxElementByScore( + arr: readonly T[], + score: (a: T) => number, +): T | undefined { + if (arr.length === 0) { + return undefined; + } + + let largest = arr[0]; + let largestScore = score(arr[0]); + + for (let i = 1; i < arr.length; ++i) { + const itemScore = score(arr[i]); + if (itemScore > largestScore) { + largest = arr[i]; + largestScore = itemScore; + } + } + + return largest; +} diff --git a/src/test/converter/exports/specs.json b/src/test/converter/exports/specs.json index ecdf2a80c..180fc5089 100644 --- a/src/test/converter/exports/specs.json +++ b/src/test/converter/exports/specs.json @@ -308,7 +308,7 @@ "url": "typedoc://mod.ts#L13" } ], - "target": 31 + "target": 30 }, { "id": 40, @@ -412,7 +412,7 @@ "url": "typedoc://mod.ts#L40" } ], - "target": 34 + "target": 29 } ], "groups": [ diff --git a/src/test/converter/exports/specs.nodoc.json b/src/test/converter/exports/specs.nodoc.json index a56de8da7..5ab9aabda 100644 --- a/src/test/converter/exports/specs.nodoc.json +++ b/src/test/converter/exports/specs.nodoc.json @@ -34,7 +34,7 @@ "url": "typedoc://mod.ts#L13" } ], - "target": 31 + "target": 30 }, { "id": 43, diff --git a/src/test/utils/array.test.ts b/src/test/utils/array.test.ts index 3e3752fa5..d91a48659 100644 --- a/src/test/utils/array.test.ts +++ b/src/test/utils/array.test.ts @@ -3,6 +3,7 @@ import { binaryFindPartition, insertOrderSorted, insertPrioritySorted, + maxElementByScore, removeIfPresent, } from "../../lib/utils/array.js"; @@ -138,4 +139,24 @@ describe("Array utils", () => { equal(arr, [2, 1]); }); }); + + describe("maxElementByScore", () => { + it("Gets the max element", () => { + const arr = [1, 2, 3]; + const item = maxElementByScore(arr, (x) => x); + equal(item, 3); + }); + + it("Prioritizes elements earlier in the array", () => { + const arr = [1, 2, 3]; + const item = maxElementByScore(arr, () => 1); + equal(item, 1); + }); + + it("Returns undefined for an empty array", () => { + const arr: unknown[] = []; + const item = maxElementByScore(arr, () => 1); + equal(item, undefined); + }); + }); });