import { randomUUID } from "crypto"; import { lstat, mkdir, readlink, rename, stat, writeFile } from "fs/promises"; import { dirname, join, parse, resolve, sep } from "path"; export async function resolveMutationTargetPath(path: string): Promise { const absolutePath = resolve(path); const { root } = parse(absolutePath); const parts = absolutePath.slice(root.length).split(sep).filter((part) => part.length > 0); const visitedSymlinks = new Set(); async function resolveFromParts(currentPath: string, remainingParts: string[]): Promise { if (remainingParts.length === 0) { return currentPath; } const [nextPart, ...tail] = remainingParts; const candidatePath = join(currentPath, nextPart); try { const candidateStats = await lstat(candidatePath); if (!candidateStats.isSymbolicLink()) { return resolveFromParts(candidatePath, tail); } if (visitedSymlinks.has(candidatePath)) { const error = new Error(`Too many symbolic links while resolving ${path}`) as NodeJS.ErrnoException; error.code = "ELOOP"; throw error; } visitedSymlinks.add(candidatePath); const linkTargetPath = resolve(dirname(candidatePath), await readlink(candidatePath)); const targetParts = linkTargetPath .slice(parse(linkTargetPath).root.length) .split(sep) .filter((part) => part.length > 0); return resolveFromParts(parse(linkTargetPath).root, [...targetParts, ...tail]); } catch (error: unknown) { if ((error as NodeJS.ErrnoException)?.code === "ENOENT") { return join(candidatePath, ...tail); } throw error; } } return resolveFromParts(root, parts); } export async function writeFileAtomically( path: string, content: string, ): Promise { const targetPath = await resolveMutationTargetPath(path); let existingStats: Awaited> | null = null; try { existingStats = await stat(targetPath); } catch (error: unknown) { if ((error as NodeJS.ErrnoException)?.code !== "ENOENT") { throw error; } } if (existingStats && existingStats.nlink > 1) { await writeFile(targetPath, content, "utf-8"); return; } const dir = dirname(targetPath); const tempPath = join(dir, `.tmp-${randomUUID()}`); await mkdir(dir, { recursive: true }); const mode = existingStats ? existingStats.mode & 0o7777 : 0o600; await writeFile(tempPath, content, { encoding: "utf-8", flag: "wx", mode }); await rename(tempPath, targetPath); }