typedoc 0.28.6 → 0.28.8

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 (63) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/lib/converter/comments/blockLexer.d.ts +2 -1
  3. package/dist/lib/converter/comments/blockLexer.js +6 -5
  4. package/dist/lib/converter/comments/discovery.js +7 -1
  5. package/dist/lib/converter/comments/index.d.ts +20 -5
  6. package/dist/lib/converter/comments/index.js +26 -23
  7. package/dist/lib/converter/comments/lineLexer.js +1 -1
  8. package/dist/lib/converter/comments/linkResolver.d.ts +2 -2
  9. package/dist/lib/converter/comments/linkResolver.js +53 -10
  10. package/dist/lib/converter/comments/parser.d.ts +2 -2
  11. package/dist/lib/converter/comments/parser.js +6 -6
  12. package/dist/lib/converter/comments/rawLexer.js +1 -1
  13. package/dist/lib/converter/comments/textParser.js +93 -19
  14. package/dist/lib/converter/context.d.ts +10 -0
  15. package/dist/lib/converter/context.js +30 -10
  16. package/dist/lib/converter/converter.d.ts +3 -1
  17. package/dist/lib/converter/converter.js +6 -3
  18. package/dist/lib/converter/factories/signature.js +1 -2
  19. package/dist/lib/converter/factories/symbol-id.d.ts +1 -1
  20. package/dist/lib/converter/factories/symbol-id.js +2 -1
  21. package/dist/lib/converter/jsdoc.js +1 -2
  22. package/dist/lib/converter/plugins/CategoryPlugin.d.ts +3 -2
  23. package/dist/lib/converter/plugins/CategoryPlugin.js +15 -3
  24. package/dist/lib/converter/plugins/GroupPlugin.d.ts +2 -1
  25. package/dist/lib/converter/plugins/GroupPlugin.js +23 -9
  26. package/dist/lib/converter/plugins/ImplementsPlugin.js +49 -11
  27. package/dist/lib/converter/plugins/LinkResolverPlugin.d.ts +1 -2
  28. package/dist/lib/converter/plugins/LinkResolverPlugin.js +1 -55
  29. package/dist/lib/converter/types.js +2 -3
  30. package/dist/lib/converter/utils/repository.js +1 -1
  31. package/dist/lib/internationalization/internationalization.js +1 -1
  32. package/dist/lib/internationalization/locales/en.cjs +4 -1
  33. package/dist/lib/internationalization/locales/en.d.cts +4 -1
  34. package/dist/lib/models/Comment.js +1 -1
  35. package/dist/lib/models/ContainerReflection.d.ts +1 -0
  36. package/dist/lib/models/ContainerReflection.js +3 -0
  37. package/dist/lib/models/Reflection.d.ts +2 -0
  38. package/dist/lib/models/Reflection.js +3 -0
  39. package/dist/lib/output/themes/default/partials/moduleReflection.js +1 -1
  40. package/dist/lib/output/themes/default/partials/navigation.js +1 -1
  41. package/dist/lib/output/themes/default/templates/hierarchy.js +1 -1
  42. package/dist/lib/serialization/schema.d.ts +1 -1
  43. package/dist/lib/utils/options/declaration.d.ts +17 -9
  44. package/dist/lib/utils/options/declaration.js +52 -31
  45. package/dist/lib/utils/options/readers/arguments.js +2 -0
  46. package/dist/lib/utils/options/sources/typedoc.js +2 -5
  47. package/dist/lib/utils/options/tsdoc-defaults.d.ts +1 -1
  48. package/dist/lib/utils/options/tsdoc-defaults.js +1 -0
  49. package/dist/lib/utils/plugins.d.ts +2 -1
  50. package/dist/lib/utils/plugins.js +26 -17
  51. package/dist/lib/utils/sort.d.ts +2 -1
  52. package/dist/lib/utils/sort.js +4 -2
  53. package/dist/lib/utils-common/array.d.ts +1 -2
  54. package/dist/lib/utils-common/array.js +0 -7
  55. package/dist/lib/utils-common/jsx.elements.d.ts +1 -1
  56. package/dist/lib/utils-common/jsx.js +7 -6
  57. package/dist/lib/utils-common/map.d.ts +1 -1
  58. package/dist/lib/utils-common/path.d.ts +6 -0
  59. package/dist/lib/utils-common/validation.js +1 -1
  60. package/dist/lib/validation/links.js +0 -3
  61. package/package.json +12 -12
  62. package/static/style.css +2 -9
  63. package/tsdoc.json +4 -0
