UNPKG

@wearesage/schema

Version:

A flexible schema definition and validation system for TypeScript with multi-database support

127 lines (103 loc) • 4.36 kB
#!/usr/bin/env node /** * @wearesage/schema CLI tool * Generates services from decorated entities */ const { Command } = require('commander'); const { ServiceGenerator } = require('../core/ServiceGenerator'); const { glob } = require('glob'); const fs = require('fs'); const path = require('path'); const program = new Command(); program .name('@wearesage/schema') .description('CLI for generating services from decorated entities') .version('1.0.0'); program .command('generate-services') .description('Generate database services from decorated entity classes') .option('-e, --entities <pattern>', 'Entity files pattern', './src/entities/**/*.ts') .option('-o, --output <dir>', 'Output directory', './src/generated/services') .option('-p, --permissions', 'Generate permission checks from @Auth decorators', true) .option('-v, --verbose', 'Verbose output', false) .action(async (options) => { try { console.log('šŸ” Scanning for decorated entities...'); // Find entity files const entityFiles = await glob(options.entities); if (entityFiles.length === 0) { console.error('āŒ No entity files found matching pattern:', options.entities); process.exit(1); } console.log(`šŸ“ Found ${entityFiles.length} entity files`); if (options.verbose) { entityFiles.forEach(file => console.log(` - ${file}`)); } // Ensure output directory exists fs.mkdirSync(options.output, { recursive: true }); // Import and process each entity const generatedServices = []; for (const entityFile of entityFiles) { try { console.log(`⚔ Processing ${path.basename(entityFile)}...`); // Dynamic import of the entity const absolutePath = path.resolve(entityFile); const entityModule = require(absolutePath); // Find the entity class (assumes export { EntityName }) const entityClasses = Object.values(entityModule).filter( item => typeof item === 'function' && Reflect.hasMetadata && Reflect.hasMetadata('entity:options', item) ); for (const EntityClass of entityClasses) { const entityName = EntityClass.name; console.log(` šŸ—ļø Generating ${entityName}Service...`); // Generate the service const serviceCode = ServiceGenerator.generateServiceClass(EntityClass, { includePermissions: options.permissions, verbose: options.verbose }); // Write to file const fileName = `${entityName.toLowerCase()}.service.ts`; const filePath = path.join(options.output, fileName); fs.writeFileSync(filePath, serviceCode); generatedServices.push({ entity: entityName, file: fileName, path: filePath }); console.log(` āœ… Generated ${fileName}`); } } catch (error) { console.error(` āŒ Failed to process ${entityFile}:`, error.message); if (options.verbose) { console.error(error.stack); } } } // Generate index file const indexContent = [ '// Auto-generated service exports', '// DO NOT EDIT MANUALLY - regenerate with: npm run build:services', '', ...generatedServices.map(({ entity, file }) => `export { ${entity}Service } from './${file.replace('.ts', '')}';` ), '' ].join('\n'); fs.writeFileSync(path.join(options.output, 'index.ts'), indexContent); console.log('\nšŸŽ‰ Service generation complete!'); console.log(`šŸ“ Generated ${generatedServices.length} services in: ${options.output}`); console.log('\nšŸ“‹ Generated services:'); generatedServices.forEach(({ entity, file }) => { console.log(` - ${entity}Service → ${file}`); }); } catch (error) { console.error('āŒ Service generation failed:', error.message); if (options.verbose) { console.error(error.stack); } process.exit(1); } }); program.parse();