typedoc 0.26.0-beta.3 → 0.26.0-beta.4

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 (46) hide show
  1. package/dist/lib/application.d.ts +6 -1
  2. package/dist/lib/application.js +5 -0
  3. package/dist/lib/cli.js +13 -0
  4. package/dist/lib/converter/comments/index.js +3 -0
  5. package/dist/lib/converter/plugins/ImplementsPlugin.js +3 -2
  6. package/dist/lib/converter/plugins/LinkResolverPlugin.js +7 -0
  7. package/dist/lib/converter/plugins/SourcePlugin.d.ts +1 -15
  8. package/dist/lib/converter/plugins/SourcePlugin.js +4 -45
  9. package/dist/lib/converter/utils/repository.d.ts +46 -1
  10. package/dist/lib/converter/utils/repository.js +191 -64
  11. package/dist/lib/internationalization/internationalization.d.ts +5 -2
  12. package/dist/lib/internationalization/internationalization.js +42 -0
  13. package/dist/lib/internationalization/locales/jp.cjs +308 -0
  14. package/dist/lib/internationalization/locales/jp.d.cts +307 -0
  15. package/dist/lib/internationalization/locales/zh.cjs +308 -0
  16. package/dist/lib/internationalization/locales/zh.d.cts +307 -0
  17. package/dist/lib/internationalization/translatable.d.ts +184 -167
  18. package/dist/lib/internationalization/translatable.js +182 -166
  19. package/dist/lib/models/comments/comment.d.ts +1 -0
  20. package/dist/lib/models/reflections/abstract.d.ts +12 -11
  21. package/dist/lib/models/reflections/abstract.js +46 -49
  22. package/dist/lib/output/plugins/IconsPlugin.js +21 -17
  23. package/dist/lib/output/renderer.d.ts +2 -2
  24. package/dist/lib/output/themes/default/DefaultTheme.js +1 -1
  25. package/dist/lib/output/themes/default/partials/comment.d.ts +1 -1
  26. package/dist/lib/output/themes/default/partials/comment.js +11 -7
  27. package/dist/lib/output/themes/default/partials/navigation.js +8 -2
  28. package/dist/lib/output/themes/default/templates/reflection.js +2 -2
  29. package/dist/lib/output/themes/lib.d.ts +0 -1
  30. package/dist/lib/output/themes/lib.js +0 -4
  31. package/dist/lib/utils/general.d.ts +1 -0
  32. package/dist/lib/utils/general.js +5 -0
  33. package/dist/lib/utils/options/sources/typedoc.js +1 -0
  34. package/dist/lib/utils/options/tsdoc-defaults.d.ts +1 -1
  35. package/dist/lib/utils/options/tsdoc-defaults.js +5 -1
  36. package/dist/lib/utils/paths.js +11 -1
  37. package/dist/lib/utils/perf.js +3 -1
  38. package/dist/lib/validation/links.d.ts +1 -1
  39. package/dist/lib/validation/links.js +42 -7
  40. package/package.json +2 -2
  41. package/static/main.js +1 -1
  42. package/static/style.css +6 -0
  43. package/dist/lib/converter/utils/base-path.d.ts +0 -36
  44. package/dist/lib/converter/utils/base-path.js +0 -117
  45. package/dist/lib/internationalization/locales/test.cjs +0 -8
  46. package/dist/lib/internationalization/locales/test.d.cts +0 -5
@@ -22,6 +22,11 @@ export declare function createAppForTesting(): Application;
22
22
  *
23
23
  * Both the {@link Converter} and the {@link Renderer} emit a series of events while processing the project.
24
24
  * Subscribe to these Events to control the application flow or alter the output.
25
+ *
26
+ * @remarks
27
+ *
28
+ * Access to an Application instance can be retrieved with {@link Application.bootstrap} or
29
+ * {@link Application.bootstrapWithPlugins}. It can not be constructed manually.
25
30
  */
