@diagramers/cli
Version:
Diagramers CLI - Command-line tools for managing Diagramers projects
538 lines ⢠26.9 kB
JavaScript
;
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