typedoc 0.28.5 → 0.28.6

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 (29) hide show
  1. package/dist/lib/converter/comments/textParser.d.ts +1 -0
  2. package/dist/lib/converter/comments/textParser.js +76 -34
  3. package/dist/lib/converter/context.js +14 -3
  4. package/dist/lib/converter/converter.js +3 -0
  5. package/dist/lib/converter/plugins/PackagePlugin.js +5 -1
  6. package/dist/lib/converter/symbols.js +37 -2
  7. package/dist/lib/internationalization/locales/de.cjs +1 -1
  8. package/dist/lib/internationalization/locales/de.d.cts +1 -1
  9. package/dist/lib/internationalization/locales/en.cjs +2 -1
  10. package/dist/lib/internationalization/locales/en.d.cts +2 -1
  11. package/dist/lib/internationalization/locales/ja.cjs +1 -1
  12. package/dist/lib/internationalization/locales/zh.cjs +1 -1
  13. package/dist/lib/internationalization/locales/zh.d.cts +1 -1
  14. package/dist/lib/models/Comment.d.ts +2 -1
  15. package/dist/lib/models/DocumentReflection.js +1 -1
  16. package/dist/lib/models/FileRegistry.d.ts +20 -8
  17. package/dist/lib/models/FileRegistry.js +19 -6
  18. package/dist/lib/models/ProjectReflection.js +1 -1
  19. package/dist/lib/models/Reflection.d.ts +4 -0
  20. package/dist/lib/models/types.d.ts +2 -2
  21. package/dist/lib/models/types.js +16 -14
  22. package/dist/lib/output/plugins/AssetsPlugin.js +7 -2
  23. package/dist/lib/output/themes/MarkedPlugin.js +2 -2
  24. package/dist/lib/serialization/deserializer.d.ts +3 -3
  25. package/dist/lib/serialization/schema.d.ts +10 -8
  26. package/dist/lib/utils/ValidatingFileRegistry.d.ts +2 -2
  27. package/dist/lib/utils/ValidatingFileRegistry.js +7 -4
  28. package/dist/lib/validation/links.js +23 -25
  29. package/package.json +1 -1
@@ -18,6 +18,7 @@ import type { NormalizedPath, TranslatedString } from "#utils";
18
18
  */
19
19
  export declare class TextParserReentryState {
20
20
  withinLinkLabel: boolean;
21
+ withinLinkDest: boolean;
21
22
  private lastPartWasNewline;
22
23
  checkState(token: Token): void;
23
24
  }
@@ -10,17 +10,20 @@ const MdHelpers = new MarkdownIt().helpers;
10
10
  */
