UNPKG

knowhub

Version:

Synchronize AI coding–agent knowledge files (rules, templates, guidelines) across your project.

157 lines • 5.05 kB
import { copyFile as fsCopyFile, mkdir, readFile, readdir, stat, symlink, writeFile, } from "node:fs/promises"; import { dirname, join, relative } from "node:path"; import { FileOperationError } from "./errors.js"; const SymlinkType = { File: "file", Dir: "dir", Junction: "junction", }; const Platform = { Win32: "win32", Darwin: "darwin", Linux: "linux", }; const ErrorCode = { Enoent: "ENOENT", Eacces: "EACCES", Eperm: "EPERM", Eexist: "EEXIST", }; export async function copyFile(src, dest, overwrite) { const exists = await pathExists(dest); if (exists && !overwrite) { return { created: false, updated: false }; } if (exists && overwrite) { const srcContent = await readFile(src); const destContent = await readFile(dest); if (srcContent.equals(destContent)) { return { created: false, updated: false }; } } await ensureDirectoryExists(dirname(dest)); await fsCopyFile(src, dest); if (exists && overwrite) { return { created: false, updated: true }; } return { created: true, updated: false }; } export async function copyDirectory(srcDir, destDir, overwrite) { let created = 0; let skipped = 0; await ensureDirectoryExists(destDir); const entries = await readdir(srcDir, { withFileTypes: true }); for (const entry of entries) { const srcPath = join(srcDir, entry.name); const destPath = join(destDir, entry.name); if (entry.isDirectory()) { const result = await copyDirectory(srcPath, destPath, overwrite); created += result.created; skipped += result.skipped; } else if (entry.isFile()) { const result = await copyFile(srcPath, destPath, overwrite); if (result.created || result.updated) { created++; } else { skipped++; } } } return { created, skipped }; } export async function symlinkOrCopy(src, dest, overwrite, isDir) { const exists = await pathExists(dest); if (exists && !overwrite) { return { created: false, updated: false, usedSymlink: false }; } await ensureDirectoryExists(dirname(dest)); try { const symlinkType = isDir ? process.platform === Platform.Win32 ? SymlinkType.Junction : SymlinkType.Dir : SymlinkType.File; const relativeSrc = relative(dirname(dest), src); await symlink(relativeSrc, dest, symlinkType); if (exists && overwrite) { return { created: false, updated: true, usedSymlink: true }; } return { created: true, updated: false, usedSymlink: true }; } catch (error) { const nodeError = error; console.warn(`Symlink failed (${nodeError.code}), falling back to copy: ${dest}`); try { if (isDir) { const result = await copyDirectory(src, dest, overwrite); return { created: result.created > 0 && !exists, updated: result.created > 0 && exists, usedSymlink: false, }; } const result = await copyFile(src, dest, overwrite); return { created: result.created, updated: result.updated, usedSymlink: false, }; } catch (fallbackError) { throw new FileOperationError(`Both symlink and copy operations failed for: ${dest}`, dest, "symlink or copy"); } } } export async function writeTextFile(dest, content, overwrite) { const exists = await pathExists(dest); if (exists && !overwrite) { return { created: false, updated: false }; } if (exists && overwrite) { try { const existingContent = await readFile(dest, "utf8"); if (existingContent === content) { return { created: false, updated: false }; } } catch { } } await ensureDirectoryExists(dirname(dest)); await writeFile(dest, content, "utf8"); if (exists && overwrite) { return { created: false, updated: true }; } return { created: true, updated: false }; } async function ensureDirectoryExists(dirPath) { try { await mkdir(dirPath, { recursive: true }); } catch (error) { const nodeError = error; if (nodeError.code !== ErrorCode.Eexist) { throw new FileOperationError(`Failed to create directory: ${dirPath}`, dirPath, "create directory"); } } } export async function isDirectory(path) { try { const stats = await stat(path); return stats.isDirectory(); } catch { return false; } } export async function pathExists(path) { try { await stat(path); return true; } catch { return false; } } //# sourceMappingURL=file.js.map