UNPKG

@diagramers/cli

Version:

Diagramers CLI - Command-line tools for managing Diagramers projects

538 lines • 26.9 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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.apiCommand = apiCommand; const api_generator_1 = require("../services/api-generator"); const table_generator_1 = require("../services/table-generator"); const relation_generator_1 = require("../services/relation-generator"); const template_processor_1 = require("../services/template-processor"); const chalk_1 = __importDefault(require("chalk")); const child_process_1 = require("child_process"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); function apiCommand(program) { const api = program .command('api') .description('API-specific commands for diagramers projects'); // Version command api .command('version') .description('Show current API template version') .option('-c, --check <version>', 'Check if specific version exists') .option('-a, --all', 'Show all available versions') .action(async (options) => { try { if (options.check) { console.log(chalk_1.default.blue(`šŸ“Œ Checking @diagramers/api version: ${options.check}`)); try { const versionInfo = (0, child_process_1.execSync)(`npm view @diagramers/api@${options.check} version`, { encoding: 'utf8', timeout: 5000 }).trim(); console.log(chalk_1.default.green(`āœ… Version ${options.check} exists: ${versionInfo}`)); } catch (error) { console.log(chalk_1.default.red(`āŒ Version ${options.check} not found`)); // Show available versions try { const versionsOutput = (0, child_process_1.execSync)('npm view @diagramers/api versions --json', { encoding: 'utf8', timeout: 10000 }); const versions = JSON.parse(versionsOutput); const recentVersions = Array.isArray(versions) ? versions.slice(-5) : [versions]; console.log(chalk_1.default.yellow('\nšŸ“¦ Recent available versions:')); recentVersions.forEach((version) => { console.log(chalk_1.default.yellow(` ${version}`)); }); } catch (versionError) { console.log(chalk_1.default.gray('Could not fetch available versions')); } } } else if (options.all) { console.log(chalk_1.default.blue('šŸ“¦ All available @diagramers/api versions:')); try { const versionsOutput = (0, child_process_1.execSync)('npm view @diagramers/api versions --json', { encoding: 'utf8', timeout: 10000 }); const versions = JSON.parse(versionsOutput); if (Array.isArray(versions)) { versions.forEach((version) => { console.log(chalk_1.default.yellow(` ${version}`)); }); console.log(chalk_1.default.gray(`\nšŸ“Š Total versions: ${versions.length}`)); } else { console.log(chalk_1.default.yellow(` ${versions}`)); } } catch (error) { console.log(chalk_1.default.red('Could not fetch all versions')); } } else { // Show current version info console.log(chalk_1.default.blue('šŸ“¦ @diagramers/api version information:')); try { const latestVersion = (0, child_process_1.execSync)('npm view @diagramers/api version', { encoding: 'utf8', timeout: 5000 }).trim(); console.log(chalk_1.default.green(`Latest: ${latestVersion}`)); } catch (error) { console.log(chalk_1.default.red('Could not fetch latest version')); } // Check local version if in a project try { const localVersion = (0, child_process_1.execSync)('npm list @diagramers/api --depth=0', { encoding: 'utf8', timeout: 5000 }); console.log(chalk_1.default.blue('Local project version:')); console.log(localVersion); } catch (error) { console.log(chalk_1.default.gray('No local @diagramers/api installation found')); } } } catch (error) { console.error(chalk_1.default.red(`āŒ Error checking version: ${error.message}`)); process.exit(1); } }); // Generate module command api .command('generate:module <name>') .description('Generate a new module with entity, schema, service, controller, and routes') .option('-f, --fields <fields>', 'Comma-separated list of fields for the module') .option('--crud', 'Generate full CRUD operations') .option('--no-routes', 'Skip route generation') .option('--no-database', 'Skip database table creation') .action(async (name, options) => { try { // Validate we're in a diagramers API project if (!isDiagramersApiProject()) { console.error(chalk_1.default.red('āŒ This command must be run from a diagramers API project')); console.log(chalk_1.default.yellow('šŸ’” Navigate to your API project directory or run: diagramers init api <project-name>')); process.exit(1); } console.log(chalk_1.default.blue(`šŸš€ Generating module: ${name}`)); if (options.fields) { console.log(chalk_1.default.gray(` Fields: ${options.fields}`)); } if (options.crud) { console.log(chalk_1.default.gray(` CRUD: Enabled`)); } await (0, api_generator_1.generateModule)(name); console.log(chalk_1.default.green(`āœ… Module '${name}' generated successfully!`)); console.log(chalk_1.default.blue(`šŸ“ Location: src/modules/${name}/`)); console.log(chalk_1.default.blue(`šŸ”— Routes: /api/${name}s`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Error generating module: ${error.message}`)); process.exit(1); } }); // Generate endpoint command api .command('generate:endpoint <module-name> <endpoint-name>') .description('Generate a new endpoint for an existing module') .option('-m, --method <method>', 'HTTP method (GET, POST, PUT, DELETE)', 'GET') .option('-p, --path <path>', 'Custom path for the endpoint (defaults to endpoint name)') .option('-d, --description <description>', 'Description for the endpoint') .option('--no-service', 'Skip service method generation') .option('--no-controller', 'Skip controller method generation') .option('--no-route', 'Skip route generation') .action(async (moduleName, endpointName, options) => { try { // Validate we're in a diagramers API project if (!isDiagramersApiProject()) { console.error(chalk_1.default.red('āŒ This command must be run from a diagramers API project')); console.log(chalk_1.default.yellow('šŸ’” Navigate to your API project directory or run: diagramers init api <project-name>')); process.exit(1); } // Validate module exists if (!fs.existsSync(path.join(process.cwd(), 'src/modules', moduleName))) { console.error(chalk_1.default.red(`āŒ Module '${moduleName}' does not exist`)); console.log(chalk_1.default.yellow(`šŸ’” Create it first with: diagramers api generate:module ${moduleName}`)); process.exit(1); } console.log(chalk_1.default.blue(`šŸš€ Generating endpoint '${endpointName}' for module '${moduleName}'`)); console.log(chalk_1.default.gray(` Method: ${options.method}`)); if (options.path) { console.log(chalk_1.default.gray(` Path: ${options.path}`)); } if (options.description) { console.log(chalk_1.default.gray(` Description: ${options.description}`)); } await (0, api_generator_1.generateEndpoint)(moduleName, endpointName, options); console.log(chalk_1.default.green(`āœ… Endpoint '${endpointName}' generated successfully!`)); console.log(chalk_1.default.blue(`šŸ”— Available at: /api/${moduleName}/${options.path || endpointName}`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Error generating endpoint: ${error.message}`)); process.exit(1); } }); // Process template command api .command('process:template <name>') .description('Process template files for a new project') .option('--force', 'Force processing even if files exist') .option('--backup', 'Create backup before processing') .action(async (name, options) => { try { // Validate we're in a diagramers API project if (!isDiagramersApiProject()) { console.error(chalk_1.default.red('āŒ This command must be run from a diagramers API project')); console.log(chalk_1.default.yellow('šŸ’” Navigate to your API project directory or run: diagramers init api <project-name>')); process.exit(1); } console.log(chalk_1.default.blue(`šŸ”§ Processing template for project: ${name}`)); if (options.backup) { console.log(chalk_1.default.gray(' Backup: Enabled')); } if (options.force) { console.log(chalk_1.default.gray(' Force: Enabled')); } await (0, template_processor_1.processTemplate)(name); console.log(chalk_1.default.green(`āœ… Template processing completed for '${name}'!`)); console.log(chalk_1.default.blue(`šŸ“ Configuration files updated`)); console.log(chalk_1.default.blue(`šŸ—„ļø Database name: ${name.toLowerCase().replace(/[^a-zA-Z0-9]/g, '_')}`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Error processing template: ${error.message}`)); process.exit(1); } }); // Generate table command api .command('generate:table <module:table>') .description('Generate a new database table with entity and schema only (format: module:table)') .option('-f, --fields <fields>', 'Comma-separated list of fields for the table') .option('--type <type>', 'Database type (mongodb, mysql, postgres)', 'mongodb') .option('--no-seeder', 'Skip seeder generation') .option('--force', 'Force generation even if table exists') .action(async (name, options) => { try { // Validate we're in a diagramers API project if (!isDiagramersApiProject()) { console.error(chalk_1.default.red('āŒ This command must be run from a diagramers API project')); console.log(chalk_1.default.yellow('šŸ’” Navigate to your API project directory or run: diagramers init api <project-name>')); process.exit(1); } // Parse module:table format const parts = name.split(':'); let moduleName; let tableName; if (parts.length === 2) { moduleName = parts[0]; tableName = parts[1]; } else { moduleName = name; tableName = name; } // Validate module exists if (!fs.existsSync(path.join(process.cwd(), 'src/modules', moduleName))) { console.error(chalk_1.default.red(`āŒ Module '${moduleName}' does not exist`)); console.log(chalk_1.default.yellow(`šŸ’” Create it first with: diagramers api generate:module ${moduleName}`)); process.exit(1); } console.log(chalk_1.default.blue(`šŸš€ Generating table: ${tableName} in module: ${moduleName}`)); console.log(chalk_1.default.gray(` Database type: ${options.type}`)); if (options.fields) { console.log(chalk_1.default.gray(` Fields: ${options.fields}`)); } if (options.seeder === false) { console.log(chalk_1.default.gray(` Seeder: Disabled`)); } await (0, table_generator_1.generateTable)(moduleName, tableName, options); console.log(chalk_1.default.green(`āœ… Table '${tableName}' generated successfully!`)); console.log(chalk_1.default.blue(`šŸ“ Location: src/modules/${moduleName}/entities/${tableName}.entity.ts`)); console.log(chalk_1.default.blue(`šŸ“ Location: src/modules/${moduleName}/schemas/${tableName}.schema.ts`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Error generating table: ${error.message}`)); process.exit(1); } }); // Generate relation command api .command('generate:relation <table1> <table2>') .description('Generate a relationship between two existing tables') .option('-t, --type <type>', 'Relationship type: one-to-one, one-to-many, many-to-many', 'one-to-many') .option('-m, --module <module>', 'Module name (if not specified, will be inferred)') .option('--force', 'Force generation even if relations exist') .action(async (table1, table2, options) => { try { // Validate we're in a diagramers API project if (!isDiagramersApiProject()) { console.error(chalk_1.default.red('āŒ This command must be run from a diagramers API project')); console.log(chalk_1.default.yellow('šŸ’” Navigate to your API project directory or run: diagramers init api <project-name>')); process.exit(1); } const relationType = options?.type || 'one-to-many'; const moduleName = options?.module || inferModuleFromTables(table1, table2); if (!moduleName) { console.error(chalk_1.default.red('āŒ Could not determine module name. Please specify with --module')); process.exit(1); } // Validate module exists if (!fs.existsSync(path.join(process.cwd(), 'src/modules', moduleName))) { console.error(chalk_1.default.red(`āŒ Module '${moduleName}' does not exist`)); console.log(chalk_1.default.yellow(`šŸ’” Create it first with: diagramers api generate:module ${moduleName}`)); process.exit(1); } console.log(chalk_1.default.blue(`šŸ”— Creating ${relationType} relationship between ${table1} and ${table2}`)); console.log(chalk_1.default.gray(` Module: ${moduleName}`)); console.log(chalk_1.default.gray(` Type: ${relationType}`)); await (0, relation_generator_1.generateRelation)(moduleName, table1, table2, relationType); console.log(chalk_1.default.green(`āœ… Relationship created successfully!`)); console.log(chalk_1.default.blue(`šŸ”— ${table1} ↔ ${table2} (${relationType}) in module ${moduleName}`)); } catch (error) { console.error(chalk_1.default.red(`āŒ Error creating relationship: ${error.message}`)); process.exit(1); } }); // List modules command api .command('list:modules') .description('List all modules in the current API project') .option('-v, --verbose', 'Show detailed module information') .action(async (options) => { try { if (!isDiagramersApiProject()) { throw new Error('This command must be run from a diagramers API project directory'); } const modulesPath = path.join(process.cwd(), 'src', 'modules'); if (!fs.existsSync(modulesPath)) { console.log(chalk_1.default.yellow('šŸ“ No modules directory found')); return; } const modules = fs.readdirSync(modulesPath, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); if (modules.length === 0) { console.log(chalk_1.default.yellow('šŸ“ No modules found')); return; } console.log(chalk_1.default.blue(`šŸ“¦ Found ${modules.length} module(s) in the project:`)); console.log(''); for (const moduleName of modules) { const modulePath = path.join(modulesPath, moduleName); console.log(chalk_1.default.green(` ${moduleName}`)); if (options.verbose) { // Check for module components const components = []; if (fs.existsSync(path.join(modulePath, 'entities'))) components.push('entities'); if (fs.existsSync(path.join(modulePath, 'schemas'))) components.push('schemas'); if (fs.existsSync(path.join(modulePath, 'services'))) components.push('services'); if (fs.existsSync(path.join(modulePath, 'controllers'))) components.push('controllers'); if (fs.existsSync(path.join(modulePath, 'routes'))) components.push('routes'); if (components.length > 0) { console.log(chalk_1.default.gray(` Components: ${components.join(', ')}`)); } // Check for package.json to get module version const packagePath = path.join(modulePath, 'package.json'); if (fs.existsSync(packagePath)) { try { const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8')); if (packageData.version) { console.log(chalk_1.default.gray(` Version: ${packageData.version}`)); } } catch (e) { // Ignore package.json parsing errors } } } console.log(''); } } catch (error) { console.error(chalk_1.default.red(`āŒ Error: ${error.message}`)); process.exit(1); } }); // Validate project command api .command('validate') .description('Validate the current API project structure') .option('-f, --fix', 'Attempt to fix validation issues automatically') .action(async (options) => { try { if (!isDiagramersApiProject()) { throw new Error('This command must be run from a diagramers API project directory'); } console.log(chalk_1.default.blue('šŸ” Validating project structure...')); const projectPath = process.cwd(); const issues = []; const warnings = []; // Check required directories const requiredDirs = [ 'src', 'src/modules', 'src/core', 'src/shared' ]; for (const dir of requiredDirs) { const dirPath = path.join(projectPath, dir); if (!fs.existsSync(dirPath)) { issues.push(`Missing required directory: ${dir}`); } } // Check required files const requiredFiles = [ 'package.json', 'src/main.ts', 'src/core/app.ts', 'src/routes/index.ts' ]; for (const file of requiredFiles) { const filePath = path.join(projectPath, file); if (!fs.existsSync(filePath)) { issues.push(`Missing required file: ${file}`); } } // Check modules structure const modulesPath = path.join(projectPath, 'src', 'modules'); if (fs.existsSync(modulesPath)) { const modules = fs.readdirSync(modulesPath, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); for (const moduleName of modules) { const modulePath = path.join(modulesPath, moduleName); const moduleComponents = [ 'entities', 'schemas', 'services', 'controllers', 'routes' ]; for (const component of moduleComponents) { const componentPath = path.join(modulePath, component); if (!fs.existsSync(componentPath)) { warnings.push(`Module '${moduleName}' missing ${component} directory`); } } } } // Report results if (issues.length === 0 && warnings.length === 0) { console.log(chalk_1.default.green('āœ… Project structure is valid!')); return; } if (issues.length > 0) { console.log(chalk_1.default.red(`āŒ Found ${issues.length} critical issue(s):`)); issues.forEach(issue => console.log(chalk_1.default.red(` - ${issue}`))); } if (warnings.length > 0) { console.log(chalk_1.default.yellow(`āš ļø Found ${warnings.length} warning(s):`)); warnings.forEach(warning => console.log(chalk_1.default.yellow(` - ${warning}`))); } if (options.fix) { console.log(chalk_1.default.blue('\nšŸ”§ Attempting to fix issues...')); // Create missing directories for (const dir of requiredDirs) { const dirPath = path.join(projectPath, dir); if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); console.log(chalk_1.default.green(` āœ… Created directory: ${dir}`)); } } // Note: Creating missing files would require more complex logic // For now, just inform the user if (issues.length > 0) { console.log(chalk_1.default.yellow('\nšŸ’” Some issues require manual fixing. Please check the errors above.')); } } else if (issues.length > 0) { console.log(chalk_1.default.yellow('\nšŸ’” Use --fix to attempt automatic fixes')); } } catch (error) { console.error(chalk_1.default.red(`āŒ Error: ${error.message}`)); process.exit(1); } }); return api; } // Helper functions function isDiagramersApiProject() { const currentDir = process.cwd(); const requiredDirs = ['src/modules', 'src/core', 'src/shared']; return requiredDirs.every(dir => fs.existsSync(path.join(currentDir, dir))); } function inferModuleFromTables(table1, table2) { const currentDir = process.cwd(); const modulesPath = path.join(currentDir, 'src/modules'); if (!fs.existsSync(modulesPath)) { return null; } const modules = fs.readdirSync(modulesPath, { withFileTypes: true }) .filter(dirent => dirent.isDirectory()) .map(dirent => dirent.name); // Try to find a module that contains both tables for (const module of modules) { const modulePath = path.join(modulesPath, module); const entitiesPath = path.join(modulePath, 'entities'); if (fs.existsSync(entitiesPath)) { const entities = fs.readdirSync(entitiesPath) .filter(file => file.endsWith('.entity.ts')) .map(file => file.replace('.entity.ts', '')); if (entities.includes(table1) && entities.includes(table2)) { return module; } } } // If no exact match, return the first module (user can override with --module) return modules.length > 0 ? modules[0] : null; } //# sourceMappingURL=api.js.map