UNPKG

credl-parser-evaluator

Version:

TypeScript-based CREDL Parser and Evaluator that processes CREDL files and outputs complete Intermediate Representations

362 lines • 16 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.createValidateCommand = createValidateCommand; exports.executeValidateCommand = executeValidateCommand; const commander_1 = require("commander"); const index_js_1 = require("../../api/index.js"); const path = __importStar(require("path")); const fs = __importStar(require("fs/promises")); // Error exit codes for different types of failures var ExitCode; (function (ExitCode) { ExitCode[ExitCode["SUCCESS"] = 0] = "SUCCESS"; ExitCode[ExitCode["VALIDATION_ERROR"] = 1] = "VALIDATION_ERROR"; ExitCode[ExitCode["FILE_NOT_FOUND"] = 2] = "FILE_NOT_FOUND"; ExitCode[ExitCode["FILE_ACCESS_ERROR"] = 3] = "FILE_ACCESS_ERROR"; ExitCode[ExitCode["PARSING_ERROR"] = 4] = "PARSING_ERROR"; ExitCode[ExitCode["OUTPUT_ERROR"] = 5] = "OUTPUT_ERROR"; ExitCode[ExitCode["INVALID_ARGUMENTS"] = 6] = "INVALID_ARGUMENTS"; ExitCode[ExitCode["GENERAL_ERROR"] = 10] = "GENERAL_ERROR"; })(ExitCode || (ExitCode = {})); function createValidateCommand() { const validateCommand = new commander_1.Command('validate'); validateCommand .description('Validate CREDL files for syntax and specification compliance') .argument('<files...>', 'Path(s) to CREDL files or glob patterns (e.g., *.credl, **/*.yaml)') .option('-o, --output <path>', 'Output validation results to file (default: console output)') .option('-f, --format <format>', 'Output format: table, json, summary', 'table') .option('-q, --quiet', 'Suppress output except errors (overrides verbose)') .option('-v, --verbose', 'Verbose output with detailed validation information') .option('--fail-fast', 'Stop validation on first error per file') .option('--check-references', 'Enable cross-reference validation', true) .option('--strict', 'Enable strict validation mode') .option('--exit-code', 'Exit with non-zero code if any files are invalid', true) .action(async (filePaths, options) => { await executeValidateCommand(filePaths, options); }); return validateCommand; } async function executeValidateCommand(filePaths, options) { try { const startTime = Date.now(); if (!options.quiet) { console.log(`�� Starting CREDL validation for ${filePaths.length} file pattern(s)...`); } // Resolve file patterns and collect all files to validate const resolvedFiles = await resolveFilePatterns(filePaths, options); if (resolvedFiles.length === 0) { console.error('āŒ No CREDL files found matching the provided patterns'); console.error('\nšŸ’” Suggestions:'); console.error(' • Check if the file paths are correct'); console.error(' • Ensure the files exist'); console.error(' • Try using glob patterns like *.credl or **/*.yaml'); console.error(` • Searched patterns: ${filePaths.join(', ')}`); process.exit(ExitCode.FILE_NOT_FOUND); } if (options.verbose) { console.log(`šŸ“‚ Found ${resolvedFiles.length} file(s) to validate:`); resolvedFiles.forEach(file => console.log(` • ${file}`)); console.log(); } // Configure validation options const validationOptions = { failFast: options['fail-fast'] || false, checkCrossReferences: options['check-references'] !== false, strict: options.strict || false }; if (options.verbose) { console.log('šŸ“‹ Validation Configuration:'); console.log(` • Mode: ${validationOptions.strict ? 'strict' : 'standard'}`); console.log(` • Cross-references: ${validationOptions.checkCrossReferences ? 'enabled' : 'disabled'}`); console.log(` • Fail-fast: ${validationOptions.failFast ? 'enabled' : 'disabled'}`); console.log(` • Output format: ${options.format}`); console.log(); } // Validate all files const results = []; let hasProgressIndicator = false; for (let i = 0; i < resolvedFiles.length; i++) { const filePath = resolvedFiles[i]; if (!filePath) continue; // Skip undefined entries if (options.verbose) { console.log(`šŸ”„ Validating ${path.basename(filePath)}... (${i + 1}/${resolvedFiles.length})`); } else if (!options.quiet && resolvedFiles.length > 1) { process.stdout.write(`Validating files: ${i + 1}/${resolvedFiles.length}\r`); hasProgressIndicator = true; } try { const result = await validateSingleFile(filePath, validationOptions); results.push(result); } catch (error) { // Handle individual file validation errors const fileError = { filePath, isValid: false, errors: [{ field: 'file_access', message: error instanceof Error ? error.message : String(error), severity: 'error' }], warnings: [], processingTime: 0 }; results.push(fileError); } } if (hasProgressIndicator) { process.stdout.write('\n'); } const totalProcessingTime = Date.now() - startTime; // Generate summary const summary = { totalFiles: results.length, validFiles: results.filter(r => r.isValid).length, invalidFiles: results.filter(r => !r.isValid).length, totalErrors: results.reduce((sum, r) => sum + r.errors.length, 0), totalWarnings: results.reduce((sum, r) => sum + r.warnings.length, 0), processingTime: totalProcessingTime, results }; // Output results await outputResults(summary, options); // Exit with appropriate code if (options['exit-code'] !== false && summary.invalidFiles > 0) { process.exit(ExitCode.VALIDATION_ERROR); } } catch (error) { console.error('āŒ Unexpected error:', error instanceof Error ? error.message : String(error)); process.exit(ExitCode.GENERAL_ERROR); } } async function resolveFilePatterns(patterns, options) { const allFiles = new Set(); for (const pattern of patterns) { try { // Check if it's a direct file path first const resolvedPath = path.resolve(pattern); const exists = await (0, index_js_1.checkFileExists)(resolvedPath); if (exists) { // Direct file path allFiles.add(resolvedPath); } else { // Try as glob pattern const { glob: globFunction } = await Promise.resolve().then(() => __importStar(require('glob'))); const matches = await globFunction(pattern, { ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**'] }); for (const match of matches) { const fullPath = path.resolve(match); const fileExists = await (0, index_js_1.checkFileExists)(fullPath); if (fileExists) { allFiles.add(fullPath); } } } } catch (error) { if (options.verbose) { console.warn(`āš ļø Could not resolve pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`); } } } return Array.from(allFiles).sort(); } async function validateSingleFile(filePath, validationOptions) { const startTime = Date.now(); try { // Get file stats for metadata const fileStats = await (0, index_js_1.getFileStats)(filePath); // Validate the file using the parseFileFromPath API const result = await (0, index_js_1.parseFileFromPath)(filePath, validationOptions); const processingTime = Date.now() - startTime; return { filePath, isValid: result.isValid, errors: result.errors || [], warnings: result.warnings || [], ...(fileStats?.size !== undefined && { fileSize: fileStats.size }), processingTime }; } catch (error) { const processingTime = Date.now() - startTime; return { filePath, isValid: false, errors: [{ field: 'file_validation', message: error instanceof Error ? error.message : String(error), severity: 'error' }], warnings: [], processingTime }; } } async function outputResults(summary, options) { const outputContent = formatValidationResults(summary, options.format || 'table'); if (options.output) { try { // Create output directory if it doesn't exist const outputDir = path.dirname(options.output); await fs.mkdir(outputDir, { recursive: true }); // Write to file await fs.writeFile(options.output, outputContent, 'utf8'); if (!options.quiet) { console.log(`āœ… Validation results written to: ${options.output}`); } } catch (writeError) { console.error(`āŒ Failed to write output file: ${options.output}`); console.error(` Error: ${writeError instanceof Error ? writeError.message : String(writeError)}`); process.exit(ExitCode.OUTPUT_ERROR); } } else { // Output to console console.log(outputContent); } // Print summary unless quiet if (!options.quiet) { const statusIcon = summary.invalidFiles === 0 ? 'āœ…' : 'āŒ'; const statusText = summary.invalidFiles === 0 ? 'All files valid' : `${summary.invalidFiles} file(s) invalid`; console.log(); console.log(`${statusIcon} Validation Summary: ${statusText}`); console.log(` šŸ“ Files: ${summary.totalFiles} total, ${summary.validFiles} valid, ${summary.invalidFiles} invalid`); console.log(` šŸ› Issues: ${summary.totalErrors} error(s), ${summary.totalWarnings} warning(s)`); console.log(` ā±ļø Time: ${summary.processingTime}ms`); } } function formatValidationResults(summary, format) { switch (format) { case 'json': return JSON.stringify(summary, null, 2); case 'summary': return formatSummaryOutput(summary); case 'table': default: return formatTableOutput(summary); } } function formatSummaryOutput(summary) { const lines = []; // Header lines.push('šŸ” CREDL Validation Summary'); lines.push('═'.repeat(50)); // Overview lines.push(`šŸ“Š Results: ${summary.validFiles}/${summary.totalFiles} files valid`); lines.push(`šŸ› Issues: ${summary.totalErrors} error(s), ${summary.totalWarnings} warning(s)`); lines.push(`ā±ļø Processing time: ${summary.processingTime}ms`); lines.push(''); // Files with issues const filesWithIssues = summary.results.filter(r => !r.isValid || r.warnings.length > 0); if (filesWithIssues.length > 0) { lines.push('šŸ“‹ Files with Issues:'); lines.push('─'.repeat(30)); filesWithIssues.forEach(result => { const fileName = path.basename(result.filePath); const status = result.isValid ? 'āš ļø ' : 'āŒ'; const issueCount = result.errors.length + result.warnings.length; lines.push(`${status} ${fileName} (${issueCount} issue${issueCount !== 1 ? 's' : ''})`); // Show first few issues const allIssues = [...result.errors, ...result.warnings]; const displayIssues = allIssues.slice(0, 3); displayIssues.forEach(issue => { const icon = issue.severity === 'error' ? ' āŒ' : ' āš ļø '; lines.push(`${icon} ${issue.field}: ${issue.message}`); }); if (allIssues.length > 3) { lines.push(` ... and ${allIssues.length - 3} more issue(s)`); } lines.push(''); }); } else { lines.push('āœ… All files are valid with no issues found!'); } return lines.join('\n'); } function formatTableOutput(summary) { const lines = []; // Header lines.push('šŸ“‹ CREDL File Validation Results'); lines.push('═'.repeat(80)); // Table header const headerRow = '| File | Status | Errors | Warnings | Time |'; const separatorRow = '|------|--------|---------|----------|------|'; lines.push(headerRow); lines.push(separatorRow); // Table rows summary.results.forEach(result => { const fileName = path.basename(result.filePath); const status = result.isValid ? 'āœ… Valid' : 'āŒ Invalid'; const errors = result.errors.length.toString(); const warnings = result.warnings.length.toString(); const time = `${result.processingTime}ms`; const row = `| ${fileName.padEnd(20)} | ${status.padEnd(9)} | ${errors.padEnd(6)} | ${warnings.padEnd(8)} | ${time.padEnd(4)} |`; lines.push(row); }); lines.push(''); // Detailed errors and warnings for invalid files const invalidFiles = summary.results.filter(r => !r.isValid); if (invalidFiles.length > 0) { lines.push('�� Detailed Issues:'); lines.push('─'.repeat(40)); invalidFiles.forEach(result => { lines.push(`šŸ“„ ${result.filePath}`); if (result.errors.length > 0) { lines.push(' āŒ Errors:'); result.errors.forEach(error => { lines.push(` • ${error.field}: ${error.message}`); }); } if (result.warnings.length > 0) { lines.push(' āš ļø Warnings:'); result.warnings.forEach(warning => { lines.push(` • ${warning.field}: ${warning.message}`); }); } lines.push(''); }); } return lines.join('\n'); } //# sourceMappingURL=validate.js.map