typedoc 0.28.14 → 0.28.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/lib/cli.js +2 -1
  2. package/dist/lib/converter/comments/index.d.ts +2 -1
  3. package/dist/lib/converter/comments/parser.js +45 -21
  4. package/dist/lib/converter/comments/textParser.d.ts +13 -9
  5. package/dist/lib/converter/comments/textParser.js +21 -16
  6. package/dist/lib/converter/converter.js +1 -0
  7. package/dist/lib/converter/plugins/CommentPlugin.js +13 -0
  8. package/dist/lib/converter/symbols.js +8 -8
  9. package/dist/lib/converter/utils/repository.js +3 -0
  10. package/dist/lib/models/DeclarationReflection.d.ts +7 -0
  11. package/dist/lib/models/DeclarationReflection.js +9 -0
  12. package/dist/lib/output/events.d.ts +2 -0
  13. package/dist/lib/output/events.js +1 -0
  14. package/dist/lib/output/plugins/JavascriptIndexPlugin.js +1 -1
  15. package/dist/lib/output/themes/default/partials/member.js +1 -0
  16. package/dist/lib/output/themes/default/partials/moduleReflection.js +1 -0
  17. package/dist/lib/output/themes/default/partials/navigation.js +1 -1
  18. package/dist/lib/serialization/schema.d.ts +1 -1
  19. package/dist/lib/utils/options/declaration.d.ts +4 -0
  20. package/dist/lib/utils/options/sources/typedoc.js +1 -0
  21. package/dist/lib/utils/options/tsdoc-defaults.d.ts +2 -2
  22. package/dist/lib/utils/options/tsdoc-defaults.js +1 -1
  23. package/dist/lib/utils-common/logger.d.ts +12 -1
  24. package/dist/lib/utils-common/logger.js +9 -0
  25. package/dist/lib/validation/documentation.js +2 -2
  26. package/dist/lib/validation/exports.js +2 -2
  27. package/dist/lib/validation/links.js +7 -7
  28. package/package.json +12 -12
  29. package/static/style.css +3 -3
  30. package/tsdoc.json +0 -4
package/dist/lib/cli.js CHANGED
@@ -75,7 +75,8 @@ async function run(app) {
75
75
  }
76
76
  const preValidationWarnCount = app.logger.warningCount;
77
77
  app.validate(project);
78
- const hadValidationWarnings = app.logger.warningCount !== preValidationWarnCount;
78
+ const hadValidationWarnings = app.logger.warningCount !== preValidationWarnCount ||
79
+ app.logger.validationWarningCount != 0;
79
80
  if (app.logger.hasErrors()) {
80
81
  return ExitCodes.ValidationError;
81
82
  }
@@ -1,6 +1,6 @@
1
1
  import ts from "typescript";
2
2
  import { Comment, ReflectionKind } from "../../models/index.js";
3
- import type { CommentStyle, JsDocCompatibility } from "../../utils/options/declaration.js";
3
+ import type { CommentStyle, JsDocCompatibility, ValidationOptions } from "../../utils/options/declaration.js";
4
4
  import type { FileRegistry } from "../../models/FileRegistry.js";
5
5
  import { type Logger } from "#utils";
6
6
  import type { Context } from "../context.js";
@@ -13,6 +13,7 @@ export interface CommentParserConfig {
13
13
  suppressCommentWarningsInDeclarationFiles: boolean;
14
14
  useTsLinkResolution: boolean;
15
15
  commentStyle: CommentStyle;
16
+ validationOptions: ValidationOptions;
16
17
  }
