UNPKG

qraft

Version:

A powerful CLI tool to qraft structured project setups from GitHub template repositories

242 lines 9.51 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.FileOperations = void 0; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); /** * FileOperations handles safe file copying with overwrite protection */ class FileOperations { /** * Copy a single file with overwrite protection * @param sourcePath Absolute path to source file * @param destinationPath Absolute path to destination file * @param force Whether to force overwrite existing files * @returns Promise<FileOperationResult> Result of the operation */ async copyFile(sourcePath, destinationPath, force = false) { try { // Check if source file exists if (!(await fs.pathExists(sourcePath))) { return { source: sourcePath, destination: destinationPath, success: false, skipped: false, error: new Error(`Source file does not exist: ${sourcePath}`) }; } // Check if destination exists and handle overwrite protection const destinationExists = await fs.pathExists(destinationPath); if (destinationExists && !force) { return { source: sourcePath, destination: destinationPath, success: false, skipped: true, skipReason: 'File already exists (use --force to overwrite)' }; } // Ensure destination directory exists const destinationDir = path.dirname(destinationPath); await fs.ensureDir(destinationDir); // Copy the file await fs.copy(sourcePath, destinationPath, { overwrite: force }); return { source: sourcePath, destination: destinationPath, success: true, skipped: false }; } catch (error) { return { source: sourcePath, destination: destinationPath, success: false, skipped: false, error: error instanceof Error ? error : new Error('Unknown error during file copy') }; } } /** * Copy multiple files from a source directory to a destination directory * @param sourceDir Absolute path to source directory * @param destinationDir Absolute path to destination directory * @param files Array of relative file paths to copy * @param force Whether to force overwrite existing files * @param excludePatterns Optional array of patterns to exclude * @returns Promise<FileOperationResult[]> Results for each file operation */ async copyFiles(sourceDir, destinationDir, files, force = false, excludePatterns = []) { const results = []; for (const file of files) { // Check if file should be excluded if (this.shouldExcludeFile(file, excludePatterns)) { results.push({ source: path.join(sourceDir, file), destination: path.join(destinationDir, file), success: false, skipped: true, skipReason: 'File excluded by pattern' }); continue; } const sourcePath = path.join(sourceDir, file); const destinationPath = path.join(destinationDir, file); const result = await this.copyFile(sourcePath, destinationPath, force); results.push(result); } return results; } /** * Copy an entire directory structure with overwrite protection * @param sourceDir Absolute path to source directory * @param destinationDir Absolute path to destination directory * @param force Whether to force overwrite existing files * @param excludePatterns Optional array of patterns to exclude * @returns Promise<FileOperationResult[]> Results for each file operation */ async copyDirectory(sourceDir, destinationDir, force = false, excludePatterns = []) { try { // Get all files in the source directory const files = await this.getAllFiles(sourceDir); // Convert to relative paths const relativeFiles = files.map(file => path.relative(sourceDir, file)); // Copy all files return await this.copyFiles(sourceDir, destinationDir, relativeFiles, force, excludePatterns); } catch (error) { return [{ source: sourceDir, destination: destinationDir, success: false, skipped: false, error: error instanceof Error ? error : new Error('Unknown error during directory copy') }]; } } /** * Check if a file exists at the given path * @param filePath Path to check * @returns Promise<boolean> True if file exists */ async fileExists(filePath) { try { return await fs.pathExists(filePath); } catch (error) { return false; } } /** * Get file stats (size, modification time, etc.) * @param filePath Path to the file * @returns Promise<fs.Stats | null> File stats or null if file doesn't exist */ async getFileStats(filePath) { try { return await fs.stat(filePath); } catch (error) { return null; } } /** * Recursively get all files in a directory * @param dirPath Directory path to scan * @returns Promise<string[]> Array of absolute file paths */ async getAllFiles(dirPath) { const files = []; const scanDirectory = async (currentPath) => { const entries = await fs.readdir(currentPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(currentPath, entry.name); if (entry.isDirectory()) { await scanDirectory(fullPath); } else { files.push(fullPath); } } }; await scanDirectory(dirPath); return files; } /** * Check if a file should be excluded based on patterns * @param filePath Relative file path * @param excludePatterns Array of patterns to match against * @returns boolean True if file should be excluded */ shouldExcludeFile(filePath, excludePatterns) { if (excludePatterns.length === 0) { return false; } const normalizedPath = filePath.replace(/\\/g, '/'); // Normalize path separators return excludePatterns.some(pattern => { // Simple pattern matching - supports wildcards const regexPattern = pattern .replace(/\./g, '\\.') // Escape dots .replace(/\*/g, '.*') // Convert * to .* .replace(/\?/g, '.'); // Convert ? to . const regex = new RegExp(`^${regexPattern}$`, 'i'); return regex.test(normalizedPath) || normalizedPath.includes(pattern); }); } /** * Create a backup of a file before overwriting * @param filePath Path to the file to backup * @returns Promise<string | null> Path to backup file or null if backup failed */ async createBackup(filePath) { try { if (!(await fs.pathExists(filePath))) { return null; } const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); const backupPath = `${filePath}.backup-${timestamp}`; await fs.copy(filePath, backupPath); return backupPath; } catch (error) { return null; } } } exports.FileOperations = FileOperations; //# sourceMappingURL=fileOperations.js.map