package/dist/index.d.ts CHANGED
@@ -41,7 +41,7 @@ export type { Icons, NavigationElement, PageDefinition, PageHeading, RendererEve
41
41
  export { Outputs } from "./lib/output/output.js";
42
42
  export { ArgumentsReader, CommentStyle, EntryPointStrategy, normalizePath, Option, OptionDefaults, Options, PackageJsonReader, ParameterHint, ParameterType, TSConfigReader, TypeDocReader, ValidatingFileRegistry, } from "./lib/utils/index.js";
43
43
  export type { ArrayDeclarationOption, BooleanDeclarationOption, DeclarationOption, DeclarationOptionBase, DeclarationOptionToOptionType, DocumentationEntryPoint, FancyConsoleLogger, FlagsDeclarationOption, JsDocCompatibility, KeyToDeclaration, ManuallyValidatedOption, MapDeclarationOption, MixedDeclarationOption, NumberDeclarationOption, ObjectDeclarationOption, OptionsReader, OutputSpecification, ParameterTypeToOptionTypeMap, SortStrategy, StringDeclarationOption, TypeDocOptionMap, TypeDocOptions, TypeDocOptionValues, ValidationOptions, } from "./lib/utils/index.js";
44
- export { type ComponentPath, ConsoleLogger, type DeclarationReference, type EnumKeys, EventDispatcher, EventHooks, type GlobString, i18n, JSX, Logger, LogLevel, type Meaning, type MeaningKeyword, type MinimalNode, MinimalSourceFile, type NormalizedPath, type NormalizedPathOrModule, type SymbolReference, type TranslatedString, translateTagName, } from "#utils";
44
+ export { type ComponentPath, ConsoleLogger, type DeclarationReference, type EnumKeys, EventDispatcher, EventHooks, type GlobString, i18n, JSX, Logger, LogLevel, type Meaning, type MeaningKeyword, type MinimalNode, MinimalSourceFile, type NormalizedPath, type NormalizedPathOrModule, type NormalizedPathOrModuleOrFunction, type SymbolReference, type TranslatedString, translateTagName, } from "#utils";
45
45
  export { type Deserializable, Deserializer, type DeserializerComponent, JSONOutput, SerializeEvent, Serializer, type SerializerComponent, type SerializerEvents, } from "./lib/serialization/index.js";
46
46
  export * as Internationalization from "./lib/internationalization/index.js";
47
47
  export type { TranslatableStrings } from "./lib/internationalization/internationalization.js";
@@ -1,3 +1,4 @@
1
1
  import ts from "typescript";
2
2
  import { type Token } from "./lexer.js";
3
- export declare function lexBlockComment(file: string, pos?: number, end?: number, jsDoc?: ts.JSDoc | undefined, checker?: ts.TypeChecker | undefined): Generator<Token, undefined, undefined>;
3
+ import type { Context } from "../context.js";
4
+ export declare function lexBlockComment(file: string, pos?: number, end?: number, createSymbolId?: Context["createSymbolId"], jsDoc?: ts.JSDoc | undefined, checker?: ts.TypeChecker | undefined): Generator<Token, undefined, undefined>;
@@ -1,11 +1,12 @@
1
1
  import ts from "typescript";
2
2
  import { TokenSyntaxKind } from "./lexer.js";
3
3
  import { resolveAliasedSymbol } from "../utils/symbols.js";
4
- import { createSymbolId } from "../factories/symbol-id.js";
5
- export function* lexBlockComment(file, pos = 0, end = file.length, jsDoc = undefined, checker = undefined) {
4
+ export function* lexBlockComment(file, pos = 0, end = file.length, createSymbolId = () => {
5
+ throw new Error("unreachable");
6
+ }, jsDoc = undefined, checker = undefined) {
6
7
  // Wrapper around our real lex function to collapse adjacent text tokens.
7
8
  let textToken;
8
- for (const token of lexBlockComment2(file, pos, end, getLinkTags(jsDoc), checker)) {
9
+ for (const token of lexBlockComment2(file, pos, end, getLinkTags(jsDoc), checker, createSymbolId)) {
9
10
  if (token.kind === TokenSyntaxKind.Text) {
10
11
  if (textToken) {
11
12
  textToken.text += token.text;
@@ -53,7 +54,7 @@ function getLinkTags(jsDoc) {
53
54
  }
54
55
  return result;
55
56
  }
56
- function* lexBlockComment2(file, pos, end, linkTags, checker) {
57
+ function* lexBlockComment2(file, pos, end, linkTags, checker, createSymbolId) {
57
58
  pos += 2; // Leading '/*'
58
59
  end -= 2; // Trailing '*/'
59
60
  if (pos < end && file[pos] === "*") {
@@ -208,7 +209,7 @@ function* lexBlockComment2(file, pos, end, linkTags, checker) {
208
209
  }
209
210
  if (lookahead !== pos + 1) {
210
211
  while (lookahead < end &&
211
- /[a-z0-9]/i.test(file[lookahead])) {
212
+ /[a-z0-9-]/i.test(file[lookahead])) {
212
213
  lookahead++;
213
214
  }
214
215
  }
@@ -78,6 +78,7 @@ const wantedKinds = {
78
78
  [ReflectionKind.Interface]: [
79
79
  ts.SyntaxKind.InterfaceDeclaration,
80
80
  ts.SyntaxKind.TypeAliasDeclaration,
81
+ ts.SyntaxKind.ClassDeclaration, // type only exports
81
82
  ],
82
83
  [ReflectionKind.Constructor]: [ts.SyntaxKind.Constructor],
83
84
  [ReflectionKind.Property]: variablePropertyKinds,
@@ -102,7 +103,12 @@ const wantedKinds = {
102
103
  [ReflectionKind.Accessor]: [ts.SyntaxKind.PropertyDeclaration],
103
104
  [ReflectionKind.GetSignature]: [ts.SyntaxKind.GetAccessor],
104
105
  [ReflectionKind.SetSignature]: [ts.SyntaxKind.SetAccessor],
105
- [ReflectionKind.TypeAlias]: [ts.SyntaxKind.TypeAliasDeclaration],
106
+ [ReflectionKind.TypeAlias]: [
107
+ ts.SyntaxKind.TypeAliasDeclaration,
108
+ ts.SyntaxKind.FunctionDeclaration, // type only exports
109
+ // Intentionally not included to avoid comments being copied for variable/alias combos
110
+ // ts.SyntaxKind.VariableDeclaration,
111
+ ],
106
112
  [ReflectionKind.Reference]: [
107
113
  ts.SyntaxKind.NamespaceExport,
108
114
  ts.SyntaxKind.ExportSpecifier,
@@ -3,6 +3,7 @@ import { Comment, ReflectionKind } from "../../models/index.js";
3
3
  import type { CommentStyle, JsDocCompatibility } from "../../utils/options/declaration.js";
4
4
  import type { FileRegistry } from "../../models/FileRegistry.js";
5
5
  import { type Logger } from "#utils";
6
+ import type { Context } from "../context.js";
6
7
  export interface CommentParserConfig {
7
8
  blockTags: Set<string>;
8
9
  inlineTags: Set<string>;
@@ -12,9 +13,23 @@ export interface CommentParserConfig {
12
13
  useTsLinkResolution: boolean;
13
14
  commentStyle: CommentStyle;
14
15
  }
16
+ export interface CommentContext {
17
+ config: CommentParserConfig;
18
+ logger: Logger;
19
+ checker: ts.TypeChecker;
20
+ files: FileRegistry;
21
+ createSymbolId: Context["createSymbolId"];
22
+ }
23
+ export interface CommentContextOptionalChecker {
24
+ config: CommentParserConfig;
25
+ logger: Logger;
26
+ checker?: ts.TypeChecker | undefined;
27
+ files: FileRegistry;
28
+ createSymbolId: Context["createSymbolId"];
29
+ }
15
30
  export declare function clearCommentCache(): void;
16
- export declare function getComment(symbol: ts.Symbol, kind: ReflectionKind, config: CommentParserConfig, logger: Logger, checker: ts.TypeChecker, files: FileRegistry): Comment | undefined;
17
- export declare function getNodeComment(node: ts.Node, moduleComment: boolean, config: CommentParserConfig, logger: Logger, checker: ts.TypeChecker | undefined, files: FileRegistry): Comment | undefined;
18
- export declare function getFileComment(file: ts.SourceFile, config: CommentParserConfig, logger: Logger, checker: ts.TypeChecker | undefined, files: FileRegistry): Comment | undefined;
19
- export declare function getSignatureComment(declaration: ts.SignatureDeclaration | ts.JSDocSignature, config: CommentParserConfig, logger: Logger, checker: ts.TypeChecker, files: FileRegistry): Comment | undefined;
20
- export declare function getJsDocComment(declaration: ts.JSDocPropertyLikeTag | ts.JSDocCallbackTag | ts.JSDocTypedefTag | ts.JSDocTemplateTag | ts.JSDocEnumTag, config: CommentParserConfig, logger: Logger, checker: ts.TypeChecker | undefined, files: FileRegistry): Comment | undefined;
31
+ export declare function getComment(symbol: ts.Symbol, kind: ReflectionKind, context: CommentContext): Comment | undefined;
32
+ export declare function getNodeComment(node: ts.Node, moduleComment: boolean, context: CommentContext): Comment | undefined;
33
+ export declare function getFileComment(file: ts.SourceFile, context: CommentContext): Comment | undefined;
34
+ export declare function getSignatureComment(declaration: ts.SignatureDeclaration | ts.JSDocSignature, context: CommentContext): Comment | undefined;
35
+ export declare function getJsDocComment(declaration: ts.JSDocPropertyLikeTag | ts.JSDocCallbackTag | ts.JSDocTypedefTag | ts.JSDocTemplateTag | ts.JSDocEnumTag, context: CommentContext): Comment | undefined;
@@ -21,7 +21,7 @@ export function clearCommentCache() {
21
21
  commentCache = new WeakMap();
22
22
  commentDiscoveryId = 0;
23
23
  }
24
- function getCommentWithCache(discovered, config, logger, checker, files) {
24
+ function getCommentWithCache(discovered, context) {
25
25
  if (!discovered)
26
26
  return;
27
27
  const { file, ranges, jsDoc } = discovered;
@@ -34,10 +34,10 @@ function getCommentWithCache(discovered, config, logger, checker, files) {
34
34
  let comment;
35
35
  switch (ranges[0].kind) {
36
36
  case ts.SyntaxKind.MultiLineCommentTrivia:
37
- comment = parseComment(lexBlockComment(file.text, ranges[0].pos, ranges[0].end, jsDoc, checker), config, file, logger, files);
37
+ comment = parseComment(lexBlockComment(file.text, ranges[0].pos, ranges[0].end, context.createSymbolId, jsDoc, context.checker), file, context);
38
38
  break;
39
39
  case ts.SyntaxKind.SingleLineCommentTrivia:
40
- comment = parseComment(lexLineComments(file.text, ranges), config, file, logger, files);
40
+ comment = parseComment(lexLineComments(file.text, ranges), file, context);
41
41
  break;
42
42
  default:
43
43
  assertNever(ranges[0].kind);
@@ -48,8 +48,11 @@ function getCommentWithCache(discovered, config, logger, checker, files) {
48
48
  commentCache.set(file, cache);
49
49
  return comment.clone();
50
50
  }
51
- function getCommentImpl(commentSource, config, logger, moduleComment, checker, files) {
52
- const comment = getCommentWithCache(commentSource, config, logger, config.useTsLinkResolution ? checker : undefined, files);
51
+ function getCommentImpl(commentSource, moduleComment, context) {
52
+ const comment = getCommentWithCache(commentSource, {
53
+ ...context,
54
+ checker: context.config.useTsLinkResolution ? context.checker : undefined,
55
+ });
53
56
  if (comment?.getTag("@import") || comment?.getTag("@license")) {
54
57
  return;
55
58
  }
@@ -70,15 +73,15 @@ function getCommentImpl(commentSource, config, logger, moduleComment, checker, f
70
73
  }
71
74
  return comment;
72
75
  }
73
- export function getComment(symbol, kind, config, logger, checker, files) {
76
+ export function getComment(symbol, kind, context) {
74
77
  const declarations = symbol.declarations || [];
75
78
  if (declarations.length &&
76
79
  declarations.every((d) => jsDocCommentKinds.includes(d.kind))) {
77
- return getJsDocComment(declarations[0], config, logger, checker, files);
80
+ return getJsDocComment(declarations[0], context);
78
81
  }
79
82
  const sf = declarations.find(ts.isSourceFile);
80
83
  if (sf) {
81
- return getFileComment(sf, config, logger, checker, files);
84
+ return getFileComment(sf, context);
82
85
  }
83
86
  const isModule = declarations.some((decl) => {
84
87
  if (ts.isModuleDeclaration(decl) && ts.isStringLiteral(decl.name)) {
@@ -86,18 +89,18 @@ export function getComment(symbol, kind, config, logger, checker, files) {
86
89
  }
87
90
  return false;
88
91
  });
89
- const comment = getCommentImpl(discoverComment(symbol, kind, logger, config.commentStyle, checker, !config.suppressCommentWarningsInDeclarationFiles), config, logger, isModule, checker, files);
92
+ const comment = getCommentImpl(discoverComment(symbol, kind, context.logger, context.config.commentStyle, context.checker, !context.config.suppressCommentWarningsInDeclarationFiles), isModule, context);
90
93
  if (!comment && kind === ReflectionKind.Property) {
91
- return getConstructorParamPropertyComment(symbol, config, logger, checker, files);
94
+ return getConstructorParamPropertyComment(symbol, context);
92
95
  }
93
96
  return comment;
94
97
  }
95
- export function getNodeComment(node, moduleComment, config, logger, checker, files) {
96
- return getCommentImpl(discoverNodeComment(node, config.commentStyle), config, logger, moduleComment, checker, files);
98
+ export function getNodeComment(node, moduleComment, context) {
99
+ return getCommentImpl(discoverNodeComment(node, context.config.commentStyle), moduleComment, context);
97
100
  }
98
- export function getFileComment(file, config, logger, checker, files) {
99
- for (const commentSource of discoverFileComments(file, config.commentStyle)) {
100
- const comment = getCommentWithCache(commentSource, config, logger, config.useTsLinkResolution ? checker : undefined, files);
101
+ export function getFileComment(file, context) {
102
+ for (const commentSource of discoverFileComments(file, context.config.commentStyle)) {
103
+ const comment = getCommentWithCache(commentSource, context);
101
104
  if (comment?.getTag("@license") || comment?.getTag("@import")) {
102
105
  continue;
103
106
  }
@@ -108,12 +111,12 @@ export function getFileComment(file, config, logger, checker, files) {
108
111
  return;
109
112
  }
110
113
  }
111
- function getConstructorParamPropertyComment(symbol, config, logger, checker, files) {
114
+ function getConstructorParamPropertyComment(symbol, context) {
112
115
  const decl = symbol.declarations?.find(ts.isParameter);
113
116
  if (!decl)
114
117
  return;
115
118
  const ctor = decl.parent;
116
- const comment = getSignatureComment(ctor, config, logger, checker, files);
119
+ const comment = getSignatureComment(ctor, context);
117
120
  const paramTag = comment?.getIdentifiedTag(symbol.name, "@param");
118
121
  if (paramTag) {
119
122
  const result = new Comment(paramTag.content);
@@ -121,10 +124,10 @@ function getConstructorParamPropertyComment(symbol, config, logger, checker, fil
121
124
  return result;
122
125
  }
123
126
  }
124
- export function getSignatureComment(declaration, config, logger, checker, files) {
125
- return getCommentImpl(discoverSignatureComment(declaration, checker, config.commentStyle), config, logger, false, checker, files);
127
+ export function getSignatureComment(declaration, context) {
128
+ return getCommentImpl(discoverSignatureComment(declaration, context.checker, context.config.commentStyle), false, context);
126
129
  }
127
- export function getJsDocComment(declaration, config, logger, checker, files) {
130
+ export function getJsDocComment(declaration, context) {
128
131
  const file = declaration.getSourceFile();
129
132
  // First, get the whole comment. We know we'll need all of it.
130
133
  let parent = declaration.parent;
@@ -143,7 +146,7 @@ export function getJsDocComment(declaration, config, logger, checker, files) {
143
146
  ],
144
147
  jsDoc: parent,
145
148
  inheritedFromParentDeclaration: false,
146
- }, config, logger, config.useTsLinkResolution ? checker : undefined, files);
149
+ }, context);
147
150
  // And pull out the tag we actually care about.
148
151
  if (ts.isJSDocEnumTag(declaration)) {
149
152
  const result = new Comment(comment.getTag("@enum")?.content);
@@ -156,7 +159,7 @@ export function getJsDocComment(declaration, config, logger, checker, files) {
156
159
  // We could just put the same comment on everything, but due to how comment parsing works,
157
160
  // we'd have to search for any @template with a name starting with the first type parameter's name
158
161
  // which feels horribly hacky.
159
- logger.warn(i18n.multiple_type_parameters_on_template_tag_unsupported(), declaration);
162
+ context.logger.warn(i18n.multiple_type_parameters_on_template_tag_unsupported(), declaration);
160
163
  return;
161
164
  }
162
165
  let name;
@@ -176,7 +179,7 @@ export function getJsDocComment(declaration, config, logger, checker, files) {
176
179
  // was a comment attached. If there wasn't, then don't error about failing to find
177
180
  // a tag because this is unsupported.
178
181
  if (!ts.isJSDocTemplateTag(declaration)) {
179
- logger.error(i18n.failed_to_find_jsdoc_tag_for_name_0(name), declaration);
182
+ context.logger.error(i18n.failed_to_find_jsdoc_tag_for_name_0(name), declaration);
180
183
  }
181
184
  }
182
185
  else {
@@ -145,7 +145,7 @@ function* lexLineComments2(file, pos, end) {
145
145
  }
146
146
  if (lookahead !== pos + 1) {
147
147
  while (lookahead < end &&
148
- /[a-z0-9]/i.test(file[lookahead])) {
148
+ /[a-z0-9-]/i.test(file[lookahead])) {
149
149
  lookahead++;
150
150
  }
151
151
  }
@@ -1,4 +1,4 @@
1
- import { type Comment, type CommentDisplayPart, Reflection, ReflectionSymbolId } from "../../models/index.js";
1
+ import { type CommentDisplayPart, Reflection, ReflectionSymbolId } from "../../models/index.js";
2
2
  import { type DeclarationReference } from "#utils";
3
3
  export type ExternalResolveResult = {
4
4
  target: string;
@@ -16,5 +16,5 @@ export type ExternalSymbolResolver = (ref: DeclarationReference, refl: Reflectio
16
16
  export type LinkResolverOptions = {
17
17
  preserveLinkText: boolean;
18
18
  };
19
- export declare function resolveLinks(comment: Comment, reflection: Reflection, externalResolver: ExternalSymbolResolver, options: LinkResolverOptions): void;
19
+ export declare function resolveLinks(reflection: Reflection, externalResolver: ExternalSymbolResolver, options: LinkResolverOptions): void;
20
20
  export declare function resolvePartLinks(reflection: Reflection, parts: readonly CommentDisplayPart[], externalResolver: ExternalSymbolResolver, options: LinkResolverOptions): CommentDisplayPart[];
@@ -1,19 +1,62 @@
1
1
  import ts from "typescript";
2
- import { DeclarationReflection, Reflection, ReflectionKind, ReflectionSymbolId, } from "../../models/index.js";
2
+ import { makeRecursiveVisitor, Reflection, ReflectionKind, ReflectionSymbolId, } from "../../models/index.js";
3
3
  import { resolveDeclarationReference } from "./declarationReferenceResolver.js";
4
4
  import { maxElementByScore, parseDeclarationReference } from "#utils";
5
5
  const urlPrefix = /^(http|ftp)s?:\/\//;
6
- export function resolveLinks(comment, reflection, externalResolver, options) {
7
- comment.summary = resolvePartLinks(reflection, comment.summary, externalResolver, options);
8
- for (const tag of comment.blockTags) {
9
- tag.content = resolvePartLinks(reflection, tag.content, externalResolver, options);
6
+ export function resolveLinks(reflection, externalResolver, options) {
7
+ if (reflection.comment) {
8
+ reflection.comment.summary = resolvePartLinks(reflection, reflection.comment.summary, externalResolver, options);
9
+ for (const tag of reflection.comment.blockTags) {
10
+ tag.content = resolvePartLinks(reflection, tag.content, externalResolver, options);
11
+ }
10
12
  }
11
- if (reflection instanceof DeclarationReflection && reflection.readme) {
13
+ if ((reflection.isDeclaration() || reflection.isProject()) && reflection.readme) {
12
14
  reflection.readme = resolvePartLinks(reflection, reflection.readme, externalResolver, options);
13
15
  }
16
+ if (reflection.isDeclaration()) {
17
+ reflection.type?.visit(makeRecursiveVisitor({
18
+ union: (type) => {
19
+ type.elementSummaries = type.elementSummaries?.map((parts) => resolvePartLinks(reflection, parts, externalResolver, options));
20
+ },
21
+ }));
22
+ }
14
23
  if (reflection.isDocument()) {
15
24
  reflection.content = resolvePartLinks(reflection, reflection.content, externalResolver, options);
16
25
  }
26
+ if (reflection.isParameter() &&
27
+ reflection.type?.type === "reference" &&
28
+ reflection.type.highlightedProperties) {
29
+ const resolved = new Map(Array.from(reflection.type.highlightedProperties, ([name, parts]) => {
30
+ return [
31
+ name,
32
+ resolvePartLinks(reflection, parts, externalResolver, options),
33
+ ];
34
+ }));
35
+ reflection.type.highlightedProperties = resolved;
36
+ }
37
+ if (reflection.isContainer()) {
38
+ if (reflection.groups) {
39
+ for (const group of reflection.groups) {
40
+ if (group.description) {
41
+ group.description = resolvePartLinks(reflection, group.description, externalResolver, options);
42
+ }
43
+ if (group.categories) {
44
+ for (const cat of group.categories) {
45
+ if (cat.description) {
46
+ cat.description = resolvePartLinks(reflection, cat.description, externalResolver, options);
47
+ }
48
+ }
49
+ }
50
+ }
51
+ }
52
+ if (reflection.categories) {
53
+ for (const cat of reflection.categories) {
54
+ if (cat.description) {
55
+ cat.description = resolvePartLinks(reflection, cat.description, externalResolver, options);
56
+ }
57
+ }
58
+ }
59
+ }
17
60
  }
18
61
  export function resolvePartLinks(reflection, parts, externalResolver, options) {
19
62
  return parts.flatMap((part) => processPart(reflection, part, externalResolver, options));
@@ -48,10 +91,10 @@ function resolveLinkTag(reflection, part, externalResolver, options) {
48
91
  const tsTargets = reflection.project.getReflectionsFromSymbolId(part.target);
49
92
  if (tsTargets.length) {
50
93
  // Find the target most likely to have a real url in the generated documentation
51
- // 1. A direct export (class, interface, variable)
52
- // 2. A property of a direct export (class/interface property)
53
- // 3. A property of a type of an export (property on type alias)
54
- // 4. Whatever the first symbol found was
94
+ // 4. A direct export (class, interface, variable)
95
+ // 3. A property of a direct export (class/interface property)
96
+ // 2. A property of a type of an export (property on type alias)
97
+ // 1. Whatever the first symbol found was
55
98
  target = maxElementByScore(tsTargets, (r) => {
56
99
  if (r.kindOf(ReflectionKind.SomeExport)) {
57
100
  return 4;
@@ -1,10 +1,10 @@
1
- import type { CommentParserConfig } from "./index.js";
1
+ import type { CommentContextOptionalChecker, CommentParserConfig } from "./index.js";
2
2
  import { Comment, type CommentDisplayPart } from "../../models/index.js";
3
3
  import type { MinimalSourceFile } from "#utils";
4
4
  import { type Token } from "./lexer.js";
5
5
  import { FileRegistry } from "../../models/FileRegistry.js";
6
6
  import { type Logger } from "#utils";
7
- export declare function parseComment(tokens: Generator<Token, undefined, undefined>, config: CommentParserConfig, file: MinimalSourceFile, logger: Logger, files: FileRegistry): Comment;
7
+ export declare function parseComment(tokens: Generator<Token, undefined, undefined>, file: MinimalSourceFile, context: CommentContextOptionalChecker): Comment;
8
8
  /**
9
9
  * Intended for parsing markdown documents. This only parses code blocks and
10
10
  * inline tags outside of code blocks, everything else is text.
@@ -40,24 +40,24 @@ function makeLookaheadGenerator(gen) {
40
40
  },
41
41
  };
42
42
  }
43
- export function parseComment(tokens, config, file, logger, files) {
43
+ export function parseComment(tokens, file, context) {
44
44
  const lexer = makeLookaheadGenerator(tokens);
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, config, i18n, warningImpl, files);
48
+ comment.summary = blockContent(comment, lexer, context.config, i18n, warningImpl, context.files);
49
49
  while (!lexer.done()) {
50
- comment.blockTags.push(blockTag(comment, lexer, config, i18n, warningImpl, files));
50
+ comment.blockTags.push(blockTag(comment, lexer, context.config, i18n, warningImpl, context.files));
51
51
  }
52
52
  const tok2 = tok;
53
- postProcessComment(comment, i18n, () => `${nicePath(file.fileName)}:${file.getLineAndCharacterOfPosition(tok2.pos).line + 1}`, (message) => logger.warn(message));
53
+ postProcessComment(comment, i18n, () => `${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
- if (config.suppressCommentWarningsInDeclarationFiles &&
56
+ if (context.config.suppressCommentWarningsInDeclarationFiles &&
57
57
  hasDeclarationFileExtension(file.fileName)) {
58
58
  return;
59
59
  }
60
- logger.warn(message, token.pos, file);
60
+ context.logger.warn(message, token.pos, file);
61
61
  }
62
62
  }
63
63
  /**
@@ -151,7 +151,7 @@ function* lexCommentString2(file) {
151
151
  }
152
152
  if (lookahead !== pos + 1) {
153
153
  while (lookahead < end &&
154
- /[a-z0-9]/i.test(file[lookahead])) {
154
+ /[a-z0-9-]/i.test(file[lookahead])) {
155
155
  lookahead++;
156
156
  }
157
157
  }
@@ -85,9 +85,11 @@ export function textContent(sourcePath, token, i18n, warning, outContent, files,
85
85
  addRef(reference);
86
86
  continue;
87
87
  }
88
- const tagLink = checkTagLink(data);
89
- if (tagLink) {
90
- addRef(tagLink);
88
+ const tagLinks = checkTagLink(data);
89
+ if (tagLinks.length) {
90
+ for (const tagLink of tagLinks) {
91
+ addRef(tagLink);
92
+ }
91
93
  continue;
92
94
  }
93
95
  const atNewLine = token.text[data.pos] === "\n";
@@ -221,39 +223,111 @@ function checkReference(data) {
221
223
  }
222
224
  }
223
225
  /**
224
- * Looks for `<a href="./relative">` and `<img src="./relative">`
226
+ * Looks for `<a href="./relative">`, `<img src="./relative">`, and `<source srcset="./relative">`
225
227
  */
226
228
  function checkTagLink(data) {
227
229
  const { pos, token } = data;
228
230
  if (token.text.startsWith("<img ", pos)) {
229
231
  data.pos += 4;
230
- return checkAttribute(data, "src");
232
+ return checkAttributes(data, {
233
+ src: checkAttributeDirectPath,
234
+ srcset: checkAttributeSrcSet,
235
+ });
236
+ }
237
+ if (token.text.startsWith("<link ", pos)) {
238
+ data.pos += 4;
239
+ return checkAttributes(data, {
240
+ imagesrcset: checkAttributeSrcSet,
241
+ });
231
242
  }
232
243
  if (token.text.startsWith("<a ", pos)) {
233
244
  data.pos += 3;
234
- return checkAttribute(data, "href");
245
+ return checkAttributes(data, { href: checkAttributeDirectPath });
235
246
  }
247
+ if (token.text.startsWith("<source ", pos)) {
248
+ data.pos += 8;
249
+ return checkAttributes(data, {
250
+ src: checkAttributeDirectPath,
251
+ srcset: checkAttributeSrcSet,
252
+ });
253
+ }
254
+ return [];
236
255
  }
237
- function checkAttribute(data, attr) {
256
+ function checkAttributes(data, attributes) {
257
+ const links = [];
238
258
  const parser = new HtmlAttributeParser(data.token.text, data.pos);
239
259
  while (parser.state !== ParserState.END) {
240
260
  if (parser.state === ParserState.BeforeAttributeValue &&
241
- parser.currentAttributeName === attr) {
261
+ Object.prototype.hasOwnProperty.call(attributes, parser.currentAttributeName)) {
242
262
  parser.step();
243
- if (isRelativePath(parser.currentAttributeValue)) {
244
- data.pos = parser.pos;
245
- const { target, anchor } = data.files.register(data.sourcePath, parser.currentAttributeValue) || { target: undefined, anchor: undefined };
246
- return {
247
- pos: parser.currentAttributeValueStart,
248
- end: parser.currentAttributeValueEnd,
249
- target,
250
- targetAnchor: anchor,
251
- };
252
- }
253
- return;
263
+ links.push(...attributes[parser.currentAttributeName](data, parser.currentAttributeValue, parser.currentAttributeValueStart, parser.currentAttributeValueEnd));
254
264
  }
255
265
  parser.step();
256
266
  }
267
+ return links;
268
+ }
269
+ function checkAttributeDirectPath(data, text, pos, end) {
270
+ if (isRelativePath(text.trim())) {
271
+ const { target, anchor } = data.files.register(data.sourcePath, text.trim()) || { target: undefined, anchor: undefined };
272
+ return [{
273
+ pos,
274
+ end,
275
+ target,
276
+ targetAnchor: anchor,
277
+ }];
278
+ }
279
+ return [];
280
+ }
281
+ // See https://html.spec.whatwg.org/multipage/images.html#srcset-attribute
282
+ function checkAttributeSrcSet(data, text, pos, _end) {
283
+ const result = [];
284
+ let textPos = 0;
285
+ parseImageCandidate();
286
+ while (textPos < text.length && text[textPos] == ",") {
287
+ ++textPos;
288
+ parseImageCandidate();
289
+ }
290
+ return result;
291
+ function parseImageCandidate() {
292
+ // 1. Zero or more ASCII whitespace
293
+ while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos]))
294
+ ++textPos;
295
+ // 2. A valid non-empty URL that does not start or end with a comma
296
+ // TypeDoc: We don't exactly match this, PR welcome! For now, just permit anything
297
+ // that's not whitespace or a comma
298
+ const url = text.slice(textPos).match(/^[^\t\r\f\n ,]+/);
299
+ if (url && isRelativePath(url[0])) {
300
+ const { target, anchor } = data.files.register(data.sourcePath, url[0]) || { target: undefined, anchor: undefined };
301
+ result.push({
302
+ pos: pos + textPos,
303
+ end: pos + textPos + url[0].length,
304
+ target,
305
+ targetAnchor: anchor,
306
+ });
307
+ }
308
+ textPos += url ? url[0].length : 0;
309
+ // 3. Zero or more ASCII whitespace
310
+ while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos]))
311
+ ++textPos;
312
+ // 4. Zero or one of the following:
313
+ {
314
+ // A width descriptor, consisting of: ASCII whitespace, a valid non-negative integer giving
315
+ // a number greater than zero representing the width descriptor value, and a U+0077 LATIN
316
+ // SMALL LETTER W character.
317
+ const w = text.slice(textPos).match(/^\+?\d+\s*w/);
318
+ textPos += w ? w[0].length : 0;
319
+ // A pixel density descriptor, consisting of: ASCII whitespace, a valid floating-point number
320
+ // giving a number greater than zero representing the pixel density descriptor value, and a
321
+ // U+0078 LATIN SMALL LETTER X character.
322
+ if (!w) {
323
+ const x = text.slice(textPos).match(/^\+?\d+(\.\d+)?([eE][+-]\d+)?\s*x/);
324
+ textPos += x ? x[0].length : 0;
325
+ }
326
+ }
327
+ // 5. Zero or more ASCII whitespace
328
+ while (textPos < text.length && /[\t\r\f\n ]/.test(text[textPos]))
329
+ ++textPos;
330
+ }
257
331
  }
258
332
  function isRelativePath(link) {
259
333
  // Lots of edge cases encoded right here!
@@ -67,6 +67,15 @@ export declare class Context {
67
67
  * This is available on Context so that it can be monkey-patched by typedoc-plugin-missing-exports
68
68
  */
69
69
  createSymbolReference(symbol: ts.Symbol, context: Context, name?: string): ReferenceType;
70
+ /**
71
+ * Create a stable {@link ReflectionSymbolId} for the provided symbol,
72
+ * optionally targeting a specific declaration.
73
+ *
74
+ * @privateRemarks
75
+ * This is available on Context so that it can be monkey-patched by typedoc-plugin-missing-exports
76
+ * It might also turn out to be generally useful for other plugin users.
77
+ */
78
+ createSymbolId(symbol: ts.Symbol, declaration?: ts.Declaration): import("../models/ReflectionSymbolId.js").ReflectionSymbolId;
70
79
  addChild(reflection: DeclarationReflection | DocumentReflection): void;
71
80
  shouldIgnore(symbol: ts.Symbol): boolean;
72
81
  /**
@@ -81,6 +90,7 @@ export declare class Context {
81
90
  getSymbolFromReflection(reflection: Reflection): ts.Symbol | undefined;
82
91
  /** @internal */
83
92
  setActiveProgram(program: ts.Program | undefined): void;
93
+ private createCommentContext;
84
94
  getComment(symbol: ts.Symbol, kind: ReflectionKind): Comment | undefined;
85
95
  getNodeComment(node: ts.Node, moduleComment: boolean): Comment | undefined;
86
96
  getFileComment(node: ts.SourceFile): Comment | undefined;