26
31
  export declare class Application extends ChildableComponent<Application, AbstractComponent<Application>> {
27
32
  /**
@@ -66,7 +71,7 @@ export declare class Application extends ChildableComponent<Application, Abstrac
66
71
  /**
67
72
  * The version number of TypeDoc.
68
73
  */
69
- static VERSION: string;
74
+ static readonly VERSION: string;
70
75
  /**
71
76
  * Emitted after plugins have been loaded and options have been read, but before they have been frozen.
72
77
  * The listener will be given an instance of {@link Application}.
@@ -130,6 +130,11 @@ const DEFAULT_READERS = [
130
130
  *
131
131
  * Both the {@link Converter} and the {@link Renderer} emit a series of events while processing the project.
132
132
  * Subscribe to these Events to control the application flow or alter the output.
133
+ *
134
+ * @remarks
135
+ *
136
+ * Access to an Application instance can be retrieved with {@link Application.bootstrap} or
137
+ * {@link Application.bootstrapWithPlugins}. It can not be constructed manually.
133
138
  */
134
139
  let Application = (() => {
135
140
  var _Application_lang_accessor_storage, _Application_skipErrorChecking_accessor_storage, _Application_entryPointStrategy_accessor_storage, _Application_entryPoints_accessor_storage;
package/dist/lib/cli.js CHANGED
@@ -49,6 +49,7 @@ async function main() {
49
49
  const exitCode = await run(app);
50
50
  if (exitCode !== ExitCodes.Watching) {
51
51
  app.logger.verbose(`Full run took ${Date.now() - start}ms`);
52
+ logRunSummary(app.logger);
52
53
  process.exit(exitCode);
53
54
  }
54
55
  }
@@ -131,3 +132,15 @@ async function run(app) {
131
132
  }
132
133
  return ExitCodes.Ok;
133
134
  }
135
+ /**
136
+ * Generate a string with the number of errors and warnings found.
137
+ */
138
+ function logRunSummary(logger) {
139
+ const { errorCount, warningCount } = logger;
140
+ if (errorCount) {
141
+ logger.error(logger.i18n.found_0_errors_and_1_warnings(errorCount.toString(), warningCount.toString()));
142
+ }
143
+ else if (warningCount) {
144
+ logger.warn(logger.i18n.found_0_errors_and_1_warnings(errorCount.toString(), warningCount.toString()));
145
+ }
146
+ }
@@ -58,6 +58,9 @@ function getCommentWithCache(discovered, config, logger, checker, files) {
58
58
  }
59
59
  function getCommentImpl(commentSource, config, logger, moduleComment, checker, files) {
60
60
  const comment = getCommentWithCache(commentSource, config, logger, checker, files);
61
+ if (comment?.getTag("@import") || comment?.getTag("@license")) {
62
+ return;
63
+ }
61
64
  if (moduleComment && comment) {
62
65
  // Module comment, make sure it is tagged with @packageDocumentation or @module.
63
66
  // If it isn't then the comment applies to the first statement in the file, so throw it away.
@@ -292,7 +292,7 @@ function findProperty(reflection, parent) {
292
292
  : prop.name === reflection.name;
293
293
  });
294
294
  }
295
- function createLink(context, reflection, clause, expr, symbol, isOverwrite) {
295
+ function createLink(context, reflection, clause, expr, symbol, isInherit) {
296
296
  const project = context.project;
297
297
  const name = `${expr.expression.getText()}.${(0, utils_1.getHumanName)(symbol.name)}`;
298
298
  link(reflection);
@@ -313,7 +313,8 @@ function createLink(context, reflection, clause, expr, symbol, isOverwrite) {
313
313
  target.implementationOf ??= types_1.ReferenceType.createBrokenReference(name, project);
314
314
  return;
315
315
  }
316
- if (isOverwrite) {
316
+ if (isInherit) {
317
+ target.setFlag(index_1.ReflectionFlag.Inherited);
317
318
  target.inheritedFrom ??= types_1.ReferenceType.createBrokenReference(name, project);
318
319
  }
319
320
  else {
@@ -86,6 +86,13 @@ let LinkResolverPlugin = (() => {
86
86
  if (reflection.comment) {
87
87
  this.owner.resolveLinks(reflection.comment, reflection);
88
88
  }
89
+ if (reflection.isDeclaration()) {
90
+ reflection.type?.visit((0, models_1.makeRecursiveVisitor)({
91
+ union: (type) => {
92
+ type.elementSummaries = type.elementSummaries?.map((parts) => this.owner.resolveLinks(parts, reflection));
93
+ },
94
+ }));
95
+ }
89
96
  if (reflection instanceof models_1.DeclarationReflection &&
90
97
  reflection.readme) {
91
98
  reflection.readme = this.owner.resolveLinks(reflection.readme, reflection);
@@ -13,14 +13,7 @@ export declare class SourcePlugin extends ConverterComponent {
13
13
  * All file names to find the base path from.
14
14
  */
15
15
  private fileNames;
16
- /**
17
- * List of known repositories.
18
- */
19
- private repositories;
20
- /**
21
- * List of paths known to be not under git control.
22
- */
23
- private ignoredPaths;
16
+ private repositories?;
24
17
  /**
25
18
  * Create a new SourceHandler instance.
26
19
  */
@@ -42,11 +35,4 @@ export declare class SourcePlugin extends ConverterComponent {
42
35
  * @param context The context object describing the current state the converter is in.
43
36
  */
44
37
  private onBeginResolve;
45
- /**
46
- * Check whether the given file is placed inside a repository.
47
- *
48
- * @param fileName The name of the file a repository should be looked for.
49
- * @returns The found repository info or undefined.
50
- */
51
- private getRepository;
52
38
  }
@@ -62,7 +62,6 @@ const nodes_1 = require("../utils/nodes");
62
62
  const path_1 = require("path");
63
63
  const models_1 = require("../../models");
64
64
  const repository_1 = require("../utils/repository");
65
- const base_path_1 = require("../utils/base-path");
66
65
  /**
67
66
  * A handler that attaches source file information to reflections.
68
67
  */
@@ -104,14 +103,6 @@ let SourcePlugin = (() => {
104
103
  * All file names to find the base path from.
105
104
  */
106
105
  this.fileNames = (__runInitializers(this, _basePath_extraInitializers), new Set());
107
- /**
108
- * List of known repositories.
109
- */
110
- this.repositories = {};
111
- /**
112
- * List of paths known to be not under git control.
113
- */
114
- this.ignoredPaths = new Set();
115
106
  }
116
107
  get disableSources() { return __classPrivateFieldGet(this, _SourcePlugin_disableSources_accessor_storage, "f"); }
117
108
  set disableSources(value) { __classPrivateFieldSet(this, _SourcePlugin_disableSources_accessor_storage, value, "f"); }
@@ -154,7 +145,7 @@ let SourcePlugin = (() => {
154
145
  const symbol = reflection.project.getSymbolFromReflection(reflection);
155
146
  for (const node of symbol?.declarations || []) {
156
147
  const sourceFile = node.getSourceFile();
157
- const fileName = base_path_1.BasePath.normalize(sourceFile.fileName);
148
+ const fileName = (0, utils_1.normalizePath)(sourceFile.fileName);
158
149
  this.fileNames.add(fileName);
159
150
  let position;
160
151
  if (typescript_1.default.isSourceFile(node)) {
@@ -171,7 +162,7 @@ let SourcePlugin = (() => {
171
162
  if (this.disableSources || !sig)
172
163
  return;
173
164
  const sourceFile = sig.getSourceFile();
174
- const fileName = base_path_1.BasePath.normalize(sourceFile.fileName);
165
+ const fileName = (0, utils_1.normalizePath)(sourceFile.fileName);
175
166
  this.fileNames.add(fileName);
176
167
  const position = typescript_1.default.getLineAndCharacterOfPosition(sourceFile, getLocationNode(sig).getStart());
177
168
  reflection.sources ||= [];
@@ -195,6 +186,7 @@ let SourcePlugin = (() => {
195
186
  this.application.logger.warn(context.i18n.disable_git_set_and_git_revision_used());
196
187
  }
197
188
  const basePath = this.basePath || (0, utils_1.getCommonDirectory)([...this.fileNames]);
189
+ this.repositories ||= new repository_1.RepositoryManager(basePath, this.gitRevision, this.gitRemote, this.sourceLinkTemplate, this.disableGit, this.application.logger);
198
190
  for (const id in context.project.reflections) {
199
191
  const refl = context.project.reflections[id];
200
192
  if (!(refl instanceof index_1.DeclarationReflection ||
@@ -206,46 +198,13 @@ let SourcePlugin = (() => {
206
198
  }
207
199
  for (const source of refl.sources || []) {
208
200
  if (this.disableGit || (0, repository_1.gitIsInstalled)()) {
209
- const repo = this.getRepository(basePath, source.fullFileName);
201
+ const repo = this.repositories.getRepository(source.fullFileName);
210
202
  source.url = repo?.getURL(source.fullFileName, source.line);
211
203
  }
212
204
  source.fileName = (0, utils_1.normalizePath)((0, path_1.relative)(basePath, source.fullFileName));
213
205
  }
214
206
  }
215
207
  }
216
- /**
217
- * Check whether the given file is placed inside a repository.
218
- *
219
- * @param fileName The name of the file a repository should be looked for.
220
- * @returns The found repository info or undefined.
221
- */
222
- getRepository(basePath, fileName) {
223
- if (this.disableGit) {
224
- return new repository_1.AssumedRepository(basePath, this.gitRevision, this.sourceLinkTemplate);
225
- }
226
- // Check for known non-repositories
227
- const dirName = (0, path_1.dirname)(fileName);
228
- const segments = dirName.split("/");
229
- for (let i = segments.length; i > 0; i--) {
230
- if (this.ignoredPaths.has(segments.slice(0, i).join("/"))) {
231
- return;
232
- }
233
- }
234
- // Check for known repositories
235
- for (const path of Object.keys(this.repositories)) {
236
- if (fileName.toLowerCase().startsWith(path)) {
237
- return this.repositories[path];
238
- }
239
- }
240
- // Try to create a new repository
241
- const repository = repository_1.GitRepository.tryCreateRepository(dirName, this.sourceLinkTemplate, this.gitRevision, this.gitRemote, this.application.logger);
242
- if (repository) {
243
- this.repositories[repository.path.toLowerCase()] = repository;
244
- return repository;
245
- }
246
- // No repository found, add path to ignored paths
247
- this.ignoredPaths.add(dirName);
248
- }
249
208
  };
250
209
  _SourcePlugin_disableSources_accessor_storage = new WeakMap();
251
210
  _SourcePlugin_gitRevision_accessor_storage = new WeakMap();
@@ -1,6 +1,7 @@
1
- import type { Logger } from "../../utils";
1
+ import { type Logger } from "../../utils";
2
2
  export declare function gitIsInstalled(): boolean;
3
3
  export interface Repository {
4
+ readonly path: string;
4
5
  getURL(fileName: string, line: number): string | undefined;
5
6
  }
6
7
  export declare class AssumedRepository implements Repository {
@@ -48,4 +49,48 @@ export declare class GitRepository implements Repository {
48
49
  */
49
50
  static tryCreateRepository(path: string, sourceLinkTemplate: string, gitRevision: string, gitRemote: string, logger: Logger): GitRepository | undefined;
50
51
  }
52
+ /**
53
+ * Responsible for keeping track of 0-N repositories which exist on a machine.
54
+ * This used to be inlined in SourcePlugin, moved out for easy unit testing.
55
+ *
56
+ * Git repositories can be nested. Files should be resolved to a repo as shown
57
+ * below:
58
+ * ```text
59
+ * /project
60
+ * /project/.git (A)
61
+ * /project/file.js (A)
62
+ * /project/folder/file.js (A)
63
+ * /project/sub/.git (B)
64
+ * /project/sub/file.js (B)
65
+ * ```
66
+ *
67
+ * In order words, it is not safe to assume that just because a file is within
68
+ * the `/project` directory, that it belongs to repo `A`. As calling git is
69
+ * expensive (~20-300ms depending on the machine, antivirus, etc.) we check for
70
+ * `.git` folders manually, and only call git if one is found.
71
+ *
72
+ * Symlinked files have the potential to further complicate this. If TypeScript's
73
+ * `preserveSymlinks` option is on, then this may be passed the path to a symlinked
74
+ * file. Unlike TypeScript, we will resolve the path, as the repo link should really
75
+ * point to the actual file.
76
+ */
77
+ export declare class RepositoryManager {
78
+ private basePath;
79
+ private gitRevision;
80
+ private gitRemote;
81
+ private sourceLinkTemplate;
82
+ private disableGit;
83
+ private logger;
84
+ private cache;
85
+ private assumedRepo;
86
+ constructor(basePath: string, gitRevision: string, gitRemote: string, sourceLinkTemplate: string, disableGit: boolean, logger: Logger);
87
+ /**
88
+ * Check whether the given file is placed inside a repository.
89
+ *
90
+ * @param fileName The name of the file a repository should be looked for.
91
+ * @returns The found repository info or undefined.
92
+ */
93
+ getRepository(fileName: string): Repository | undefined;
94
+ private getRepositoryFolder;
95
+ }
51
96
  export declare function guessSourceUrlTemplate(remotes: string[]): string | undefined;
@@ -1,10 +1,47 @@
1
1
  "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
2
36
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GitRepository = exports.AssumedRepository = void 0;
37
+ exports.RepositoryManager = exports.GitRepository = exports.AssumedRepository = void 0;
4
38
  exports.gitIsInstalled = gitIsInstalled;
5
39
  exports.guessSourceUrlTemplate = guessSourceUrlTemplate;
6
40
  const child_process_1 = require("child_process");
7
- const base_path_1 = require("../utils/base-path");
41
+ const utils_1 = require("../../utils");
42
+ const general_1 = require("../../utils/general");
43
+ const path_1 = require("path");
44
+ const fs_1 = require("fs");
8
45
  const TEN_MEGABYTES = 1024 * 10000;
9
46
  function git(...args) {
10
47
  return (0, child_process_1.spawnSync)("git", args, {
@@ -38,82 +75,172 @@ exports.AssumedRepository = AssumedRepository;
38
75
  /**
39
76
  * Stores data of a repository.
40
77
  */
41
- class GitRepository {
42
- /**
43
- * Create a new Repository instance.
44
- *
45
- * @param path The root path of the repository.
46
- */
47
- constructor(path, gitRevision, urlTemplate) {
48
- /**
49
- * All files tracked by the repository.
50
- */
51
- this.files = new Set();
52
- this.path = path;
53
- this.gitRevision = gitRevision;
54
- this.urlTemplate = urlTemplate;
55
- const out = git("-C", path, "ls-files", "-z");
56
- if (out.status === 0) {
57
- out.stdout.split("\0").forEach((file) => {
58
- if (file !== "") {
59
- this.files.add(base_path_1.BasePath.normalize(path + "/" + file));
78
+ let GitRepository = (() => {
79
+ var _a;
80
+ let _files_decorators;
81
+ let _files_initializers = [];
82
+ let _files_extraInitializers = [];
83
+ return _a = class GitRepository {
84
+ /**
85
+ * Create a new Repository instance.
86
+ *
87
+ * @param path The root path of the repository.
88
+ */
89
+ constructor(path, gitRevision, urlTemplate) {
90
+ /**
91
+ * All files tracked by the repository.
92
+ */
93
+ this.files = __runInitializers(this, _files_initializers, new Set());
94
+ this.urlTemplate = __runInitializers(this, _files_extraInitializers);
95
+ this.path = path;
96
+ this.gitRevision = gitRevision;
97
+ this.urlTemplate = urlTemplate;
98
+ const out = git("-C", path, "ls-files", "-z");
99
+ if (out.status === 0) {
100
+ out.stdout.split("\0").forEach((file) => {
101
+ if (file !== "") {
102
+ this.files.add((0, utils_1.normalizePath)(path + "/" + file));
103
+ }
104
+ });
60
105
  }
61
- });
62
- }
106
+ }
107
+ /**
108
+ * Get the URL of the given file on GitHub or Bitbucket.
109
+ *
110
+ * @param fileName The file whose URL should be determined.
111
+ * @returns A URL pointing to the web preview of the given file or undefined.
112
+ */
113
+ getURL(fileName, line) {
114
+ if (!this.files.has(fileName)) {
115
+ return;
116
+ }
117
+ const replacements = {
118
+ gitRevision: this.gitRevision,
119
+ "gitRevision:short": this.gitRevision.substring(0, 8),
120
+ path: fileName.substring(this.path.length + 1),
121
+ line,
122
+ };
123
+ return this.urlTemplate.replace(/\{(gitRevision|gitRevision:short|path|line)\}/g, (_, key) => replacements[key]);
124
+ }
125
+ /**
126
+ * Try to create a new repository instance.
127
+ *
128
+ * Checks whether the given path is the root of a valid repository and if so
129
+ * creates a new instance of {@link GitRepository}.
130
+ *
131
+ * @param path The potential repository root.
132
+ * @returns A new instance of {@link GitRepository} or undefined.
133
+ */
134
+ static tryCreateRepository(path, sourceLinkTemplate, gitRevision, gitRemote, logger) {
135
+ gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim();
136
+ if (!gitRevision)
137
+ return; // Will only happen in a repo with no commits.
138
+ let urlTemplate;
139
+ if (sourceLinkTemplate) {
140
+ urlTemplate = sourceLinkTemplate;
141
+ }
142
+ else {
143
+ const remotesOut = git("-C", path, "remote", "get-url", gitRemote);
144
+ if (remotesOut.status === 0) {
145
+ urlTemplate = guessSourceUrlTemplate(remotesOut.stdout.split("\n"));
146
+ }
147
+ else {
148
+ logger.warn(logger.i18n.git_remote_0_not_valid(gitRemote));
149
+ }
150
+ }
151
+ if (!urlTemplate)
152
+ return;
153
+ return new _a((0, utils_1.normalizePath)(path), gitRevision, urlTemplate);
154
+ }
155
+ },
156
+ (() => {
157
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
158
+ _files_decorators = [general_1.NonEnumerable];
159
+ __esDecorate(null, null, _files_decorators, { kind: "field", name: "files", static: false, private: false, access: { has: obj => "files" in obj, get: obj => obj.files, set: (obj, value) => { obj.files = value; } }, metadata: _metadata }, _files_initializers, _files_extraInitializers);
160
+ if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
161
+ })(),
162
+ _a;
163
+ })();
164
+ exports.GitRepository = GitRepository;
165
+ /**
166
+ * Responsible for keeping track of 0-N repositories which exist on a machine.
167
+ * This used to be inlined in SourcePlugin, moved out for easy unit testing.
168
+ *
169
+ * Git repositories can be nested. Files should be resolved to a repo as shown
170
+ * below:
171
+ * ```text
172
+ * /project
173
+ * /project/.git (A)
174
+ * /project/file.js (A)
175
+ * /project/folder/file.js (A)
176
+ * /project/sub/.git (B)
177
+ * /project/sub/file.js (B)
178
+ * ```
179
+ *
180
+ * In order words, it is not safe to assume that just because a file is within
181
+ * the `/project` directory, that it belongs to repo `A`. As calling git is
182
+ * expensive (~20-300ms depending on the machine, antivirus, etc.) we check for
183
+ * `.git` folders manually, and only call git if one is found.
184
+ *
185
+ * Symlinked files have the potential to further complicate this. If TypeScript's
186
+ * `preserveSymlinks` option is on, then this may be passed the path to a symlinked
187
+ * file. Unlike TypeScript, we will resolve the path, as the repo link should really
188
+ * point to the actual file.
189
+ */
190
+ class RepositoryManager {
191
+ constructor(basePath, gitRevision, gitRemote, sourceLinkTemplate, disableGit, logger) {
192
+ this.basePath = basePath;
193
+ this.gitRevision = gitRevision;
194
+ this.gitRemote = gitRemote;
195
+ this.sourceLinkTemplate = sourceLinkTemplate;
196
+ this.disableGit = disableGit;
197
+ this.logger = logger;
198
+ this.cache = new Map();
199
+ this.assumedRepo = new AssumedRepository(this.basePath, this.gitRevision, this.sourceLinkTemplate);
63
200
  }
64
201
  /**
65
- * Get the URL of the given file on GitHub or Bitbucket.
202
+ * Check whether the given file is placed inside a repository.
66
203
  *
67
- * @param fileName The file whose URL should be determined.
68
- * @returns A URL pointing to the web preview of the given file or undefined.
204
+ * @param fileName The name of the file a repository should be looked for.
205
+ * @returns The found repository info or undefined.
69
206
  */
70
- getURL(fileName, line) {
71
- if (!this.files.has(fileName)) {
72
- return;
207
+ getRepository(fileName) {
208
+ if (this.disableGit) {
209
+ return this.assumedRepo;
73
210
  }
74
- const replacements = {
75
- gitRevision: this.gitRevision,
76
- "gitRevision:short": this.gitRevision.substring(0, 8),
77
- path: fileName.substring(this.path.length + 1),
78
- line,
79
- };
80
- return this.urlTemplate.replace(/\{(gitRevision|gitRevision:short|path|line)\}/g, (_, key) => replacements[key]);
211
+ return this.getRepositoryFolder((0, utils_1.normalizePath)((0, path_1.dirname)(fileName)));
81
212
  }
82
- /**
83
- * Try to create a new repository instance.
84
- *
85
- * Checks whether the given path is the root of a valid repository and if so
86
- * creates a new instance of {@link GitRepository}.
87
- *
88
- * @param path The potential repository root.
89
- * @returns A new instance of {@link GitRepository} or undefined.
90
- */
91
- static tryCreateRepository(path, sourceLinkTemplate, gitRevision, gitRemote, logger) {
92
- const topLevel = git("-C", path, "rev-parse", "--show-toplevel");
93
- if (topLevel.status !== 0)
94
- return;
95
- gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim();
96
- if (!gitRevision)
97
- return; // Will only happen in a repo with no commits.
98
- let urlTemplate;
99
- if (sourceLinkTemplate) {
100
- urlTemplate = sourceLinkTemplate;
213
+ getRepositoryFolder(dir) {
214
+ if (this.cache.has(dir)) {
215
+ return this.cache.get(dir);
101
216
  }
102
- else {
103
- const remotesOut = git("-C", path, "remote", "get-url", gitRemote);
104
- if (remotesOut.status === 0) {
105
- urlTemplate = guessSourceUrlTemplate(remotesOut.stdout.split("\n"));
217
+ if ((0, fs_1.existsSync)((0, path_1.join)(dir, ".git"))) {
218
+ // This might just be a git repo, or we might be in some self-recursive symlink
219
+ // loop, and the repo is actually somewhere else. Ask Git where the repo actually is.
220
+ const repo = git("-C", dir, "rev-parse", "--show-toplevel");
221
+ if (repo.status === 0) {
222
+ const repoDir = repo.stdout.replace("\n", "");
223
+ // This check is only necessary if we're in a symlink loop, otherwise
224
+ // it will always be true.
225
+ if (!this.cache.has(repoDir)) {
226
+ this.cache.set(repoDir, GitRepository.tryCreateRepository(repoDir, this.sourceLinkTemplate, this.gitRevision, this.gitRemote, this.logger));
227
+ }
228
+ this.cache.set(dir, this.cache.get(repoDir));
106
229
  }
107
230
  else {
108
- logger.warn(logger.i18n.git_remote_0_not_valid(gitRemote));
231
+ // Not a git repo, probably corrupt.
232
+ this.cache.set(dir, undefined);
109
233
  }
110
234
  }
111
- if (!urlTemplate)
112
- return;
113
- return new GitRepository(base_path_1.BasePath.normalize(topLevel.stdout.replace("\n", "")), gitRevision, urlTemplate);
235
+ else {
236
+ // We may be at the root of the file system, in which case there is no repo.
237
+ this.cache.set(dir, undefined);
238
+ this.cache.set(dir, this.getRepositoryFolder((0, path_1.dirname)(dir)));
239
+ }
240
+ return this.cache.get(dir);
114
241
  }
115
242
  }
116
- exports.GitRepository = GitRepository;
243
+ exports.RepositoryManager = RepositoryManager;
117
244
  // Should have three capturing groups:
118
245
  // 1. hostname
119
246
  // 2. user
@@ -1,6 +1,7 @@
1
1
  import type { Application } from "../application";
2
- import { type BuiltinTranslatableStringArgs } from "./translatable";
2
+ import { translatable, type BuiltinTranslatableStringArgs } from "./translatable";
3
3
  import { ReflectionKind } from "../models/reflections/kind";
4
+ import { ReflectionFlag } from "../models";
4
5
  /**
5
6
  * ### What is translatable?
6
7
  * TypeDoc includes a lot of literal strings. By convention, messages which are displayed
@@ -73,9 +74,11 @@ export declare class Internationalization {
73
74
  * Get the translation of the specified key, replacing placeholders
74
75
  * with the arguments specified.
75
76
  */
76
- translate<T extends keyof TranslatableStrings>(key: T, ...args: TranslatableStrings[T]): TranslatedString;
77
+ translate<T extends keyof typeof translatable>(key: T, ...args: TranslatableStrings[T]): TranslatedString;
77
78
  kindSingularString(kind: ReflectionKind): TranslatedString;
78
79
  kindPluralString(kind: ReflectionKind): TranslatedString;
80
+ flagString(flag: ReflectionFlag): TranslatedString;
81
+ translateTagName(tag: `@${string}`): TranslatedString;
79
82
  /**
80
83
  * Add translations for a string which will be displayed to the user.
81
84
  */
@@ -7,6 +7,7 @@ const translatable_1 = require("./translatable");
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = require("path");
9
9
  const kind_1 = require("../models/reflections/kind");
10
+ const models_1 = require("../models");
10
11
  // If we're running in ts-node, then we need the TS source rather than
11
12
  // the compiled file.
12
13
  const ext = process[Symbol.for("ts-node.register.instance")]
@@ -163,6 +164,47 @@ class Internationalization {
163
164
  return this.proxy.kind_plural_document();
164
165
  }
165
166
  }
167
+ flagString(flag) {
168
+ switch (flag) {
169
+ case models_1.ReflectionFlag.None:
170
+ throw new Error("Should be unreachable");
171
+ case models_1.ReflectionFlag.Private:
172
+ return this.proxy.flag_private();
173
+ case models_1.ReflectionFlag.Protected:
174
+ return this.proxy.flag_protected();
175
+ case models_1.ReflectionFlag.Public:
176
+ return this.proxy.flag_public();
177
+ case models_1.ReflectionFlag.Static:
178
+ return this.proxy.flag_static();
179
+ case models_1.ReflectionFlag.External:
180
+ return this.proxy.flag_external();
181
+ case models_1.ReflectionFlag.Optional:
182
+ return this.proxy.flag_optional();
183
+ case models_1.ReflectionFlag.Rest:
184
+ return this.proxy.flag_rest();
185
+ case models_1.ReflectionFlag.Abstract:
186
+ return this.proxy.flag_abstract();
187
+ case models_1.ReflectionFlag.Const:
188
+ return this.proxy.flag_const();
189
+ case models_1.ReflectionFlag.Readonly:
190
+ return this.proxy.flag_readonly();
191
+ case models_1.ReflectionFlag.Inherited:
192
+ return this.proxy.flag_inherited();
193
+ }
194
+ }
195
+ translateTagName(tag) {
196
+ const tagName = tag.substring(1);
197
+ const translations = this.allTranslations.get(this.application?.lang ?? "en");
198
+ if (translations.has(`tag_${tagName}`)) {
199
+ return translations.get(`tag_${tagName}`);
200
+ }
201
+ // In English, the tag names are the translated names, once turned
202
+ // into title case.
203
+ return (tagName.substring(0, 1).toUpperCase() +
204
+ tagName
205
+ .substring(1)
206
+ .replace(/[a-z][A-Z]/g, (x) => `${x[0]} ${x[1]}`));
207
+ }
166
208
  /**
167
209
  * Add translations for a string which will be displayed to the user.
168
210
  */