Skip to content

Commit

Permalink
Merge pull request #1695 from srmagura/bitbucket
Browse files Browse the repository at this point in the history
Add support for Bitbucket repositories
  • Loading branch information
Gerrit0 committed Sep 18, 2021
2 parents 7def34d + e75c28d commit d3e1876
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 54 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
### Features

- Flag option types like `validation` can now be set to true/false to enable/disable all flags within them.
- Source code links now work with Bitbucket repositories.
- Added `githubPages` option (default: true), which will create a `.nojekyll` page in the generated output.

### Thanks!
Expand Down
2 changes: 1 addition & 1 deletion examples/self/run
@@ -1,3 +1,3 @@
#!/bin/sh
cd ${0%/*}
node ../../bin/typedoc --mode file --name "TypeDoc Documentation" --tsconfig ../../tsconfig.json --readme ../../README.md --out doc/ ../../src/lib/
node ../../bin/typedoc --name "TypeDoc Documentation" --tsconfig ../../tsconfig.json --readme ../../README.md --out doc/ --entryPointStrategy expand ../../src/lib/
2 changes: 1 addition & 1 deletion examples/self/run.bat
Expand Up @@ -2,6 +2,6 @@
set curr_dir=%cd%
chdir /D "%~dp0"

node ..\..\bin\typedoc --mode file --name "TypeDoc Documentation" --tsconfig ../../tsconfig.json --readme ../../README.md --out doc\ ..\..\src\lib\
node ..\..\bin\typedoc --name "TypeDoc Documentation" --tsconfig ../../tsconfig.json --readme ../../README.md --out doc\ --entryPointStrategy expand ..\..\src\lib\

chdir /D "%curr_dir%"
86 changes: 57 additions & 29 deletions src/lib/converter/plugins/GitHubPlugin.ts
Expand Up @@ -7,6 +7,7 @@ import { BasePath } from "../utils/base-path";
import { Converter } from "../converter";
import type { Context } from "../context";
import { BindOption } from "../../utils";
import { RepositoryType } from "../../models";

function git(...args: string[]) {
return spawnSync("git", args, { encoding: "utf-8", windowsHide: true });
Expand Down Expand Up @@ -34,22 +35,27 @@ export class Repository {
/**
* The user/organization name of this repository on GitHub.
*/
gitHubUser?: string;
user?: string;

/**
* The project name of this repository on GitHub.
*/
gitHubProject?: string;
project?: string;

/**
* The hostname for this github project.
* The hostname for this GitHub or Bitbucket project.
*
* Defaults to: `github.com` (for normal, public GitHub instance projects)
*
* Or the hostname for an enterprise version of GitHub, e.g. `github.acme.com`
* (if found as a match in the list of git remotes).
*/
gitHubHostname = "github.com";
hostname = "github.com";

/**
* Whether this is a GitHub or Bitbucket repository.
*/
type: RepositoryType = RepositoryType.GitHub;

/**
* Create a new Repository instance.
Expand All @@ -61,25 +67,33 @@ export class Repository {
this.branch = gitRevision || "master";

for (let i = 0, c = repoLinks.length; i < c; i++) {
const url =
let match =
/(github(?:\.[a-z]+)*\.[a-z]{2,})[:/]([^/]+)\/(.*)/.exec(
repoLinks[i]
);

if (url) {
this.gitHubHostname = url[1];
this.gitHubUser = url[2];
this.gitHubProject = url[3];
if (this.gitHubProject.substr(-4) === ".git") {
this.gitHubProject = this.gitHubProject.substr(
if (!match) {
match = /(bitbucket.org)[:/]([^/]+)\/(.*)/.exec(repoLinks[i]);
}

if (match) {
this.hostname = match[1];
this.user = match[2];
this.project = match[3];
if (this.project.substr(-4) === ".git") {
this.project = this.project.substr(
0,
this.gitHubProject.length - 4
this.project.length - 4
);
}
break;
}
}

if (this.hostname.includes("bitbucket.org"))
this.type = RepositoryType.Bitbucket;
else this.type = RepositoryType.GitHub;

let out = git("-C", path, "ls-files");
if (out.status === 0) {
out.stdout.split("\n").forEach((file) => {
Expand Down Expand Up @@ -108,25 +122,21 @@ export class Repository {
}

/**
* Get the URL of the given file on GitHub.
* Get the URL of the given file on GitHub or Bitbucket.
*
* @param fileName The file whose GitHub URL should be determined.
* @returns An url pointing to the web preview of the given file or NULL.
* @param fileName The file whose URL should be determined.
* @returns A URL pointing to the web preview of the given file or undefined.
*/
getGitHubURL(fileName: string): string | undefined {
if (
!this.gitHubUser ||
!this.gitHubProject ||
!this.contains(fileName)
) {
getURL(fileName: string): string | undefined {
if (!this.user || !this.project || !this.contains(fileName)) {
return;
}

return [
`https://${this.gitHubHostname}`,
this.gitHubUser,
this.gitHubProject,
"blob",
`https://${this.hostname}`,
this.user,
this.project,
this.type === "github" ? "blob" : "src",
this.branch,
fileName.substr(this.path.length + 1),
].join("/");
Expand Down Expand Up @@ -159,6 +169,19 @@ export class Repository {
remotesOutput.stdout.split("\n")
);
}

static getLineNumberAnchor(
lineNumber: number,
repositoryType: RepositoryType | undefined
): string {
switch (repositoryType) {
default:
case RepositoryType.GitHub:
return "L" + lineNumber;
case RepositoryType.Bitbucket:
return "lines-" + lineNumber;
}
}
}

/**
Expand Down Expand Up @@ -248,9 +271,8 @@ export class GitHubPlugin extends ConverterComponent {
project.files.forEach((sourceFile) => {
const repository = this.getRepository(sourceFile.fullFileName);
if (repository) {
sourceFile.url = repository.getGitHubURL(
sourceFile.fullFileName
);
sourceFile.url = repository.getURL(sourceFile.fullFileName);
sourceFile.repositoryType = repository.type;
}
});

Expand All @@ -259,7 +281,13 @@ export class GitHubPlugin extends ConverterComponent {
if (reflection.sources) {
reflection.sources.forEach((source: SourceReference) => {
if (source.file && source.file.url) {
source.url = source.file.url + "#L" + source.line;
source.url =
source.file.url +
"#" +
Repository.getLineNumberAnchor(
source.line,
source.file.repositoryType
);
}
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/lib/models/sources/file.ts
Expand Up @@ -3,6 +3,7 @@ import * as Path from "path";
import type { Reflection } from "../reflections/abstract";
import type { ReflectionGroup } from "../ReflectionGroup";
import type { SourceDirectory } from "./directory";
import type { RepositoryType } from "./repository";

/**
* Represents references of reflections to their defining source files.
Expand Down Expand Up @@ -61,10 +62,15 @@ export class SourceFile {
name: string;

/**
* A url pointing to a page displaying the contents of this file.
* A URL pointing to a page displaying the contents of this file.
*/
url?: string;

/**
* The type of repository where this file is hosted.
*/
repositoryType?: RepositoryType;

/**
* The representation of the parent directory of this source file.
*/
Expand Down
1 change: 1 addition & 0 deletions src/lib/models/sources/index.ts
@@ -1,3 +1,4 @@
export { SourceDirectory } from "./directory";
export { SourceFile } from "./file";
export type { SourceReference } from "./file";
export { RepositoryType } from "./repository";
4 changes: 4 additions & 0 deletions src/lib/models/sources/repository.ts
@@ -0,0 +1,4 @@
export enum RepositoryType {
GitHub = "github",
Bitbucket = "bitbucket",
}
4 changes: 2 additions & 2 deletions src/lib/utils/options/sources/typedoc.ts
Expand Up @@ -232,11 +232,11 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
});
options.addDeclaration({
name: "gitRevision",
help: "Use specified revision instead of the last revision for linking to GitHub source files.",
help: "Use specified revision instead of the last revision for linking to GitHub/Bitbucket source files.",
});
options.addDeclaration({
name: "gitRemote",
help: "Use the specified remote for linking to GitHub source files.",
help: "Use the specified remote for linking to GitHub/Bitbucket source files.",
defaultValue: "origin",
});
options.addDeclaration({
Expand Down
99 changes: 99 additions & 0 deletions src/test/converter3/plugins/GitHubPlugin.test.ts
@@ -0,0 +1,99 @@
import * as github from "../../../lib/converter/plugins/GitHubPlugin";
import { RepositoryType } from "../../../lib/models";
import Assert = require("assert");

describe("Repository", function () {
describe("constructor", function () {
it("defaults to github.com hostname", function () {
const repository = new github.Repository("", "", []);

Assert.equal(repository.hostname, "github.com");
Assert.equal(repository.type, RepositoryType.GitHub);
});

it("handles a personal GitHub HTTPS URL", function () {
const mockRemotes = ["https://github.com/joebloggs/foobar.git"];

const repository = new github.Repository("", "", mockRemotes);

Assert.equal(repository.hostname, "github.com");
Assert.equal(repository.user, "joebloggs");
Assert.equal(repository.project, "foobar");
Assert.equal(repository.type, RepositoryType.GitHub);
});

it("handles an enterprise GitHub URL", function () {
const mockRemotes = ["git@github.acme.com:joebloggs/foobar.git"];

const repository = new github.Repository("", "", mockRemotes);

Assert.equal(repository.hostname, "github.acme.com");
Assert.equal(repository.user, "joebloggs");
Assert.equal(repository.project, "foobar");
Assert.equal(repository.type, RepositoryType.GitHub);
});

it("handles a Bitbucket HTTPS URL", function () {
const mockRemotes = [
"https://joebloggs@bitbucket.org/joebloggs/foobar.git",
];

const repository = new github.Repository("", "", mockRemotes);

Assert.equal(repository.hostname, "bitbucket.org");
Assert.equal(repository.user, "joebloggs");
Assert.equal(repository.project, "foobar");
Assert.equal(repository.type, RepositoryType.Bitbucket);
});

it("handles a Bitbucket SSH URL", function () {
const mockRemotes = ["git@bitbucket.org:joebloggs/foobar.git"];

const repository = new github.Repository("", "", mockRemotes);

Assert.equal(repository.hostname, "bitbucket.org");
Assert.equal(repository.user, "joebloggs");
Assert.equal(repository.project, "foobar");
Assert.equal(repository.type, RepositoryType.Bitbucket);
});
});

describe("getURL", () => {
const repositoryPath = "C:/Projects/foobar";
const filePath = repositoryPath + "/src/index.ts";

it("returns a GitHub URL", function () {
const mockRemotes = ["https://github.com/joebloggs/foobar.git"];

const repository = new github.Repository(
repositoryPath,
"main",
mockRemotes
);
repository.files = [filePath];

Assert.equal(
repository.getURL(filePath),
"https://github.com/joebloggs/foobar/blob/main/src/index.ts"
);
});

it("returns a Bitbucket URL", function () {
const mockRemotes = [
"https://joebloggs@bitbucket.org/joebloggs/foobar.git",
];

const repository = new github.Repository(
repositoryPath,
"main",
mockRemotes
);
repository.files = [filePath];

Assert.equal(
repository.getURL(filePath),
"https://bitbucket.org/joebloggs/foobar/src/main/src/index.ts"
);
});
});
});
20 changes: 0 additions & 20 deletions src/test/githubplugin.test.ts

This file was deleted.

0 comments on commit d3e1876

Please sign in to comment.