Fix file operations

This commit is contained in:
Andras Schmelczer 2025-11-22 20:49:53 +00:00
parent 91675ea99c
commit 10fd928459
2 changed files with 64 additions and 3 deletions

View file

@ -159,4 +159,61 @@ describe("File operations", () => {
"a/b.c/e (1)"
);
});
it("should continue deconfliction from existing number in filename", async () => {
const fileSystemOperations = new FakeFileSystemOperations();
const fileOperations = new FileOperations(
new Logger(),
new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
fileSystemOperations
);
await fileOperations.create("document (5).md", new Uint8Array());
await fileOperations.create("other.md", new Uint8Array());
await fileOperations.move("other.md", "document (5).md");
assertSetContainsExactly(
fileSystemOperations.names,
"document (5).md",
"document (6).md"
);
await fileOperations.create("another.md", new Uint8Array());
await fileOperations.move("another.md", "document (5).md");
assertSetContainsExactly(
fileSystemOperations.names,
"document (5).md",
"document (6).md",
"document (7).md"
);
});
it("should handle dotfiles correctly", async () => {
const fileSystemOperations = new FakeFileSystemOperations();
const fileOperations = new FileOperations(
new Logger(),
new MockDatabase() as Database, // eslint-disable-line @typescript-eslint/no-unsafe-type-assertion
fileSystemOperations
);
await fileOperations.create(".gitignore", new Uint8Array());
await fileOperations.create("temp", new Uint8Array());
await fileOperations.move("temp", ".gitignore");
assertSetContainsExactly(
fileSystemOperations.names,
".gitignore",
".gitignore (1)"
);
await fileOperations.create(".config.json", new Uint8Array());
await fileOperations.create("temp2", new Uint8Array());
await fileOperations.move("temp2", ".config.json");
assertSetContainsExactly(
fileSystemOperations.names,
".gitignore",
".gitignore (1)",
".config.json",
".config (1).json"
);
});
});

View file

@ -25,7 +25,7 @@ export class FileOperations {
): [RelativePath, RelativePath] {
const pathParts = path.split("/");
const fileName = pathParts.pop();
if (fileName == "" || fileName == null) {
if (!fileName || fileName === "") {
throw new Error(`Path '${path}' cannot be empty`);
}
@ -234,11 +234,15 @@ export class FileOperations {
}
const nameParts = fileName.split(".");
// Handle dotfiles: ".gitignore" should have no extension, ".config.json" should have ".json"
const isDotfile = fileName.startsWith(".") && nameParts[0] === "";
const extension =
nameParts.length > 1 ? "." + nameParts[nameParts.length - 1] : "";
nameParts.length > 1 && !(isDotfile && nameParts.length === 2)
? "." + nameParts[nameParts.length - 1]
: "";
let stem = extension ? nameParts.slice(0, -1).join(".") : fileName;
let currentCount = Number.parseInt(
FileOperations.PARENTHESES_REGEX.exec(stem)?.groups?.[0] ?? "0"
FileOperations.PARENTHESES_REGEX.exec(stem)?.[1] ?? "0"
);
stem = stem.replace(FileOperations.PARENTHESES_REGEX, "");