@asad_dev/leo-generator 1.6.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +194 -0
  2. package/COMMAND_REFERENCE.md +412 -0
  3. package/README.md +486 -0
  4. package/dist/app/modules/imagemodule/imagemodule.constants.js +18 -0
  5. package/dist/app/modules/imagemodule/imagemodule.controller.js +98 -0
  6. package/dist/app/modules/imagemodule/imagemodule.interface.js +2 -0
  7. package/dist/app/modules/imagemodule/imagemodule.model.js +10 -0
  8. package/dist/app/modules/imagemodule/imagemodule.route.js +20 -0
  9. package/dist/app/modules/imagemodule/imagemodule.service.js +137 -0
  10. package/dist/app/modules/imagemodule/imagemodule.validation.js +12 -0
  11. package/dist/app/modules/skiptest/skiptest.controller.js +81 -0
  12. package/dist/app/modules/skiptest/skiptest.route.js +19 -0
  13. package/dist/app/modules/skiptest/skiptest.service.js +129 -0
  14. package/dist/app/modules/skiptest/skiptest.validation.js +12 -0
  15. package/dist/app/modules/testmodule/testmodule.constants.js +18 -0
  16. package/dist/app/modules/testmodule/testmodule.controller.js +81 -0
  17. package/dist/app/modules/testmodule/testmodule.interface.js +2 -0
  18. package/dist/app/modules/testmodule/testmodule.model.js +11 -0
  19. package/dist/app/modules/testmodule/testmodule.route.js +19 -0
  20. package/dist/app/modules/testmodule/testmodule.service.js +129 -0
  21. package/dist/app/modules/testmodule/testmodule.validation.js +14 -0
  22. package/dist/helpers/fileHelper.js +44 -0
  23. package/dist/index.js +586 -0
  24. package/dist/templates/constants.template.js +24 -0
  25. package/dist/templates/controller.template.js +108 -0
  26. package/dist/templates/route.template.js +68 -0
  27. package/dist/templates/service.template.js +184 -0
  28. package/dist/types.js +2 -0
  29. package/dist/utils/documentationUpdater.js +430 -0
  30. package/dist/utils/fieldParser.js +163 -0
  31. package/dist/utils/helperGenerator.js +87 -0
  32. package/dist/utils/interfaceGenerator.js +158 -0
  33. package/dist/utils/modelGenerator.js +140 -0
  34. package/dist/utils/postmanApi.js +113 -0
  35. package/dist/utils/postmanGenerator.js +283 -0
  36. package/dist/utils/swaggerGenerator.js +444 -0
  37. package/dist/utils/validationGenerator.js +170 -0
  38. package/package.json +58 -0
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TestmoduleValidations = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.TestmoduleValidations = {
6
+ create: zod_1.z.object({
7
+ name: zod_1.z.string(),
8
+ email: zod_1.z.string(),
9
+ }),
10
+ update: zod_1.z.object({
11
+ name: zod_1.z.string().optional(),
12
+ email: zod_1.z.string().optional(),
13
+ }),
14
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const promises_1 = __importDefault(require("fs/promises"));
16
+ const path_1 = __importDefault(require("path"));
17
+ function removeFile(filenames) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ if (!filenames || (Array.isArray(filenames) && filenames.length === 0))
20
+ return;
21
+ // Normalize to array for consistent handling
22
+ const files = Array.isArray(filenames) ? filenames : [filenames];
23
+ for (const filename of files) {
24
+ if (!filename)
25
+ continue;
26
+ // Remove leading '/images/' if included
27
+ const cleanedName = filename.replace(/^\/?images\//, '');
28
+ const filePath = path_1.default.join(process.cwd(), 'uploads', 'images', cleanedName);
29
+ try {
30
+ yield promises_1.default.unlink(filePath);
31
+ console.log(`Deleted image: ${cleanedName}`);
32
+ }
33
+ catch (err) {
34
+ if (err.code === 'ENOENT') {
35
+ console.warn(`File not found: ${cleanedName}`);
36
+ }
37
+ else {
38
+ console.error(`Error deleting file ${cleanedName}:`, err);
39
+ }
40
+ }
41
+ }
42
+ });
43
+ }
44
+ exports.default = removeFile;
package/dist/index.js ADDED
@@ -0,0 +1,586 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ const commander_1 = require("commander");
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ // Import new utilities
50
+ const fieldParser_1 = require("./utils/fieldParser");
51
+ const interfaceGenerator_1 = require("./utils/interfaceGenerator");
52
+ const documentationUpdater_1 = require("./utils/documentationUpdater");
53
+ const helperGenerator_1 = require("./utils/helperGenerator");
54
+ // Import template generators
55
+ const route_template_1 = require("./templates/route.template");
56
+ const constants_template_1 = require("./templates/constants.template");
57
+ const controller_template_1 = require("./templates/controller.template");
58
+ const service_template_1 = require("./templates/service.template");
59
+ // Default configuration
60
+ const defaultConfig = {
61
+ modulesDir: "src/app/modules",
62
+ routesFile: "src/routes/index.ts",
63
+ };
64
+ // Load configuration from package.json or use defaults
65
+ function loadConfig() {
66
+ const config = Object.assign({}, defaultConfig);
67
+ // 1. Try to load from .env file in the current directory
68
+ try {
69
+ const envPath = path.join(process.cwd(), ".env");
70
+ if (fs.existsSync(envPath)) {
71
+ const envContent = fs.readFileSync(envPath, "utf-8");
72
+ const lines = envContent.split("\n");
73
+ lines.forEach(line => {
74
+ const [key, ...valueParts] = line.split("=");
75
+ if (key && valueParts.length > 0) {
76
+ const value = valueParts.join("=").trim().replace(/^['"]|['"]$/g, "");
77
+ if (key.trim() === "POSTMAN_API_KEY")
78
+ config.postmanApiKey = value;
79
+ if (key.trim() === "POSTMAN_COLLECTION_ID")
80
+ config.postmanCollectionId = value;
81
+ }
82
+ });
83
+ }
84
+ }
85
+ catch (error) {
86
+ // Ignore env loading errors
87
+ }
88
+ // 2. Try to load from package.json
89
+ try {
90
+ const packageJsonPath = path.join(process.cwd(), "package.json");
91
+ if (fs.existsSync(packageJsonPath)) {
92
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
93
+ const userConfig = packageJson.moduleGenerator || {};
94
+ if (userConfig.modulesDir)
95
+ config.modulesDir = userConfig.modulesDir;
96
+ if (userConfig.routesFile)
97
+ config.routesFile = userConfig.routesFile;
98
+ if (userConfig.postmanApiKey)
99
+ config.postmanApiKey = userConfig.postmanApiKey;
100
+ if (userConfig.postmanCollectionId)
101
+ config.postmanCollectionId = userConfig.postmanCollectionId;
102
+ }
103
+ }
104
+ catch (error) {
105
+ console.warn("Could not load configuration from package.json, using defaults");
106
+ }
107
+ return config;
108
+ }
109
+ function toCamelCase(str) {
110
+ return str
111
+ .replace(/(?:^\w|[A-Z]|\b\w|\s+)/g, (match, index) => index === 0 ? match.toUpperCase() : match.toLowerCase())
112
+ .replace(/\s+/g, "");
113
+ }
114
+ // Enhanced model generation with better nested schema support
115
+ function generateModelContent(camelCaseName, folderName, fields) {
116
+ // Collect enum imports
117
+ const enumImports = fields
118
+ .filter(f => { var _a; return f.type === 'enum' && ((_a = f.enumValues) === null || _a === void 0 ? void 0 : _a.length); })
119
+ .map(f => `${toCamelCase(f.name)}Enum`);
120
+ let modelContent = `import { Schema, model } from 'mongoose';\nimport { I${camelCaseName}, ${camelCaseName}Model`;
121
+ if (enumImports.length > 0) {
122
+ modelContent += `, ${enumImports.join(', ')}`;
123
+ }
124
+ modelContent += ` } from './${folderName}.interface'; \n\n`;
125
+ // Generate nested schemas
126
+ const nestedSchemas = generateNestedSchemas(fields);
127
+ modelContent += nestedSchemas;
128
+ modelContent += `const ${folderName}Schema = new Schema<I${camelCaseName}, ${camelCaseName}Model>({\n`;
129
+ // Add fields to schema
130
+ if (fields.length > 0) {
131
+ fields.forEach((field) => {
132
+ let schemaType = mapToMongooseType(field);
133
+ let additionalProps = "";
134
+ // Add required property if marked as required
135
+ if (field.isRequired) {
136
+ additionalProps += ", required: true";
137
+ }
138
+ modelContent += ` ${field.name}: ${schemaType}${additionalProps},\n`;
139
+ });
140
+ }
141
+ else {
142
+ modelContent += " // Define schema fields here\n";
143
+ }
144
+ modelContent += `}, {\n timestamps: true\n});\n\nexport const ${camelCaseName} = model<I${camelCaseName}, ${camelCaseName}Model>('${camelCaseName}', ${folderName}Schema);\n`;
145
+ return modelContent;
146
+ }
147
+ // Helper function to generate nested schemas
148
+ function generateNestedSchemas(fields) {
149
+ let schemas = "";
150
+ fields.forEach((field) => {
151
+ var _a, _b;
152
+ if (field.type.toLowerCase() === "array" &&
153
+ ((_a = field.ref) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "object" &&
154
+ ((_b = field.objectProperties) === null || _b === void 0 ? void 0 : _b.length)) {
155
+ // Check for nested objects within this schema - GENERATE CHILDREN FIRST
156
+ const nestedSchemas = generateNestedSchemas(field.objectProperties);
157
+ schemas += nestedSchemas;
158
+ const nestedSchemaName = `${field.name}ItemSchema`;
159
+ schemas += `const ${nestedSchemaName} = new Schema({\n`;
160
+ // Add properties
161
+ field.objectProperties.forEach((prop) => {
162
+ let schemaType = mapToMongooseType(prop);
163
+ let additionalProps = "";
164
+ // Add required property if marked as required
165
+ if (prop.isRequired) {
166
+ additionalProps += ", required: true";
167
+ }
168
+ schemas += ` ${prop.name}: ${schemaType}${additionalProps},\n`;
169
+ });
170
+ schemas += `}, { _id: false });\n\n`;
171
+ }
172
+ });
173
+ return schemas;
174
+ }
175
+ // Helper function to map field definitions to Mongoose schema types
176
+ function mapToMongooseType(field) {
177
+ var _a, _b, _c;
178
+ switch (field.type.toLowerCase()) {
179
+ case "string":
180
+ return "{ type: String }";
181
+ case "number":
182
+ return "{ type: Number }";
183
+ case "boolean":
184
+ return "{ type: Boolean }";
185
+ case "date":
186
+ return "{ type: Date }";
187
+ case "enum":
188
+ if (field.enumValues && field.enumValues.length > 0) {
189
+ const enumName = `${toCamelCase(field.name)}Enum`;
190
+ return `{ type: String, enum: Object.values(${enumName}) }`;
191
+ }
192
+ return "{ type: String }";
193
+ case "array":
194
+ if (((_a = field.ref) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "object" &&
195
+ ((_b = field.objectProperties) === null || _b === void 0 ? void 0 : _b.length)) {
196
+ // Array of objects with defined structure
197
+ const nestedSchemaName = `${field.name}ItemSchema`;
198
+ return `[${nestedSchemaName}]`;
199
+ }
200
+ else if (field.arrayItemType) {
201
+ // Array with specified item type
202
+ switch (field.arrayItemType.toLowerCase()) {
203
+ case "string":
204
+ return "{ type: [String] }";
205
+ case "number":
206
+ return "{ type: [Number] }";
207
+ case "boolean":
208
+ return "{ type: [Boolean] }";
209
+ case "date":
210
+ return "{ type: [Date] }";
211
+ case "objectid":
212
+ case "id":
213
+ return `{ type: [Schema.Types.ObjectId], ref: '${field.ref || "Document"}' }`;
214
+ default:
215
+ return "{ type: [String] }";
216
+ }
217
+ }
218
+ else {
219
+ return "{ type: [String] }";
220
+ }
221
+ case "object":
222
+ if ((_c = field.objectProperties) === null || _c === void 0 ? void 0 : _c.length) {
223
+ // Object with defined properties
224
+ return `{ ${field.objectProperties
225
+ .map((prop) => {
226
+ return `${prop.name}: ${mapToMongooseType(prop)}`;
227
+ })
228
+ .join(", ")} }`;
229
+ }
230
+ else {
231
+ return "{ type: Schema.Types.Mixed }";
232
+ }
233
+ case "objectid":
234
+ case "id":
235
+ return field.ref
236
+ ? `{ type: Schema.Types.ObjectId, ref: '${field.ref}' }`
237
+ : "{ type: Schema.Types.ObjectId }";
238
+ default:
239
+ return "{ type: String }";
240
+ }
241
+ }
242
+ // Enhanced validation generation
243
+ function generateValidationContent(camelCaseName, fields) {
244
+ const folderName = camelCaseName.toLowerCase();
245
+ let validationContent = `import { z } from 'zod';\n`;
246
+ // Collect enum imports
247
+ const enumImports = fields
248
+ .filter(f => { var _a; return f.type === 'enum' && ((_a = f.enumValues) === null || _a === void 0 ? void 0 : _a.length); })
249
+ .map(f => `${toCamelCase(f.name)}Enum`);
250
+ if (enumImports.length > 0) {
251
+ validationContent += `import { ${enumImports.join(', ')} } from './${folderName}.interface';\n`;
252
+ }
253
+ validationContent += `\n`;
254
+ // Add nested schemas for array of objects
255
+ fields.forEach((field) => {
256
+ var _a, _b;
257
+ if (field.type.toLowerCase() === "array" &&
258
+ ((_a = field.ref) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === "object" &&
259
+ ((_b = field.objectProperties) === null || _b === void 0 ? void 0 : _b.length)) {
260
+ const nestedSchemaName = `${field.name}ItemSchema`;
261
+ validationContent += `const ${nestedSchemaName} = z.object({\n`;
262
+ // Add properties from the objectProperties array
263
+ field.objectProperties.forEach((prop) => {
264
+ let zodType = mapToZodType(prop.type, prop);
265
+ // Add required/optional modifiers
266
+ if (prop.isRequired) {
267
+ // Already required by default in Zod
268
+ }
269
+ else if (prop.isOptional) {
270
+ zodType += ".optional()";
271
+ }
272
+ validationContent += ` ${prop.name}: ${zodType},\n`;
273
+ });
274
+ validationContent += `});\n\n`;
275
+ }
276
+ });
277
+ validationContent += `export const ${camelCaseName}Validations = {\n`;
278
+ // Create validation schema
279
+ validationContent += ` create: z.object({\n`;
280
+ // Add validation for each field
281
+ if (fields.length > 0) {
282
+ fields.forEach((field) => {
283
+ let zodType = mapToZodType(field.type, field);
284
+ // Add required/optional modifiers
285
+ if (field.isRequired) {
286
+ // Already required by default in Zod
287
+ }
288
+ else if (field.isOptional) {
289
+ zodType += ".optional()";
290
+ }
291
+ validationContent += ` ${field.name}: ${zodType},\n`;
292
+ });
293
+ }
294
+ else {
295
+ validationContent += ` // Add validation fields\n`;
296
+ }
297
+ validationContent += ` }),\n\n`;
298
+ // Add update validation schema (similar to create but all fields optional)
299
+ validationContent += ` update: z.object({\n`;
300
+ if (fields.length > 0) {
301
+ fields.forEach((field) => {
302
+ let zodType = mapToZodType(field.type, field);
303
+ // All fields are optional in update
304
+ zodType += ".optional()";
305
+ validationContent += ` ${field.name}: ${zodType},\n`;
306
+ });
307
+ }
308
+ else {
309
+ validationContent += ` // Add validation fields\n`;
310
+ }
311
+ validationContent += ` }),\n};\n`;
312
+ return validationContent;
313
+ }
314
+ // Helper function to map types to Zod validators
315
+ function mapToZodType(type, field) {
316
+ var _a;
317
+ switch (type.toLowerCase()) {
318
+ case "string":
319
+ return "z.string()";
320
+ case "number":
321
+ return "z.number()";
322
+ case "boolean":
323
+ return "z.boolean()";
324
+ case "date":
325
+ return "z.string().datetime()";
326
+ case "enum":
327
+ if ((field === null || field === void 0 ? void 0 : field.enumValues) && field.enumValues.length > 0) {
328
+ const enumName = `${toCamelCase(field.name)}Enum`;
329
+ return `z.nativeEnum(${enumName})`;
330
+ }
331
+ return "z.string()";
332
+ case "array":
333
+ if (field === null || field === void 0 ? void 0 : field.ref) {
334
+ if (field.ref.toLowerCase() === "object" &&
335
+ ((_a = field.objectProperties) === null || _a === void 0 ? void 0 : _a.length)) {
336
+ // Array of objects with defined structure
337
+ const nestedSchemaName = `${field.name}ItemSchema`;
338
+ return `z.array(${nestedSchemaName})`;
339
+ }
340
+ else {
341
+ // Array of references to other models
342
+ return "z.array(z.string())";
343
+ }
344
+ }
345
+ else {
346
+ return "z.array(z.string())";
347
+ }
348
+ case "object":
349
+ return "z.record(z.string(), z.any())";
350
+ case "objectid":
351
+ case "id":
352
+ return "z.string()"; // ObjectId as string
353
+ default:
354
+ return "z.string()";
355
+ }
356
+ }
357
+ // Route generation is now handled by the imported template
358
+ // Enhanced createModule function with documentation generation
359
+ function createModule(name, fields, skipFiles, config, docOptions = {}, hasFile = false) {
360
+ const camelCaseName = toCamelCase(name);
361
+ const folderName = camelCaseName.toLowerCase();
362
+ const folderPath = path.join(process.cwd(), config.modulesDir, folderName);
363
+ // Check if file removal helper is needed
364
+ if (hasFile) {
365
+ // Generate the helper file in src/helpers
366
+ (0, helperGenerator_1.generateFileHelper)();
367
+ }
368
+ // Check if the folder already exists
369
+ if (!fs.existsSync(folderPath)) {
370
+ fs.mkdirSync(folderPath, { recursive: true });
371
+ console.log(`✅ Created folder: ${folderName}`);
372
+ }
373
+ else {
374
+ console.log(`⚠️ Folder ${folderName} already exists.`);
375
+ return;
376
+ }
377
+ // Generate content using enhanced generators
378
+ const templates = {
379
+ interface: (0, interfaceGenerator_1.generateInterfaceContent)(camelCaseName, fields),
380
+ model: generateModelContent(camelCaseName, folderName, fields),
381
+ controller: (0, controller_template_1.generateControllerContent)(camelCaseName, folderName, fields),
382
+ service: (0, service_template_1.generateServiceContent)(camelCaseName, folderName, fields, hasFile),
383
+ route: (0, route_template_1.generateRouteContent)(camelCaseName, folderName, fields),
384
+ validation: generateValidationContent(camelCaseName, fields),
385
+ constants: (0, constants_template_1.generateConstantsContent)(camelCaseName, folderName, fields),
386
+ };
387
+ // Create each file, skipping those specified in skipFiles
388
+ Object.entries(templates).forEach(([key, content]) => {
389
+ // Skip if this file type is in the skipFiles array
390
+ if (skipFiles.includes(key)) {
391
+ console.log(`⏭️ Skipping file: ${folderName}.${key}.ts`);
392
+ return;
393
+ }
394
+ const filePath = path.join(folderPath, `${folderName}.${key}.ts`);
395
+ fs.writeFileSync(filePath, content);
396
+ console.log(`✅ Created file: ${filePath}`);
397
+ });
398
+ // Add the new module to the central router file
399
+ updateRouterFile(folderName, camelCaseName, config);
400
+ // Generate documentation
401
+ if (docOptions.updatePostman || docOptions.updateSwagger) {
402
+ console.log(`\n📚 Generating documentation for ${camelCaseName}...`);
403
+ (0, documentationUpdater_1.updateAllDocumentation)(camelCaseName, fields, docOptions);
404
+ }
405
+ console.log(`\n🎉 Module '${camelCaseName}' created successfully!`);
406
+ }
407
+ // Update router file function
408
+ function updateRouterFile(folderName, camelCaseName, config) {
409
+ const routerFilePath = path.join(process.cwd(), config.routesFile);
410
+ // Check if the router file exists
411
+ if (!fs.existsSync(routerFilePath)) {
412
+ console.warn(`⚠️ Router file not found: ${routerFilePath}`);
413
+ return;
414
+ }
415
+ try {
416
+ let routerContent = fs.readFileSync(routerFilePath, "utf-8");
417
+ // Check if the import already exists
418
+ const importStatement = `import { ${camelCaseName}Routes } from '../app/modules/${folderName}/${folderName}.route'`;
419
+ if (!routerContent.includes(importStatement)) {
420
+ // Find the last import statement
421
+ const lastImportIndex = routerContent.lastIndexOf("import ");
422
+ const lastImportEndIndex = routerContent.indexOf("\n", lastImportIndex);
423
+ if (lastImportIndex !== -1) {
424
+ // Insert the new import after the last import
425
+ routerContent =
426
+ routerContent.slice(0, lastImportEndIndex + 1) +
427
+ importStatement +
428
+ "\n" +
429
+ routerContent.slice(lastImportEndIndex + 1);
430
+ }
431
+ else {
432
+ // No imports found, add at the beginning
433
+ routerContent = importStatement + "\n" + routerContent;
434
+ }
435
+ }
436
+ // Check if the route registration already exists in the apiRoutes array
437
+ const routeRegistration = `{ path: '/${folderName}', route: ${camelCaseName}Routes }`;
438
+ if (!routerContent.includes(routeRegistration)) {
439
+ // Find the apiRoutes array initialization (after the equals sign)
440
+ const apiRoutesDeclaration = routerContent.indexOf("const apiRoutes");
441
+ if (apiRoutesDeclaration !== -1) {
442
+ // Find the equals sign
443
+ const equalsSignIndex = routerContent.indexOf("=", apiRoutesDeclaration);
444
+ if (equalsSignIndex !== -1) {
445
+ // Find the opening bracket of the array initialization
446
+ const arrayStartIndex = routerContent.indexOf("[", equalsSignIndex);
447
+ // Find the closing bracket of the array
448
+ const arrayEndIndex = routerContent.indexOf("]", arrayStartIndex);
449
+ if (arrayStartIndex !== -1 && arrayEndIndex !== -1) {
450
+ // Check if there are existing routes
451
+ const arrayContent = routerContent.substring(arrayStartIndex, arrayEndIndex);
452
+ const hasRoutes = arrayContent.includes("{");
453
+ // Insert the new route at the end of the array
454
+ const insertPosition = arrayEndIndex;
455
+ const insertText = hasRoutes
456
+ ? `,\n ${routeRegistration}`
457
+ : ` ${routeRegistration}`;
458
+ routerContent =
459
+ routerContent.slice(0, insertPosition) +
460
+ insertText +
461
+ routerContent.slice(insertPosition);
462
+ }
463
+ }
464
+ }
465
+ else {
466
+ console.warn("Could not find apiRoutes array in router file");
467
+ }
468
+ }
469
+ // Write the updated content back to the file
470
+ fs.writeFileSync(routerFilePath, routerContent);
471
+ console.log(`✅ Updated router file: ${routerFilePath}`);
472
+ }
473
+ catch (error) {
474
+ console.error(`❌ Error updating router file: ${error}`);
475
+ }
476
+ }
477
+ // Main function with enhanced CLI
478
+ function main() {
479
+ try {
480
+ const config = loadConfig();
481
+ const program = new commander_1.Command();
482
+ program
483
+ .name("leo-generate")
484
+ .description("Enhanced Express module generator with Mongoose models")
485
+ .version("1.4.1");
486
+ // Main module generation command
487
+ program
488
+ .command("generate")
489
+ .alias("g")
490
+ .argument("<name>", "Module name")
491
+ .option("-c, --config <path>", "Path to custom config file")
492
+ .option("--modules-dir <path>", "Path to modules directory")
493
+ .option("--routes-file <path>", "Path to routes file")
494
+ .option("--no-postman", "Skip Postman collection generation")
495
+ .option("--no-swagger", "Skip Swagger documentation generation")
496
+ .option("--postman-dir <path>", "Custom Postman output directory", "postman")
497
+ .option("--swagger-file <path>", "Custom Swagger file path", "swagger.json")
498
+ .option("--postman-api-key <string>", "Postman API Key")
499
+ .option("--postman-collection-id <string>", "Postman Collection ID")
500
+ .allowUnknownOption(true)
501
+ .action((name, options) => {
502
+ // Override config with CLI options
503
+ if (options.modulesDir) {
504
+ config.modulesDir = options.modulesDir;
505
+ }
506
+ if (options.routesFile) {
507
+ config.routesFile = options.routesFile;
508
+ }
509
+ if (options.postmanApiKey) {
510
+ config.postmanApiKey = options.postmanApiKey;
511
+ }
512
+ if (options.postmanCollectionId) {
513
+ config.postmanCollectionId = options.postmanCollectionId;
514
+ }
515
+ // Get field definitions from remaining arguments
516
+ const fieldArgs = program.args.slice(2); // Skip 'generate' and module name
517
+ console.log("Processing field arguments:", fieldArgs);
518
+ const { fields, skipFiles, hasFile } = (0, fieldParser_1.parseFieldDefinitions)(fieldArgs);
519
+ if (fields.length === 0) {
520
+ console.log("⚠️ No fields were parsed. Check your command syntax.");
521
+ console.log("Example: leo-generate generate User name:string email:string age:number");
522
+ return;
523
+ }
524
+ createModule(name, fields, skipFiles, config, {
525
+ updatePostman: options.postman !== false,
526
+ updateSwagger: options.swagger !== false,
527
+ postmanDir: options.postmanDir,
528
+ swaggerFile: options.swaggerFile,
529
+ postmanApiKey: config.postmanApiKey,
530
+ postmanCollectionId: config.postmanCollectionId
531
+ }, hasFile);
532
+ });
533
+ // Documentation update command
534
+ program
535
+ .command("update-docs")
536
+ .alias("docs")
537
+ .description("Update Postman and Swagger documentation for existing modules")
538
+ .option("--modules-dir <path>", "Path to modules directory", "src/app/modules")
539
+ .option("--no-postman", "Skip Postman collection generation")
540
+ .option("--no-swagger", "Skip Swagger documentation generation")
541
+ .option("--postman-dir <path>", "Custom Postman output directory", "postman")
542
+ .option("--swagger-file <path>", "Custom Swagger file path", "swagger.json")
543
+ .action((options) => __awaiter(this, void 0, void 0, function* () {
544
+ yield (0, documentationUpdater_1.updateExistingModulesDocumentation)(options.modulesDir, {
545
+ updatePostman: options.postman !== false,
546
+ updateSwagger: options.swagger !== false,
547
+ postmanDir: options.postmanDir,
548
+ swaggerFile: options.swaggerFile,
549
+ postmanApiKey: config.postmanApiKey,
550
+ postmanCollectionId: config.postmanCollectionId
551
+ });
552
+ }));
553
+ // Legacy support - direct module generation (backward compatibility)
554
+ program
555
+ .argument("[name]", "Module name (legacy support)")
556
+ .allowUnknownOption(true)
557
+ .action((name) => {
558
+ if (name && !["generate", "g", "update-docs", "docs"].includes(name)) {
559
+ // Legacy mode - treat first argument as module name
560
+ const fieldArgs = program.args.slice(1);
561
+ console.log("Legacy mode - Processing field arguments:", fieldArgs);
562
+ const { fields, skipFiles, hasFile } = (0, fieldParser_1.parseFieldDefinitions)(fieldArgs);
563
+ if (fields.length === 0) {
564
+ console.log("⚠️ No fields were parsed. Check your command syntax.");
565
+ console.log("Example: leo-generate User name:string email:string age:number");
566
+ return;
567
+ }
568
+ createModule(name, fields, skipFiles, config, {
569
+ updatePostman: true,
570
+ updateSwagger: true,
571
+ postmanDir: "postman",
572
+ swaggerFile: "swagger.json",
573
+ postmanApiKey: config.postmanApiKey,
574
+ postmanCollectionId: config.postmanCollectionId
575
+ }, hasFile);
576
+ }
577
+ });
578
+ program.parse();
579
+ }
580
+ catch (error) {
581
+ console.error("❌ Error executing command:", error);
582
+ process.exit(1);
583
+ }
584
+ }
585
+ // Call the main function to start the CLI
586
+ main();
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateConstantsContent = void 0;
4
+ const generateConstantsContent = (camelCaseName, folderName, fields) => {
5
+ // Generate filterable fields (string and enum types)
6
+ const filterableFields = fields.filter(f => f.type.toLowerCase() === "string" || f.type.toLowerCase() === "enum");
7
+ // Generate searchable fields (string types only)
8
+ const searchableFields = fields.filter(f => f.type.toLowerCase() === "string");
9
+ return `// Filterable fields for ${camelCaseName}
10
+ export const ${folderName}Filterables = [${filterableFields.map(f => `'${f.name}'`).join(', ')}];
11
+
12
+ // Searchable fields for ${camelCaseName}
13
+ export const ${folderName}SearchableFields = [${searchableFields.map(f => `'${f.name}'`).join(', ')}];
14
+
15
+ // Helper function for set comparison
16
+ export const isSetEqual = (setA: Set<string>, setB: Set<string>): boolean => {
17
+ if (setA.size !== setB.size) return false;
18
+ for (const item of setA) {
19
+ if (!setB.has(item)) return false;
20
+ }
21
+ return true;
22
+ };`;
23
+ };
24
+ exports.generateConstantsContent = generateConstantsContent;