11
11
  export class TextParserReentryState {
12
12
  withinLinkLabel = false;
13
+ withinLinkDest = false;
13
14
  lastPartWasNewline = false;
14
15
  checkState(token) {
15
16
  switch (token.kind) {
16
17
  case TokenSyntaxKind.Code:
17
18
  if (/\n\s*\n/.test(token.text)) {
18
19
  this.withinLinkLabel = false;
20
+ this.withinLinkDest = false;
19
21
  }
20
22
  break;
21
23
  case TokenSyntaxKind.NewLine:
22
24
  if (this.lastPartWasNewline) {
23
25
  this.withinLinkLabel = false;
26
+ this.withinLinkDest = false;
24
27
  }
25
28
  break;
26
29
  }
@@ -33,16 +36,17 @@ export class TextParserReentryState {
33
36
  */
34
37
  export function textContent(sourcePath, token, i18n, warning, outContent, files, atNewLine, reentry) {
35
38
  let lastPartEnd = 0;
39
+ let canEndMarkdownLink = true;
36
40
  const data = {
37
41
  sourcePath,
38
42
  token,
39
43
  pos: 0, // relative to the token
40
- i18n,
41
44
  warning,
42
45
  files: files,
43
46
  atNewLine,
44
47
  };
45
48
  function addRef(ref) {
49
+ canEndMarkdownLink = true;
46
50
  outContent.push({
47
51
  kind: "text",
48
52
  text: token.text.slice(lastPartEnd, ref.pos),
@@ -66,10 +70,15 @@ export function textContent(sourcePath, token, i18n, warning, outContent, files,
66
70
  }
67
71
  }
68
72
  while (data.pos < token.text.length) {
69
- const link = checkMarkdownLink(data, reentry);
70
- if (link) {
71
- addRef(link);
72
- continue;
73
+ if (canEndMarkdownLink) {
74
+ const link = checkMarkdownLink(data, reentry);
75
+ if (link) {
76
+ addRef(link);
77
+ continue;
78
+ }
79
+ // If we're within a Markdown link, then `checkMarkdownLink`
80
+ // already scanned `token` up to a line feed (if any).
81
+ canEndMarkdownLink = !reentry.withinLinkLabel && !reentry.withinLinkDest;
73
82
  }
74
83
  const reference = checkReference(data);
75
84
  if (reference) {
@@ -81,7 +90,10 @@ export function textContent(sourcePath, token, i18n, warning, outContent, files,
81
90
  addRef(tagLink);
82
91
  continue;
83
92
  }
84
- data.atNewLine = token.text[data.pos] === "\n";
93
+ const atNewLine = token.text[data.pos] === "\n";
94
+ data.atNewLine = atNewLine;
95
+ if (atNewLine && !reentry.withinLinkDest)
96
+ canEndMarkdownLink = true;
85
97
  ++data.pos;
86
98
  }
87
99
  if (lastPartEnd !== token.text.length) {
@@ -101,9 +113,8 @@ export function textContent(sourcePath, token, i18n, warning, outContent, files,
101
113
  function checkMarkdownLink(data, reentry) {
102
114
  const { token, sourcePath, files } = data;
103
115
  let searchStart;
104
- if (reentry.withinLinkLabel) {
116
+ if (reentry.withinLinkLabel || reentry.withinLinkDest) {
105
117
  searchStart = data.pos;
106
- reentry.withinLinkLabel = false;
107
118
  }
108
119
  else if (token.text[data.pos] === "[") {
109
120
  searchStart = data.pos + 1;
@@ -111,34 +122,60 @@ function checkMarkdownLink(data, reentry) {
111
122
  else {
112
123
  return;
113
124
  }
114
- const labelEnd = findLabelEnd(token.text, searchStart);
115
- if (labelEnd === -1) {
116
- // This markdown link might be split across multiple display parts
117
- // [ `text` ](link)
118
- // ^^ text
119
- // ^^^^^^ code
120
- // ^^^^^^^^ text
121
- reentry.withinLinkLabel = true;
122
- return;
125
+ if (!reentry.withinLinkDest) {
126
+ const labelEnd = findLabelEnd(token.text, searchStart);
127
+ if (labelEnd === -1 || token.text[labelEnd] === "\n") {
128
+ // This markdown link might be split across multiple lines or input tokens
129
+ // [prefix `code` suffix](target)
130
+ // ........^^^^^^................
131
+ // Unless we encounter two consecutive line feeds, expect it to keep going.
132
+ reentry.withinLinkLabel = labelEnd !== data.pos || !data.atNewLine;
133
+ return;
134
+ }
135
+ reentry.withinLinkLabel = false;
136
+ if (!token.text.startsWith("](", labelEnd))
137
+ return;
138
+ searchStart = labelEnd + 2;
123
139
  }
124
- if (token.text[labelEnd] === "]" && token.text[labelEnd + 1] === "(") {
125
- const link = MdHelpers.parseLinkDestination(token.text, labelEnd + 2, token.text.length);
126
- if (link.ok) {
127
- // Only make a relative-link display part if it's actually a relative link.
128
- // Discard protocol:// links, unix style absolute paths, and windows style absolute paths.
129
- if (isRelativePath(link.str)) {
130
- const { target, anchor } = files.register(sourcePath, link.str) || { target: undefined, anchor: undefined };
131
- return {
132
- pos: labelEnd + 2,
133
- end: link.pos,
134
- target,
135
- targetAnchor: anchor,
136
- };
137
- }
138
- // This was a link, skip ahead to ensure we don't happen to parse
139
- // something else as a link within the link.
140
- data.pos = link.pos - 1;
140
+ // Skip whitespace (including line breaks) between "](" and the link destination.
141
+ // https://spec.commonmark.org/0.31.2/#links
142
+ const end = token.text.length;
143
+ let lookahead = searchStart;
144
+ for (let newlines = 0;; ++lookahead) {
145
+ if (lookahead === end) {
146
+ reentry.withinLinkDest = true;
147
+ return;
148
+ }
149
+ switch (token.text[lookahead]) {
150
+ case "\n":
151
+ if (++newlines === 2) {
152
+ reentry.withinLinkDest = false;
153
+ return;
154
+ }
155
+ continue;
156
+ case " ":
157
+ case "\t":
158
+ continue;
141
159
  }
160
+ break;
161
+ }
162
+ reentry.withinLinkDest = false;
163
+ const link = MdHelpers.parseLinkDestination(token.text, lookahead, end);
164
+ if (link.ok) {
165
+ // Only make a relative-link display part if it's actually a relative link.
166
+ // Discard protocol:// links, unix style absolute paths, and windows style absolute paths.
167
+ if (isRelativePath(link.str)) {
168
+ const { target, anchor } = files.register(sourcePath, link.str) || { target: undefined, anchor: undefined };
169
+ return {
170
+ pos: lookahead,
171
+ end: link.pos,
172
+ target,
173
+ targetAnchor: anchor,
174
+ };
175
+ }
176
+ // This was a link, skip ahead to ensure we don't happen to parse
177
+ // something else as a link within the link.
178
+ data.pos = link.pos - 1;
142
179
  }
143
180
  }
144
181
  /**
@@ -230,6 +267,11 @@ function isRelativePath(link) {
230
267
  function findLabelEnd(text, pos) {
231
268
  while (pos < text.length) {
232
269
  switch (text[pos]) {
270
+ case "\\":
271
+ ++pos;
272
+ if (pos < text.length && text[pos] === "\n")
273
+ return pos;
274
+ break;
233
275
  case "\n":
234
276
  case "]":
235
277
  case "[":
@@ -130,14 +130,25 @@ export class Context {
130
130
  return reflection;
131
131
  }
132
132
  postReflectionCreation(reflection, symbol, exportSymbol) {
133
- if (exportSymbol &&
134
- reflection.kind &
135
- (ReflectionKind.SomeModule | ReflectionKind.Reference)) {
133
+ // Allow comments on export declarations to take priority over comments directly
134
+ // on the symbol to enable overriding comments on modules/references, #1504
135
+ if (!reflection.comment &&
136
+ exportSymbol &&
137
+ (reflection.kind &
138
+ (ReflectionKind.SomeModule | ReflectionKind.Reference))) {
136
139
  reflection.comment = this.getComment(exportSymbol, reflection.kind);
137
140
  }
141
+ // If that didn't get us a comment (the normal case), then get the comment from
142
+ // the source declarations, this is the common case.
138
143
  if (symbol && !reflection.comment) {
139
144
  reflection.comment = this.getComment(symbol, reflection.kind);
140
145
  }
146
+ // If we still don't have a comment, check for any comments on the export declaration,
147
+ // we don't have to worry about functions being weird in this case as the regular declaration
148
+ // doesn't have any comment.
149
+ if (exportSymbol && !reflection.comment) {
150
+ reflection.comment = this.getComment(exportSymbol, ReflectionKind.Reference);
151
+ }
141
152
  if (this.shouldBeStatic) {
142
153
  reflection.setFlag(ReflectionFlag.Static);
143
154
  }
@@ -305,6 +305,9 @@ let Converter = (() => {
305
305
  const programs = unique(entryPoints.map((e) => e.program));
306
306
  this.externalPatternCache = void 0;
307
307
  const project = new ProjectReflection(this.application.options.getValue("name"), this.application.files);
308
+ if (this.owner.options.packageDir) {
309
+ project.files.registerReflectionPath(normalizePath(this.owner.options.packageDir), project);
310
+ }
308
311
  const context = new Context(this, programs, project);
309
312
  this.trigger(Converter.EVENT_BEGIN, context);
310
313
  this.addProjectDocuments(project);
@@ -36,7 +36,7 @@ import * as Path from "path";
36
36
  import { ConverterComponent } from "../components.js";
37
37
  import { ApplicationEvents } from "../../application-events.js";
38
38
  import { ConverterEvents } from "../converter-events.js";
39
- import { i18n, MinimalSourceFile } from "#utils";
39
+ import { i18n, MinimalSourceFile, NormalizedPathUtils } from "#utils";
40
40
  import { discoverPackageJson, getCommonDirectory, nicePath, normalizePath, Option, readFile, } from "#node-utils";
41
41
  import { existsSync } from "fs";
42
42
  /**
@@ -161,6 +161,10 @@ let PackagePlugin = (() => {
161
161
  if (this.readmeFile && this.readmeContents) {
162
162
  const { content } = this.application.converter.parseRawComment(new MinimalSourceFile(this.readmeContents, this.readmeFile), project.files);
163
163
  project.readme = content;
164
+ project.files.registerReflectionPath(this.readmeFile, project);
165
+ // In packages mode, this probably won't do anything unless someone uses the readme
166
+ // option to select a different file.
167
+ project.files.registerReflectionPath(NormalizedPathUtils.dirname(this.readmeFile), project);
164
168
  // This isn't ideal, but seems better than figuring out the readme
165
169
  // path over in the include plugin...
166
170
  this.owner.includePlugin.checkIncludeTagsParts(project, Path.dirname(this.readmeFile), content);
@@ -238,6 +238,25 @@ function convertTypeAlias(context, symbol, exportSymbol) {
238
238
  convertJsDocCallback(context, symbol, declaration, exportSymbol);
239
239
  }
240
240
  }
241
+ function convertTypeAliasFromValueDeclaration(context, symbol, exportSymbol, valueKind) {
242
+ const comment = context.getComment(symbol, valueKind);
243
+ const reflection = new DeclarationReflection(exportSymbol?.name || symbol.name, ReflectionKind.TypeAlias, context.scope);
244
+ reflection.comment = comment;
245
+ context.postReflectionCreation(reflection, symbol, exportSymbol);
246
+ context.finalizeDeclarationReflection(reflection);
247
+ reflection.type = context.converter.convertType(context.withScope(reflection), context.checker.getTypeOfSymbol(symbol));
248
+ if (reflection.type.type === "reflection" && reflection.type.declaration.children) {
249
+ // #2817 lift properties of object literal types up to the reflection level.
250
+ const typeDecl = reflection.type.declaration;
251
+ reflection.project.mergeReflections(typeDecl, reflection);
252
+ delete reflection.type;
253
+ // When created any signatures will be created with __type as their
254
+ // name, rename them so that they have the alias's name as their name
255
+ for (const sig of reflection.signatures || []) {
256
+ sig.name = reflection.name;
257
+ }
258
+ }
259
+ }
241
260
  function attachUnionComments(context, declaration, union) {
242
261
  const list = declaration.type.getChildAt(0);
243
262
  if (list.kind !== ts.SyntaxKind.SyntaxList)
@@ -285,6 +304,9 @@ function convertTypeAliasAsInterface(context, symbol, exportSymbol, declaration)
285
304
  convertIndexSignatures(rc, type);
286
305
  }
287
306
  function convertFunctionOrMethod(context, symbol, exportSymbol) {
307
+ if (isTypeOnlyExport(exportSymbol)) {
308
+ return convertTypeAliasFromValueDeclaration(context, symbol, exportSymbol, ReflectionKind.Function);
309
+ }
288
310
  // Can't just check method flag because this might be called for properties as well
289
311
  // This will *NOT* be called for variables that look like functions, they need a special case.
290
312
  const isMethod = !!(symbol.flags &
@@ -330,7 +352,7 @@ function convertFunctionOrMethod(context, symbol, exportSymbol) {
330
352
  // getDeclaredTypeOfSymbol gets the INSTANCE type
331
353
  // getTypeOfSymbolAtLocation gets the STATIC type
332
354
  function convertClassOrInterface(context, symbol, exportSymbol) {
333
- const reflection = context.createDeclarationReflection(ts.SymbolFlags.Class & symbol.flags
355
+ const reflection = context.createDeclarationReflection((ts.SymbolFlags.Class & symbol.flags) && !isTypeOnlyExport(exportSymbol)
334
356
  ? ReflectionKind.Class
335
357
  : ReflectionKind.Interface, symbol, exportSymbol, void 0);
336
358
  const classDeclaration = symbol
@@ -355,7 +377,7 @@ function convertClassOrInterface(context, symbol, exportSymbol) {
355
377
  reflection.implementedTypes = implementedTypes;
356
378
  }
357
379
  context.finalizeDeclarationReflection(reflection);
358
- if (classDeclaration) {
380
+ if (classDeclaration && reflection.kind === ReflectionKind.Class) {
359
381
  // Classes can have static props
360
382
  const staticType = context.checker.getTypeOfSymbolAtLocation(symbol, classDeclaration);
361
383
  reflectionContext.shouldBeStatic = true;
@@ -548,6 +570,9 @@ function createAlias(target, context, symbol, exportSymbol) {
548
570
  context.finalizeDeclarationReflection(ref);
549
571
  }
550
572
  function convertVariable(context, symbol, exportSymbol) {
573
+ if (isTypeOnlyExport(exportSymbol)) {
574
+ return convertTypeAliasFromValueDeclaration(context, symbol, exportSymbol, ReflectionKind.Variable);
575
+ }
551
576
  const declaration = symbol.getDeclarations()?.[0];
552
577
  const comment = context.getComment(symbol, ReflectionKind.Variable);
553
578
  const type = declaration
@@ -826,3 +851,13 @@ function isFunctionLikeInitializer(node) {
826
851
  }
827
852
  return false;
828
853
  }
854
+ function isTypeOnlyExport(symbol) {
855
+ if (!symbol)
856
+ return false;
857
+ const declaration = symbol.declarations?.[0];
858
+ if (!declaration)
859
+ return false;
860
+ if (!ts.isExportSpecifier(declaration))
861
+ return false;
862
+ return declaration.isTypeOnly || declaration.parent.parent.isTypeOnly;
863
+ }
@@ -126,7 +126,7 @@ module.exports = localeUtils.buildIncompleteTranslation({
126
126
  file_0_not_an_object: "Die Datei {0} ist kein Objekt",
127
127
  // deserialization
128
128
  serialized_project_referenced_0_not_part_of_project: "Serialisiertes Projekt referenziert Reflection {0}, welche kein Teil des Projekts ist",
129
- saved_relative_path_0_resolved_from_1_is_not_a_file: "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert",
129
+ saved_relative_path_0_resolved_from_1_does_not_exist: "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert",
130
130
  // options
131
131
  circular_reference_extends_0: `Zyklische Referenz im "extends"-Feld von {0} gefunden`,
132
132
  failed_resolve_0_to_file_in_1: "Konnte {0} in {1} nicht zu einer Datei auflösen",
@@ -114,7 +114,7 @@ declare const _default: {
114
114
  entry_point_0_did_not_match_any_packages: "Der Einstiegspunkt-Glob {0} passte auf keine Verzeichnisse mit einer package.json-Datei";
115
115
  file_0_not_an_object: "Die Datei {0} ist kein Objekt";
116
116
  serialized_project_referenced_0_not_part_of_project: "Serialisiertes Projekt referenziert Reflection {0}, welche kein Teil des Projekts ist";
117
- saved_relative_path_0_resolved_from_1_is_not_a_file: "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert";
117
+ saved_relative_path_0_resolved_from_1_does_not_exist: "Serialisiertes Projekt referenziert {0}, was relativ zu {1} nicht existiert";
118
118
  circular_reference_extends_0: "Zyklische Referenz im \"extends\"-Feld von {0} gefunden";
119
119
  failed_resolve_0_to_file_in_1: "Konnte {0} in {1} nicht zu einer Datei auflösen";
120
120
  glob_0_should_use_posix_slash: "Der Glob \"{0}\" maskiert nichtspezielle Zeichen. Glob-Eingaben für TypeDoc dürfen keine Windows-Pfadtrennzeichen (\\) verwenden, nutzen Sie stattdessen Posix-Pfadtrennzeichen (/)";
@@ -54,6 +54,7 @@ module.exports = {
54
54
  open_brace_within_inline_tag: `Encountered an open brace within an inline tag, this is likely a mistake`,
55
55
  inline_tag_not_closed: `Inline tag is not closed`,
56
56
  // validation
57
+ comment_for_0_links_to_1_not_included_in_docs_use_external_link_2: `The comment for {0} links to "{1}" which was resolved but is not included in the documentation. To fix this warning export it or add {2} to the externalSymbolLinkMappings option`,
57
58
  failed_to_resolve_link_to_0_in_comment_for_1: `Failed to resolve link to "{0}" in comment for {1}`,
58
59
  failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2: `Failed to resolve link to "{0}" in comment for {1}. You may have wanted "{2}"`,
59
60
  failed_to_resolve_link_to_0_in_readme_for_1: `Failed to resolve link to "{0}" in readme for {1}`,
@@ -124,7 +125,7 @@ module.exports = {
124
125
  file_0_not_an_object: `The file {0} is not an object`,
125
126
  // deserialization
126
127
  serialized_project_referenced_0_not_part_of_project: `Serialized project referenced reflection {0}, which was not a part of the project`,
127
- saved_relative_path_0_resolved_from_1_is_not_a_file: `Serialized project referenced {0}, which does not exist relative to {1}`,
128
+ saved_relative_path_0_resolved_from_1_does_not_exist: `Serialized project referenced {0}, which does not exist relative to {1}`,
128
129
  // options
129
130
  circular_reference_extends_0: `Circular reference encountered for "extends" field of {0}`,
130
131
  failed_resolve_0_to_file_in_1: `Failed to resolve {0} to a file in {1}`,
@@ -49,6 +49,7 @@ declare const _default: {
49
49
  readonly unknown_inline_tag_0: "Encountered an unknown inline tag {0}";
50
50
  readonly open_brace_within_inline_tag: "Encountered an open brace within an inline tag, this is likely a mistake";
51
51
  readonly inline_tag_not_closed: "Inline tag is not closed";
52
+ readonly comment_for_0_links_to_1_not_included_in_docs_use_external_link_2: "The comment for {0} links to \"{1}\" which was resolved but is not included in the documentation. To fix this warning export it or add {2} to the externalSymbolLinkMappings option";
52
53
  readonly failed_to_resolve_link_to_0_in_comment_for_1: "Failed to resolve link to \"{0}\" in comment for {1}";
53
54
  readonly failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2: "Failed to resolve link to \"{0}\" in comment for {1}. You may have wanted \"{2}\"";
54
55
  readonly failed_to_resolve_link_to_0_in_readme_for_1: "Failed to resolve link to \"{0}\" in readme for {1}";
@@ -114,7 +115,7 @@ declare const _default: {
114
115
  readonly entry_point_0_did_not_match_any_packages: "The entry point glob {0} did not match any directories containing package.json";
115
116
  readonly file_0_not_an_object: "The file {0} is not an object";
116
117
  readonly serialized_project_referenced_0_not_part_of_project: "Serialized project referenced reflection {0}, which was not a part of the project";
117
- readonly saved_relative_path_0_resolved_from_1_is_not_a_file: "Serialized project referenced {0}, which does not exist relative to {1}";
118
+ readonly saved_relative_path_0_resolved_from_1_does_not_exist: "Serialized project referenced {0}, which does not exist relative to {1}";
118
119
  readonly circular_reference_extends_0: "Circular reference encountered for \"extends\" field of {0}";
119
120
  readonly failed_resolve_0_to_file_in_1: "Failed to resolve {0} to a file in {1}";
120
121
  readonly glob_0_should_use_posix_slash: "The glob \"{0}\" escapes a non-special character. Glob inputs to TypeDoc may not use Windows path separators (\\), try replacing with posix path separators (/)";
@@ -94,7 +94,7 @@ module.exports = localeUtils.buildIncompleteTranslation({
94
94
  entry_point_0_did_not_match_any_packages: "エントリ ポイント glob {0} は、package.json を含むディレクトリと一致しませんでした。",
95
95
  file_0_not_an_object: "ファイル {0} はオブジェクトではありません",
96
96
  serialized_project_referenced_0_not_part_of_project: "シリアル化されたプロジェクトは、プロジェクトの一部ではないリフレクション {0} を参照しました",
97
- // saved_relative_path_0_resolved_from_1_is_not_a_file
97
+ // saved_relative_path_0_resolved_from_1_does_not_exist
98
98
  circular_reference_extends_0: '{0} の "extends" フィールドで循環参照が検出されました',
99
99
  failed_resolve_0_to_file_in_1: "{0} を {1} 内のファイルに解決できませんでした",
100
100
  option_0_can_only_be_specified_by_config_file: "'{0}' オプションは設定ファイル経由でのみ指定できます",
@@ -126,7 +126,7 @@ module.exports = localeUtils.buildIncompleteTranslation({
126
126
  file_0_not_an_object: "文件 {0} 不是对象",
127
127
  // deserialization
128
128
  serialized_project_referenced_0_not_part_of_project: "序列化项目引用了反射 {0},但它不是项目的一部分",
129
- saved_relative_path_0_resolved_from_1_is_not_a_file: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到",
129
+ saved_relative_path_0_resolved_from_1_does_not_exist: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到",
130
130
  // options
131
131
  circular_reference_extends_0: "{0} 的“extends”字段出现循环引用",
132
132
  failed_resolve_0_to_file_in_1: "无法将 {0} 解析为 {1} 中的文件",
@@ -114,7 +114,7 @@ declare const _default: {
114
114
  entry_point_0_did_not_match_any_packages: "入口点 glob {0} 与任何包含 package.json 的目录不匹配";
115
115
  file_0_not_an_object: "文件 {0} 不是对象";
116
116
  serialized_project_referenced_0_not_part_of_project: "序列化项目引用了反射 {0},但它不是项目的一部分";
117
- saved_relative_path_0_resolved_from_1_is_not_a_file: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到";
117
+ saved_relative_path_0_resolved_from_1_does_not_exist: "序列化项目引用的 {0} 不存在或无法在 {1} 下找到";
118
118
  circular_reference_extends_0: "{0} 的“extends”字段出现循环引用";
119
119
  failed_resolve_0_to_file_in_1: "无法将 {0} 解析为 {1} 中的文件";
120
120
  option_0_can_only_be_specified_by_config_file: "“{0}”选项只能通过配置文件指定";
@@ -2,6 +2,7 @@ import { type NormalizedPath } from "#utils";
2
2
  import type { Reflection } from "./Reflection.js";
3
3
  import { ReflectionSymbolId } from "./ReflectionSymbolId.js";
4
4
  import type { Deserializer, JSONOutput, Serializer } from "#serialization";
5
+ import type { FileId } from "./FileRegistry.js";
5
6
  /**
6
7
  * Represents a parsed piece of a comment.
7
8
  * @category Comments
@@ -62,7 +63,7 @@ export interface RelativeLinkDisplayPart {
62
63
  * A link to either some document outside of the project or a reflection.
63
64
  * This may be `undefined` if the relative path does not exist.
64
65
  */
65
- target: number | undefined;
66
+ target: FileId | undefined;
66
67
  /**
67
68
  * Anchor within the target page, validated after rendering if possible
68
69
  */
@@ -61,6 +61,6 @@ export class DocumentReflection extends Reflection {
61
61
  this.content = Comment.deserializeDisplayParts(de, obj.content);
62
62
  this.frontmatter = obj.frontmatter;
63
63
  this.relevanceBoost = obj.relevanceBoost;
64
- this.children = de.reviveMany(obj.children, (obj) => de.reflectionBuilders.document(this, obj));
64
+ this.children = de.reviveMany(obj.children, (obj) => de.constructReflection(obj));
65
65
  }
66
66
  }
@@ -2,27 +2,39 @@ import type { Deserializer, JSONOutput, Serializer } from "#serialization";
2
2
  import type { ProjectReflection, Reflection } from "./index.js";
3
3
  import type { ReflectionId } from "./Reflection.js";
4
4
  import { type NormalizedPath } from "#utils";
5
+ export type FileId = number & {
6
+ __mediaIdBrand: never;
7
+ };
5
8
  export declare class FileRegistry {
6
9
  protected nextId: number;
7
- protected mediaToReflection: Map<number, ReflectionId>;
8
- protected mediaToPath: Map<number, NormalizedPath>;
10
+ protected mediaToReflection: Map<FileId, ReflectionId>;
11
+ protected mediaToPath: Map<FileId, NormalizedPath>;
9
12
  protected reflectionToPath: Map<ReflectionId, NormalizedPath>;
10
- protected pathToMedia: Map<NormalizedPath, number>;
11
- protected names: Map<number, string>;
13
+ protected pathToMedia: Map<NormalizedPath, FileId>;
14
+ protected names: Map<FileId, string>;
12
15
  protected nameUsage: Map<string, number>;
13
16
  registerAbsolute(absolute: NormalizedPath): {
14
- target: number;
17
+ target: FileId;
15
18
  anchor: string | undefined;
16
19
  };
20
+ /**
21
+ * Registers the specified path as the canonical file for this reflection
22
+ */
17
23
  registerReflection(absolute: NormalizedPath, reflection: Reflection): void;
24
+ /**
25
+ * Registers the specified path as a path which should be resolved to the specified
26
+ * reflection. A reflection *may* be associated with multiple paths.
27
+ */
28
+ registerReflectionPath(absolute: NormalizedPath, reflection: Reflection): void;
18
29
  getReflectionPath(reflection: Reflection): string | undefined;
19
30
  register(sourcePath: NormalizedPath, relativePath: NormalizedPath): {
20
- target: number;
31
+ target: FileId;
21
32
  anchor: string | undefined;
22
33
  } | undefined;
23
34
  removeReflection(reflection: Reflection): void;
24
- resolve(id: number, project: ProjectReflection): string | Reflection | undefined;
25
- getName(id: number): string | undefined;
35
+ resolve(id: FileId, project: ProjectReflection): string | Reflection | undefined;
36
+ resolvePath(id: FileId): string | undefined;
37
+ getName(id: FileId): string | undefined;
26
38
  getNameToAbsoluteMap(): ReadonlyMap<string, string>;
27
39
  toObject(ser: Serializer): JSONOutput.FileRegistry;
28
40
  /**
@@ -16,7 +16,6 @@ export class FileRegistry {
16
16
  anchor = absolute.substring(anchorIndex + 1);
17
17
  absolute = absolute.substring(0, anchorIndex);
18
18
  }
19
- absolute = absolute.replace(/#.*/, "");
20
19
  const existing = this.pathToMedia.get(absolute);
21
20
  if (existing) {
22
21
  return { target: existing, anchor };
@@ -25,11 +24,22 @@ export class FileRegistry {
25
24
  this.pathToMedia.set(absolute, this.nextId);
26
25
  return { target: this.nextId++, anchor };
27
26
  }
27
+ /**
28
+ * Registers the specified path as the canonical file for this reflection
29
+ */
28
30
  registerReflection(absolute, reflection) {
29
31
  const { target } = this.registerAbsolute(absolute);
30
32
  this.reflectionToPath.set(reflection.id, absolute);
31
33
  this.mediaToReflection.set(target, reflection.id);
32
34
  }
35
+ /**
36
+ * Registers the specified path as a path which should be resolved to the specified
37
+ * reflection. A reflection *may* be associated with multiple paths.
38
+ */
39
+ registerReflectionPath(absolute, reflection) {
40
+ const { target } = this.registerAbsolute(absolute);
41
+ this.mediaToReflection.set(target, reflection.id);
42
+ }
33
43
  getReflectionPath(reflection) {
34
44
  return this.reflectionToPath.get(reflection.id);
35
45
  }
@@ -50,6 +60,9 @@ export class FileRegistry {
50
60
  }
51
61
  return this.mediaToPath.get(id);
52
62
  }
63
+ resolvePath(id) {
64
+ return this.mediaToPath.get(id);
65
+ }
53
66
  getName(id) {
54
67
  const absolute = this.mediaToPath.get(id);
55
68
  if (!absolute)
@@ -104,15 +117,15 @@ export class FileRegistry {
104
117
  * a single object, and should merge in files from the other registries.
105
118
  */
106
119
  fromObject(de, obj) {
107
- for (const [key, val] of Object.entries(obj.entries)) {
108
- const absolute = NormalizedPathUtils.resolve(de.projectRoot, val);
109
- de.oldFileIdToNewFileId[+key] = this.registerAbsolute(absolute).target;
120
+ for (const [fileId, path] of Object.entries(obj.entries)) {
121
+ const absolute = NormalizedPathUtils.resolve(de.projectRoot, path);
122
+ de.oldFileIdToNewFileId[+fileId] = this.registerAbsolute(absolute).target;
110
123
  }
111
124
  de.defer((project) => {
112
- for (const [media, reflId] of Object.entries(obj.reflections)) {
125
+ for (const [fileId, reflId] of Object.entries(obj.reflections)) {
113
126
  const refl = project.getReflectionById(de.oldIdToNewId[reflId]);
114
127
  if (refl) {
115
- this.mediaToReflection.set(de.oldFileIdToNewFileId[+media], refl.id);
128
+ this.mediaToReflection.set(de.oldFileIdToNewFileId[+fileId], refl.id);
116
129
  }
117
130
  }
118
131
  });
@@ -32,7 +32,7 @@ export class ProjectReflection extends ContainerReflection {
32
32
  *
33
33
  * This may be replaced with a `Map<number, Reflection>` someday.
34
34
  */
35
- reflections = { [this.id]: this };
35
+ reflections = {};
36
36
  /**
37
37
  * The name of the package that this reflection documents according to package.json.
38
38
  */
@@ -99,6 +99,10 @@ export interface TraverseCallback {
99
99
  export type ReflectionVisitor = {
100
100
  [K in keyof ReflectionVariant]?: (refl: ReflectionVariant[K]) => void;
101
101
  };
102
+ /**
103
+ * Alias for a `number` which references a reflection.
104
+ * `-1` may be used for an invalid reflection ID.
105
+ */
102
106
  export type ReflectionId = number & {
103
107
  __reflectionIdBrand: never;
104
108
  };
@@ -1,4 +1,4 @@
1
- import type { Reflection } from "./Reflection.js";
1
+ import type { Reflection, ReflectionId } from "./Reflection.js";
2
2
  import type { DeclarationReflection } from "./DeclarationReflection.js";
3
3
  import type { ProjectReflection } from "./ProjectReflection.js";
4
4
  import type { Deserializer, JSONOutput, Serializer } from "#serialization";
@@ -386,7 +386,7 @@ export declare class ReferenceType extends Type {
386
386
  private _project;
387
387
  private constructor();
388
388
  static createUnresolvedReference(name: string, target: ReflectionSymbolId, project: ProjectReflection, qualifiedName: string): ReferenceType;
389
- static createResolvedReference(name: string, target: Reflection | number, project: ProjectReflection | null): ReferenceType;
389
+ static createResolvedReference(name: string, target: Reflection | ReflectionId, project: ProjectReflection | null): ReferenceType;
390
390
  /**
391
391
  * This is used for type parameters, which don't actually point to something,
392
392
  * and also for temporary references which will be cleaned up with real references
@@ -894,20 +894,22 @@ let ReferenceType = (() => {
894
894
  }
895
895
  fromObject(de, obj) {
896
896
  this.typeArguments = de.reviveMany(obj.typeArguments, (t) => de.constructType(t));
897
- if (typeof obj.target === "number" && obj.target !== -1) {
898
- de.defer((project) => {
899
- const target = project.getReflectionById(de.oldIdToNewId[obj.target] ?? -1);
900
- if (target) {
901
- this._project = project;
902
- this._target = target.id;
903
- }
904
- else {
905
- de.logger.warn(i18n.serialized_project_referenced_0_not_part_of_project(JSON.stringify(obj.target)));
906
- }
907
- });
908
- }
909
- else if (obj.target === -1) {
910
- this._target = -1;
897
+ if (typeof obj.target === "number") {
898
+ if (obj.target === -1) {
899
+ this._target = -1;
900
+ }
901
+ else {
902
+ de.defer((project) => {
903
+ const target = project.getReflectionById(de.oldIdToNewId[obj.target] ?? -1);
904
+ if (target) {
905
+ this._project = project;
906
+ this._target = target.id;
907
+ }
908
+ else {
909
+ de.logger.warn(i18n.serialized_project_referenced_0_not_part_of_project(JSON.stringify(obj.target)));
910
+ }
911
+ });
912
+ }
911
913
  }
912
914
  else {
913
915
  this._project = de.project;
@@ -34,7 +34,7 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
34
34
  };
35
35
  import { RendererComponent } from "../components.js";
36
36
  import { RendererEvent } from "../events.js";
37
- import { copySync, readFile, writeFileSync } from "../../utils/fs.js";
37
+ import { copySync, isFile, readFile, writeFileSync } from "../../utils/fs.js";
38
38
  import { DefaultTheme } from "../themes/default/DefaultTheme.js";
39
39
  import { getStyles } from "../../utils/highlighter.js";
40
40
  import { getEnumKeys, i18n } from "#utils";
@@ -146,7 +146,12 @@ let AssetsPlugin = (() => {
146
146
  const media = join(event.outputDirectory, "media");
147
147
  const toCopy = event.project.files.getNameToAbsoluteMap();
148
148
  for (const [fileName, absolute] of toCopy.entries()) {
149
- copySync(absolute, join(media, fileName));
149
+ if (isFile(absolute)) {
150
+ copySync(absolute, join(media, fileName));
151
+ }
152
+ else {
153
+ this.application.logger.warn(i18n.relative_path_0_is_not_a_file_and_will_not_be_copied_to_output(absolute));
154
+ }
150
155
  }
151
156
  }
152
157
  }
@@ -129,8 +129,8 @@ let MarkedPlugin = (() => {
129
129
  getHighlighted(text, lang) {
130
130
  lang = lang || "typescript";
131
131
  lang = lang.toLowerCase();
132
- if (!isSupportedLanguage(lang)) {
133
- if (isLoadedLanguage(lang)) {
132
+ if (!isLoadedLanguage(lang)) {
133
+ if (isSupportedLanguage(lang)) {
134
134
  this.application.logger.warn(i18n.unloaded_language_0_not_highlighted_in_comment_for_1(lang, getFriendlyFullName(this.page?.model || { name: "(unknown)" })));
135
135
  }
136
136
  else {
@@ -1,4 +1,4 @@
1
- import { type FileRegistry, ProjectReflection, type ReflectionVariant, type TypeKindMap } from "#models";
1
+ import { type FileId, type FileRegistry, ProjectReflection, type ReflectionId, type ReflectionVariant, type TypeKindMap } from "#models";
2
2
  import { type Logger, type NormalizedPath } from "#utils";
3
3
  import * as JSONOutput from "./schema.js";
4
4
  export interface DeserializerComponent {
@@ -30,8 +30,8 @@ export declare class Deserializer {
30
30
  * Only set when deserializing.
31
31
  */
32
32
  projectRoot: NormalizedPath;
33
- oldIdToNewId: Record<number, number | undefined>;
34
- oldFileIdToNewFileId: Record<number, number | undefined>;
33
+ oldIdToNewId: Record<ReflectionId, ReflectionId | undefined>;
34
+ oldFileIdToNewFileId: Record<FileId, FileId | undefined>;
35
35
  project: ProjectReflection | undefined;
36
36
  constructor(logger: Logger);
37
37
  addDeserializer(de: DeserializerComponent): void;
@@ -52,6 +52,8 @@ type ToSerialized<T> = T extends Primitive ? T : T extends bigint ? {
52
52
  type S<T, K extends keyof T> = {
53
53
  -readonly [K2 in K]: ToSerialized<T[K2]>;
54
54
  };
55
+ export type ReflectionId = M.ReflectionId;
56
+ export type FileId = M.FileId;
55
57
  export interface ReflectionSymbolId {
56
58
  packageName: string;
57
59
  packagePath: NormalizedPath;
@@ -85,7 +87,7 @@ export interface ReferenceReflection extends Omit<DeclarationReflection, "varian
85
87
  * -1 if the reference refers to a symbol that does not exist in the documentation.
86
88
  * Otherwise, the reflection ID.
87
89
  */
88
- target: number;
90
+ target: ReflectionId;
89
91
  }
90
92
  /** @category Reflections */
91
93
  export interface SignatureReflection extends Omit<Reflection, "variant">, S<M.SignatureReflection, "variant" | "sources" | "parameters" | "typeParameters" | "type" | "overwrites" | "inheritedFrom" | "implementationOf"> {
@@ -107,12 +109,12 @@ export interface ProjectReflection extends Omit<ContainerReflection, "variant">,
107
109
  * See {@link SCHEMA_VERSION} for the current version.
108
110
  */
109
111
  schemaVersion: string;
110
- symbolIdMap: Record<number, ReflectionSymbolId> | IfInternal<undefined, never>;
112
+ symbolIdMap: Record<ReflectionId, ReflectionSymbolId> | IfInternal<undefined, never>;
111
113
  files: FileRegistry;
112
114
  }
113
115
  /** @category Reflections */
114
116
  export interface ContainerReflection extends Reflection, S<M.ContainerReflection, "children" | "documents" | "groups" | "categories"> {
115
- childrenIncludingDocuments?: number[];
117
+ childrenIncludingDocuments?: ReflectionId[];
116
118
  }
117
119
  /** @category Reflections */
118
120
  export interface Reflection extends S<M.Reflection, "id" | "variant" | "name" | "kind" | "comment"> {
@@ -172,7 +174,7 @@ export interface QueryType extends Type, S<M.QueryType, "type" | "queryType"> {
172
174
  }
173
175
  /** @category Types */
174
176
  export interface ReferenceType extends Type, S<M.ReferenceType, "type" | "name" | "typeArguments" | "package" | "externalUrl"> {
175
- target: number | ReflectionSymbolId;
177
+ target: ReflectionId | ReflectionSymbolId;
176
178
  qualifiedName?: string;
177
179
  refersToTypeParameter?: boolean;
178
180
  preferValues?: boolean;
@@ -247,7 +249,7 @@ export interface InlineTagDisplayPart {
247
249
  kind: "inline-tag";
248
250
  tag: `@${string}`;
249
251
  text: string;
250
- target?: string | number | ReflectionSymbolId;
252
+ target?: string | ReflectionId | ReflectionSymbolId;
251
253
  tsLinkText?: string;
252
254
  }
253
255
  /**
@@ -264,7 +266,7 @@ export interface RelativeLinkDisplayPart {
264
266
  /**
265
267
  * File ID, if present
266
268
  */
267
- target?: number;
269
+ target?: FileId;
268
270
  /**
269
271
  * Anchor within the target file, if present
270
272
  */
@@ -274,8 +276,8 @@ export interface SourceReference extends S<M.SourceReference, "fileName" | "line
274
276
  }
275
277
  export interface FileRegistry {
276
278
  /** Relative path according to the serialization root */
277
- entries: Record<number, NormalizedPath>;
279
+ entries: Record<FileId, NormalizedPath>;
278
280
  /** File ID to reflection ID */
279
- reflections: Record<number, number>;
281
+ reflections: Record<FileId, ReflectionId>;
280
282
  }
281
283
  export {};
@@ -1,9 +1,9 @@
1
- import { FileRegistry } from "../models/FileRegistry.js";
1
+ import { type FileId, FileRegistry } from "../models/FileRegistry.js";
2
2
  import type { Deserializer, JSONOutput } from "#serialization";
3
3
  import { type NormalizedPath } from "#utils";
4
4
  export declare class ValidatingFileRegistry extends FileRegistry {
5
5
  register(sourcePath: NormalizedPath, relativePath: NormalizedPath): {
6
- target: number;
6
+ target: FileId;
7
7
  anchor: string | undefined;
8
8
  } | undefined;
9
9
  fromObject(de: Deserializer, obj: JSONOutput.FileRegistry): void;
@@ -1,11 +1,14 @@
1
1
  import { FileRegistry } from "../models/FileRegistry.js";
2
- import { isFile } from "./fs.js";
3
2
  import { i18n, NormalizedPathUtils } from "#utils";
3
+ import { existsSync } from "fs";
4
4
  export class ValidatingFileRegistry extends FileRegistry {
5
5
  register(sourcePath, relativePath) {
6
6
  const absolute = NormalizedPathUtils.resolve(NormalizedPathUtils.dirname(sourcePath), relativePath);
7
7
  const absoluteWithoutAnchor = absolute.replace(/#.*/, "");
8
- if (!isFile(absoluteWithoutAnchor)) {
8
+ // Note: We allow paths to directories to be registered here, but the AssetsPlugin will not
9
+ // copy them to the output path. This is so that we can link to directories and associate them
10
+ // with reflections in packages mode.
11
+ if (!existsSync(absoluteWithoutAnchor)) {
9
12
  return;
10
13
  }
11
14
  return this.registerAbsolute(absolute);
@@ -13,8 +16,8 @@ export class ValidatingFileRegistry extends FileRegistry {
13
16
  fromObject(de, obj) {
14
17
  for (const [key, val] of Object.entries(obj.entries)) {
15
18
  const absolute = NormalizedPathUtils.resolve(de.projectRoot, val);
16
- if (!isFile(absolute)) {
17
- de.logger.warn(i18n.saved_relative_path_0_resolved_from_1_is_not_a_file(val, de.projectRoot));
19
+ if (!existsSync(absolute)) {
20
+ de.logger.warn(i18n.saved_relative_path_0_resolved_from_1_does_not_exist(val, de.projectRoot));
18
21
  continue;
19
22
  }
20
23
  de.oldFileIdToNewFileId[+key] = this.registerAbsolute(absolute).target;
@@ -7,7 +7,7 @@ function getBrokenPartLinks(parts) {
7
7
  if (part.kind === "inline-tag" &&
8
8
  linkTags.includes(part.tag) &&
9
9
  (!part.target || part.target instanceof ReflectionSymbolId)) {
10
- links.push(part.text.trim());
10
+ links.push(part);
11
11
  }
12
12
  }
13
13
  return links;
@@ -32,52 +32,50 @@ export function validateLinks(project, logger) {
32
32
  function checkReflection(reflection, logger) {
33
33
  if (reflection.isProject() || reflection.isDeclaration()) {
34
34
  for (const broken of getBrokenPartLinks(reflection.readme || [])) {
35
+ const linkText = broken.text.trim();
35
36
  // #2360, "@" is a future reserved character in TSDoc component paths
36
37
  // If a link starts with it, and doesn't include a module source indicator "!"
37
38
  // then the user probably is trying to link to a package containing "@" with an absolute link.
38
- if (broken.startsWith("@") && !broken.includes("!")) {
39
- logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2(broken, reflection.getFriendlyFullName(), broken.replace(/[.#~]/, "!")));
39
+ if (linkText.startsWith("@") && !linkText.includes("!")) {
40
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
40
41
  }
41
42
  else {
42
- logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1(broken, reflection.getFriendlyFullName()));
43
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_readme_for_1(linkText, reflection.getFriendlyFullName()));
43
44
  }
44
45
  }
45
46
  }
46
47
  if (reflection.isDocument()) {
47
48
  for (const broken of getBrokenPartLinks(reflection.content)) {
48
- // #2360, "@" is a future reserved character in TSDoc component paths
49
- // If a link starts with it, and doesn't include a module source indicator "!"
50
- // then the user probably is trying to link to a package containing "@" with an absolute link.
51
- if (broken.startsWith("@") && !broken.includes("!")) {
52
- logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2(broken, reflection.getFriendlyFullName(), broken.replace(/[.#~]/, "!")));
49
+ const linkText = broken.text.trim();
50
+ if (linkText.startsWith("@") && !linkText.includes("!")) {
51
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
53
52
  }
54
53
  else {
55
- logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1(broken, reflection.getFriendlyFullName()));
54
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_document_1(linkText, reflection.getFriendlyFullName()));
56
55
  }
57
56
  }
58
57
  }
59
58
  for (const broken of getBrokenLinks(reflection.comment)) {
60
- // #2360, "@" is a future reserved character in TSDoc component paths
61
- // If a link starts with it, and doesn't include a module source indicator "!"
62
- // then the user probably is trying to link to a package containing "@" with an absolute link.
63
- if (broken.startsWith("@") && !broken.includes("!")) {
64
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(broken, reflection.getFriendlyFullName(), broken.replace(/[.#~]/, "!")));
65
- }
66
- else {
67
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1(broken, reflection.getFriendlyFullName()));
68
- }
59
+ reportBrokenCommentLink(broken, reflection, logger);
69
60
  }
70
61
  if (reflection.isDeclaration() &&
71
62
  reflection.kindOf(ReflectionKind.TypeAlias) &&
72
63
  reflection.type?.type === "union" &&
73
64
  reflection.type.elementSummaries) {
74
65
  for (const broken of reflection.type.elementSummaries.flatMap(getBrokenPartLinks)) {
75
- if (broken.startsWith("@") && !broken.includes("!")) {
76
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(broken, reflection.getFriendlyFullName(), broken.replace(/[.#~]/, "!")));
77
- }
78
- else {
79
- logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1(broken, reflection.getFriendlyFullName()));
80
- }
66
+ reportBrokenCommentLink(broken, reflection, logger);
81
67
  }
82
68
  }
83
69
  }
70
+ function reportBrokenCommentLink(broken, reflection, logger) {
71
+ const linkText = broken.text.trim();
72
+ if (broken.target instanceof ReflectionSymbolId) {
73
+ 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}": "#" }}`));
74
+ }
75
+ else if (linkText.startsWith("@") && !linkText.includes("!")) {
76
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1_may_have_meant_2(linkText, reflection.getFriendlyFullName(), linkText.replace(/[.#~]/, "!")));
77
+ }
78
+ else {
79
+ logger.warn(i18n.failed_to_resolve_link_to_0_in_comment_for_1(linkText, reflection.getFriendlyFullName()));
80
+ }
81
+ }
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.5",
4
+ "version": "0.28.6",
5
5
  "homepage": "https://typedoc.org",
6
6
  "type": "module",
7
7
  "exports": {