feat(utils): add uniqueFilename helper with -2 collision suffix

This commit is contained in:
Julien Calixte
2026-05-25 21:17:42 +02:00
parent 01bb4b8c70
commit 0b3411626c
2 changed files with 92 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest"
import { uniqueFilename } from "./uniqueFilename"
describe("uniqueFilename", () => {
it("returns basename.ext when nothing collides", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".png",
existingPaths: ["docs/notes/myfile.md"],
directory: "docs/notes"
})
).toEqual("myfile.png")
})
it("appends -2 on first collision (skips -1)", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".png",
existingPaths: ["docs/notes/myfile.png"],
directory: "docs/notes"
})
).toEqual("myfile-2.png")
})
it("appends -3 when both base and -2 exist", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".png",
existingPaths: ["docs/notes/myfile.png", "docs/notes/myfile-2.png"],
directory: "docs/notes"
})
).toEqual("myfile-3.png")
})
it("fills gaps when -3 exists but -2 does not", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".png",
existingPaths: ["docs/notes/myfile.png", "docs/notes/myfile-3.png"],
directory: "docs/notes"
})
).toEqual("myfile-2.png")
})
it("handles repo-root directory", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".jpg",
existingPaths: ["myfile.jpg"],
directory: ""
})
).toEqual("myfile-2.jpg")
})
it("does not collide with a different extension", () => {
expect(
uniqueFilename({
basename: "myfile",
extension: ".png",
existingPaths: ["docs/myfile.jpg"],
directory: "docs"
})
).toEqual("myfile.png")
})
})

View File

@@ -0,0 +1,21 @@
export const uniqueFilename = ({
basename,
extension,
existingPaths,
directory
}: {
basename: string
extension: string
existingPaths: string[]
directory: string
}): string => {
const prefix = directory ? `${directory}/` : ""
const candidate = (n: number) =>
n === 1 ? `${basename}${extension}` : `${basename}-${n}${extension}`
const taken = new Set(existingPaths)
let n = 1
while (taken.has(`${prefix}${candidate(n)}`)) {
n = n === 1 ? 2 : n + 1
}
return candidate(n)
}