import { camelCase } from 'change-case'; import fs from 'node:fs/promises'; import path from 'node:path'; import { isEnoentError } from './validation'; export async function folderExists(dir: string) { try { const stat = await fs.stat(dir); return stat.isDirectory(); } catch (error: unknown) { return false; } } export async function copyFolder({ source: source, destination, ignore = [], }: { source: string; destination: string; ignore?: string[] }): Promise { const ignoreSet = new Set(ignore); async function walkAndCopy(currentSrc: string, currentDest: string): Promise { const entries = await fs.readdir(currentSrc, { withFileTypes: true }); await Promise.all( entries.map(async (entry) => { if (ignoreSet.has(entry.name)) return; const srcPath = path.join(currentSrc, entry.name); const destPath = path.join(currentDest, entry.name); if (entry.isDirectory()) { await fs.mkdir(destPath, { recursive: true }); await walkAndCopy(srcPath, destPath); } else { await fs.copyFile(srcPath, destPath); } }), ); } await fs.mkdir(destination, { recursive: true }); await walkAndCopy(source, destination); } export async function delayAtLeast(promise: Promise, minMs: number): Promise { const delayPromise = new Promise((res) => setTimeout(res, minMs)); const [result] = await Promise.all([promise, delayPromise]); return result; } export async function writeFileSafe( filePath: string, contents: string | Uint8Array, ): Promise { await fs.mkdir(path.dirname(filePath), { recursive: true }); await fs.writeFile(filePath, contents); } export async function ensureFolder(dir: string) { return await fs.mkdir(dir, { recursive: true }); } export async function renameFilesInDirectory( dirPath: string, oldName: string, newName: string, ): Promise { const files = await fs.readdir(dirPath); for (const file of files) { const oldPath = path.resolve(dirPath, file); const oldFileName = path.basename(oldPath); const newFileName = oldFileName .replace(oldName, newName) .replace(camelCase(oldName), camelCase(newName)); if (newFileName !== oldFileName) { const newPath = path.resolve(dirPath, newFileName); await fs.rename(oldPath, newPath); } } } export async function renameDirectory(oldDirPath: string, newDirName: string): Promise { const parentDir = path.dirname(oldDirPath); const newDirPath = path.resolve(parentDir, newDirName); await fs.rename(oldDirPath, newDirPath); return newDirPath; } export async function createSymlink(target: string, linkPath: string): Promise { await fs.mkdir(path.dirname(linkPath), { recursive: true }); try { const stats = await fs.lstat(linkPath); if (stats.isSymbolicLink() || stats.isFile()) { await fs.unlink(linkPath); } else if (stats.isDirectory()) { await fs.rm(linkPath, { recursive: true, force: true }); } } catch (error) { if (!isEnoentError(error)) { throw error; } } try { const targetStats = await fs.stat(target); const type = targetStats.isDirectory() ? 'dir' : 'file'; await fs.symlink(target, linkPath, type); } catch { await fs.symlink(target, linkPath, 'junction'); } }