diffjam
Version:
cli for diffjam.com
94 lines (78 loc) • 2.59 kB
text/typescript
/*
Calls back a single listener with all non-gitignored files in the current working directory.
Uses git ls-tree by default, falling back to fdir if git is not available or this isn't a git repo.
We maintain a queue for if files are available before the listener is ready.
*/
import { equal } from "node:assert";
import { spawn } from "child_process"
import { createInterface } from "node:readline";
import { fdir } from "fdir";
import { join } from "path";
import { GitIgnore } from "./GitIgnore"
export class CurrentWorkingDirectory {
private queue: string[] = [];
private closed = false;
private listener: undefined | {
onFile: (path: string) => void;
onClose: () => void;
}
constructor(public cwd: string, gitIgnoreFileName?: string) {
if (gitIgnoreFileName) {
this.manualDirCrawl(gitIgnoreFileName);
} else {
const gitLsTree = spawn("git", ["ls-tree", "-r", "HEAD", "--name-only"]);
gitLsTree.on("error", () => this.manualDirCrawl());
const nonGitIgnoredFiles = createInterface({
input: gitLsTree.stdout,
});
nonGitIgnoredFiles.on("line", this.onFile.bind(this));
nonGitIgnoredFiles.on("close", this.onClose.bind(this));
}
}
private async manualDirCrawl(gitIgnoreFileName?: string) {
// git is not installed or this isn't a git repo, falling back to fdir
const gitignore = new GitIgnore(join(this.cwd, gitIgnoreFileName || ".gitignore"));
const gettingFiles = new fdir()
.withRelativePaths()
.withErrors()
.filter((_, isDirectory) => !isDirectory)
.crawl(this.cwd)
.withPromise() as Promise<string[]>
await gitignore.ready;
const files = await gettingFiles;
files.forEach(file => {
if (!gitignore.isIgnored(file) && file !== gitIgnoreFileName) {
this.onFile(file);
}
});
this.onClose();
}
private onFile(path: string) {
if (this.listener) {
this.listener.onFile(path);
} else {
this.queue.push(path);
}
}
private onClose() {
equal(this.closed, false, "CurrentWorkingDirectory.onClose() should only be called once");
this.closed = true;
if (this.listener) {
this.listener.onClose();
}
}
allNonGitIgnoredFiles(
onFile: (path: string) => void,
onClose: () => void
) {
if (this.listener) throw new Error("Already listening");
this.listener = { onFile, onClose };
if (this.queue.length) {
this.queue.forEach(onFile);
this.queue = [];
}
if (this.closed) {
this.listener.onClose();
}
}
}