UNPKG

structure-validation

Version:

A Node.js CLI tool for validating codebase folder and file structure using a clean declarative configuration. Part of the guardz ecosystem for comprehensive TypeScript development.

284 lines 11.9 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.FileOperationService = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const ImportUpdateService_1 = require("./ImportUpdateService"); const FileDiscoveryService_1 = require("./FileDiscoveryService"); /** * Application service for handling file operations with automatic import updates */ class FileOperationService { constructor() { this.importUpdateService = new ImportUpdateService_1.ImportUpdateService(); this.fileDiscoveryService = new FileDiscoveryService_1.FileDiscoveryService(); } /** * Move a file to a new location and update all imports * @param sourcePath The source file path * @param targetPath The target file path * @param createDirectory Whether to create the target directory if it doesn't exist * @returns Promise<FileOperationResult> */ async moveFile(sourcePath, targetPath, createDirectory = true) { try { // Check if source file exists if (!fs.existsSync(sourcePath)) { return { success: false, message: `Source file does not exist: ${path.relative(process.cwd(), sourcePath)}` }; } // Check if target file already exists if (fs.existsSync(targetPath)) { return { success: false, message: `Target file already exists: ${path.relative(process.cwd(), targetPath)}` }; } // Create target directory if needed if (createDirectory) { const targetDirectory = path.dirname(targetPath); if (!fs.existsSync(targetDirectory)) { fs.mkdirSync(targetDirectory, { recursive: true }); } } // Move the file fs.renameSync(sourcePath, targetPath); // Update imports after the move const importResult = await this.updateImportsAfterMove(sourcePath, targetPath); // Clear file discovery cache this.clearFileDiscoveryCache(); return { success: true, message: `Moved: ${path.relative(process.cwd(), sourcePath)}${path.relative(process.cwd(), targetPath)}`, oldPath: sourcePath, newPath: targetPath, movedFile: { oldPath: sourcePath, newPath: targetPath, oldRelativePath: path.relative(process.cwd(), sourcePath), newRelativePath: path.relative(process.cwd(), targetPath) } }; } catch (error) { return { success: false, message: `Failed to move file: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Clear file discovery cache to ensure fresh file discovery after operations */ clearFileDiscoveryCache() { try { this.fileDiscoveryService.clearCache(); } catch (error) { console.error(`❌ Failed to clear file discovery cache: ${error instanceof Error ? error.message : String(error)}`); } } /** * Rename a file and update all imports * @param filePath The current file path * @param newFileName The new file name (with extension) * @returns Promise<FileOperationResult> */ async renameFile(filePath, newFileName) { try { // Check if source file exists if (!fs.existsSync(filePath)) { return { success: false, message: `Source file does not exist: ${path.relative(process.cwd(), filePath)}` }; } const directory = path.dirname(filePath); const newFilePath = path.join(directory, newFileName); // Check if target file already exists if (fs.existsSync(newFilePath)) { return { success: false, message: `Target file already exists: ${path.relative(process.cwd(), newFilePath)}` }; } // Rename the file fs.renameSync(filePath, newFilePath); // Update imports after the rename const importResult = await this.updateImportsAfterMove(filePath, newFilePath); // Clear file discovery cache this.clearFileDiscoveryCache(); return { success: true, message: `Renamed: ${path.basename(filePath)}${newFileName}`, oldPath: filePath, newPath: newFilePath, movedFile: { oldPath: filePath, newPath: newFilePath, oldRelativePath: path.relative(process.cwd(), filePath), newRelativePath: path.relative(process.cwd(), newFilePath) } }; } catch (error) { return { success: false, message: `Failed to rename file: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Delete a file and update all imports (remove references to the deleted file) * @param filePath The file path to delete * @returns Promise<FileOperationResult> */ async deleteFile(filePath) { try { // Check if file exists if (!fs.existsSync(filePath)) { return { success: false, message: `File does not exist: ${path.relative(process.cwd(), filePath)}` }; } // Delete the file fs.unlinkSync(filePath); // Update imports to remove references to the deleted file const importResult = await this.removeImportsForDeletedFile(filePath); // Clear file discovery cache this.clearFileDiscoveryCache(); return { success: true, message: `Deleted: ${path.relative(process.cwd(), filePath)}`, oldPath: filePath }; } catch (error) { return { success: false, message: `Failed to delete file: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Update imports after a file has been moved * @param oldPath The old file path * @param newPath The new file path * @returns Promise<{ updated: string[]; errors: string[] }> */ async updateImportsAfterMove(oldPath, newPath) { try { const movedFile = { oldPath, newPath, oldRelativePath: path.relative(process.cwd(), oldPath), newRelativePath: path.relative(process.cwd(), newPath) }; const result = await this.importUpdateService.updateImportsAfterMove([movedFile]); if (result.updated.length > 0) { console.log(`🔄 Updated imports in ${result.updated.length} file(s)`); } if (result.errors.length > 0) { console.log(`⚠️ Import update errors: ${result.errors.length}`); result.errors.forEach(error => console.log(` - ${error}`)); } return result; } catch (error) { console.error(`❌ Failed to update imports: ${error instanceof Error ? error.message : String(error)}`); return { updated: [], errors: [`Failed to update imports: ${error instanceof Error ? error.message : String(error)}`] }; } } /** * Remove imports for a deleted file * @param deletedFilePath The path of the deleted file * @returns Promise<{ updated: string[]; errors: string[] }> */ async removeImportsForDeletedFile(deletedFilePath) { try { console.log(`🔄 Checking for imports of deleted file: ${path.relative(process.cwd(), deletedFilePath)}`); // TODO: Implement import removal for deleted files // This would involve: // 1. Finding all files that import the deleted file // 2. Removing those import statements // 3. Cleaning up any unused imports return { updated: [], errors: [] }; } catch (error) { console.error(`❌ Failed to remove imports for deleted file: ${error instanceof Error ? error.message : String(error)}`); return { updated: [], errors: [`Failed to remove imports: ${error instanceof Error ? error.message : String(error)}`] }; } } /** * Batch move multiple files and update all imports * @param moves Array of { sourcePath, targetPath } objects * @returns Promise<FileOperationResult[]> */ async batchMoveFiles(moves) { const results = []; const movedFiles = []; // First, perform all moves for (const move of moves) { const result = await this.moveFile(move.sourcePath, move.targetPath); results.push(result); if (result.success && result.movedFile) { movedFiles.push(result.movedFile); } } // Then, update all imports in a single batch operation if (movedFiles.length > 0) { try { const importResult = await this.importUpdateService.updateImportsAfterMove(movedFiles); if (importResult.updated.length > 0) { console.log(`🔄 Updated imports in ${importResult.updated.length} file(s)`); } if (importResult.errors.length > 0) { console.log(`⚠️ Import update errors: ${importResult.errors.length}`); importResult.errors.forEach(error => console.log(` - ${error}`)); } } catch (error) { console.error(`❌ Failed to update imports in batch operation: ${error instanceof Error ? error.message : String(error)}`); } } return results; } } exports.FileOperationService = FileOperationService; //# sourceMappingURL=FileOperationService.js.map