17
18
  export interface CommentContext {
18
19
  config: CommentParserConfig;
@@ -45,12 +45,12 @@ export function parseComment(tokens, file, context) {
45
45
  const tok = lexer.done() || lexer.peek();
46
46
  const comment = new Comment();
47
47
  comment.sourcePath = file.fileName;
48
- comment.summary = blockContent(comment, lexer, context.config, i18n, warningImpl, context.files);
48
+ comment.summary = blockContent(comment, lexer, context.config, warningImpl, validationWarningImpl, context.files);
49
49
  while (!lexer.done()) {
50
- comment.blockTags.push(blockTag(comment, lexer, context.config, i18n, warningImpl, context.files));
50
+ comment.blockTags.push(blockTag(comment, lexer, context.config, warningImpl, validationWarningImpl, context.files));
51
51
  }
52
52
  const tok2 = tok;
53
- postProcessComment(comment, i18n, () => `${nicePath(file.fileName)}:${file.getLineAndCharacterOfPosition(tok2.pos).line + 1}`, (message) => context.logger.warn(message));
53
+ postProcessComment(comment, () => `${nicePath(file.fileName)}:${file.getLineAndCharacterOfPosition(tok2.pos).line + 1}`, (message) => context.logger.warn(message));
54
54
  return comment;
55
55
  function warningImpl(message, token) {
56
56
  if (context.config.suppressCommentWarningsInDeclarationFiles &&
@@ -59,6 +59,13 @@ export function parseComment(tokens, file, context) {
59
59
  }
60
60
  context.logger.warn(message, token.pos, file);
61
61
  }
62
+ function validationWarningImpl(message, token) {
63
+ if (context.config.suppressCommentWarningsInDeclarationFiles &&
64
+ hasDeclarationFileExtension(file.fileName)) {
65
+ return;
66
+ }
67
+ context.logger.validationWarning(message, token.pos, file);
68
+ }
62
69
  }
63
70
  /**
64
71
  * Intended for parsing markdown documents. This only parses code blocks and
@@ -95,13 +102,22 @@ export function parseCommentString(tokens, config, file, logger, files) {
95
102
  case TokenSyntaxKind.Text:
96
103
  case TokenSyntaxKind.Tag:
97
104
  case TokenSyntaxKind.CloseBrace:
98
- textContent(file.fileName, next, i18n, (msg, token) => logger.warn(msg, token.pos, file), content, files, atNewLine, reentry);
105
+ textContent({
106
+ sourcePath: file.fileName,
107
+ token: next,
108
+ warning: (msg, token) => logger.warn(msg, token.pos, file),
109
+ validationWarning: (msg, token) => logger.validationWarning(msg, token.pos, file),
110
+ files,
111
+ atNewLine,
112
+ validationOptions: config.validationOptions,
113
+ },
114
+ /* out */ content, reentry);
99
115
  break;
100
116
  case TokenSyntaxKind.Code:
101
117
  content.push({ kind: "code", text: next.text });
102
118
  break;
103
119
  case TokenSyntaxKind.OpenBrace:
104
- inlineTag(lexer, content, suppressWarningsConfig, i18n, (message, token) => logger.warn(message, token.pos, file));
120
+ inlineTag(lexer, content, suppressWarningsConfig, (message, token) => logger.warn(message, token.pos, file));
105
121
  consume = false;
106
122
  break;
107
123
  default:
@@ -161,7 +177,7 @@ function makeCodeBlock(text) {
161
177
  * Loop over comment, produce lint warnings, and set tag names for tags
162
178
  * which have them.
163
179
  */
164
- function postProcessComment(comment, i18n, getPosition, warning) {
180
+ function postProcessComment(comment, getPosition, warning) {
165
181
  for (const tag of comment.blockTags) {
166
182
  if (HAS_USER_IDENTIFIER.includes(tag.tag) && tag.content.length) {
167
183
  const first = tag.content[0];
@@ -211,7 +227,7 @@ function postProcessComment(comment, i18n, getPosition, warning) {
211
227
  }
212
228
  }
213
229
  const aliasedTags = new Map([["@return", "@returns"]]);
214
- function blockTag(comment, lexer, config, i18n, warning, files) {
230
+ function blockTag(comment, lexer, config, warning, validationWarning, files) {
215
231
  const blockTag = lexer.take();
216
232
  ok(blockTag.kind === TokenSyntaxKind.Tag, "blockTag called not at the start of a block tag."); // blockContent is broken if this fails.
217
233
  if (!config.blockTags.has(blockTag.text)) {
@@ -220,7 +236,7 @@ function blockTag(comment, lexer, config, i18n, warning, files) {
220
236
  const tagName = aliasedTags.get(blockTag.text) || blockTag.text;
221
237
  let content;
222
238
  if (tagName === "@example") {
223
- return exampleBlock(comment, lexer, config, i18n, warning, files);
239
+ return exampleBlock(comment, lexer, config, warning, validationWarning, files);
224
240
  }
225
241
  let typeAnnotation;
226
242
  if (!lexer.done() &&
@@ -234,10 +250,10 @@ function blockTag(comment, lexer, config, i18n, warning, files) {
234
250
  }
235
251
  if (["@default", "@defaultValue"].includes(tagName) &&
236
252
  config.jsDocCompatibility.defaultTag) {
237
- content = defaultBlockContent(comment, lexer, config, i18n, warning, files);
253
+ content = defaultBlockContent(comment, lexer, config, warning, validationWarning, files);
238
254
  }
239
255
  else {
240
- content = blockContent(comment, lexer, config, i18n, warning, files);
256
+ content = blockContent(comment, lexer, config, warning, validationWarning, files);
241
257
  }
242
258
  const tag = new CommentTag(tagName, content);
243
259
  if (typeAnnotation) {
@@ -249,14 +265,14 @@ function blockTag(comment, lexer, config, i18n, warning, files) {
249
265
  * The `@default` tag gets a special case because otherwise we will produce many warnings
250
266
  * about unescaped/mismatched/missing braces in legacy JSDoc comments
251
267
  */
252
- function defaultBlockContent(comment, lexer, config, i18n, warning, files) {
268
+ function defaultBlockContent(comment, lexer, config, warning, validationWarning, files) {
253
269
  lexer.mark();
254
270
  const tempRegistry = new FileRegistry();
255
- const content = blockContent(comment, lexer, config, i18n, () => { }, tempRegistry);
271
+ const content = blockContent(comment, lexer, config, () => { }, () => { }, tempRegistry);
256
272
  const end = lexer.done() || lexer.peek();
257
273
  lexer.release();
258
274
  if (content.some((part) => part.kind === "code" || part.kind === "inline-tag")) {
259
- return blockContent(comment, lexer, config, i18n, warning, files);
275
+ return blockContent(comment, lexer, config, warning, validationWarning, files);
260
276
  }
261
277
  const tokens = [];
262
278
  while ((lexer.done() || lexer.peek()) !== end) {
@@ -279,10 +295,10 @@ function defaultBlockContent(comment, lexer, config, i18n, warning, files) {
279
295
  *
280
296
  * In TSDoc, we also want to treat the first line of the block as the example name.
281
297
  */
282
- function exampleBlock(comment, lexer, config, i18n, warning, files) {
298
+ function exampleBlock(comment, lexer, config, warning, validationWarning, files) {
283
299
  lexer.mark();
284
300
  const tempRegistry = new FileRegistry();
285
- const content = blockContent(comment, lexer, config, i18n, () => { }, tempRegistry);
301
+ const content = blockContent(comment, lexer, config, () => { }, () => { }, tempRegistry);
286
302
  const end = lexer.done() || lexer.peek();
287
303
  lexer.release();
288
304
  if (!config.jsDocCompatibility.exampleTag ||
@@ -323,7 +339,7 @@ function exampleBlock(comment, lexer, config, i18n, warning, files) {
323
339
  assertNever(next.kind);
324
340
  }
325
341
  }
326
- const content = blockContent(comment, lexer, config, i18n, warning, files);
342
+ const content = blockContent(comment, lexer, config, warning, validationWarning, files);
327
343
  const tag = new CommentTag("@example", content);
328
344
  if (exampleName.trim()) {
329
345
  tag.name = exampleName.trim();
@@ -362,7 +378,7 @@ function exampleBlock(comment, lexer, config, i18n, warning, files) {
362
378
  * If you change this, also look at parseCommentString as it
363
379
  * likely needs similar modifications to ensure parsing is consistent.
364
380
  */
365
- function blockContent(comment, lexer, config, i18n, warning, files) {
381
+ function blockContent(comment, lexer, config, warning, validationWarning, files) {
366
382
  const content = [];
367
383
  let atNewLine = true;
368
384
  const reentry = new TextParserReentryState();
@@ -375,8 +391,16 @@ function blockContent(comment, lexer, config, i18n, warning, files) {
375
391
  content.push({ kind: "text", text: next.text });
376
392
  break;
377
393
  case TokenSyntaxKind.Text:
378
- textContent(comment.sourcePath, next, i18n, warning,
379
- /*out*/ content, files, atNewLine, reentry);
394
+ textContent({
395
+ sourcePath: comment.sourcePath,
396
+ token: next,
397
+ files,
398
+ atNewLine,
399
+ warning,
400
+ validationWarning,
401
+ validationOptions: config.validationOptions,
402
+ },
403
+ /*out*/ content, reentry);
380
404
  break;
381
405
  case TokenSyntaxKind.Code:
382
406
  content.push({ kind: "code", text: next.text });
@@ -414,7 +438,7 @@ function blockContent(comment, lexer, config, i18n, warning, files) {
414
438
  content.push({ kind: "text", text: next.text });
415
439
  break;
416
440
  case TokenSyntaxKind.OpenBrace:
417
- inlineTag(lexer, content, config, i18n, warning);
441
+ inlineTag(lexer, content, config, warning);
418
442
  consume = false;
419
443
  break;
420
444
  default:
@@ -451,7 +475,7 @@ function blockContent(comment, lexer, config, i18n, warning, files) {
451
475
  }
452
476
  return content;
453
477
  }
454
- function inlineTag(lexer, block, config, i18n, warning) {
478
+ function inlineTag(lexer, block, config, warning) {
455
479
  const openBrace = lexer.take();
456
480
  // Now skip whitespace to grab the tag name.
457
481
  // If the first non-whitespace text after the brace isn't a tag,
@@ -1,15 +1,18 @@
1
- /**
2
- * Parser to handle plain text markdown.
3
- *
4
- * Responsible for recognizing relative paths within the text and turning
5
- * them into references.
6
- * @module
7
- */
8
- import type { TranslationProxy } from "../../internationalization/index.js";
9
1
  import type { CommentDisplayPart } from "../../models/index.js";
10
2
  import type { FileRegistry } from "../../models/FileRegistry.js";
3
+ import { type ValidationOptions } from "#node-utils";
11
4
  import { type Token } from "./lexer.js";
12
5
  import type { NormalizedPath, TranslatedString } from "#utils";
6
+ interface TextParserData {
7
+ sourcePath: NormalizedPath;
8
+ token: Token;
9
+ pos: number;
10
+ warning: (msg: TranslatedString, token: Token) => void;
11
+ validationWarning: (msg: TranslatedString, token: Token) => void;
12
+ files: FileRegistry;
13
+ atNewLine: boolean;
14
+ validationOptions: ValidationOptions;
15
+ }
13
16
  /**
14
17
  * This is incredibly unfortunate. The comment lexer owns the responsibility
15
18
  * for splitting up text into text/code, this is totally fine for HTML links
@@ -26,4 +29,5 @@ export declare class TextParserReentryState {
26
29
  * Look for relative links within a piece of text and add them to the {@link FileRegistry}
27
30
  * so that they can be correctly resolved during rendering.
28
31
  */
29
- export declare function textContent(sourcePath: NormalizedPath, token: Token, i18n: TranslationProxy, warning: (msg: TranslatedString, token: Token) => void, outContent: CommentDisplayPart[], files: FileRegistry, atNewLine: boolean, reentry: TextParserReentryState): void;
32
+ export declare function textContent(parserData: Omit<TextParserData, "pos">, outContent: CommentDisplayPart[], reentry: TextParserReentryState): void;
33
+ export {};
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Parser to handle plain text markdown.
3
+ *
4
+ * Responsible for recognizing relative paths within the text and turning
5
+ * them into references.
6
+ * @module
7
+ */
8
+ import { i18n } from "#utils";
1
9
  import { HtmlAttributeParser, ParserState } from "#node-utils";
2
10
  import { TokenSyntaxKind } from "./lexer.js";
3
11
  import MarkdownIt from "markdown-it";
@@ -34,42 +42,38 @@ export class TextParserReentryState {
34
42
  * Look for relative links within a piece of text and add them to the {@link FileRegistry}
35
43
  * so that they can be correctly resolved during rendering.
36
44
  */
37
- export function textContent(sourcePath, token, i18n, warning, outContent, files, atNewLine, reentry) {
45
+ export function textContent(parserData, outContent, reentry) {
38
46
  let lastPartEnd = 0;
39
47
  let canEndMarkdownLink = true;
40
48
  const data = {
41
- sourcePath,
42
- token,
49
+ ...parserData,
43
50
  pos: 0, // relative to the token
44
- warning,
45
- files: files,
46
- atNewLine,
47
51
  };
48
52
  function addRef(ref) {
49
53
  canEndMarkdownLink = true;
50
54
  outContent.push({
51
55
  kind: "text",
52
- text: token.text.slice(lastPartEnd, ref.pos),
56
+ text: data.token.text.slice(lastPartEnd, ref.pos),
53
57
  });
54
58
  const link = {
55
59
  kind: "relative-link",
56
- text: token.text.slice(ref.pos, ref.end),
60
+ text: data.token.text.slice(ref.pos, ref.end),
57
61
  target: ref.target,
58
62
  targetAnchor: ref.targetAnchor,
59
63
  };
60
64
  outContent.push(link);
61
65
  lastPartEnd = ref.end;
62
66
  data.pos = ref.end;
63
- if (!ref.target) {
64
- warning(i18n.relative_path_0_is_not_a_file_and_will_not_be_copied_to_output(token.text.slice(ref.pos, ref.end)), {
67
+ if (!ref.target && data.validationOptions.invalidPath) {
68
+ data.validationWarning(i18n.relative_path_0_is_not_a_file_and_will_not_be_copied_to_output(data.token.text.slice(ref.pos, ref.end)), {
65
69
  kind: TokenSyntaxKind.Text,
66
70
  // ref.pos is relative to the token, but this pos is relative to the file.
67
- pos: token.pos + ref.pos,
68
- text: token.text.slice(ref.pos, ref.end),
71
+ pos: data.token.pos + ref.pos,
72
+ text: data.token.text.slice(ref.pos, ref.end),
69
73
  });
70
74
  }
71
75
  }
72
- while (data.pos < token.text.length) {
76
+ while (data.pos < data.token.text.length) {
73
77
  if (canEndMarkdownLink) {
74
78
  const link = checkMarkdownLink(data, reentry);
75
79
  if (link) {
@@ -92,14 +96,14 @@ export function textContent(sourcePath, token, i18n, warning, outContent, files,
92
96
  }
93
97
  continue;
94
98
  }
95
- const atNewLine = token.text[data.pos] === "\n";
99
+ const atNewLine = data.token.text[data.pos] === "\n";
96
100
  data.atNewLine = atNewLine;
97
101
  if (atNewLine && !reentry.withinLinkDest)
98
102
  canEndMarkdownLink = true;
99
103
  ++data.pos;
100
104
  }
101
- if (lastPartEnd !== token.text.length) {
102
- outContent.push({ kind: "text", text: token.text.slice(lastPartEnd) });
105
+ if (lastPartEnd !== data.token.text.length) {
106
+ outContent.push({ kind: "text", text: data.token.text.slice(lastPartEnd) });
103
107
  }
104
108
  }
105
109
  /**
@@ -245,6 +249,7 @@ function checkTagLink(data) {
245
249
  if (token.text.startsWith("<link ", pos)) {
246
250
  data.pos += 4;
247
251
  return checkAttributes(data, {
252
+ // cspell:words imagesrcset
248
253
  imagesrcset: checkAttributeSrcSet,
249
254
  });
250
255
  }
@@ -614,6 +614,7 @@ let Converter = (() => {
614
614
  suppressCommentWarningsInDeclarationFiles: this.application.options.getValue("suppressCommentWarningsInDeclarationFiles"),
615
615
  useTsLinkResolution: this.application.options.getValue("useTsLinkResolution"),
616
616
  commentStyle: this.application.options.getValue("commentStyle"),
617
+ validationOptions: this.application.options.getValue("validation"),
617
618
  };
618
619
  // Can't be included in options because the TSDoc parser blows up if we do.
619
620
  // TypeDoc supports it as one, so it should always be included here.
@@ -290,6 +290,8 @@ let CommentPlugin = (() => {
290
290
  * @param reflection The reflection that is currently processed.
291
291
  */
292
292
  onCreateTypeParameter(_context, reflection) {
293
+ if (reflection.comment)
294
+ return;
293
295
  const comment = reflection.parent?.comment;
294
296
  if (comment) {
295
297
  let tag = comment.getIdentifiedTag(reflection.name, "@typeParam");
@@ -306,6 +308,17 @@ let CommentPlugin = (() => {
306
308
  reflection.comment = new Comment(tag.content);
307
309
  reflection.comment.sourcePath = comment.sourcePath;
308
310
  removeIfPresent(comment.blockTags, tag);
311
+ return;
312
+ }
313
+ }
314
+ // #3031 if this is a class constructor, also check for type parameters
315
+ // that live on the class itself and potentially copy their comment.
316
+ if (reflection.parent?.kindOf(ReflectionKind.ConstructorSignature) &&
317
+ reflection.parent.parent?.kindOf(ReflectionKind.Constructor)) {
318
+ const cls = reflection.parent.parent.parent;
319
+ const typeParam = cls.typeParameters?.find(param => param.name === reflection.name);
320
+ if (typeParam?.comment) {
321
+ reflection.comment = typeParam.comment.clone();
309
322
  }
310
323
  }
311
324
  }
@@ -377,6 +377,14 @@ function convertClassOrInterface(context, symbol, exportSymbol) {
377
377
  reflection.implementedTypes = implementedTypes;
378
378
  }
379
379
  context.finalizeDeclarationReflection(reflection);
380
+ // Convert type parameters early so that we get access them when converting the constructors if any
381
+ if (instanceType.typeParameters) {
382
+ reflection.typeParameters = instanceType.typeParameters.map((param) => {
383
+ const declaration = param.symbol.declarations?.[0];
384
+ assert(declaration && ts.isTypeParameterDeclaration(declaration));
385
+ return createTypeParamReflection(declaration, reflectionContext);
386
+ });
387
+ }
380
388
  if (classDeclaration && reflection.kind === ReflectionKind.Class) {
381
389
  // Classes can have static props
382
390
  const staticType = context.checker.getTypeOfSymbolAtLocation(symbol, classDeclaration);
@@ -404,14 +412,6 @@ function convertClassOrInterface(context, symbol, exportSymbol) {
404
412
  }
405
413
  // Classes/interfaces usually just have properties...
406
414
  convertSymbols(reflectionContext, context.checker.getPropertiesOfType(instanceType));
407
- // And type arguments
408
- if (instanceType.typeParameters) {
409
- reflection.typeParameters = instanceType.typeParameters.map((param) => {
410
- const declaration = param.symbol.declarations?.[0];
411
- assert(declaration && ts.isTypeParameterDeclaration(declaration));
412
- return createTypeParamReflection(declaration, reflectionContext);
413
- });
414
- }
415
415
  // Interfaces might also have call signatures
416
416
  // Classes might too, because of declaration merging
417
417
  context.checker
@@ -139,6 +139,9 @@ let GitRepository = (() => {
139
139
  * @returns A new instance of {@link GitRepository} or undefined.
140
140
  */
141
141
  static tryCreateRepository(path, sourceLinkTemplate, gitRevision, gitRemote, logger) {
142
+ if (gitRevision === "{branch}") {
143
+ gitRevision = git("-C", path, "branch", "--show-current").stdout.trim();
144
+ }
142
145
  gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim();
143
146
  if (gitRevision == "HEAD")
144
147
  return; // Will only happen in a repo with no commits.
@@ -38,6 +38,13 @@ export declare class DeclarationReflection extends ContainerReflection {
38
38
  * A list of all source files that contributed to this reflection.
39
39
  */
40
40
  sources?: SourceReference[];
41
+ /**
42
+ * Precomputed boost for search results, may be less than 1 to de-emphasize this member in search results.
43
+ * Does NOT include group/category values as they are computed when building the JS index.
44
+ *
45
+ * This is exposed purely for plugin use, see #3036 for details.
46
+ */
47
+ relevanceBoost?: number;
41
48
  /**
42
49
  * The escaped name of this declaration assigned by the TS compiler if there is an associated symbol.
43
50
  * This is used to retrieve properties for analyzing inherited members.
@@ -19,6 +19,13 @@ export class DeclarationReflection extends ContainerReflection {
19
19
  * A list of all source files that contributed to this reflection.
20
20
  */
21
21
  sources;
22
+ /**
23
+ * Precomputed boost for search results, may be less than 1 to de-emphasize this member in search results.
24
+ * Does NOT include group/category values as they are computed when building the JS index.
25
+ *
26
+ * This is exposed purely for plugin use, see #3036 for details.
27
+ */
28
+ relevanceBoost;
22
29
  /**
23
30
  * The escaped name of this declaration assigned by the TS compiler if there is an associated symbol.
24
31
  * This is used to retrieve properties for analyzing inherited members.
@@ -217,6 +224,7 @@ export class DeclarationReflection extends ContainerReflection {
217
224
  variant: this.variant,
218
225
  packageVersion: this.packageVersion,
219
226
  sources: serializer.toObjectsOptional(this.sources),
227
+ relevanceBoost: this.relevanceBoost === 1 ? undefined : this.relevanceBoost,
220
228
  typeParameters: serializer.toObjectsOptional(this.typeParameters),
221
229
  type: serializer.toObject(this.type),
222
230
  signatures: serializer.toObjectsOptional(this.signatures),
@@ -260,6 +268,7 @@ export class DeclarationReflection extends ContainerReflection {
260
268
  }
261
269
  this.packageVersion = obj.packageVersion;
262
270
  this.sources = de.reviveMany(obj.sources, (src) => new SourceReference(src.fileName, src.line, src.character));
271
+ this.relevanceBoost = obj.relevanceBoost;
263
272
  this.typeParameters = de.reviveMany(obj.typeParameters, (tp) => de.constructReflection(tp));
264
273
  this.type = de.revive(obj.type, (t) => de.constructType(t));
265
274
  this.signatures = de.reviveMany(obj.signatures, (r) => de.constructReflection(r));
@@ -1,6 +1,7 @@
1
1
  import type { ProjectReflection } from "../models/ProjectReflection.js";
2
2
  import { type DeclarationReflection, type DocumentReflection, Reflection, type ReflectionKind } from "../models/index.js";
3
3
  import type { PageDefinition, PageKind, RouterTarget } from "./router.js";
4
+ import { type Icons } from "./themes/default/partials/icon.js";
4
5
  /**
5
6
  * An event emitted by the {@link Renderer} class at the very beginning and
6
7
  * ending of the entire rendering process.
@@ -39,6 +40,7 @@ export interface PageHeading {
39
40
  level?: number;
40
41
  kind?: ReflectionKind;
41
42
  classes?: string;
43
+ icon?: keyof Icons & (string | number);
42
44
  }
43
45
  /**
44
46
  * An event emitted by the {@link Renderer} class before and after the
@@ -1,4 +1,5 @@
1
1
  import { Reflection, } from "../models/index.js";
2
+ import {} from "./themes/default/partials/icon.js";
2
3
  /**
3
4
  * An event emitted by the {@link Renderer} class at the very beginning and
4
5
  * ending of the entire rendering process.
@@ -174,7 +174,7 @@ let JavascriptIndexPlugin = (() => {
174
174
  }
175
175
  }
176
176
  getBoost(refl) {
177
- let boost = 1;
177
+ let boost = refl.relevanceBoost ?? 1;
178
178
  for (const group of GroupPlugin.getGroups(refl, this.groupReferencesByType)) {
179
179
  boost *= this.searchGroupBoosts[group] ?? 1;
180
180
  this.unusedGroupBoosts.delete(group);
@@ -9,6 +9,7 @@ export function member(context, props) {
9
9
  text: getDisplayName(props),
10
10
  kind: props.kind,
11
11
  classes: context.getReflectionClasses(props),
12
+ icon: context.theme.getReflectionIcon(props),
12
13
  });
13
14
  // With the default url derivation, we'll never hit this case as documents are always placed into their
14
15
  // own pages. Handle it here in case someone creates a custom url scheme which embeds guides within the page.
@@ -35,6 +35,7 @@ export function moduleMemberSummary(context, member) {
35
35
  text: getDisplayName(member),
36
36
  kind: member instanceof ReferenceReflection ? member.getTargetReflectionDeep().kind : member.kind,
37
37
  classes: context.getReflectionClasses(member),
38
+ icon: context.theme.getReflectionIcon(member),
38
39
  });
39
40
  let name;
40
41
  if (member instanceof ReferenceReflection) {
@@ -109,7 +109,7 @@ function buildSectionNavigation(context, headings) {
109
109
  levels.push([]);
110
110
  }
111
111
  levels[levels.length - 1].push(JSX.createElement("a", { href: heading.link, class: classNames({}, heading.classes) },
112
- heading.kind && context.icons[heading.kind](),
112
+ heading.icon && context.icons[heading.icon](),
113
113
  JSX.createElement("span", null, wbr(heading.text))));
114
114
  }
115
115
  while (levels.length > 1) {
@@ -97,7 +97,7 @@ export interface ParameterReflection extends Omit<Reflection, "variant">, S<M.Pa
97
97
  variant: "param";
98
98
  }
99
99
  /** @category Reflections */
100
- export interface DeclarationReflection extends Omit<ContainerReflection, "variant">, S<M.DeclarationReflection, "variant" | "packageVersion" | "sources" | "type" | "signatures" | "indexSignatures" | "defaultValue" | "overwrites" | "inheritedFrom" | "implementationOf" | "extendedTypes" | "extendedBy" | "implementedTypes" | "implementedBy" | "getSignature" | "setSignature" | "typeParameters" | "readme"> {
100
+ export interface DeclarationReflection extends Omit<ContainerReflection, "variant">, S<M.DeclarationReflection, "variant" | "packageVersion" | "sources" | "relevanceBoost" | "type" | "signatures" | "indexSignatures" | "defaultValue" | "overwrites" | "inheritedFrom" | "implementationOf" | "extendedTypes" | "extendedBy" | "implementedTypes" | "implementedBy" | "getSignature" | "setSignature" | "typeParameters" | "readme"> {
101
101
  }
102
102
  /** @category Reflections */
103
103
  export interface TypeParameterReflection extends Omit<Reflection, "variant">, S<M.TypeParameterReflection, "variant" | "type" | "default" | "varianceModifier"> {
@@ -242,6 +242,10 @@ export type ValidationOptions = {
242
242
  * If set, TypeDoc will produce warnings about \{\@link\} tags which will produce broken links.
243
243
  */
244
244
  invalidLink: boolean;
245
+ /**
246
+ * If set, TypeDoc will produce warnings about relative paths within the documentation which could not be resolved.
247
+ */
248
+ invalidPath: boolean;
245
249
  /**
246
250
  * If set, TypeDoc will produce warnings about \{\@link\} tags which do not link directly to their target.
247
251
  */
@@ -864,6 +864,7 @@ export function addTypeDocOptions(options) {
864
864
  defaults: {
865
865
  notExported: true,
866
866
  invalidLink: true,
867
+ invalidPath: true,
867
868
  rewrittenLink: true,
868
869
  notDocumented: false,
869
870
  unusedMergeModuleWith: true,
@@ -1,5 +1,5 @@
1
- export declare const tsdocBlockTags: readonly ["@defaultValue", "@deprecated", "@example", "@param", "@privateRemarks", "@remarks", "@returns", "@see", "@throws", "@typeParam"];
2
- export declare const blockTags: readonly ["@defaultValue", "@deprecated", "@example", "@param", "@privateRemarks", "@remarks", "@returns", "@see", "@throws", "@typeParam", "@author", "@callback", "@category", "@categoryDescription", "@default", "@document", "@extends", "@augments", "@yields", "@group", "@groupDescription", "@import", "@inheritDoc", "@jsx", "@license", "@module", "@mergeModuleWith", "@prop", "@property", "@return", "@satisfies", "@since", "@sortStrategy", "@template", "@this", "@type", "@typedef", "@summary", "@preventInline", "@inlineType", "@preventExpand", "@expandType"];
1
+ export declare const tsdocBlockTags: readonly ["@defaultValue", "@deprecated", "@example", "@jsx", "@param", "@privateRemarks", "@remarks", "@returns", "@see", "@throws", "@typeParam"];
2
+ export declare const blockTags: readonly ["@defaultValue", "@deprecated", "@example", "@jsx", "@param", "@privateRemarks", "@remarks", "@returns", "@see", "@throws", "@typeParam", "@author", "@callback", "@category", "@categoryDescription", "@default", "@document", "@extends", "@augments", "@yields", "@group", "@groupDescription", "@import", "@inheritDoc", "@license", "@module", "@mergeModuleWith", "@prop", "@property", "@return", "@satisfies", "@since", "@sortStrategy", "@template", "@this", "@type", "@typedef", "@summary", "@preventInline", "@inlineType", "@preventExpand", "@expandType"];
3
3
  export declare const tsdocInlineTags: readonly ["@link", "@inheritDoc", "@label"];
4
4
  export declare const inlineTags: readonly ["@link", "@inheritDoc", "@label", "@linkcode", "@linkplain", "@include", "@includeCode"];
5
5
  export declare const tsdocModifierTags: readonly ["@alpha", "@beta", "@eventProperty", "@experimental", "@internal", "@override", "@packageDocumentation", "@public", "@readonly", "@sealed", "@virtual"];
@@ -3,6 +3,7 @@ export const tsdocBlockTags = [
3
3
  "@defaultValue",
4
4
  "@deprecated",
5
5
  "@example",
6
+ "@jsx",
6
7
  "@param",
7
8
  "@privateRemarks",
8
9
  "@remarks",
@@ -26,7 +27,6 @@ export const blockTags = [
26
27
  "@groupDescription",
27
28
  "@import",
28
29
  "@inheritDoc",
29
- "@jsx",
30
30
  "@license",
31
31
  "@module",
32
32
  "@mergeModuleWith",
@@ -26,6 +26,10 @@ export declare class Logger {
26
26
  * How many warning messages have been logged?
27
27
  */
28
28
  warningCount: number;
29
+ /**
30
+ * How many validation warning messages have been logged?
31
+ */
32
+ validationWarningCount: number;
29
33
  /**
30
34
  * The minimum logging level to print.
31
35
  */
@@ -57,10 +61,17 @@ export declare class Logger {
57
61
  /**
58
62
  * Log the given warning.
59
63
  *
60
- * @param text The warning that should be logged.
64
+ * @param text The warning that should be logged.
61
65
  */
62
66
  warn(text: IfInternal<TranslatedString, string>, node?: MinimalNode): void;
63
67
  warn(text: IfInternal<TranslatedString, string>, pos: number, file: MinimalSourceFile): void;
68
+ /**
69
+ * Log the given warning and records that a validation warning has occurred.
70
+ *
71
+ * @param text The warning that should be logged.
72
+ */
73
+ validationWarning(text: IfInternal<TranslatedString, string>, node?: MinimalNode): void;
74
+ validationWarning(text: IfInternal<TranslatedString, string>, pos: number, file: MinimalSourceFile): void;
64
75
  /**
65
76
  * Log the given error.
66
77
  *
@@ -30,6 +30,10 @@ export class Logger {
30
30
  * How many warning messages have been logged?
31
31
  */
32
32
  warningCount = 0;
33
+ /**
34
+ * How many validation warning messages have been logged?
35
+ */
36
+ validationWarningCount = 0;
33
37
  /**
34
38
  * The minimum logging level to print.
35
39
  */
@@ -57,6 +61,7 @@ export class Logger {
57
61
  */
58
62
  resetWarnings() {
59
63
  this.warningCount = 0;
64
+ this.validationWarningCount = 0;
60
65
  }
61
66
  /**
62
67
  * Log the given verbose message.
@@ -74,6 +79,10 @@ export class Logger {
74
79
  const text2 = this.addContext(text, LogLevel.Warn, ...args);
75
80
  this.log(text2, LogLevel.Warn);
76
81
  }
82
+ validationWarning(...args) {
83
+ this.validationWarningCount += 1;
84
+ this.warn(...args);
85
+ }
77
86
  error(text, ...args) {
78
87
  const text2 = this.addContext(text, LogLevel.Error, ...args);
79
88
  this.log(text2, LogLevel.Error);
@@ -81,11 +81,11 @@ export function validateDocumentation(project, logger, requiredToBeDocumented, i
81
81
  intentionalUsage.add(intentionalIndex);
82
82
  continue;
83
83
  }
84
- logger.warn(i18n.reflection_0_kind_1_defined_in_2_does_not_have_any_documentation(ref.getFriendlyFullName(), ReflectionKind[ref.kind], `${symbolId.packageName}/${symbolId.packagePath}`));
84
+ logger.validationWarning(i18n.reflection_0_kind_1_defined_in_2_does_not_have_any_documentation(ref.getFriendlyFullName(), ReflectionKind[ref.kind], `${symbolId.packageName}/${symbolId.packagePath}`));
85
85
  }
86
86
  }
87
87
  const unusedIntentional = intentionallyNotDocumented.filter((_, i) => !intentionalUsage.has(i));
88
88
  if (unusedIntentional.length) {
89
- logger.warn(i18n.invalid_intentionally_not_documented_names_0(unusedIntentional.join("\n\t")));
89
+ logger.validationWarning(i18n.invalid_intentionally_not_documented_names_0(unusedIntentional.join("\n\t")));
90
90
  }
91
91
  }
@@ -55,11 +55,11 @@ export function validateExports(project, logger, intentionallyNotExported) {
55
55
  !warned.has(uniqueId) &&
56
56
  !project.symbolIdHasBeenRemoved(type.symbolId)) {
57
57
  warned.add(uniqueId);
58
- logger.warn(i18n.type_0_defined_in_1_is_referenced_by_2_but_not_included_in_docs(type.qualifiedName, `${type.symbolId.packageName}/${type.symbolId.packagePath}`, owner.getFriendlyFullName()));
58
+ logger.validationWarning(i18n.type_0_defined_in_1_is_referenced_by_2_but_not_included_in_docs(type.qualifiedName, `${type.symbolId.packageName}/${type.symbolId.packagePath}`, owner.getFriendlyFullName()));
59
59
  }
60
60
  }
61
61
  const unusedIntentional = intentional.getUnused();
62
62
  if (unusedIntentional.length) {
63
- logger.warn(i18n.invalid_intentionally_not_exported_symbols_0(unusedIntentional.join("\n\t")));
63
+ logger.validationWarning(i18n.invalid_intentionally_not_exported_symbols_0(unusedIntentional.join("\n\t")));
64
64
  }
65
65
  }
@@ -34,10 +34,10 @@ function checkReflection(reflection, logger) {
34
34
  // If a link starts with it, and doesn't include a module source indicator "!"
35
35
  // then the user probably is trying to link to a package containing "@" with an absolute link.
36
36
  if (linkText.startsWith("@") && !linkText.includes("!")) {
37
- logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
37
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
38
38
  }
39
39
  else {
40
- logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1(linkText, reflection.getFriendlyFullName()));
40
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_readme_for_1(linkText, reflection.getFriendlyFullName()));
41
41
  }
42
42
  }
43
43
  }
@@ -45,10 +45,10 @@ function checkReflection(reflection, logger) {
45
45
  for (const broken of getBrokenPartLinks(reflection.content)) {
46
46
  const linkText = broken.text.trim();
47
47
  if (linkText.startsWith("@") && !linkText.includes("!")) {
48
- logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
48
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
49
49
  }
50
50
  else {
51
- logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1(linkText, reflection.getFriendlyFullName()));
51
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_document_1(linkText, reflection.getFriendlyFullName()));
52
52
  }
53
53
  }
54
54
  }
@@ -67,12 +67,12 @@ function checkReflection(reflection, logger) {
67
67
  function reportBrokenCommentLink(broken, reflection, logger) {
68
68
  const linkText = broken.text.trim();
69
69
  if (broken.target instanceof ReflectionSymbolId) {
70
- logger.warn(i18n.comment_for_0_links_to_1_not_included_in_docs_use_external_link_2(reflection.getFriendlyFullName(), linkText, `{ "${broken.target.packageName}": { "${broken.target.qualifiedName}": "#" }}`));
70
+ logger.validationWarning(i18n.comment_for_0_links_to_1_not_included_in_docs_use_external_link_2(reflection.getFriendlyFullName(), linkText, `{ "${broken.target.packageName}": { "${broken.target.qualifiedName}": "#" }}`));
71
71
  }
72
72
  else if (linkText.startsWith("@") && !linkText.includes("!")) {
73
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
73
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
74
74
  }
75
75
  else {
76
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1(linkText, reflection.getFriendlyFullName()));
76
+ logger.validationWarning(i18n.failed_to_resolve_link_to_0_in_comment_for_1(linkText, reflection.getFriendlyFullName()));
77
77
  }
78
78
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typedoc",
3
3
  "description": "Create api documentation for TypeScript projects.",
4
- "version": "0.28.14",
4
+ "version": "0.28.15",
5
5
  "homepage": "https://typedoc.org",
6
6
  "type": "module",
7
7
  "exports": {
@@ -31,7 +31,7 @@
31
31
  "pnpm": ">= 10"
32
32
  },
33
33
  "dependencies": {
34
- "@gerrit0/mini-shiki": "^3.12.0",
34
+ "@gerrit0/mini-shiki": "^3.17.0",
35
35
  "lunr": "^2.3.9",
36
36
  "markdown-it": "^14.1.0",
37
37
  "minimatch": "^9.0.5",
@@ -41,22 +41,22 @@
41
41
  "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x"
42
42
  },
43
43
  "devDependencies": {
44
- "@eslint/js": "^9.34.0",
44
+ "@eslint/js": "^9.39.1",
45
45
  "@types/lunr": "^2.3.7",
46
46
  "@types/markdown-it": "^14.1.2",
47
47
  "@types/mocha": "^10.0.10",
48
48
  "@types/node": "18",
49
49
  "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe",
50
50
  "c8": "^10.1.3",
51
- "dprint": "^0.50.1",
52
- "esbuild": "^0.25.9",
53
- "eslint": "^9.34.0",
54
- "mocha": "^11.7.1",
55
- "puppeteer": "^24.17.1",
56
- "semver": "^7.7.2",
57
- "tsx": "^4.20.5",
58
- "typescript": "5.9.2",
59
- "typescript-eslint": "^8.41.0"
51
+ "dprint": "^0.50.2",
52
+ "esbuild": "^0.27.0",
53
+ "eslint": "^9.39.1",
54
+ "mocha": "^11.7.5",
55
+ "puppeteer": "^24.31.0",
56
+ "semver": "^7.7.3",
57
+ "tsx": "^4.20.6",
58
+ "typescript": "5.9.3",
59
+ "typescript-eslint": "^8.48.0"
60
60
  },
61
61
  "files": [
62
62
  "/bin",
package/static/style.css CHANGED
@@ -1595,9 +1595,9 @@
1595
1595
  .container-main {
1596
1596
  grid-template-columns:
1597
1597
  minmax(0, 1fr) minmax(0, 2.5fr) minmax(
1598
- 0,
1599
- 20rem
1600
- );
1598
+ 0,
1599
+ 20rem
1600
+ );
1601
1601
  grid-template-areas: "sidebar content toc";
1602
1602
  }
1603
1603
 
package/tsdoc.json CHANGED
@@ -231,10 +231,6 @@
231
231
  "tagName": "@hideconstructor",
232
232
  "syntaxKind": "modifier"
233
233
  },
234
- {
235
- "tagName": "@jsx",
236
- "syntaxKind": "block"
237
- },
238
234
  {
239
235
  "tagName": "@summary",
240
236
  "syntaxKind": "block"