fix(shared): handle Windows rename-over-existing in write-file-atomically (#3222)
On Windows, renameSync fails with EPERM/EACCES when the target file already exists. Fall back to unlink + rename on Windows permission errors while preserving atomic semantics on other platforms. 🤖 Generated with OhMyOpenCode assistance https://github.com/code-yeongyu/oh-my-opencode
This commit is contained in:
@@ -1,13 +1,28 @@
|
||||
import { closeSync, fsyncSync, openSync, renameSync, writeFileSync } from "node:fs"
|
||||
import { closeSync, fsyncSync, openSync, renameSync, unlinkSync, writeFileSync } from "node:fs"
|
||||
|
||||
export function writeFileAtomically(filePath: string, content: string): void {
|
||||
const tempPath = `${filePath}.tmp`
|
||||
writeFileSync(tempPath, content, "utf-8")
|
||||
const tempPath = `${filePath}.tmp`
|
||||
writeFileSync(tempPath, content, "utf-8")
|
||||
const tempFileDescriptor = openSync(tempPath, "r")
|
||||
try {
|
||||
fsyncSync(tempFileDescriptor)
|
||||
} finally {
|
||||
closeSync(tempFileDescriptor)
|
||||
}
|
||||
renameSync(tempPath, filePath)
|
||||
|
||||
try {
|
||||
renameSync(tempPath, filePath)
|
||||
} catch (error) {
|
||||
const isWindows = process.platform === "win32"
|
||||
const isPermissionError =
|
||||
error instanceof Error &&
|
||||
(error.message.includes("EPERM") || error.message.includes("EACCES"))
|
||||
|
||||
if (isWindows && isPermissionError) {
|
||||
unlinkSync(filePath)
|
||||
renameSync(tempPath, filePath)
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user