fix(fs): improve filesystem helpers: use sync rename for reliability on certain filesystems; retry rmdir with delays and avoid recursive rm; bump @push.rocks/smartfs to ^1.3.2

This commit is contained in:
2026-03-05 12:46:49 +00:00
parent 5fa93923d2
commit f136fe2e40
5 changed files with 45 additions and 22 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/tsbuild',
version: '4.1.8',
version: '4.1.9',
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
}

View File

@@ -130,22 +130,38 @@ export class FsHelpers {
/**
* Move/rename a file or directory
* Uses renameSync for reliability on XFS/mounted filesystems where async
* rename can be interrupted by signals from process managers
*/
public static async move(src: string, dest: string): Promise<void> {
await fs.promises.rename(src, dest);
fs.renameSync(src, dest);
}
/**
* Remove an empty directory
*/
public static async removeEmptyDirectory(dirPath: string): Promise<void> {
try {
await fs.promises.rmdir(dirPath);
} catch (err: any) {
if (err.code === 'ENOTEMPTY') {
// Filesystem metadata lag — use recursive rm as fallback
await fs.promises.rm(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
} else if (err.code !== 'ENOENT') {
// Retry rmdir with delays to handle filesystem metadata lag (XFS, NFS, etc.)
// NEVER use recursive rm here — if rmdir fails with ENOTEMPTY, entries may
// still be valid references to renamed files/dirs that haven't fully detached
for (let attempt = 0; attempt < 5; attempt++) {
try {
await fs.promises.rmdir(dirPath);
return;
} catch (err: any) {
if (err.code === 'ENOENT') {
return; // Already gone
}
if (err.code === 'ENOTEMPTY' && attempt < 4) {
// Wait for filesystem metadata to catch up
await new Promise(resolve => setTimeout(resolve, 100 * (attempt + 1)));
continue;
}
// Final attempt failed or non-retryable error — leave directory in place
// It will be cleaned up by the next build's "clear output directory" step
if (err.code === 'ENOTEMPTY') {
return;
}
throw err;
}
}