@wearesage/schema
Version:
A flexible schema definition and validation system for TypeScript with multi-database support
127 lines (103 loc) ⢠4.36 kB
JavaScript
/**
* @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();