Skip to content

Commit

Permalink
Add tests for SourcePlugin
Browse files Browse the repository at this point in the history
Also properly clean up fixture dirs when running tests

Resolves #2586
  • Loading branch information
Gerrit0 committed Jun 14, 2024
1 parent c16217c commit 6faf2f6
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 278 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"@types/markdown-it": "^14.1.1",
"@types/mocha": "^10.0.6",
"@types/node": "18",
"@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#8abd1494280116ff5318dde2c50ad01e1663790c",
"@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe",
"c8": "^9.1.0",
"esbuild": "^0.21.3",
"eslint": "^9.3.0",
Expand Down
5 changes: 2 additions & 3 deletions src/lib/converter/plugins/SourcePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { isNamedNode } from "../utils/nodes";
import { relative } from "path";
import { SourceReference } from "../../models";
import { gitIsInstalled, RepositoryManager } from "../utils/repository";
import { BasePath } from "../utils/base-path";

/**
* A handler that attaches source file information to reflections.
Expand Down Expand Up @@ -78,7 +77,7 @@ export class SourcePlugin extends ConverterComponent {
const symbol = reflection.project.getSymbolFromReflection(reflection);
for (const node of symbol?.declarations || []) {
const sourceFile = node.getSourceFile();
const fileName = BasePath.normalize(sourceFile.fileName);
const fileName = normalizePath(sourceFile.fileName);
this.fileNames.add(fileName);

let position: ts.LineAndCharacter;
Expand Down Expand Up @@ -113,7 +112,7 @@ export class SourcePlugin extends ConverterComponent {
if (this.disableSources || !sig) return;

const sourceFile = sig.getSourceFile();
const fileName = BasePath.normalize(sourceFile.fileName);
const fileName = normalizePath(sourceFile.fileName);
this.fileNames.add(fileName);

const position = ts.getLineAndCharacterOfPosition(
Expand Down
106 changes: 0 additions & 106 deletions src/lib/converter/utils/base-path.ts

This file was deleted.

122 changes: 58 additions & 64 deletions src/lib/converter/utils/repository.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { spawnSync } from "child_process";
import type { Logger } from "../../utils";
import { BasePath } from "../utils/base-path";
import { normalizePath, type Logger } from "../../utils";
import { NonEnumerable } from "../../utils/general";
import { dirname } from "path";
import { insertSorted } from "../../utils/array";
import { dirname, join } from "path";
import { existsSync } from "fs";

const TEN_MEGABYTES = 1024 * 10000;

Expand Down Expand Up @@ -80,7 +79,7 @@ export class GitRepository implements Repository {
if (out.status === 0) {
out.stdout.split("\0").forEach((file) => {
if (file !== "") {
this.files.add(BasePath.normalize(path + "/" + file));
this.files.add(normalizePath(path + "/" + file));
}
});
}
Expand Down Expand Up @@ -126,9 +125,6 @@ export class GitRepository implements Repository {
gitRemote: string,
logger: Logger,
): GitRepository | undefined {
const topLevel = git("-C", path, "rev-parse", "--show-toplevel");
if (topLevel.status !== 0) return;

gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim();
if (!gitRevision) return; // Will only happen in a repo with no commits.

Expand All @@ -148,11 +144,7 @@ export class GitRepository implements Repository {

if (!urlTemplate) return;

return new GitRepository(
BasePath.normalize(topLevel.stdout.replace("\n", "")),
gitRevision,
urlTemplate,
);
return new GitRepository(normalizePath(path), gitRevision, urlTemplate);
}
}

Expand All @@ -176,19 +168,26 @@ export class GitRepository implements Repository {
* expensive (~20-300ms depending on the machine, antivirus, etc.) we check for
* `.git` folders manually, and only call git if one is found.
*
* Symlinks further complicate this.
* Symlinked files have the potential to further complicate this. If TypeScript's
* `preserveSymlinks` option is on, then this may be passed the path to a symlinked
* file. Unlike TypeScript, we will resolve the path, as the repo link should really
* point to the actual file.
*/
export class RepositoryManager {
ignoredPaths = new Set<string>();
repositories: Repository[] = [];
private cache = new Map<string, Repository | undefined>();
private assumedRepo = new AssumedRepository(
this.basePath,
this.gitRevision,
this.sourceLinkTemplate,
);

constructor(
readonly basePath: string,
readonly gitRevision: string,
readonly gitRemote: string,
readonly sourceLinkTemplate: string,
readonly disableGit: boolean,
readonly logger: Logger,
private basePath: string,
private gitRevision: string,
private gitRemote: string,
private sourceLinkTemplate: string,
private disableGit: boolean,
private logger: Logger,
) {}

/**
Expand All @@ -199,54 +198,49 @@ export class RepositoryManager {
*/
getRepository(fileName: string): Repository | undefined {
if (this.disableGit) {
return new AssumedRepository(
this.basePath,
this.gitRevision,
this.sourceLinkTemplate,
);
}
// Check for known non-repositories
const dirName = dirname(fileName);
if (this.ignoredPaths.has(dirName)) {
return;
return this.assumedRepo;
}
return this.getRepositoryFolder(normalizePath(dirname(fileName)));
}

// Check for known repositories
for (const repo of this.repositories) {
if (fileName.startsWith(repo.path)) {
return repo;
}
private getRepositoryFolder(dir: string): Repository | undefined {
if (this.cache.has(dir)) {
return this.cache.get(dir);
}

// If git has been disabled, and this is outside of our base path,
// then we shouldn't create links to the file.
if (this.disableGit) return;

// Try to create a new repository
const repository = GitRepository.tryCreateRepository(
dirName,
this.sourceLinkTemplate,
this.gitRevision,
this.gitRemote,
this.logger,
);
if (repository) {
insertSorted(
this.repositories,
repository,
(repo) => repo.path.length < repository.path.length,
);
return repository;
}
if (existsSync(join(dir, ".git"))) {
// This might just be a git repo, or we might be in some self-recursive symlink
// loop, and the repo is actually somewhere else. Ask Git where the repo actually is.
const repo = git("-C", dir, "rev-parse", "--show-toplevel");
if (repo.status === 0) {
const repoDir = repo.stdout.replace("\n", "");
// This check is only necessary if we're in a symlink loop, otherwise
// it will always be true.
if (!this.cache.has(repoDir)) {
this.cache.set(
repoDir,
GitRepository.tryCreateRepository(
repoDir,
this.sourceLinkTemplate,
this.gitRevision,
this.gitRemote,
this.logger,
),
);
}

// No repository found, add all parent paths to ignored paths
// as they definitely don't contain git repos.
this.ignoredPaths.add(dirName);
let index = dirName.lastIndexOf("/");
while (index > 0) {
this.ignoredPaths.add(dirName.slice(0, index));
index = dirName.lastIndexOf("/", index - 1);
this.cache.set(dir, this.cache.get(repoDir));
} else {
// Not a git repo, probably corrupt.
this.cache.set(dir, undefined);
}
} else {
// We may be at the root of the file system, in which case there is no repo.
this.cache.set(dir, undefined);
this.cache.set(dir, this.getRepositoryFolder(dirname(dir)));
}

return this.cache.get(dir);
}
}

Expand Down
5 changes: 0 additions & 5 deletions src/lib/utils/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,6 @@ export function insertOrderSorted<T extends { order: number }>(
return arr;
}

export function insertSorted<T>(arr: T[], item: T, less: (a: T) => boolean) {
const index = binaryFindPartition(arr, less);
arr.splice(index === -1 ? arr.length : index, 0, item);
}

/**
* Performs a binary search of a given array, returning the index of the first item
* for which `partition` returns true. Returns the -1 if there are no items in `arr`
Expand Down
18 changes: 17 additions & 1 deletion src/lib/utils/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,21 @@ export function nicePath(absPath: string) {
* @returns The normalized path.
*/
export function normalizePath(path: string) {
return path.replace(/\\/g, "/");
if (process.platform === "win32") {
// Ensure forward slashes
path = path.replace(/\\/g, "/");

// Msys2 git on windows will give paths which use unix-style
// absolute paths, like /c/users/you. Since the rest of TypeDoc
// expects drive letters, convert it to that here.
path = path.replace(/^\/([a-zA-Z])\//, (_m, m1: string) => `${m1}:/`);

// Make Windows drive letters upper case
path = path.replace(
/^([^:]+):\//,
(_m, m1: string) => m1.toUpperCase() + ":/",
);
}

return path;
}
Loading

0 comments on commit 6faf2f6

Please sign in to comment.