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:
@@ -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.'
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user