Skip to content

Commit 80e1894

Browse files
committed
fix: prevent nested renames
1 parent f7821a5 commit 80e1894

File tree

1 file changed

+28
-17
lines changed

1 file changed

+28
-17
lines changed

src/utils/search-and-replace.ts

+28-17
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,33 @@ export async function searchAndReplace(
1616

1717
async function processFile(filePath: string): Promise<void> {
1818
try {
19-
const content = await readFile(filePath, 'utf8')
20-
let newContent = content
19+
let newContent = await readFile(filePath, 'utf8')
20+
let changesMade = false
2121

2222
for (const [i, fromString] of fromStrings.entries()) {
2323
const regex = new RegExp(fromString, 'g')
24-
newContent = newContent.replace(regex, toStrings[i])
24+
if (regex.test(newContent)) {
25+
newContent = newContent.replace(regex, toStrings[i])
26+
changesMade = true
27+
}
2528
}
2629

27-
if (content !== newContent) {
30+
if (changesMade) {
2831
if (!isDryRun) {
2932
await writeFile(filePath, newContent, 'utf8')
3033
}
3134
if (isVerbose) {
3235
console.log(`${isDryRun ? '[Dry Run] ' : ''}File modified: ${filePath}`)
33-
}
34-
for (const [index, fromStr] of fromStrings.entries()) {
35-
const count = (newContent.match(new RegExp(toStrings[index], 'g')) || []).length
36-
if (count > 0 && isVerbose) {
37-
console.log(` Replaced "${fromStr}" with "${toStrings[index]}" ${count} time(s)`)
36+
for (const [index, fromStr] of fromStrings.entries()) {
37+
const count = (newContent.match(new RegExp(toStrings[index], 'g')) || []).length
38+
if (count > 0) {
39+
console.log(` Replaced "${fromStr}" → "${toStrings[index]}" ${count} time(s)`)
40+
}
3841
}
3942
}
4043
}
4144
} catch (error) {
42-
console.error(`Error processing file ${filePath}:`, error)
45+
console.error(`Error processing file: ${filePath}`, error)
4346
}
4447
}
4548

@@ -79,6 +82,7 @@ export async function searchAndReplace(
7982
async function renamePaths(directoryPath: string): Promise<void> {
8083
try {
8184
const entries = await readdir(directoryPath, { withFileTypes: true })
85+
const renameQueue: { oldPath: string; newPath: string }[] = []
8286

8387
for (const entry of entries) {
8488
if (EXCLUDED_DIRECTORIES.has(entry.name)) {
@@ -96,16 +100,23 @@ export async function searchAndReplace(
96100
}
97101

98102
if (oldPath !== newPath) {
99-
if (!isDryRun) {
100-
await rename(oldPath, newPath)
101-
}
102-
if (isVerbose) {
103-
console.log(`${isDryRun ? '[Dry Run] ' : ''}Renamed: ${oldPath} -> ${newPath}`)
104-
}
103+
renameQueue.push({ oldPath, newPath })
105104
}
106105

107106
if (entry.isDirectory()) {
108-
await renamePaths(entry.isDirectory() ? newPath : oldPath)
107+
await renamePaths(oldPath) // Process subdirectories first
108+
}
109+
}
110+
111+
// Sort by descending path length to rename deepest paths first
112+
renameQueue.sort((a, b) => b.oldPath.length - a.oldPath.length)
113+
114+
for (const { oldPath, newPath } of renameQueue) {
115+
if (!isDryRun) {
116+
await rename(oldPath, newPath)
117+
}
118+
if (isVerbose) {
119+
console.log(`${isDryRun ? '[Dry Run] ' : ''}Renamed: ${oldPath} -> ${newPath}`)
109120
}
110121
}
111122
} catch (error) {

0 commit comments

Comments
 (0)