Skip to content

Commit cdca330

Browse files
committed
fix(windows): add file flush delay to prevent ENOENT errors in tests
On Windows, file operations can be asynchronous at the OS level. After writeFile() completes, the file handle may not be fully released immediately, causing ENOENT errors when code tries to read the file right after writing. Enhanced retryWrite() to: - Increase initial delay to 20ms (was 10ms) for more reliable file flushing - Add retry loop for file accessibility check (up to 3 retries) - Use exponential backoff within access retries (20ms per retry) - Use node:timers/promises sleep instead of raw setTimeout for cleaner async handling This fixes the intermittent test failures in Windows CI runners, including: - "should handle load -> update -> save workflow" - "should use default 2-space indent for new files"
1 parent 1fb817e commit cdca330

File tree

1 file changed

+23
-1
lines changed

1 file changed

+23
-1
lines changed

src/json/edit.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* @fileoverview Editable JSON file manipulation with formatting preservation.
33
*/
44

5+
import { setTimeout as sleep } from 'node:timers/promises'
6+
57
import {
68
INDENT_SYMBOL,
79
NEWLINE_SYMBOL,
@@ -58,6 +60,26 @@ async function retryWrite(
5860
try {
5961
// eslint-disable-next-line no-await-in-loop
6062
await fsPromises.writeFile(filepath, content)
63+
// On Windows, add a small delay and verify file exists to ensure it's fully flushed
64+
// This prevents ENOENT errors when immediately reading after write
65+
if (process.platform === 'win32') {
66+
// eslint-disable-next-line no-await-in-loop
67+
await sleep(20)
68+
// Verify the file is actually readable
69+
let accessRetries = 0
70+
while (accessRetries < 3) {
71+
try {
72+
// eslint-disable-next-line no-await-in-loop
73+
await fsPromises.access(filepath)
74+
break
75+
} catch {
76+
// If file isn't accessible yet, wait a bit more
77+
// eslint-disable-next-line no-await-in-loop
78+
await sleep(20)
79+
accessRetries++
80+
}
81+
}
82+
}
6183
return
6284
} catch (err) {
6385
const isLastAttempt = attempt === retries
@@ -74,7 +96,7 @@ async function retryWrite(
7496
// Exponential backoff: 10ms, 20ms, 40ms
7597
const delay = baseDelay * 2 ** attempt
7698
// eslint-disable-next-line no-await-in-loop
77-
await new Promise(resolve => setTimeout(resolve, delay))
99+
await sleep(delay)
78100
}
79101
}
80102
}

0 commit comments

Comments
 (0)