@diagramers/cli 4.0.19 → 4.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -37,12 +37,17 @@ exports.generateModule = generateModule;
37
37
  exports.generateEndpoint = generateEndpoint;
38
38
  const fs = __importStar(require("fs"));
39
39
  const path = __importStar(require("path"));
40
- async function generateModule(moduleName) {
40
+ async function generateModule(moduleName, options = {}) {
41
41
  // Validate module name
42
42
  if (!moduleName || !/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(moduleName)) {
43
43
  throw new Error('Invalid module name. Use only letters, numbers, hyphens, and underscores. Must start with a letter.');
44
44
  }
45
- const moduleNameCapitalized = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
45
+ // Convert module name to valid TypeScript identifiers
46
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
47
+ const moduleNameCapitalized = moduleNameCamelCase.charAt(0).toUpperCase() + moduleNameCamelCase.slice(1);
48
+ console.log(`🔍 Debug: moduleName = "${moduleName}"`);
49
+ console.log(`🔍 Debug: moduleNameCamelCase = "${moduleNameCamelCase}"`);
50
+ console.log(`🔍 Debug: moduleNameCapitalized = "${moduleNameCapitalized}"`);
46
51
  const currentDir = process.cwd();
47
52
  // Check for required API project structure
48
53
  const requiredDirs = ['src/modules'];
@@ -60,34 +65,49 @@ async function generateModule(moduleName) {
60
65
  fs.mkdirSync(path.join(modulePath, 'controllers'), { recursive: true });
61
66
  fs.mkdirSync(path.join(modulePath, 'routes'), { recursive: true });
62
67
  }
68
+ // Parse options
69
+ const fields = options.fields || ['name', 'description', 'status'];
70
+ const generateRoutes = options.generateRoutes !== false;
71
+ const generateDatabase = options.generateDatabase !== false;
72
+ const crud = options.crud || false;
63
73
  console.log('📝 Creating entity...');
64
- const entityContent = generateEntityContent(moduleName, moduleNameCapitalized);
65
- fs.writeFileSync(path.join(modulePath, 'entities', `${moduleName}.entity.ts`), entityContent);
74
+ const entityContent = generateEntityContent(moduleNameCamelCase, moduleNameCapitalized, fields);
75
+ fs.writeFileSync(path.join(modulePath, 'entities', `${moduleNameCamelCase}.entity.ts`), entityContent);
66
76
  console.log('📋 Creating schema...');
67
- const schemaContent = generateSchemaContent(moduleName, moduleNameCapitalized);
68
- fs.writeFileSync(path.join(modulePath, 'schemas', `${moduleName}.schema.ts`), schemaContent);
77
+ const schemaContent = generateSchemaContent(moduleNameCamelCase, moduleNameCapitalized, fields);
78
+ fs.writeFileSync(path.join(modulePath, 'schemas', `${moduleNameCamelCase}.schema.ts`), schemaContent);
69
79
  console.log('🔧 Creating service...');
70
- const serviceContent = generateServiceContent(moduleName, moduleNameCapitalized);
71
- fs.writeFileSync(path.join(modulePath, 'services', `${moduleName}.service.ts`), serviceContent);
80
+ const serviceContent = generateServiceContent(moduleNameCamelCase, moduleNameCapitalized, fields, crud);
81
+ fs.writeFileSync(path.join(modulePath, 'services', `${moduleNameCamelCase}.service.ts`), serviceContent);
72
82
  console.log('🎮 Creating controller...');
73
- const controllerContent = generateControllerContent(moduleName, moduleNameCapitalized);
74
- fs.writeFileSync(path.join(modulePath, 'controllers', `${moduleName}.controller.ts`), controllerContent);
75
- console.log('🛣️ Creating routes...');
76
- const routesContent = generateRoutesContent(moduleName, moduleNameCapitalized);
77
- fs.writeFileSync(path.join(modulePath, 'routes', `${moduleName}.routes.ts`), routesContent);
78
- // Register routes in server manager
79
- console.log('🔗 Registering routes...');
80
- await registerModuleRoutes(currentDir, moduleName, moduleNameCapitalized);
81
- // Create database table/collection
82
- console.log('🗄️ Creating database table...');
83
- await createDatabaseTable(currentDir, moduleName, moduleNameCapitalized);
83
+ const controllerContent = generateControllerContent(moduleNameCamelCase, moduleNameCapitalized, fields, crud);
84
+ fs.writeFileSync(path.join(modulePath, 'controllers', `${moduleNameCamelCase}.controller.ts`), controllerContent);
85
+ if (generateRoutes) {
86
+ console.log('🛣️ Creating routes...');
87
+ const routesContent = generateRoutesContent(moduleNameCamelCase, moduleNameCapitalized, crud, fields);
88
+ fs.writeFileSync(path.join(modulePath, 'routes', `${moduleNameCamelCase}.routes.ts`), routesContent);
89
+ // Register routes in server manager
90
+ console.log('🔗 Registering routes...');
91
+ await registerModuleRoutes(currentDir, moduleName, moduleNameCapitalized);
92
+ }
93
+ if (generateDatabase) {
94
+ // Create database table/collection
95
+ console.log('🗄️ Creating database table...');
96
+ await createDatabaseTable(currentDir, moduleName, moduleNameCapitalized);
97
+ }
84
98
  console.log('✅ Module generation completed!');
85
99
  console.log(`📁 Module: src/modules/${moduleName}/`);
86
- console.log(`🔗 Routes: /api/${moduleName}s`);
87
- console.log(`🗄️ Database: ${moduleName} collection`);
100
+ if (generateRoutes) {
101
+ console.log(`🔗 Routes: /api/${moduleName}s`);
102
+ }
103
+ if (generateDatabase) {
104
+ console.log(`🗄️ Database: ${moduleName} collection`);
105
+ }
88
106
  }
89
- function generateEntityContent(moduleName, moduleNameCapitalized) {
90
- return `import * as mongoose from 'mongoose';
107
+ function generateEntityContent(moduleName, moduleNameCapitalized, fields) {
108
+ // Convert moduleName to camelCase for internal identifiers
109
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
110
+ let entityContent = `import * as mongoose from 'mongoose';
91
111
  import { ObjectId } from "bson";
92
112
 
93
113
  export interface I${moduleNameCapitalized} extends mongoose.Document {
@@ -98,12 +118,33 @@ export interface I${moduleNameCapitalized} extends mongoose.Document {
98
118
  createdAt: Date,
99
119
  updatedAt: Date
100
120
  }`;
121
+ if (fields.length > 0) {
122
+ entityContent += `
123
+ export interface I${moduleNameCapitalized} extends mongoose.Document {
124
+ _id: ObjectId,
125
+ name: string,
126
+ description?: string,
127
+ status: number,
128
+ createdAt: Date,
129
+ updatedAt: Date
130
+ `;
131
+ fields.forEach(field => {
132
+ entityContent += `
133
+ ${field}: string;
134
+ `;
135
+ });
136
+ entityContent += `
137
+ }`;
138
+ }
139
+ return entityContent;
101
140
  }
102
- function generateSchemaContent(moduleName, moduleNameCapitalized) {
103
- return `import * as mongoose from 'mongoose';
104
- import { I${moduleNameCapitalized} } from '../entities/${moduleName}';
141
+ function generateSchemaContent(moduleName, moduleNameCapitalized, fields) {
142
+ // Convert moduleName to camelCase for file paths
143
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
144
+ let schemaContent = `import * as mongoose from 'mongoose';
145
+ import { I${moduleNameCapitalized} } from '../entities/${moduleNameCamelCase}.entity';
105
146
 
106
- export const ${moduleName}Schema = new mongoose.Schema(
147
+ export const ${moduleNameCamelCase}Schema = new mongoose.Schema(
107
148
  {
108
149
  name: {
109
150
  type: mongoose.SchemaTypes.String,
@@ -121,12 +162,90 @@ export const ${moduleName}Schema = new mongoose.Schema(
121
162
  { timestamps: true, suppressReservedKeysWarning: true },
122
163
  );
123
164
 
124
- export const ${moduleNameCapitalized}Model = mongoose.model<I${moduleNameCapitalized}>('${moduleName}', ${moduleName}Schema);`;
165
+ export const ${moduleNameCapitalized}Model = mongoose.model<I${moduleNameCapitalized}>('${moduleNameCamelCase}', ${moduleNameCamelCase}Schema);`;
166
+ if (fields.length > 0) {
167
+ schemaContent += `
168
+ export const ${moduleNameCamelCase}Schema = new mongoose.Schema(
169
+ {
170
+ name: {
171
+ type: mongoose.SchemaTypes.String,
172
+ required: true,
173
+ },
174
+ description: {
175
+ type: mongoose.SchemaTypes.String,
176
+ required: false,
177
+ },
178
+ status: {
179
+ type: mongoose.SchemaTypes.Number,
180
+ default: 1,
181
+ }
182
+ `;
183
+ fields.forEach(field => {
184
+ schemaContent += `
185
+ ${field}: {
186
+ type: mongoose.SchemaTypes.String,
187
+ required: true,
188
+ },
189
+ `;
190
+ });
191
+ schemaContent += `
192
+ },
193
+ { timestamps: true, suppressReservedKeysWarning: true },
194
+ );
195
+ `;
196
+ }
197
+ return schemaContent;
125
198
  }
126
- function generateServiceContent(moduleName, moduleNameCapitalized) {
127
- return `import { ObjectId } from "bson";
128
- import { ${moduleNameCapitalized}Model } from '../schemas/${moduleName}.schema';
199
+ function generateServiceContent(moduleName, moduleNameCapitalized, fields, crud) {
200
+ // Convert moduleName to camelCase for file paths
201
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
202
+ let serviceContent = `import { ObjectId } from "bson";
203
+ import { ${moduleNameCapitalized}Model } from '../schemas/${moduleNameCamelCase}.schema';
129
204
 
205
+ export class ${moduleNameCapitalized}Service {
206
+ async getAll() {
207
+ try {
208
+ return await ${moduleNameCapitalized}Model.find({ status: 1 });
209
+ } catch (error) {
210
+ throw new Error(\`Failed to fetch ${moduleNameCamelCase}s: \${error.message}\`);
211
+ }
212
+ }
213
+
214
+ async getById(id: string) {
215
+ try {
216
+ return await ${moduleNameCapitalized}Model.findById(id);
217
+ } catch (error) {
218
+ throw new Error(\`Failed to fetch ${moduleNameCamelCase}: \${error.message}\`);
219
+ }
220
+ }
221
+
222
+ async create(data: any) {
223
+ try {
224
+ const entity = new ${moduleNameCapitalized}Model({ _id: new ObjectId(), ...data, status: 1 });
225
+ return await ${moduleNameCapitalized}Model.create(entity);
226
+ } catch (error) {
227
+ throw new Error(\`Failed to create ${moduleNameCamelCase}: \${error.message}\`);
228
+ }
229
+ }
230
+
231
+ async update(id: string, data: any) {
232
+ try {
233
+ return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { ...data }, { new: true });
234
+ } catch (error) {
235
+ throw new Error(\`Failed to update ${moduleNameCamelCase}: \${error.message}\`);
236
+ }
237
+ }
238
+
239
+ async delete(id: string) {
240
+ try {
241
+ return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { status: 0 }, { new: true });
242
+ } catch (error) {
243
+ throw new Error(\`Failed to delete ${moduleNameCamelCase}: \${error.message}\`);
244
+ }
245
+ }
246
+ }`;
247
+ if (crud) {
248
+ serviceContent += `
130
249
  export class ${moduleNameCapitalized}Service {
131
250
  async getAll() {
132
251
  return await ${moduleNameCapitalized}Model.find({ status: 1 });
@@ -144,10 +263,21 @@ export class ${moduleNameCapitalized}Service {
144
263
  async delete(id: string) {
145
264
  return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { status: 0 }, { new: true });
146
265
  }
147
- }`;
266
+ `;
267
+ fields.forEach(field => {
268
+ serviceContent += `
269
+ async ${field}(id: string, data: any) {
270
+ return await ${moduleNameCapitalized}Model.findByIdAndUpdate(id, { ${field}: data }, { new: true });
271
+ }
272
+ `;
273
+ });
274
+ }
275
+ return serviceContent;
148
276
  }
149
- function generateControllerContent(moduleName, moduleNameCapitalized) {
150
- return `import { ${moduleNameCapitalized}Service } from '../services/${moduleName}.service';
277
+ function generateControllerContent(moduleName, moduleNameCapitalized, fields, crud) {
278
+ // Convert moduleName to camelCase for file paths
279
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
280
+ let controllerContent = `import { ${moduleNameCapitalized}Service } from '../services/${moduleNameCamelCase}.service';
151
281
 
152
282
  /**
153
283
  * @openapi
@@ -189,14 +319,14 @@ export class ${moduleNameCapitalized}Controller {
189
319
 
190
320
  /**
191
321
  * @openapi
192
- * /api/${moduleName}:
322
+ * /api/${moduleNameCamelCase}:
193
323
  * get:
194
- * summary: Get all ${moduleName}s
195
- * description: Retrieve a list of all active ${moduleName}s
324
+ * summary: Get all ${moduleNameCamelCase}s
325
+ * description: Retrieve a list of all active ${moduleNameCamelCase}s
196
326
  * tags: [${moduleNameCapitalized}s]
197
327
  * responses:
198
328
  * 200:
199
- * description: List of ${moduleName}s retrieved successfully
329
+ * description: List of ${moduleNameCamelCase}s retrieved successfully
200
330
  * content:
201
331
  * application/json:
202
332
  * schema:
@@ -213,16 +343,28 @@ export class ${moduleNameCapitalized}Controller {
213
343
  * description: Bad request
214
344
  */
215
345
  async getAll(req, res) {
216
- const data = await this.service.getAll();
217
- res.json(data);
346
+ try {
347
+ const data = await this.service.getAll();
348
+ res.json({
349
+ Data: data,
350
+ StatusCode: 1000,
351
+ Message: '${moduleNameCapitalized}s retrieved successfully'
352
+ });
353
+ } catch (error: any) {
354
+ res.status(400).json({
355
+ Data: null,
356
+ StatusCode: 4000,
357
+ Message: error.message
358
+ });
359
+ }
218
360
  }
219
361
 
220
362
  /**
221
363
  * @openapi
222
- * /api/${moduleName}/{id}:
364
+ * /api/${moduleNameCamelCase}/{id}:
223
365
  * get:
224
- * summary: Get ${moduleName} by ID
225
- * description: Retrieve a specific ${moduleName} by its ID
366
+ * summary: Get ${moduleNameCamelCase} by ID
367
+ * description: Retrieve a specific ${moduleNameCamelCase} by its ID
226
368
  * tags: [${moduleNameCapitalized}s]
227
369
  * parameters:
228
370
  * - in: path
@@ -248,8 +390,27 @@ export class ${moduleNameCapitalized}Controller {
248
390
  * description: ${moduleNameCapitalized} not found
249
391
  */
250
392
  async getById(req, res) {
251
- const data = await this.service.getById(req.params.id);
252
- res.json(data);
393
+ try {
394
+ const data = await this.service.getById(req.params.id);
395
+ if (!data) {
396
+ return res.status(404).json({
397
+ Data: null,
398
+ StatusCode: 4004,
399
+ Message: '${moduleNameCapitalized} not found'
400
+ });
401
+ }
402
+ res.json({
403
+ Data: data,
404
+ StatusCode: 1000,
405
+ Message: '${moduleNameCapitalized} retrieved successfully'
406
+ });
407
+ } catch (error: any) {
408
+ res.status(400).json({
409
+ Data: null,
410
+ StatusCode: 4000,
411
+ Message: error.message
412
+ });
413
+ }
253
414
  }
254
415
 
255
416
  /**
@@ -295,8 +456,20 @@ export class ${moduleNameCapitalized}Controller {
295
456
  * description: Bad request or missing required fields
296
457
  */
297
458
  async create(req, res) {
298
- const data = await this.service.create(req.body);
299
- res.json(data);
459
+ try {
460
+ const data = await this.service.create(req.body);
461
+ res.status(201).json({
462
+ Data: data,
463
+ StatusCode: 1000,
464
+ Message: '${moduleNameCapitalized} created successfully'
465
+ });
466
+ } catch (error: any) {
467
+ res.status(400).json({
468
+ Data: null,
469
+ StatusCode: 4000,
470
+ Message: error.message
471
+ });
472
+ }
300
473
  }
301
474
 
302
475
  /**
@@ -382,19 +555,115 @@ export class ${moduleNameCapitalized}Controller {
382
555
  * description: ${moduleNameCapitalized} not found
383
556
  */
384
557
  async delete(req, res) {
385
- const data = await this.service.delete(req.params.id);
386
- res.json(data);
558
+ try {
559
+ const data = await this.service.delete(req.params.id);
560
+ if (!data) {
561
+ return res.status(404).json({
562
+ Data: null,
563
+ StatusCode: 4004,
564
+ Message: '${moduleNameCapitalized} not found'
565
+ });
566
+ }
567
+ res.json({
568
+ Data: data,
569
+ StatusCode: 1000,
570
+ Message: '${moduleNameCapitalized} deleted successfully'
571
+ });
572
+ } catch (error: any) {
573
+ res.status(400).json({
574
+ Data: null,
575
+ StatusCode: 4000,
576
+ Message: error.message
577
+ });
578
+ }
387
579
  }
388
- }`;
580
+ `;
581
+ if (crud) {
582
+ controllerContent += `
583
+ `;
584
+ fields.forEach(field => {
585
+ controllerContent += `
586
+ /**
587
+ * @openapi
588
+ * /api/${moduleName}/{id}:
589
+ * put:
590
+ * summary: Update ${moduleName}
591
+ * description: Update an existing ${moduleName} record
592
+ * tags: [${moduleNameCapitalized}s]
593
+ * parameters:
594
+ * - in: path
595
+ * name: id
596
+ * required: true
597
+ * schema:
598
+ * type: string
599
+ * description: ${moduleNameCapitalized} ID
600
+ * requestBody:
601
+ * required: true
602
+ * content:
603
+ * application/json:
604
+ * schema:
605
+ * type: object
606
+ * properties:
607
+ * ${field}:
608
+ * type: string
609
+ * description: ${field} of the ${moduleNameCapitalized}
610
+ * responses:
611
+ * 200:
612
+ * description: ${moduleNameCapitalized} updated successfully
613
+ * content:
614
+ * application/json:
615
+ * schema:
616
+ * type: object
617
+ * properties:
618
+ * Data:
619
+ * $ref: '#/components/schemas/${moduleNameCapitalized}'
620
+ * StatusCode:
621
+ * type: integer
622
+ * example: 1000
623
+ * 404:
624
+ * description: ${moduleNameCapitalized} not found
625
+ */
626
+ async update(req, res) {
627
+ try {
628
+ const data = await this.service.update(req.params.id, req.body);
629
+ if (!data) {
630
+ return res.status(404).json({
631
+ Data: null,
632
+ StatusCode: 4004,
633
+ Message: '${moduleNameCapitalized} not found'
634
+ });
635
+ }
636
+ res.json({
637
+ Data: data,
638
+ StatusCode: 1000,
639
+ Message: '${moduleNameCapitalized} updated successfully'
640
+ });
641
+ } catch (error: any) {
642
+ res.status(400).json({
643
+ Data: null,
644
+ StatusCode: 4000,
645
+ Message: error.message
646
+ });
647
+ }
648
+ }
649
+ `;
650
+ });
651
+ }
652
+ return controllerContent;
389
653
  }
390
- function generateRoutesContent(moduleName, moduleNameCapitalized) {
391
- return `import { Router } from 'express';
392
- import { ${moduleNameCapitalized}Controller } from '../controllers/${moduleName}.controller';
654
+ function generateRoutesContent(moduleName, moduleNameCapitalized, crud, fields = []) {
655
+ // Convert moduleName to camelCase for file paths
656
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
657
+ let routesContent = `import { Router } from 'express';
658
+ import { ${moduleNameCapitalized}Controller } from '../controllers/${moduleNameCamelCase}.controller';
393
659
 
394
660
  const router = Router();
395
661
  const controller = new ${moduleNameCapitalized}Controller();
396
662
 
397
- router.get('/', (req, res) => res.send('${moduleNameCapitalized} API is up.'));
663
+ // Health check endpoint
664
+ router.get('/health', (req, res) => res.send('${moduleNameCapitalized} API is up.'));
665
+
666
+ // CRUD endpoints
398
667
  router.get('/all', (req, res) => controller.getAll(req, res));
399
668
  router.get('/:id', (req, res) => controller.getById(req, res));
400
669
  router.post('/', (req, res) => controller.create(req, res));
@@ -402,6 +671,16 @@ router.put('/:id', (req, res) => controller.update(req, res));
402
671
  router.delete('/:id', (req, res) => controller.delete(req, res));
403
672
 
404
673
  export default router;`;
674
+ if (crud && fields.length > 0) {
675
+ routesContent += `
676
+ `;
677
+ fields.forEach((field) => {
678
+ routesContent += `
679
+ router.put('/${field}/:id', (req, res) => controller.update(req, res));
680
+ `;
681
+ });
682
+ }
683
+ return routesContent;
405
684
  }
406
685
  function updateRoutesIndex(currentDir, moduleName) {
407
686
  const routesIndexPath = path.join(currentDir, 'src/routes/index.ts');
@@ -409,15 +688,17 @@ function updateRoutesIndex(currentDir, moduleName) {
409
688
  console.log('⚠️ routes/index.ts not found, skipping routes index update');
410
689
  return;
411
690
  }
691
+ // Convert moduleName to camelCase for file paths
692
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
412
693
  let routesContent = fs.readFileSync(routesIndexPath, 'utf8');
413
- // Add import
414
- const importStatement = `import ${moduleName}Routes from '../modules/${moduleName}/routes/${moduleName}.routes';`;
694
+ // Add import - use camelCase for file path
695
+ const importStatement = `import ${moduleNameCamelCase}Routes from '../modules/${moduleName}/routes/${moduleNameCamelCase}.routes';`;
415
696
  if (!routesContent.includes(importStatement)) {
416
697
  // Add import after the userRoutes import
417
- routesContent = routesContent.replace(/import userRoutes from '\.\.\/modules\/users\/routes\/user\.routes';/, `import userRoutes from '../modules/users/routes/user.routes';\n${importStatement}`);
698
+ routesContent = routesContent.replace(/import userRoutes from '\.\.\/modules\/user\/routes\/user\.routes';/, `import userRoutes from '../modules/user/routes/user.routes';\n${importStatement}`);
418
699
  }
419
- // Add route registration
420
- const routeRegistration = `router.use('/${moduleName}', ${moduleName}Routes);`;
700
+ // Add route registration - use original moduleName for route path
701
+ const routeRegistration = `router.use('/${moduleName}', ${moduleNameCamelCase}Routes);`;
421
702
  if (!routesContent.includes(routeRegistration)) {
422
703
  // Add route after the users route
423
704
  routesContent = routesContent.replace(/router\.use\('\/users', userRoutes\);/, `router.use('/users', userRoutes);\n${routeRegistration}`);
@@ -435,9 +716,10 @@ async function createDatabaseTable(currentDir, moduleName, moduleNameCapitalized
435
716
  if (fs.existsSync(mainSeederPath)) {
436
717
  let seederContent = fs.readFileSync(mainSeederPath, 'utf8');
437
718
  // Add import for the model
438
- const importStatement = `import { ${moduleNameCapitalized}Model } from '../../modules/${moduleName}/schemas/${moduleName}.schema';`;
719
+ const moduleNameCamelCase = moduleName.replace(/[-_]([a-z0-9])/g, (_, char) => char.toUpperCase());
720
+ const importStatement = `import { ${moduleNameCapitalized}Model } from '../../modules/${moduleName}/schemas/${moduleNameCamelCase}.schema';`;
439
721
  if (!seederContent.includes(importStatement)) {
440
- seederContent = seederContent.replace(/import { UserModel } from '\.\.\/\.\.\/modules\/users\/schemas\/user\.schema';/, `import { UserModel } from '../../modules/users/schemas/user.schema';\n${importStatement}`);
722
+ seederContent = seederContent.replace(/import { UserModel } from '\.\.\/\.\.\/modules\/user\/schemas\/user\.schema';/, `import { UserModel } from '../../modules/user/schemas/user.schema';\n${importStatement}`);
441
723
  }
442
724
  // Add seeder function - use exact module name provided by developer
443
725
  const seederFunction = ` private async seed${moduleNameCapitalized}(data: any[]): Promise<void> {
@@ -491,27 +773,46 @@ async function generateEndpoint(moduleName, endpointName, options = {}) {
491
773
  if (!fs.existsSync(modulePath)) {
492
774
  throw new Error(`Module '${moduleName}' does not exist. Please create the module first using 'generate:module ${moduleName}'`);
493
775
  }
494
- // Set default options
776
+ // Parse options
495
777
  const method = (options.method || 'GET').toUpperCase();
496
778
  const customPath = options.path || endpointName;
497
779
  const description = options.description || `${endpointNameCapitalized} endpoint for ${moduleName}`;
780
+ const generateService = options.generateService !== false;
781
+ const generateController = options.generateController !== false;
782
+ const generateRoute = options.generateRoute !== false;
783
+ const force = options.force || false;
498
784
  console.log(`🔧 Adding endpoint '${endpointName}' to module '${moduleName}'...`);
499
785
  // Convert endpoint name to camelCase for method names
500
786
  const camelCaseEndpoint = endpointName.replace(/-/g, '_').replace(/_([a-z])/g, (g) => g[1].toUpperCase());
501
787
  const camelCaseEndpointCapitalized = camelCaseEndpoint.charAt(0).toUpperCase() + camelCaseEndpoint.slice(1);
502
- // Add service method
503
- console.log('📝 Adding service method...');
504
- await addServiceMethod(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, description);
505
- // Add controller method
506
- console.log('🎮 Adding controller method...');
507
- await addControllerMethod(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, customPath, description);
508
- // Add route
509
- console.log('🛣️ Adding route...');
510
- await addRoute(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, customPath);
788
+ // Add service method if requested
789
+ if (generateService) {
790
+ console.log('📝 Adding service method...');
791
+ await addServiceMethod(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, description, force);
792
+ }
793
+ // Add controller method if requested
794
+ if (generateController) {
795
+ console.log('🎮 Adding controller method...');
796
+ await addControllerMethod(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, customPath, description, force);
797
+ }
798
+ // Add route if requested
799
+ if (generateRoute) {
800
+ console.log('🛣️ Adding route...');
801
+ await addRoute(modulePath, moduleName, moduleNameCapitalized, camelCaseEndpoint, camelCaseEndpointCapitalized, method, customPath);
802
+ }
511
803
  console.log(`✅ Endpoint '${endpointName}' added to module '${moduleName}' successfully!`);
512
804
  console.log(`🔗 New endpoint available at: ${method} /api/${moduleName}/${customPath}`);
805
+ if (generateService) {
806
+ console.log(`📝 Service: Method '${camelCaseEndpoint}' added to service`);
807
+ }
808
+ if (generateController) {
809
+ console.log(`🎮 Controller: Method '${camelCaseEndpoint}' added to controller`);
810
+ }
811
+ if (generateRoute) {
812
+ console.log(`🛣️ Route: ${method} /${customPath} added to routes`);
813
+ }
513
814
  }
514
- async function addServiceMethod(modulePath, moduleName, moduleNameCapitalized, endpointName, endpointNameCapitalized, method, description) {
815
+ async function addServiceMethod(modulePath, moduleName, moduleNameCapitalized, endpointName, endpointNameCapitalized, method, description, force) {
515
816
  // Use singular form for file names (convert plural to singular if needed)
516
817
  const singularName = moduleName.endsWith('s') && moduleName.length > 1 ? moduleName.slice(0, -1) : moduleName;
517
818
  const servicePath = path.join(modulePath, 'services', `${singularName}.service.ts`);
@@ -519,6 +820,10 @@ async function addServiceMethod(modulePath, moduleName, moduleNameCapitalized, e
519
820
  throw new Error(`Service file not found: ${servicePath}`);
520
821
  }
521
822
  let serviceContent = fs.readFileSync(servicePath, 'utf8');
823
+ // Check if method already exists
824
+ if (serviceContent.includes(`async ${endpointName}(`) && !force) {
825
+ throw new Error(`Service method '${endpointName}' already exists. Use --force to override.`);
826
+ }
522
827
  // Generate service method based on HTTP method
523
828
  let serviceMethod = '';
524
829
  switch (method) {
@@ -556,12 +861,19 @@ async function addServiceMethod(modulePath, moduleName, moduleNameCapitalized, e
556
861
  return { message: '${endpointNameCapitalized} endpoint - implement your logic here', params };
557
862
  }`;
558
863
  }
559
- // Add method before the closing brace
560
- const lastBraceIndex = serviceContent.lastIndexOf('}');
561
- serviceContent = serviceContent.slice(0, lastBraceIndex) + serviceMethod + '\n' + serviceContent.slice(lastBraceIndex);
864
+ // If method exists and force is enabled, replace it
865
+ if (serviceContent.includes(`async ${endpointName}(`) && force) {
866
+ const methodRegex = new RegExp(`\\s*async ${endpointName}\\([^)]*\\)\\s*\\{[^}]*\\}`, 's');
867
+ serviceContent = serviceContent.replace(methodRegex, serviceMethod);
868
+ }
869
+ else {
870
+ // Add method before the closing brace
871
+ const lastBraceIndex = serviceContent.lastIndexOf('}');
872
+ serviceContent = serviceContent.slice(0, lastBraceIndex) + serviceMethod + '\n' + serviceContent.slice(lastBraceIndex);
873
+ }
562
874
  fs.writeFileSync(servicePath, serviceContent);
563
875
  }
564
- async function addControllerMethod(modulePath, moduleName, moduleNameCapitalized, endpointName, endpointNameCapitalized, method, customPath, description) {
876
+ async function addControllerMethod(modulePath, moduleName, moduleNameCapitalized, endpointName, endpointNameCapitalized, method, customPath, description, force) {
565
877
  // Use singular form for file names (convert plural to singular if needed)
566
878
  const singularName = moduleName.endsWith('s') && moduleName.length > 1 ? moduleName.slice(0, -1) : moduleName;
567
879
  const controllerPath = path.join(modulePath, 'controllers', `${singularName}.controller.ts`);
@@ -569,132 +881,117 @@ async function addControllerMethod(modulePath, moduleName, moduleNameCapitalized
569
881
  throw new Error(`Controller file not found: ${controllerPath}`);
570
882
  }
571
883
  let controllerContent = fs.readFileSync(controllerPath, 'utf8');
572
- // Add necessary imports if they don't exist
573
- if (!controllerContent.includes('import { Request, Response }')) {
574
- controllerContent = `import { Request, Response } from 'express';\n` + controllerContent;
575
- }
576
- if (!controllerContent.includes('import { handleResponse }')) {
577
- controllerContent = `import { handleResponse } from '../../../shared/utils/handle-response';\n` + controllerContent;
884
+ // Check if method already exists
885
+ if (controllerContent.includes(`async ${endpointName}(`) && !force) {
886
+ throw new Error(`Controller method '${endpointName}' already exists. Use --force to override.`);
578
887
  }
579
- if (!controllerContent.includes('import { Result }')) {
580
- controllerContent = `import { Result } from '../../../shared/types/result';\n` + controllerContent;
581
- }
582
- if (!controllerContent.includes('import { v4 as uuidv4 }')) {
583
- controllerContent = `import { v4 as uuidv4 } from 'uuid';\n` + controllerContent;
584
- }
585
- if (!controllerContent.includes('import { ResponseCode }')) {
586
- controllerContent = `import { ResponseCode } from '../../../shared/constants/enums';\n` + controllerContent;
587
- }
588
- // Generate Swagger documentation and controller method
589
- let swaggerDoc = '';
888
+ // Generate controller method based on HTTP method
590
889
  let controllerMethod = '';
591
890
  switch (method) {
592
891
  case 'GET':
593
- swaggerDoc = ` /**
594
- * @swagger
892
+ controllerMethod = ` /**
893
+ * @openapi
595
894
  * /api/${moduleName}/${customPath}:
596
895
  * get:
597
- * summary: ${endpointNameCapitalized} ${moduleName}
896
+ * summary: ${endpointNameCapitalized}
598
897
  * description: ${description}
599
- * tags: [${moduleNameCapitalized}]
898
+ * tags: [${moduleNameCapitalized}s]
600
899
  * parameters:
601
900
  * - in: query
602
- * name: filter
901
+ * name: params
603
902
  * schema:
604
- * type: string
605
- * description: Filter parameters
903
+ * type: object
904
+ * description: Query parameters
606
905
  * responses:
607
906
  * 200:
608
- * description: ${endpointNameCapitalized} retrieved successfully
907
+ * description: ${endpointNameCapitalized} executed successfully
609
908
  * content:
610
909
  * application/json:
611
910
  * schema:
612
911
  * type: object
613
912
  * properties:
614
- * data:
913
+ * Data:
615
914
  * type: object
616
- * statusCode:
915
+ * StatusCode:
617
916
  * type: integer
618
917
  * example: 1000
619
- * errors:
620
- * type: array
621
- * items:
622
- * type: object
623
- * requestIdentifier:
624
- * type: string
625
918
  * 400:
626
919
  * description: Bad request
627
- */`;
628
- controllerMethod = ` async ${endpointName}(req: Request, res: Response): Promise<void> {
629
- const requestIdentifier = req.headers['x-request-id'] || uuidv4();
920
+ */
921
+ async ${endpointName}(req, res) {
630
922
  try {
631
- const data = await this.service.${endpointName}(req.query);
632
- const result = new Result(data, ResponseCode.Ok, [], null, requestIdentifier as string);
633
- return handleResponse(res, result);
634
- } catch (error: any) {
635
- const result = new Result(null, ResponseCode.Error, [{ message: error.message }], null, requestIdentifier as string);
636
- return handleResponse(res, result);
923
+ const params = req.query;
924
+ const result = await this.service.${endpointName}(params);
925
+ res.json({
926
+ Data: result,
927
+ StatusCode: 1000,
928
+ Message: '${endpointNameCapitalized} executed successfully'
929
+ });
930
+ } catch (error) {
931
+ res.status(400).json({
932
+ Data: null,
933
+ StatusCode: 4000,
934
+ Message: error.message
935
+ });
637
936
  }
638
- }`;
937
+ }`;
639
938
  break;
640
939
  case 'POST':
641
- swaggerDoc = ` /**
642
- * @swagger
940
+ controllerMethod = ` /**
941
+ * @openapi
643
942
  * /api/${moduleName}/${customPath}:
644
943
  * post:
645
- * summary: ${endpointNameCapitalized} ${moduleName}
944
+ * summary: ${endpointNameCapitalized}
646
945
  * description: ${description}
647
- * tags: [${moduleNameCapitalized}]
946
+ * tags: [${moduleNameCapitalized}s]
648
947
  * requestBody:
649
948
  * required: true
650
949
  * content:
651
950
  * application/json:
652
951
  * schema:
653
952
  * type: object
654
- * properties:
655
- * # Add your request body properties here
656
953
  * responses:
657
954
  * 200:
658
- * description: ${endpointNameCapitalized} completed successfully
955
+ * description: ${endpointNameCapitalized} executed successfully
659
956
  * content:
660
957
  * application/json:
661
958
  * schema:
662
959
  * type: object
663
960
  * properties:
664
- * data:
961
+ * Data:
665
962
  * type: object
666
- * statusCode:
963
+ * StatusCode:
667
964
  * type: integer
668
965
  * example: 1000
669
- * errors:
670
- * type: array
671
- * items:
672
- * type: object
673
- * requestIdentifier:
674
- * type: string
675
966
  * 400:
676
967
  * description: Bad request
677
- */`;
678
- controllerMethod = ` async ${endpointName}(req: Request, res: Response): Promise<void> {
679
- const requestIdentifier = req.headers['x-request-id'] || uuidv4();
968
+ */
969
+ async ${endpointName}(req, res) {
680
970
  try {
681
- const data = await this.service.${endpointName}(req.body);
682
- const result = new Result(data, ResponseCode.Ok, [], null, requestIdentifier as string);
683
- return handleResponse(res, result);
684
- } catch (error: any) {
685
- const result = new Result(null, ResponseCode.Error, [{ message: error.message }], null, requestIdentifier as string);
686
- return handleResponse(res, result);
971
+ const data = req.body;
972
+ const result = await this.service.${endpointName}(data);
973
+ res.json({
974
+ Data: result,
975
+ StatusCode: 1000,
976
+ Message: '${endpointNameCapitalized} executed successfully'
977
+ });
978
+ } catch (error) {
979
+ res.status(400).json({
980
+ Data: null,
981
+ StatusCode: 4000,
982
+ Message: error.message
983
+ });
687
984
  }
688
- }`;
985
+ }`;
689
986
  break;
690
987
  case 'PUT':
691
- swaggerDoc = ` /**
692
- * @swagger
988
+ controllerMethod = ` /**
989
+ * @openapi
693
990
  * /api/${moduleName}/${customPath}/{id}:
694
991
  * put:
695
- * summary: ${endpointNameCapitalized} ${moduleName}
992
+ * summary: ${endpointNameCapitalized}
696
993
  * description: ${description}
697
- * tags: [${moduleNameCapitalized}]
994
+ * tags: [${moduleNameCapitalized}s]
698
995
  * parameters:
699
996
  * - in: path
700
997
  * name: id
@@ -708,50 +1005,49 @@ async function addControllerMethod(modulePath, moduleName, moduleNameCapitalized
708
1005
  * application/json:
709
1006
  * schema:
710
1007
  * type: object
711
- * properties:
712
- * # Add your request body properties here
713
1008
  * responses:
714
1009
  * 200:
715
- * description: ${endpointNameCapitalized} updated successfully
1010
+ * description: ${endpointNameCapitalized} executed successfully
716
1011
  * content:
717
1012
  * application/json:
718
1013
  * schema:
719
1014
  * type: object
720
1015
  * properties:
721
- * data:
1016
+ * Data:
722
1017
  * type: object
723
- * statusCode:
1018
+ * StatusCode:
724
1019
  * type: integer
725
1020
  * example: 1000
726
- * errors:
727
- * type: array
728
- * items:
729
- * type: object
730
- * requestIdentifier:
731
- * type: string
732
- * 404:
733
- * description: ${moduleNameCapitalized} not found
734
- */`;
735
- controllerMethod = ` async ${endpointName}(req: Request, res: Response): Promise<void> {
736
- const requestIdentifier = req.headers['x-request-id'] || uuidv4();
1021
+ * 400:
1022
+ * description: Bad request
1023
+ */
1024
+ async ${endpointName}(req, res) {
737
1025
  try {
738
- const data = await this.service.${endpointName}(req.params.id, req.body);
739
- const result = new Result(data, ResponseCode.Ok, [], null, requestIdentifier as string);
740
- return handleResponse(res, result);
741
- } catch (error: any) {
742
- const result = new Result(null, ResponseCode.Error, [{ message: error.message }], null, requestIdentifier as string);
743
- return handleResponse(res, result);
1026
+ const { id } = req.params;
1027
+ const data = req.body;
1028
+ const result = await this.service.${endpointName}(id, data);
1029
+ res.json({
1030
+ Data: result,
1031
+ StatusCode: 1000,
1032
+ Message: '${endpointNameCapitalized} executed successfully'
1033
+ });
1034
+ } catch (error) {
1035
+ res.status(400).json({
1036
+ Data: null,
1037
+ StatusCode: 4000,
1038
+ Message: error.message
1039
+ });
744
1040
  }
745
- }`;
1041
+ }`;
746
1042
  break;
747
1043
  case 'DELETE':
748
- swaggerDoc = ` /**
749
- * @swagger
1044
+ controllerMethod = ` /**
1045
+ * @openapi
750
1046
  * /api/${moduleName}/${customPath}/{id}:
751
1047
  * delete:
752
- * summary: ${endpointNameCapitalized} ${moduleName}
1048
+ * summary: ${endpointNameCapitalized}
753
1049
  * description: ${description}
754
- * tags: [${moduleNameCapitalized}]
1050
+ * tags: [${moduleNameCapitalized}s]
755
1051
  * parameters:
756
1052
  * - in: path
757
1053
  * name: id
@@ -761,84 +1057,90 @@ async function addControllerMethod(modulePath, moduleName, moduleNameCapitalized
761
1057
  * description: ${moduleNameCapitalized} ID
762
1058
  * responses:
763
1059
  * 200:
764
- * description: ${endpointNameCapitalized} completed successfully
1060
+ * description: ${endpointNameCapitalized} executed successfully
765
1061
  * content:
766
1062
  * application/json:
767
1063
  * schema:
768
1064
  * type: object
769
1065
  * properties:
770
- * data:
1066
+ * Data:
771
1067
  * type: object
772
- * statusCode:
1068
+ * StatusCode:
773
1069
  * type: integer
774
1070
  * example: 1000
775
- * errors:
776
- * type: array
777
- * items:
778
- * type: object
779
- * requestIdentifier:
780
- * type: string
781
- * 404:
782
- * description: ${moduleNameCapitalized} not found
783
- */`;
784
- controllerMethod = ` async ${endpointName}(req: Request, res: Response): Promise<void> {
785
- const requestIdentifier = req.headers['x-request-id'] || uuidv4();
1071
+ * 400:
1072
+ * description: Bad request
1073
+ */
1074
+ async ${endpointName}(req, res) {
786
1075
  try {
787
- const data = await this.service.${endpointName}(req.params.id);
788
- const result = new Result(data, ResponseCode.Ok, [], null, requestIdentifier as string);
789
- return handleResponse(res, result);
790
- } catch (error: any) {
791
- const result = new Result(null, ResponseCode.Error, [{ message: error.message }], null, requestIdentifier as string);
792
- return handleResponse(res, result);
1076
+ const { id } = req.params;
1077
+ const result = await this.service.${endpointName}(id);
1078
+ res.json({
1079
+ Data: result,
1080
+ StatusCode: 1000,
1081
+ Message: '${endpointNameCapitalized} executed successfully'
1082
+ });
1083
+ } catch (error) {
1084
+ res.status(400).json({
1085
+ Data: null,
1086
+ StatusCode: 4000,
1087
+ Message: error.message
1088
+ });
793
1089
  }
794
- }`;
1090
+ }`;
795
1091
  break;
796
1092
  default:
797
- swaggerDoc = ` /**
798
- * @swagger
1093
+ controllerMethod = ` /**
1094
+ * @openapi
799
1095
  * /api/${moduleName}/${customPath}:
800
1096
  * ${method.toLowerCase()}:
801
- * summary: ${endpointNameCapitalized} ${moduleName}
1097
+ * summary: ${endpointNameCapitalized}
802
1098
  * description: ${description}
803
- * tags: [${moduleNameCapitalized}]
1099
+ * tags: [${moduleNameCapitalized}s]
804
1100
  * responses:
805
1101
  * 200:
806
- * description: ${endpointNameCapitalized} completed successfully
1102
+ * description: ${endpointNameCapitalized} executed successfully
807
1103
  * content:
808
1104
  * application/json:
809
1105
  * schema:
810
1106
  * type: object
811
1107
  * properties:
812
- * data:
1108
+ * Data:
813
1109
  * type: object
814
- * statusCode:
1110
+ * StatusCode:
815
1111
  * type: integer
816
1112
  * example: 1000
817
- * errors:
818
- * type: array
819
- * items:
820
- * type: object
821
- * requestIdentifier:
822
- * type: string
823
1113
  * 400:
824
1114
  * description: Bad request
825
- */`;
826
- controllerMethod = ` async ${endpointName}(req: Request, res: Response): Promise<void> {
827
- const requestIdentifier = req.headers['x-request-id'] || uuidv4();
1115
+ */
1116
+ async ${endpointName}(req, res) {
828
1117
  try {
829
- const data = await this.service.${endpointName}(req.query);
830
- const result = new Result(data, ResponseCode.Ok, [], null, requestIdentifier as string);
831
- return handleResponse(res, result);
832
- } catch (error: any) {
833
- const result = new Result(null, ResponseCode.Error, [{ message: error.message }], null, requestIdentifier as string);
834
- return handleResponse(res, result);
1118
+ const params = req.query || req.body || req.params;
1119
+ const result = await this.service.${endpointName}(params);
1120
+ res.json({
1121
+ Data: result,
1122
+ StatusCode: 1000,
1123
+ Message: '${endpointNameCapitalized} executed successfully'
1124
+ });
1125
+ } catch (error) {
1126
+ res.status(400).json({
1127
+ Data: null,
1128
+ StatusCode: 4000,
1129
+ Message: error.message
1130
+ });
835
1131
  }
836
- }`;
1132
+ }`;
1133
+ }
1134
+ // If method exists and force is enabled, replace it
1135
+ if (controllerContent.includes(`async ${endpointName}(`) && force) {
1136
+ const methodRegex = new RegExp(`\\s*async ${endpointName}\\([^)]*\\)\\s*\\{[^}]*\\}`, 's');
1137
+ controllerContent = controllerContent.replace(methodRegex, controllerMethod);
1138
+ }
1139
+ else {
1140
+ // Add method before the closing brace
1141
+ const lastBraceIndex = controllerContent.lastIndexOf('}');
1142
+ controllerContent = controllerContent.slice(0, lastBraceIndex) + controllerMethod + '\n' + controllerContent.slice(lastBraceIndex);
837
1143
  }
838
- // Add method before the closing brace
839
- const lastBraceIndex = controllerContent.lastIndexOf('}');
840
- const methodWithDoc = swaggerDoc + '\n' + controllerMethod;
841
- controllerContent = controllerContent.slice(0, lastBraceIndex) + methodWithDoc + '\n' + controllerContent.slice(lastBraceIndex);
842
1144
  fs.writeFileSync(controllerPath, controllerContent);
843
1145
  }
844
1146
  async function addRoute(modulePath, moduleName, moduleNameCapitalized, endpointName, endpointNameCapitalized, method, customPath) {