Skip to content

Commit

Permalink
Add disableGit option
Browse files Browse the repository at this point in the history
Resolves #2326
  • Loading branch information
Gerrit0 committed Jul 9, 2023
1 parent 833c875 commit 424bb37
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 15 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Expand Up @@ -42,6 +42,7 @@
"cSpell.words": [
"cname",
"deserializers",
"githubprivate",
"linkcode",
"linkplain",
"shiki",
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

- Implemented several miscellaneous performance improvements to generate docs faster, this took the time to generate TypeDoc's
site from ~5.6 seconds to ~5.4 seconds.
- Added `--disableGit` option to prevent TypeDoc from using Git to try to determine if sources can be linked, #2326.

### Bug Fixes

Expand Down
48 changes: 43 additions & 5 deletions src/lib/converter/plugins/SourcePlugin.ts
Expand Up @@ -11,7 +11,12 @@ import { BindOption, normalizePath, getCommonDirectory } from "../../utils";
import { isNamedNode } from "../utils/nodes";
import { dirname, relative } from "path";
import { SourceReference } from "../../models";
import { gitIsInstalled, Repository } from "../utils/repository";
import {
AssumedRepository,
gitIsInstalled,
GitRepository,
Repository,
} from "../utils/repository";
import { BasePath } from "../utils/base-path";

/**
Expand All @@ -28,6 +33,9 @@ export class SourcePlugin extends ConverterComponent {
@BindOption("gitRemote")
readonly gitRemote!: string;

@BindOption("disableGit")
readonly disableGit!: boolean;

@BindOption("sourceLinkTemplate")
readonly sourceLinkTemplate!: string;

Expand Down Expand Up @@ -145,6 +153,22 @@ export class SourcePlugin extends ConverterComponent {
private onBeginResolve(context: Context) {
if (this.disableSources) return;

if (this.disableGit && !this.sourceLinkTemplate) {
this.application.logger.error(
`disableGit is set, but sourceLinkTemplate is not, so source links cannot be produced. Set a sourceLinkTemplate or disableSources to prevent source tracking.`
);
return;
}
if (
this.disableGit &&
this.sourceLinkTemplate.includes("{gitRevision}") &&
!this.gitRevision
) {
this.application.logger.warn(
`disableGit is set and sourceLinkTemplate contains {gitRevision}, which will be replaced with an empty string as no revision was provided.`
);
}

const basePath =
this.basePath || getCommonDirectory([...this.fileNames]);

Expand All @@ -161,8 +185,11 @@ export class SourcePlugin extends ConverterComponent {
}

for (const source of refl.sources || []) {
if (gitIsInstalled) {
const repo = this.getRepository(source.fullFileName);
if (this.disableGit || gitIsInstalled()) {
const repo = this.getRepository(
basePath,
source.fullFileName
);
source.url = repo?.getURL(source.fullFileName, source.line);
}

Expand All @@ -179,7 +206,18 @@ export class SourcePlugin extends ConverterComponent {
* @param fileName The name of the file a repository should be looked for.
* @returns The found repository info or undefined.
*/
private getRepository(fileName: string): Repository | undefined {
private getRepository(
basePath: string,
fileName: string
): Repository | undefined {
if (this.disableGit) {
return new AssumedRepository(
basePath,
this.gitRevision,
this.sourceLinkTemplate
);
}

// Check for known non-repositories
const dirName = dirname(fileName);
const segments = dirName.split("/");
Expand All @@ -197,7 +235,7 @@ export class SourcePlugin extends ConverterComponent {
}

// Try to create a new repository
const repository = Repository.tryCreateRepository(
const repository = GitRepository.tryCreateRepository(
dirName,
this.sourceLinkTemplate,
this.gitRevision,
Expand Down
43 changes: 36 additions & 7 deletions src/lib/converter/utils/repository.ts
Expand Up @@ -2,7 +2,7 @@ import { spawnSync } from "child_process";
import type { Logger } from "../../utils";
import { BasePath } from "../utils/base-path";

const TEN_MEGABYTES: number = 1024 * 10000;
const TEN_MEGABYTES = 1024 * 10000;

function git(...args: string[]) {
return spawnSync("git", args, {
Expand All @@ -12,12 +12,41 @@ function git(...args: string[]) {
});
}

export const gitIsInstalled = git("--version").status === 0;
let haveGit: boolean;
export function gitIsInstalled() {
haveGit ??= git("--version").status === 0;
return haveGit;
}

export interface Repository {
getURL(fileName: string, line: number): string | undefined;
}

export class AssumedRepository implements Repository {
constructor(
readonly path: string,
readonly gitRevision: string,
readonly sourceLinkTemplate: string
) {}

getURL(fileName: string, line: number): string | undefined {
const replacements = {
gitRevision: this.gitRevision,
path: fileName.substring(this.path.length + 1),
line,
};

return this.sourceLinkTemplate.replace(
/\{(path|line)\}/g,
(_, key) => replacements[key as never]
);
}
}

/**
* Stores data of a repository.
*/
export class Repository {
export class GitRepository implements Repository {
/**
* The path of this repository on disk.
*/
Expand Down Expand Up @@ -78,18 +107,18 @@ export class Repository {
* Try to create a new repository instance.
*
* Checks whether the given path is the root of a valid repository and if so
* creates a new instance of {@link Repository}.
* creates a new instance of {@link GitRepository}.
*
* @param path The potential repository root.
* @returns A new instance of {@link Repository} or undefined.
* @returns A new instance of {@link GitRepository} or undefined.
*/
static tryCreateRepository(
path: string,
sourceLinkTemplate: string,
gitRevision: string,
gitRemote: string,
logger: Logger
): Repository | undefined {
): GitRepository | undefined {
const topLevel = git("-C", path, "rev-parse", "--show-toplevel");
if (topLevel.status !== 0) return;

Expand Down Expand Up @@ -125,7 +154,7 @@ export class Repository {

if (!urlTemplate) return;

return new Repository(
return new GitRepository(
BasePath.normalize(topLevel.stdout.replace("\n", "")),
gitRevision,
urlTemplate
Expand Down
3 changes: 2 additions & 1 deletion src/lib/utils/options/declaration.ts
Expand Up @@ -109,6 +109,7 @@ export interface TypeDocOptionMap {
includeVersion: boolean;
disableSources: boolean;
sourceLinkTemplate: string;
disableGit: boolean;
gitRevision: string;
gitRemote: string;
readme: string;
Expand Down Expand Up @@ -288,7 +289,7 @@ export enum ParameterType {
*/
GlobArray,
/**
* An unopinionated object that preserves default settings unless explicitly overridden
* An object which partially merges user-set values into the defaults.
*/
Object,
/**
Expand Down
9 changes: 7 additions & 2 deletions src/lib/utils/options/sources/typedoc.ts
Expand Up @@ -337,13 +337,18 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
name: "sourceLinkTemplate",
help: "Specify a link template to be used when generating source urls. If not set, will be automatically created using the git remote. Supports {path}, {line}, {gitRevision} placeholders.",
});
options.addDeclaration({
name: "disableGit",
help: "Assume that all can be linked to with the sourceLinkTemplate, sourceLinkTemplate must be set if this is enabled. {path} will be rooted at basePath",
type: ParameterType.Boolean,
});
options.addDeclaration({
name: "gitRevision",
help: "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.",
help: "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files. Has no effect if disableSources is set.",
});
options.addDeclaration({
name: "gitRemote",
help: "Use the specified remote for linking to GitHub/Bitbucket source files.",
help: "Use the specified remote for linking to GitHub/Bitbucket source files. Has no effect if disableGit or disableSources is set.",
defaultValue: "origin",
});
options.addDeclaration({
Expand Down

0 comments on commit 424bb37

Please sign in to comment.