#!/usr/bin/env node /** * Generated Code Quality Validator * * Validates the quality and correctness of auto-generated code files. * Checks generated preferences schema and migration mapping files. * * Validates: * - preferenceSchemas.ts - Interface definitions and default values * - PreferencesMappings.ts - Source to target key mappings * * Usage: * node v2-refactor-temp/tools/data-classify/scripts/validate-generation.js */ const fs = require('fs') const path = require('path') class GenerationValidator { constructor() { this.projectRoot = path.resolve(__dirname, '../../../../') // Updated paths to match actual project structure this.preferencesFile = path.join(this.projectRoot, 'packages/shared/data/preference/preferenceSchemas.ts') this.mappingsFile = path.join( this.projectRoot, 'src/main/data/migration/v2/migrators/mappings/PreferencesMappings.ts' ) this.results = { preferences: { valid: false, errors: [], warnings: [] }, mappings: { valid: false, errors: [], warnings: [] } } } /** * Main validation entry point */ validate() { console.log('Validating generated code quality...\n') this.validatePreferencesFile() this.validateMappingsFile() this.printSummary() return this.results } /** * Validate preferenceSchemas.ts file */ validatePreferencesFile() { console.log('Validating preferenceSchemas.ts...') if (!fs.existsSync(this.preferencesFile)) { this.results.preferences.errors.push(`File not found: ${this.preferencesFile}`) return } try { const content = fs.readFileSync(this.preferencesFile, 'utf8') // Check for auto-generation marker if (!content.includes('AUTO-GENERATED CONTENT START')) { this.results.preferences.errors.push('Missing auto-generated content marker') } // Check for interface definition (updated name) if (!content.includes('export interface PreferenceSchemas')) { this.results.preferences.errors.push('Missing PreferenceSchemas interface definition') } // Check for ESLint configuration if (!content.includes('/* eslint')) { this.results.preferences.warnings.push('Missing ESLint configuration comment') } // Check for imports if (!content.includes("from '@shared/data/preference/preferenceTypes'")) { this.results.preferences.warnings.push('Missing preferenceTypes import') } // Count preference keys const keyMatches = content.match(/'[\w.]+'/g) if (keyMatches) { const keyCount = keyMatches.length console.log(` Found ${keyCount} preference keys`) if (keyCount < 50) { this.results.preferences.warnings.push(`Low preference key count: ${keyCount} (expected 100+)`) } } else { this.results.preferences.warnings.push('Could not parse preference key count') } // Check key naming convention const invalidKeys = [] const keyPattern = /^\s*'([^']+)':/gm let match while ((match = keyPattern.exec(content)) !== null) { const key = match[1] // Keys should be dot-separated lowercase if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$/.test(key)) { invalidKeys.push(key) } } if (invalidKeys.length > 0 && invalidKeys.length <= 5) { this.results.preferences.warnings.push( `Found ${invalidKeys.length} keys not matching naming convention: ${invalidKeys.slice(0, 3).join(', ')}...` ) } else if (invalidKeys.length > 5) { this.results.preferences.warnings.push(`Found ${invalidKeys.length} keys not matching naming convention`) } if (this.results.preferences.errors.length === 0) { this.results.preferences.valid = true console.log(' preferenceSchemas.ts validation passed') } } catch (error) { this.results.preferences.errors.push(`Failed to read file: ${error.message}`) } } /** * Validate PreferencesMappings.ts file */ validateMappingsFile() { console.log('\nValidating PreferencesMappings.ts...') if (!fs.existsSync(this.mappingsFile)) { this.results.mappings.errors.push(`File not found: ${this.mappingsFile}`) return } try { const content = fs.readFileSync(this.mappingsFile, 'utf8') // Check for auto-generation marker if (!content.includes('AUTO-GENERATED CONTENT')) { this.results.mappings.warnings.push('Missing auto-generated content marker') } // Check for ELECTRON_STORE_MAPPINGS export if (!content.includes('export const ELECTRON_STORE_MAPPINGS')) { this.results.mappings.errors.push('Missing ELECTRON_STORE_MAPPINGS export') } // Check for REDUX_STORE_MAPPINGS export if (!content.includes('export const REDUX_STORE_MAPPINGS')) { this.results.mappings.errors.push('Missing REDUX_STORE_MAPPINGS export') } // Check for as const assertion for type safety if (!content.includes('as const')) { this.results.mappings.warnings.push('Missing "as const" assertion for type safety') } // Count mapping entries const originalKeyMatches = content.match(/originalKey:/g) if (originalKeyMatches) { const mappingCount = originalKeyMatches.length console.log(` Found ${mappingCount} mapping entries`) if (mappingCount < 10) { this.results.mappings.warnings.push(`Low mapping count: ${mappingCount} (expected 50+)`) } } // Check for valid mapping structure const mappingPattern = /\{\s*originalKey:\s*'[^']+',\s*targetKey:\s*'[^']+'\s*\}/g const validMappings = content.match(mappingPattern) if (!validMappings || validMappings.length === 0) { this.results.mappings.warnings.push('Could not find valid mapping structures') } if (this.results.mappings.errors.length === 0) { this.results.mappings.valid = true console.log(' PreferencesMappings.ts validation passed') } } catch (error) { this.results.mappings.errors.push(`Failed to read file: ${error.message}`) } } /** * Print validation summary */ printSummary() { console.log('\n' + '='.repeat(50)) console.log('Validation Summary') console.log('='.repeat(50)) // Preferences results console.log(`\npreferenceSchemas.ts: ${this.results.preferences.valid ? 'PASSED' : 'FAILED'}`) for (const error of this.results.preferences.errors) { console.log(` ERROR: ${error}`) } for (const warning of this.results.preferences.warnings) { console.log(` WARNING: ${warning}`) } // Mappings results console.log(`\nPreferencesMappings.ts: ${this.results.mappings.valid ? 'PASSED' : 'FAILED'}`) for (const error of this.results.mappings.errors) { console.log(` ERROR: ${error}`) } for (const warning of this.results.mappings.warnings) { console.log(` WARNING: ${warning}`) } // Overall result const overallValid = this.results.preferences.valid && this.results.mappings.valid const totalErrors = this.results.preferences.errors.length + this.results.mappings.errors.length const totalWarnings = this.results.preferences.warnings.length + this.results.mappings.warnings.length console.log('\n' + '='.repeat(50)) console.log(`Overall: ${overallValid ? 'PASSED' : 'FAILED'}`) console.log(`Errors: ${totalErrors}, Warnings: ${totalWarnings}`) if (overallValid) { console.log('\nGenerated code quality is acceptable!') } else { console.log('\nPlease fix the errors and regenerate the code.') } } } // Run script if (require.main === module) { const validator = new GenerationValidator() const results = validator.validate() const hasErrors = results.preferences.errors.length > 0 || results.mappings.errors.length > 0 process.exit(hasErrors ? 1 : 0) } module.exports = GenerationValidator