Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion src/fsutils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { expect } from "chai";
import * as fs from "fs";
import * as path from "path";
import * as tmp from "tmp";
import { fileExistsSync, dirExistsSync, readFile, listFiles, moveAll } from "./fsutils";
import {
fileExistsSync,
dirExistsSync,
readFile,
listFiles,
moveAll,
removeDirectoryIfEmpty,
} from "./fsutils";

describe("fsutils", () => {
let tmpDir: tmp.DirResult;
Expand Down Expand Up @@ -124,4 +131,40 @@ describe("fsutils", () => {
expect(fs.existsSync(path.join(destDir, "dest"))).to.be.false;
});
});

describe("removeDirectoryIfEmpty", () => {
it("should delete directory if it exists and is empty", async () => {
const dirPath = path.join(tmpDir.name, "empty-dir");
fs.mkdirSync(dirPath);
expect(fs.existsSync(dirPath)).to.be.true;

await removeDirectoryIfEmpty(dirPath);

expect(fs.existsSync(dirPath)).to.be.false;
});

it("should not delete directory if it contains files", async () => {
const dirPath = path.join(tmpDir.name, "non-empty-dir");
fs.mkdirSync(dirPath);
fs.writeFileSync(path.join(dirPath, "file.txt"), "content");

await removeDirectoryIfEmpty(dirPath);

expect(fs.existsSync(dirPath)).to.be.true;
});

it("should do nothing if path does not exist", async () => {
const nonExistent = path.join(tmpDir.name, "does-not-exist");
await expect(removeDirectoryIfEmpty(nonExistent)).to.be.fulfilled;
});

it("should do nothing if path is not a directory", async () => {
const filePath = path.join(tmpDir.name, "regular-file.txt");
fs.writeFileSync(filePath, "content");

await removeDirectoryIfEmpty(filePath);

expect(fs.existsSync(filePath)).to.be.true;
});
});
});
27 changes: 24 additions & 3 deletions src/fsutils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync } from "fs";
import * as path from "path";
import { FirebaseError } from "./error";
import { moveSync } from "fs-extra";
import * as fs from "fs-extra";
import { FirebaseError, getErrMsg } from "./error";
import { logger } from "./logger";

export function fileExistsSync(path: string): boolean {

Check warning on line 7 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment
try {
return statSync(path).isFile();
} catch (e: any) {

Check warning on line 10 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type
return false;
}
}

export function dirExistsSync(path: string): boolean {

Check warning on line 15 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment
try {
return statSync(path).isDirectory();
} catch (e: any) {

Check warning on line 18 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Unexpected any. Specify a different type
return false;
}
}

export function readFile(path: string): string {

Check warning on line 23 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment
try {
return readFileSync(path).toString();
} catch (e: unknown) {
Expand All @@ -30,7 +31,7 @@
}
}

export function listFiles(path: string): string[] {

Check warning on line 34 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment
try {
return readdirSync(path);
} catch (e: unknown) {
Expand All @@ -42,7 +43,7 @@
}

// Move all files and directories inside srcDir to destDir
export function moveAll(srcDir: string, destDir: string) {

Check warning on line 46 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing JSDoc comment

Check warning on line 46 in src/fsutils.ts

View workflow job for this annotation

GitHub Actions / lint (24)

Missing return type on function
if (!existsSync(destDir)) {
mkdirSync(destDir, { recursive: true });
}
Expand All @@ -50,6 +51,26 @@
for (const f of files) {
const srcPath = path.join(srcDir, f);
if (srcPath === destDir) continue;
moveSync(srcPath, path.join(destDir, f));
fs.moveSync(srcPath, path.join(destDir, f));
}
}

/**
* Removes an empty directory if it exists and contains no files or subdirectories,
* suppressing any errors and logging to debug.
*/
export async function removeDirectoryIfEmpty(absDirPath: string): Promise<void> {
try {
if (await fs.pathExists(absDirPath)) {
const stat = await fs.stat(absDirPath);
if (stat.isDirectory()) {
const entries = await fs.readdir(absDirPath);
if (entries.length === 0) {
await fs.remove(absDirPath);
}
}
}
} catch (err: unknown) {
logger.debug(`Failed to clean up directory '${absDirPath}' if empty: ${getErrMsg(err)}`);
}
}
Loading
Loading