Skip to content

Commit 9eb1e05

Browse files
committed
Produce warnings if links cannot be resolved
Ref: #2808
1 parent e4f991d commit 9eb1e05

File tree

27 files changed

+313
-31
lines changed

27 files changed

+313
-31
lines changed

.config/typedoc.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
],
1616
"name": "TypeDoc API",
1717

18+
// Don't document the debug entry point
19+
"entryPoints": ["../src/index.ts"],
1820
"outputs": [
1921
{
2022
"name": "html",

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ title: Changelog
66

77
### Bug Fixes
88

9+
- Possibly Breaking: TypeDoc will no longer render anchors within the page for
10+
deeply nested properties. This only affects links to properties of
11+
properties of types, which did not have a clickable link exposed so are
12+
unlikely to have been linked to. Furthermore, these links were not always
13+
created by TypeDoc, only being created if all parent properties contained
14+
comments, #2808.
15+
- TypeDoc will now warn if a property which does not have a URL within the
16+
rendered document and the parent property/page will be linked to instead,
17+
#2808. These warnings can be disabled with the `validation.rewrittenLink`
18+
option.
919
- Fix restoration of groups/categories including documents, #2801.
1020
- Fixed missed relative paths within markdown link references in documents.
1121
- Improved handling of incomplete inline code blocks within markdown.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"exports": {
88
".": "./dist/index.js",
99
"./tsdoc.json": "./tsdoc.json",
10-
"./package.json": "./package.json"
10+
"./package.json": "./package.json",
11+
"./debug": "./dist/lib/debug/index.js"
1112
},
1213
"types": "./dist/index.d.ts",
1314
"bin": {

scripts/capture_screenshots.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ export async function captureScreenshots(
9393
headless,
9494
theme,
9595
) {
96+
await fs.promises.rm(outputDirectory, { force: true, recursive: true });
97+
await fs.promises.mkdir(outputDirectory, { recursive: true });
98+
9699
const browser = await puppeteer.launch({
97100
args:
98101
platform() === "win32"

site/development/plugins.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ to. Plugins should assume that they may be loaded multiple times for different
1313
applications, and that a single load of an application class may be used to
1414
convert multiple projects.
1515

16-
Plugins may be either ESM or CommonJS.
16+
Plugins may be either ESM or CommonJS, but TypeDoc ships with ESM, so they should
17+
generally published as ESM to avoid `require(esm)` experimental warnings.
1718

1819
```js
1920
// @ts-check

site/options/validation.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,32 @@ typedoc.json (defaults):
2020
"validation": {
2121
"notExported": true,
2222
"invalidLink": true,
23+
"rewrittenLink": true,
2324
"notDocumented": false,
2425
"unusedMergeModuleWith": true
2526
}
2627
}
2728
```
2829

29-
Specifies validation steps TypeDoc should perform on your generated documentation.
30+
Specifies validation steps TypeDoc should perform on your generated
31+
documentation. Most validation occurs before rendering, but `rewrittenLink` is
32+
done during HTML rendering as links have not been generated before rendering
33+
begins.
34+
35+
- **notExported** - Produce warnings if a type is referenced by the
36+
documentation but the type isn't exported and therefore included in the
37+
documentation.
38+
- **invalidLink** - Produce warnings for `@link` tags which cannot be resolved.
39+
- **rewrittenLink** - Produce warnings for `@link` tags which are resolved,
40+
but whose target does not have a unique URL in the documentation. TypeDoc
41+
will rewrite these links to point to the first parent with a URL.
42+
- **notDocumented** - Produce warnings for reflections which do not have a
43+
documentation comment. This is also controlled by the
44+
[requiredToBeDocumented](#requiredtobedocumented) option.
45+
- **unusedMergeModuleWith** - Produce warnings for
46+
[`@mergeModuleWith`](../tags/mergeModuleWith.md) tags which are not
47+
resolved. This option should generally be disabled if generating JSON which
48+
will be combined with another document later.
3049

3150
## treatWarningsAsErrors
3251

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
/**
22
* @module TypeDoc API
3+
*
4+
* In addition to the members documented here, TypeDoc exports a `typedoc/debug`
5+
* entry point which exports some functions which may be useful during plugin
6+
* development or debugging. Exports from that entry point are **not stable**
7+
* and may change or be removed at any time.
38
*/
49
export { Application, type ApplicationEvents } from "./lib/application.js";
510

src/lib/converter/comments/declarationReferenceResolver.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ok } from "assert";
22
import {
33
ContainerReflection,
44
DeclarationReflection,
5+
type DocumentReflection,
56
type ProjectReflection,
67
ReferenceReflection,
78
type Reflection,
@@ -225,10 +226,18 @@ function resolveSymbolReferencePart(
225226
let high: Reflection[] = [];
226227
let low: Reflection[] = [];
227228

228-
if (
229-
!(refl instanceof ContainerReflection) ||
230-
!refl.childrenIncludingDocuments
231-
) {
229+
let children:
230+
| ReadonlyArray<DocumentReflection | DeclarationReflection>
231+
| undefined;
232+
233+
if (refl instanceof ContainerReflection) {
234+
children = refl.childrenIncludingDocuments;
235+
}
236+
if (!children && refl.isDeclaration() && refl.type?.type === "reflection") {
237+
children = refl.type.declaration.childrenIncludingDocuments;
238+
}
239+
240+
if (!children) {
232241
return { high, low };
233242
}
234243

@@ -238,12 +247,12 @@ function resolveSymbolReferencePart(
238247
// so that resolution doesn't behave very poorly with projects using JSDoc style resolution.
239248
// Also is more consistent with how TypeScript resolves link tags.
240249
case ".":
241-
high = refl.childrenIncludingDocuments.filter(
250+
high = children.filter(
242251
(r) =>
243252
r.name === path.path &&
244253
(r.kindOf(ReflectionKind.SomeExport) || r.flags.isStatic),
245254
);
246-
low = refl.childrenIncludingDocuments.filter(
255+
low = children.filter(
247256
(r) =>
248257
r.name === path.path &&
249258
(!r.kindOf(ReflectionKind.SomeExport) || !r.flags.isStatic),
@@ -254,7 +263,7 @@ function resolveSymbolReferencePart(
254263
// enum members, type literal properties
255264
case "#":
256265
high =
257-
refl.children?.filter((r) => {
266+
children?.filter((r) => {
258267
return (
259268
r.name === path.path &&
260269
r.kindOf(ReflectionKind.SomeMember) &&
@@ -269,7 +278,7 @@ function resolveSymbolReferencePart(
269278
if (
270279
refl.kindOf(ReflectionKind.SomeModule | ReflectionKind.Project)
271280
) {
272-
high = refl.children?.filter((r) => r.name === path.path) || [];
281+
high = children?.filter((r) => r.name === path.path) || [];
273282
}
274283
break;
275284
}

src/lib/converter/comments/linkResolver.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DeclarationReflection,
66
type InlineTagDisplayPart,
77
Reflection,
8+
ReflectionKind,
89
ReflectionSymbolId,
910
} from "../../models/index.js";
1011
import {
@@ -131,12 +132,20 @@ function resolveLinkTag(
131132

132133
// Might already know where it should go if useTsLinkResolution is turned on
133134
if (part.target instanceof ReflectionSymbolId) {
134-
const tsTarget = reflection.project.getReflectionFromSymbolId(
135+
const tsTargets = reflection.project.getReflectionsFromSymbolId(
135136
part.target,
136137
);
137138

138-
if (tsTarget) {
139-
target = tsTarget;
139+
if (tsTargets.length) {
140+
// Find the target most likely to have a real url in the generated documentation
141+
target =
142+
tsTargets.find((r) => r.kindOf(ReflectionKind.SomeExport)) ||
143+
tsTargets.find(
144+
(r) =>
145+
r.kindOf(ReflectionKind.SomeMember) &&
146+
r.parent?.kindOf(ReflectionKind.SomeExport),
147+
) ||
148+
tsTargets[0];
140149
pos = end;
141150
defaultDisplayText =
142151
part.tsLinkText ||
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/* eslint-disable no-console */
2+
import type { Application } from "../application.js";
3+
import { ConverterEvents } from "../converter/converter-events.js";
4+
import type { Reflection } from "../models/index.js";
5+
6+
export function debugReflectionLifetimes(app: Application) {
7+
app.converter.on(ConverterEvents.CREATE_PROJECT, logCreate);
8+
app.converter.on(ConverterEvents.CREATE_SIGNATURE, logCreate);
9+
app.converter.on(ConverterEvents.CREATE_TYPE_PARAMETER, logCreate);
10+
app.converter.on(ConverterEvents.CREATE_DECLARATION, logCreate);
11+
app.converter.on(ConverterEvents.CREATE_DOCUMENT, logCreate);
12+
app.converter.on(ConverterEvents.CREATE_PARAMETER, logCreate);
13+
14+
app.converter.on(ConverterEvents.CREATE_PROJECT, (_context, project) => {
15+
const oldRemove = project["_removeReflection"];
16+
project["_removeReflection"] = function (reflection) {
17+
console.log("Remove", reflection.id, reflection.getFullName());
18+
return oldRemove.call(this, reflection);
19+
};
20+
});
21+
}
22+
23+
function logCreate(_context: unknown, refl: Reflection) {
24+
console.log("Create", refl.variant, refl.id, refl.getFullName());
25+
}

0 commit comments

Comments
 (0)