commit
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises'
|
||||
import { mkdir, readdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises'
|
||||
import { dirname, join, relative, sep } from 'node:path'
|
||||
import { listFiles, rgAvailable } from './search-service'
|
||||
|
||||
@@ -261,6 +261,29 @@ export async function createProjectDir(root: string, rel: string): Promise<void>
|
||||
await mkdir(target, { recursive: true })
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a project file or folder. `rel` is the current relative path, `name`
|
||||
* the new basename (no slashes — a rename stays in the same folder). Returns the
|
||||
* new relative path. Refuses to escape the project root and throws if the target
|
||||
* name is already taken, so a rename never clobbers an existing file.
|
||||
*/
|
||||
export async function renameProjectEntry(root: string, rel: string, name: string): Promise<string> {
|
||||
const clean = name.trim().replace(/\/+$/, '')
|
||||
if (!clean || clean.includes('/') || clean === '.' || clean === '..') throw new Error('invalid name')
|
||||
const from = join(root, rel)
|
||||
const parent = rel.includes('/') ? rel.slice(0, rel.lastIndexOf('/')) : ''
|
||||
const next = parent ? `${parent}/${clean}` : clean
|
||||
const to = join(root, next)
|
||||
if (relative(root, from).startsWith('..') || relative(root, to).startsWith('..')) throw new Error('outside project root')
|
||||
if (from === to) return rel
|
||||
// Case-only renames (foo.md → Foo.md) hit an existing path on macOS' case
|
||||
// insensitive filesystem, so only guard when the name really differs.
|
||||
const sameName = from.toLowerCase() === to.toLowerCase()
|
||||
if (!sameName && await stat(to).catch(() => null)) throw new Error('name already exists')
|
||||
await rename(from, to)
|
||||
return next
|
||||
}
|
||||
|
||||
/** Delete a project file or folder (relative path). Stays inside the project root. */
|
||||
export async function deleteProjectFile(root: string, rel: string): Promise<void> {
|
||||
const target = join(root, rel)
|
||||
|
||||
Reference in New Issue
Block a user