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.

164 lines • 7 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const yargs_1 = __importDefault(require("yargs")); const helpers_1 = require("yargs/helpers"); const ValidationOrchestrator_1 = require("../application/services/ValidationOrchestrator"); const OutputFormatter_1 = require("../infrastructure/output/OutputFormatter"); const AutoFixService_1 = require("../application/services/AutoFixService"); const InteractiveFixService_1 = require("../application/services/InteractiveFixService"); /** * CLI entry point for structure-validation */ async function main() { const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)) .usage('$0 [options]', 'Validate codebase folder and file structure') .option('scope', { type: 'string', description: 'Validation scope: all, staged, or changes', choices: ['all', 'staged', 'changes'], default: 'staged' }) .option('files', { type: 'array', string: true, description: 'Specific files to validate' }) .option('config', { type: 'string', description: 'Path to configuration file', default: 'structure-validation.config.json' }) .option('fix', { type: 'boolean', description: 'Automatically fix file structure issues and update imports', default: false }) .option('dry-run', { type: 'boolean', description: 'Show what would be fixed without making changes', default: false }) .option('backup', { type: 'boolean', description: 'Create backup before auto-fixing', default: true }) .option('verify-root', { type: 'boolean', description: 'Enable verification of files in the root folder using {root} pattern', default: false }) .option('skip-error', { type: 'boolean', description: 'Skip interactive mode and continue on errors', default: false }) .help() .alias('h', 'help') .version() .alias('v', 'version') .argv; try { const orchestrator = new ValidationOrchestrator_1.ValidationOrchestrator(argv.config); const formatter = new OutputFormatter_1.OutputFormatter(); const autoFixService = new AutoFixService_1.AutoFixService(); const interactiveFixService = new InteractiveFixService_1.InteractiveFixService(); let result; if (argv.files && argv.files.length > 0) { console.log('šŸ” Validating specific files...'); result = await orchestrator.validateSpecificFiles(argv.files, argv.verifyRoot); } else { const scopeMessages = { all: 'šŸ” Validating all files...', staged: 'šŸ” Validating staged files...', changes: 'šŸ” Validating all changed files...' }; console.log(scopeMessages[argv.scope]); result = await orchestrator.validateFilesByScope(argv.scope, argv.verifyRoot); } // Output the validation results console.log(formatter.formatResult(result)); // Handle interactive mode if there are errors and --skip-error is not used if (!result.isValid && !argv.skipError && !argv.fix && !argv.dryRun) { console.log('\nšŸ”„ Starting interactive mode...'); await interactiveFixService.handleInteractiveMode([...result.errors], [...result.suggestions]); return; } // Handle auto-fix if requested if (argv.fix || argv.dryRun) { if (result.suggestions.length > 0) { console.log('\n' + '='.repeat(50)); // Create backup if requested let backupDir = null; if (argv.fix && argv.backup) { try { backupDir = await autoFixService.createBackup(); console.log(`šŸ’¾ Backup created: ${backupDir}`); } catch (error) { console.error('āŒ Failed to create backup:', error instanceof Error ? error.message : String(error)); process.exit(1); } } // Perform auto-fix const dryRun = argv.dryRun || !argv.fix; const fixResult = await autoFixService.autoFix(result.suggestions, dryRun); // Output fix results if (fixResult.fixed.length > 0) { console.log('\nāœ… Fixed files:'); fixResult.fixed.forEach(message => console.log(` ${message}`)); } if (fixResult.errors.length > 0) { console.log('\nāŒ Errors:'); fixResult.errors.forEach(error => console.log(` ${error}`)); } // Output import update results if (fixResult.importUpdates.length > 0) { console.log('\nšŸ”„ Import updates:'); fixResult.importUpdates.forEach(update => console.log(` ${update}`)); } if (fixResult.importErrors.length > 0) { console.log('\nāŒ Import update errors:'); fixResult.importErrors.forEach(error => console.log(` ${error}`)); } console.log(fixResult.summary); // If this was a dry run and there are fixes available, suggest running with --fix if (dryRun && (fixResult.fixed.length > 0 || fixResult.importUpdates.length > 0)) { console.log('\nšŸ’” To apply these changes, run:'); console.log(` npx structure-validation ${argv.scope !== 'staged' ? `--scope ${argv.scope}` : ''} --fix`); } } else { console.log('\nāœ… No files need fixing!'); } } // Exit with appropriate code (moved after interactive mode) if (!result.isValid && !argv.fix && !argv.dryRun && !argv.skipError) { process.exit(1); } } catch (error) { console.error('āŒ Error:', error instanceof Error ? error.message : String(error)); process.exit(1); } } // Handle uncaught exceptions process.on('uncaughtException', (error) => { console.error('āŒ Uncaught Exception:', error.message); process.exit(1); }); process.on('unhandledRejection', (reason) => { console.error('āŒ Unhandled Rejection:', reason); process.exit(1); }); // Run the CLI main().catch((error) => { console.error('āŒ Fatal Error:', error instanceof Error ? error.message : String(error)); process.exit(1); }); //# sourceMappingURL=cli.js.map