@diagramers/cli
Version:
Diagramers CLI - Command-line tools for managing Diagramers projects
263 lines • 15.8 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.extendCommand = extendCommand;
const project_extender_1 = require("../services/project-extender");
const table_generator_1 = require("../services/table-generator");
const relation_generator_1 = require("../services/relation-generator");
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
function extendCommand(program) {
program
.command('extend')
.description(`Extend project with additional features and modules\n\nSupported usages:\n\n --module <module> [--crud] [--fields field1,field2] Generate new module with optional CRUD and fields\n --table <module:table> --fields field1,field2 Generate new table in a module\n --endpoint <module:endpoint> [--method GET|POST|...] Generate new endpoint for a module\n --relation <module:table1-table2[:type]> Generate relation between tables (type: one-to-one, one-to-many, many-to-many)\n --feature <feature> Add a feature (auth, email, etc.)\n\nExamples:\n diagramers extend --module products --crud\n diagramers extend --module users --fields email,username,role --crud\n diagramers extend --table products:categories --fields name,description,slug\n diagramers extend --endpoint products:search --method GET\n diagramers extend --relation products:category-product:one-to-many\n diagramers extend --feature auth\n`)
.option('-f, --feature <feature>', 'Feature to add (auth, email, socket, etc.)')
.option('-m, --module <module>', 'Generate new module with full CRUD operations')
.option('-e, --endpoint <module:endpoint>', 'Generate new endpoint for existing module (format: module:endpoint)')
.option('-t, --table <table>', 'Generate database table/collection with seeding')
.option('-r, --relation <relation>', 'Generate database relations')
.option('-l, --list', 'List available features and templates')
.option('--crud', 'Generate full CRUD operations for module')
.option('--fields <fields>', 'Comma-separated list of fields for module/table')
.option('--type <type>', 'Database type (mongodb, mysql, postgres)')
.option('--method <method>', 'HTTP method for endpoint (GET, POST, PUT, DELETE)', 'GET')
.option('--path <path>', 'Custom path for endpoint (defaults to endpoint name)')
.option('--description <description>', 'Description for the endpoint')
.option('--force', 'Force generation even if conflicts exist')
.option('--no-routes', 'Skip route generation for modules')
.option('--no-database', 'Skip database setup for modules')
.action(async (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);
}
if (options.list) {
const extender = new project_extender_1.ProjectExtender();
await extender.listFeatures();
return;
}
const extender = new project_extender_1.ProjectExtender();
// Generate new endpoint for existing module
if (options.endpoint) {
const [moduleName, endpointName] = options.endpoint.split(':');
if (!moduleName || !endpointName) {
console.error(chalk_1.default.red('❌ Endpoint format should be module:endpoint (e.g., product:search)'));
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 extend --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 extender.generateEndpoint(moduleName, endpointName, {
method: options.method,
path: options.path,
description: options.description
});
console.log(chalk_1.default.green(`✅ Endpoint '${endpointName}' generated successfully!`));
console.log(chalk_1.default.blue(`🔗 Available at: /api/${moduleName}/${options.path || endpointName}`));
return;
}
// Generate new module
if (options.module) {
console.log(chalk_1.default.blue(`🔧 Generating module: ${options.module}`));
if (options.crud) {
console.log(chalk_1.default.gray(` CRUD: Enabled`));
}
if (options.fields) {
console.log(chalk_1.default.gray(` Fields: ${options.fields}`));
}
if (options.type) {
console.log(chalk_1.default.gray(` Database type: ${options.type}`));
}
if (options.routes === false) {
console.log(chalk_1.default.gray(` Routes: Disabled`));
}
if (options.database === false) {
console.log(chalk_1.default.gray(` Database: Disabled`));
}
await extender.generateModule(options.module, {
crud: options.crud,
fields: options.fields?.split(','),
type: options.type || 'mongodb',
generateRoutes: options.routes !== false,
generateDatabase: options.database !== false
});
console.log(chalk_1.default.green(`✅ Module ${options.module} generated successfully!`));
console.log(chalk_1.default.blue(`📁 Location: src/modules/${options.module}/`));
if (options.routes !== false) {
console.log(chalk_1.default.blue(`🔗 Routes: /api/${options.module}s`));
}
return;
}
// Generate database table
if (options.table) {
// Parse table format: module:table or just table (assume same name for module)
const tableParts = options.table.split(':');
let moduleName;
let tableName;
if (tableParts.length === 2) {
moduleName = tableParts[0];
tableName = tableParts[1];
}
else {
// If no module specified, use table name as module name
moduleName = options.table;
tableName = options.table;
}
// 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 extend --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 || 'mongodb'}`));
if (options.fields) {
console.log(chalk_1.default.gray(` Fields: ${options.fields}`));
}
await (0, table_generator_1.generateTable)(moduleName, tableName, {
fields: options.fields?.split(','),
type: options.type || 'mongodb',
force: options.force
});
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`));
return;
}
// Generate database relations
if (options.relation) {
console.log(chalk_1.default.blue(`🔗 Generating relations for: ${options.relation}`));
// Parse relation parameters (format: module:table1-table2 or module:table1-table2:type)
const relationParts = options.relation.split(':');
let moduleName;
let tablesAndType;
if (relationParts.length >= 2) {
moduleName = relationParts[0];
tablesAndType = relationParts.slice(1).join(':');
}
else {
console.error(chalk_1.default.red('❌ Relation format should be module:table1-table2 or module:table1-table2:type'));
console.error(chalk_1.default.red(' Examples: products:product-category, users:user-profile:one-to-one'));
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 extend --module ${moduleName}`));
process.exit(1);
}
// Parse tables and relation type
const typeIndex = tablesAndType.lastIndexOf(':');
let tables;
let relationType;
if (typeIndex > 0 && ['one-to-one', 'one-to-many', 'many-to-many'].includes(tablesAndType.substring(typeIndex + 1))) {
tables = tablesAndType.substring(0, typeIndex);
relationType = tablesAndType.substring(typeIndex + 1);
}
else {
tables = tablesAndType;
relationType = 'one-to-many'; // Default to one-to-many
}
const tableNames = tables.split('-');
if (tableNames.length !== 2) {
console.error(chalk_1.default.red('❌ Tables should be separated by a dash (-)'));
console.error(chalk_1.default.red(' Examples: product-category, user-profile'));
process.exit(1);
}
const [table1, table2] = tableNames;
console.log(chalk_1.default.gray(` Module: ${moduleName}`));
console.log(chalk_1.default.gray(` Tables: ${table1} ↔ ${table2}`));
console.log(chalk_1.default.gray(` Type: ${relationType}`));
// Import and call the relation generator
await (0, relation_generator_1.generateRelation)(moduleName, table1, table2, relationType);
console.log(chalk_1.default.green(`✅ Relations ${options.relation} generated successfully!`));
console.log(chalk_1.default.blue(`🔗 ${table1} ↔ ${table2} (${relationType}) in module ${moduleName}`));
return;
}
// Add feature (legacy support)
if (options.feature) {
console.log(chalk_1.default.blue(`🔧 Adding feature: ${options.feature}`));
await extender.addFeature(options.feature);
console.log(chalk_1.default.green(`✅ Feature ${options.feature} added successfully!`));
return;
}
console.log(chalk_1.default.red('❌ Please specify what to generate: --module, --table, --relation, or --feature'));
console.log(chalk_1.default.yellow('💡 Use --list to see available options'));
console.log(chalk_1.default.gray('💡 Examples:'));
console.log(chalk_1.default.gray(' diagramers extend --module products --crud'));
console.log(chalk_1.default.gray(' diagramers extend --table products:categories --fields name,description'));
console.log(chalk_1.default.gray(' diagramers extend --endpoint products:search --method GET'));
process.exit(1);
}
catch (error) {
console.error(chalk_1.default.red(`❌ Failed to extend project: ${error.message}`));
if (error.code === 'MODULE_NOT_FOUND') {
console.log(chalk_1.default.yellow('💡 Create the module first with: diagramers extend --module <module-name>'));
}
else if (error.code === 'TABLE_ALREADY_EXISTS') {
console.log(chalk_1.default.yellow('💡 Use --force to override existing table or choose a different name'));
}
else if (error.code === 'INVALID_FIELD_FORMAT') {
console.log(chalk_1.default.yellow('💡 Field format should be: fieldName,fieldName2,fieldName3'));
}
process.exit(1);
}
});
}
// Helper function to validate diagramers API project
function isDiagramersApiProject() {
const currentDir = process.cwd();
const requiredDirs = ['src/modules', 'src/core', 'src/shared'];
return requiredDirs.every(dir => fs.existsSync(path.join(currentDir, dir)));
}
//# sourceMappingURL=extend.js.map