opticore-feature-component 1.0.0 → 1.0.2

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.
@@ -1,15 +1,34 @@
1
1
  // src/core/core.ts
2
- import process4 from "process";
3
- import fs4 from "fs";
4
- import path6 from "path";
5
- import { isCancel as isCancel4 } from "@clack/prompts";
2
+ import process3 from "process";
3
+ import fs5 from "fs";
4
+ import path7 from "path";
5
+ import { isCancel as isCancel5 } from "@clack/prompts";
6
6
 
7
7
  // src/utils/welcomeMessage.utils.ts
8
- import gradient from "gradient-string";
8
+ import cfonts from "cfonts";
9
+ import chalk from "chalk";
9
10
  function UWelcomeMessage() {
10
- return console.log(gradient("cyan", "pink", "orange")(
11
- "\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E\n\u2502 \u2502\n\u2502 Welcome to Feature Component CLI \u2502\n\u2502 \u2502\n\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F\n"
12
- ));
11
+ cfonts.say("OpticoreJS", {
12
+ font: "block",
13
+ align: "left",
14
+ colors: ["yellow", "#FF6B35"],
15
+ background: "transparent",
16
+ letterSpacing: 1,
17
+ lineHeight: 1,
18
+ space: true,
19
+ maxLength: "0"
20
+ });
21
+ const blue = chalk.bold.hex("#00D4FF");
22
+ const orange = chalk.bold.hex("#FF6B35");
23
+ const dim = chalk.bold.yellow;
24
+ const subTitle = ` ${blue("")} ${orange(" OPTICORE F E A T U R E M O D U L E")}
25
+ ${blue("")} ${dim(" Create \xB7 Structure \xB7 Scaffold \xB7 Generate \xB7 Organize ")} ${blue("")}
26
+ ${blue("")}`;
27
+ const FOOTER = `
28
+ ${chalk.bold.dim("Documentation")} ${chalk.underline.cyan("https://github.com/guyzoum77/opticore-feature-cli")}
29
+ `;
30
+ console.log(subTitle);
31
+ console.log(FOOTER);
13
32
  }
14
33
 
15
34
  // src/utils/choiceFeatureCleanModule.ts
@@ -118,31 +137,38 @@ async function choiceFeatureCleanModule() {
118
137
  }
119
138
  }
120
139
 
121
- // src/utils/constants/customFeatureInfoMessage.constant.ts
122
- import colors3 from "ansi-colors";
123
- import process2 from "process";
124
- function customFeatureInfo() {
125
- console.log(`${colors3.bgCyan(`${colors3.white("\nYou will have to create and configure your created modules manually.")}`)}`);
126
- process2.exit(0);
127
- }
128
-
129
140
  // src/utils/customFeatureInfo.utils.ts
130
- import colors4 from "ansi-colors";
141
+ import colors3 from "ansi-colors";
131
142
  function UCustomFeatureInfo(choice) {
132
- console.log(`You have chosen : ${choice === "opticore_clean_module" ? colors4.cyan("OptiCoreJs CLEAN Module") : colors4.cyan("Custom feature")}
133
- `);
134
- console.log(`Clean module contains the following files :`);
135
- console.log(`${colors4.cyan("application")}`);
136
- console.log(` \u2570\u2500\u2500${colors4.bgBlackBright(`${colors4.white("{controller, service, validator}")}`)}`);
137
- console.log(`${colors4.cyan("domain")}`);
138
- console.log(` \u2570\u2500\u2500${colors4.bgBlackBright(`${colors4.white("{constants, entities, enums, interfaces}")}`)}`);
139
- console.log(`${colors4.cyan("infrastructures")}`);
140
- console.log(` \u2570\u2500\u2500${colors4.bgBlackBright(`${colors4.white("{api, middleware}")}`)}`);
141
- console.log(`${colors4.cyan("persistence")}`);
142
- console.log(` \u2570\u2500\u2500${colors4.bgBlackBright(`${colors4.white("{cache, contrats, repositories, usecases}")}`)}`);
143
- console.log(`${colors4.cyan("file")} : route of feature named ${colors4.cyan("featureName.router")}
144
- `);
145
- console.log(colors4.cyan("Now let's start create a feature !"));
143
+ console.log(
144
+ `
145
+ You have chosen : ${choice === "opticore_clean_module" ? colors3.cyan("OptiCoreJs CLEAN Module") : colors3.cyan("Custom feature")}
146
+ `
147
+ );
148
+ const I = (s) => colors3.yellow(s);
149
+ const A = (s) => colors3.cyan(s);
150
+ const D = (s) => colors3.yellowBright(s);
151
+ const E = (s) => colors3.white(s);
152
+ const st = (s) => colors3.gray(s);
153
+ const diagram = [
154
+ ` ` + I(`INFRASTRUCTURE`),
155
+ ` ` + I(`controllers \xB7 repositories \xB7 routes`),
156
+ ` ` + I(`presenters \xB7 routes`),
157
+ ``,
158
+ ` ` + A(`APPLICATION`),
159
+ ` ` + A(`ports \xB7 interfaces \xB7 dtos \xB7 use-cases`),
160
+ ``,
161
+ ` ` + D(`DOMAIN`),
162
+ ` ` + D(`events \xB7 exceptions`),
163
+ ``,
164
+ ` ` + E(`ENTITIES`),
165
+ ` ` + E(`core \xB7 no external deps`),
166
+ ``,
167
+ ` ` + colors3.dim(`Dependency direction : outer layers depend on inner layers`),
168
+ ``
169
+ ];
170
+ console.log(diagram.join(`
171
+ `));
146
172
  }
147
173
 
148
174
  // src/services/featureName.service.ts
@@ -165,12 +191,12 @@ var _featureName = async () => {
165
191
  // src/services/base.service.ts
166
192
  import path from "path";
167
193
  import fs2 from "fs";
168
- import colors5 from "ansi-colors";
169
- import process3 from "process";
194
+ import colors4 from "ansi-colors";
195
+ import process2 from "process";
170
196
  var BaseService = class {
171
197
  static _isSubdirectoryExists(subdirectory) {
172
198
  const subdirectoryString = typeof subdirectory === "symbol" ? subdirectory.toString() : String(subdirectory);
173
- const subdirectoryPath = path.join(process3.cwd(), "src", "features", subdirectoryString);
199
+ const subdirectoryPath = path.join(process2.cwd(), "src", "features", subdirectoryString);
174
200
  try {
175
201
  const stats = fs2.statSync(subdirectoryPath);
176
202
  return stats.isDirectory();
@@ -179,27 +205,27 @@ var BaseService = class {
179
205
  }
180
206
  }
181
207
  static _featureFounded(feature) {
182
- console.log(`${colors5.bgCyan(`${colors5.white(`${colors5.bold(`feature ${feature} is already exists.`)}`)}`)}`);
183
- process3.exit(0);
208
+ console.log(`${colors4.bgCyan(`${colors4.white(`${colors4.bold(`feature ${feature} is already exists.`)}`)}`)}`);
209
+ process2.exit(0);
184
210
  }
185
211
  static _checkFeatureDir() {
186
- console.error(`${colors5.bgRed(`${colors5.white("Check if directory src/features exists, and try again.")}`)}`);
187
- process3.exit();
212
+ console.error(`${colors4.bgRed(`${colors4.white("Check if directory src/features exists, and try again.")}`)}`);
213
+ process2.exit();
188
214
  }
189
215
  static _dirFounded(feature) {
190
- console.error(`${colors5.bgRed(`${colors5.white(`The ${colors5.bgCyan(`${colors5.bold(`feature ${feature} is already exists.`)}`)}`)}`)}`);
191
- process3.exit();
216
+ console.error(`${colors4.bgRed(`${colors4.white(`The ${colors4.bgCyan(`${colors4.bold(`feature ${feature} is already exists.`)}`)}`)}`)}`);
217
+ process2.exit();
192
218
  }
193
219
  static _cancelOperation(controllerPath) {
194
- console.log(`${colors5.bgRed(`${colors5.white("Operation cancelled.")}`)}`);
220
+ console.log(`${colors4.bgRed(`${colors4.white("Operation cancelled.")}`)}`);
195
221
  UFsModule.removeDir(controllerPath, true, true);
196
- process3.exit(0);
222
+ process2.exit(0);
197
223
  }
198
224
  static async _createFeatureModuleDir(featureDir, feature) {
199
225
  let ora = (await import("ora")).default;
200
226
  const spinner = ora("Feature's name creation...").start();
201
227
  UFsModule.createDirectoryRecursively(featureDir);
202
- spinner.succeed(`Your feature"s name ${colors5.bgCyan(colors5.white(`${feature}`))} has been created successfully.
228
+ spinner.succeed(`Your feature"s name ${colors4.bgCyan(colors4.white(`${feature}`))} has been created successfully.
203
229
  `);
204
230
  return { featureDir, feature };
205
231
  }
@@ -250,11 +276,11 @@ var CCleanType = {
250
276
 
251
277
  // src/templates/simpleComponent/simpleComponent.template.ts
252
278
  import path3 from "path";
253
- import colors7 from "ansi-colors";
279
+ import colors6 from "ansi-colors";
254
280
 
255
281
  // src/utils/featureComponentGenerator.utils.ts
256
282
  import path2 from "path";
257
- import colors6 from "ansi-colors";
283
+ import colors5 from "ansi-colors";
258
284
  import fs3 from "fs";
259
285
  import { LoggerCore as LoggerCore2 } from "opticore-logger";
260
286
 
@@ -273,13 +299,6 @@ import { ${capitalized}Repository } from "../repositories/${featureName}.reposit
273
299
 
274
300
  /**
275
301
  * ${controllerName} \u2014 HTTP Controller (Infrastructure Layer)
276
- *
277
- * Handles Express Request/Response only.
278
- * Delegates all business logic to the use case.
279
- * Uses the presenter to format the output.
280
- *
281
- * Wiring: Controller \u2192 UseCase \u2192 Repository (all injected here manually).
282
- * For larger projects, replace manual wiring with a DI container (e.g., tsyringe, awilix).
283
302
  */
284
303
  export class ${controllerName} {
285
304
  private static buildUseCase(): ${useCaseName} {
@@ -407,7 +426,9 @@ var generateDeleteMethod = (method, featureName, capitalized) => `
407
426
  `;
408
427
  var errorHandlerAppend = (capitalized) => `
409
428
  private static handleError(error: unknown) {
410
- const message = error instanceof Error ? error.message : "Internal server error";
429
+ const message = error instanceof Error
430
+ ? error.message
431
+ : "Internal server error";
411
432
  return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
412
433
  }
413
434
  `;
@@ -439,10 +460,6 @@ export class ${entityName} {
439
460
  this.validate();
440
461
  }
441
462
 
442
- // -------------------------------------------------------------------------
443
- // Getters
444
- // -------------------------------------------------------------------------
445
-
446
463
  get id(): string {
447
464
  return this._id;
448
465
  }
@@ -455,10 +472,6 @@ export class ${entityName} {
455
472
  return this._updatedAt;
456
473
  }
457
474
 
458
- // -------------------------------------------------------------------------
459
- // Business methods (behavior belongs to the entity)
460
- // -------------------------------------------------------------------------
461
-
462
475
  /**
463
476
  * Marks the entity as updated.
464
477
  * Call this whenever a business mutation occurs.
@@ -479,10 +492,6 @@ export class ${entityName} {
479
492
  };
480
493
  }
481
494
 
482
- // -------------------------------------------------------------------------
483
- // Invariant validation (domain rules enforced at construction)
484
- // -------------------------------------------------------------------------
485
-
486
495
  private validate(): void {
487
496
  if (!this._id || this._id.trim().length === 0) {
488
497
  throw new Error(\`[${entityName}] id must not be empty.\`);
@@ -562,16 +571,14 @@ export class ${implName} implements ${interfaceName} {
562
571
 
563
572
  /**
564
573
  * TODO: Inject your database client / ORM model here via the constructor.
565
- * Example with TypeORM:
566
- * constructor(private readonly repo: Repository<${capitalized}Model>) {}
574
+ * Example with TypeORM: constructor(private readonly repo: Repository<${capitalized}Model>) {}
567
575
  *
568
- * Example with Prisma:
569
- * constructor(private readonly prisma: PrismaClient) {}
576
+ * Example with Prisma: constructor(private readonly prisma: PrismaClient) {}
570
577
  */
571
578
 
572
- // -------------------------------------------------------------------------
573
- // In-memory store (replace with real persistence)
574
- // -------------------------------------------------------------------------
579
+ /**
580
+ * In-memory store (replace with real persistence)
581
+ */
575
582
  private store: Map<string, ${entityName}> = new Map();
576
583
 
577
584
  async findAll(): Promise<${entityName}[]> {
@@ -614,9 +621,6 @@ export class ${implName} implements ${interfaceName} {
614
621
  return this.store.delete(id);
615
622
  }
616
623
 
617
- // -------------------------------------------------------------------------
618
- // Private mappers (ORM model \u2194 Domain entity)
619
- // -------------------------------------------------------------------------
620
624
 
621
625
  /**
622
626
  * Maps an ORM/DB row to a domain entity.
@@ -671,13 +675,7 @@ import {
671
675
  */
672
676
  export class ${useCaseName} {
673
677
 
674
- constructor(
675
- private readonly repository: ${interfaceName},
676
- ) {}
677
-
678
- // -------------------------------------------------------------------------
679
- // Query methods (read)
680
- // -------------------------------------------------------------------------
678
+ constructor(private readonly repository: ${interfaceName}) {}
681
679
 
682
680
  async findAll(): Promise<${capitalized}ResponseDto[]> {
683
681
  const entities = await this.repository.findAll();
@@ -686,14 +684,12 @@ export class ${useCaseName} {
686
684
 
687
685
  async findById(id: string): Promise<${capitalized}ResponseDto | null> {
688
686
  const entity = await this.repository.findById(id);
689
- if (!entity) return null;
687
+ if (!entity) {
688
+ return null;
689
+ }
690
690
  return ${capitalized}DtoMapper.toResponse(entity);
691
691
  }
692
692
 
693
- // -------------------------------------------------------------------------
694
- // Command methods (write)
695
- // -------------------------------------------------------------------------
696
-
697
693
  async create(dto: Create${capitalized}Dto): Promise<${capitalized}ResponseDto> {
698
694
  // TODO: Map DTO to domain entity. Generate id using your preferred strategy (uuid, etc.)
699
695
  const id = crypto.randomUUID();
@@ -708,14 +704,18 @@ export class ${useCaseName} {
708
704
 
709
705
  async update(dto: Update${capitalized}Dto): Promise<${capitalized}ResponseDto | null> {
710
706
  const existing = await this.repository.findById(dto.id);
711
- if (!existing) return null;
707
+ if (!existing) {
708
+ return null;
709
+ }
712
710
 
713
711
  // TODO: Apply the update to the domain entity
714
712
  // existing.updateName(dto.name);
715
713
  existing.touch();
716
714
 
717
715
  const updated = await this.repository.update(existing);
718
- if (!updated) return null;
716
+ if (!updated) {
717
+ return null;
718
+ }
719
719
  return ${capitalized}DtoMapper.toResponse(updated);
720
720
  }
721
721
 
@@ -738,9 +738,6 @@ var generateDtoTemplate = (featureName) => {
738
738
  * Validation is handled at the infrastructure layer (e.g., in the controller).
739
739
  */
740
740
 
741
- // -------------------------------------------------------------------------
742
- // Input DTOs (data coming IN to use cases)
743
- // -------------------------------------------------------------------------
744
741
 
745
742
  export interface Create${capitalized}Dto {
746
743
  // TODO: Define the fields required to create a ${featureName}
@@ -751,28 +748,17 @@ export interface Create${capitalized}Dto {
751
748
 
752
749
  export interface Update${capitalized}Dto {
753
750
  id: string;
754
- // TODO: Define the fields allowed to be updated
755
- // All fields should be optional (partial update pattern)
756
- // Example:
757
- // name?: string;
751
+ // TODO: Define the fields allowed to be updated. All fields should be optional (partial update pattern)
752
+ // Example: name?: string;
758
753
  }
759
754
 
760
- // -------------------------------------------------------------------------
761
- // Output DTOs (data going OUT from use cases to the controller/presenter)
762
- // -------------------------------------------------------------------------
763
-
764
755
  export interface ${capitalized}ResponseDto {
765
756
  id: string;
766
757
  createdAt: Date;
767
758
  updatedAt: Date;
768
- // TODO: Mirror the fields you expose to clients
769
- // Avoid leaking internal domain model details (e.g., private fields, passwords)
759
+ // TODO: Mirror the fields you expose to clients. Avoid leaking internal domain model details (e.g., private fields, passwords)
770
760
  }
771
761
 
772
- // -------------------------------------------------------------------------
773
- // Mapper helper (Domain Entity \u2192 Response DTO)
774
- // -------------------------------------------------------------------------
775
-
776
762
  import { ${capitalized}Entity } from "../../domain/entities/${featureName}.entity";
777
763
 
778
764
  export class ${capitalized}DtoMapper {
@@ -891,9 +877,9 @@ var generateRouterHandlerTemplate = (featureName, controllerMethods) => {
891
877
  const handlerName = `${capitalized}HandlerRouter`;
892
878
  const controllerName = `${capitalized}Controller`;
893
879
  const routesCode = (controllerMethods || []).map((method) => {
894
- const { httpMethod, path: path7 } = resolveRoute(method, featureName);
880
+ const { httpMethod, path: path8 } = resolveRoute(method, featureName);
895
881
  return `{
896
- path: \`${path7}\`,
882
+ path: \`${path8}\`,
897
883
  method: "${httpMethod}",
898
884
  middlewares: [],
899
885
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
@@ -946,24 +932,22 @@ var generateEventTemplate = (featureName) => {
946
932
  * - Feed an event bus / message broker (RabbitMQ, Kafka, etc.)
947
933
  * - Build an audit log or event sourcing stream
948
934
  */
935
+
949
936
 
950
- // -------------------------------------------------------------------------
951
- // Base interface \u2014 all domain events share these fields
952
- // -------------------------------------------------------------------------
953
-
937
+ /**
938
+ * Unique event identifier (uuid)
939
+ * When the event occurred
940
+ * The aggregate that emitted this event
941
+ */
954
942
  export interface IDomainEvent {
955
- /** Unique event identifier (uuid) */
956
943
  readonly eventId: string;
957
- /** When the event occurred */
958
944
  readonly occurredAt: Date;
959
- /** The aggregate that emitted this event */
960
945
  readonly aggregateId: string;
961
946
  }
962
947
 
963
- // -------------------------------------------------------------------------
964
- // Concrete events for the "${featureName}" feature
965
- // -------------------------------------------------------------------------
966
-
948
+ /**
949
+ * Concrete events for the "${featureName}" feature
950
+ */
967
951
  export class ${capitalized}CreatedEvent implements IDomainEvent {
968
952
  readonly eventId: string;
969
953
  readonly occurredAt: Date;
@@ -1022,10 +1006,6 @@ var generateExceptionTemplate = (featureName) => {
1022
1006
  * Rule: never import HTTP status codes or Express here \u2014 this is pure domain logic.
1023
1007
  */
1024
1008
 
1025
- // -------------------------------------------------------------------------
1026
- // Base domain exception
1027
- // -------------------------------------------------------------------------
1028
-
1029
1009
  export class ${capitalized}DomainException extends Error {
1030
1010
  /** Machine-readable error code for programmatic handling */
1031
1011
  readonly code: string;
@@ -1040,9 +1020,6 @@ export class ${capitalized}DomainException extends Error {
1040
1020
  }
1041
1021
  }
1042
1022
 
1043
- // -------------------------------------------------------------------------
1044
- // Specific domain exceptions
1045
- // -------------------------------------------------------------------------
1046
1023
 
1047
1024
  /**
1048
1025
  * Thrown when a ${featureName} cannot be found by its identifier.
@@ -1086,10 +1063,6 @@ export class ${capitalized}InvalidDataException extends ${capitalized}DomainExce
1086
1063
  }
1087
1064
  }
1088
1065
 
1089
- // -------------------------------------------------------------------------
1090
- // Exception guard helper
1091
- // -------------------------------------------------------------------------
1092
-
1093
1066
  /**
1094
1067
  * Type guard to check if an unknown error is a ${capitalized}DomainException.
1095
1068
  * Use this in your controller's catch block to map domain errors to HTTP responses.
@@ -1274,7 +1247,9 @@ export class ${serviceName} {
1274
1247
 
1275
1248
  async update(id: string, data: Record<string, unknown>): Promise<${modelName} | null> {
1276
1249
  const existing = await this.repository.findById(id);
1277
- if (!existing) return null;
1250
+ if (!existing) {
1251
+ return null;
1252
+ }
1278
1253
  // TODO: Apply data fields onto existing model
1279
1254
  return this.repository.update(existing);
1280
1255
  }
@@ -1311,11 +1286,21 @@ ${methodsCode}
1311
1286
  };
1312
1287
  var generateMethod2 = (method, capitalized, serviceName) => {
1313
1288
  const lower = method.toLowerCase();
1314
- if (lower.includes("findall") || lower.includes("getall")) return generateFindAllMethod2(method, capitalized);
1315
- if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return generateFindByIdMethod2(method, capitalized);
1316
- if (lower.includes("create") || lower.includes("add")) return generateCreateMethod2(method, capitalized);
1317
- if (lower.includes("update") || lower.includes("edit")) return generateUpdateMethod2(method, capitalized);
1318
- if (lower.includes("delete") || lower.includes("remove")) return generateDeleteMethod2(method, capitalized);
1289
+ if (lower.includes("findall") || lower.includes("getall")) {
1290
+ return generateFindAllMethod2(method, capitalized);
1291
+ }
1292
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) {
1293
+ return generateFindByIdMethod2(method, capitalized);
1294
+ }
1295
+ if (lower.includes("create") || lower.includes("add")) {
1296
+ return generateCreateMethod2(method, capitalized);
1297
+ }
1298
+ if (lower.includes("update") || lower.includes("edit")) {
1299
+ return generateUpdateMethod2(method, capitalized);
1300
+ }
1301
+ if (lower.includes("delete") || lower.includes("remove")) {
1302
+ return generateDeleteMethod2(method, capitalized);
1303
+ }
1319
1304
  return `
1320
1305
  static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1321
1306
  try {
@@ -1412,9 +1397,9 @@ var generateSimpleRouterHandlerTemplate = (featureName, controllerMethods) => {
1412
1397
  const handlerName = `${capitalized}HandlerRouter`;
1413
1398
  const controllerName = `${capitalized}Controller`;
1414
1399
  const routesCode = (controllerMethods || []).map((method) => {
1415
- const { httpMethod, path: path7 } = resolveRoute2(method, featureName);
1400
+ const { httpMethod, path: path8 } = resolveRoute2(method, featureName);
1416
1401
  return `{
1417
- path: \`${path7}\`,
1402
+ path: \`${path8}\`,
1418
1403
  method: "${httpMethod}",
1419
1404
  middlewares: [],
1420
1405
  handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
@@ -1460,107 +1445,101 @@ var FeatureComponentGeneratorUtils = class {
1460
1445
  static setStructure(s) {
1461
1446
  this.structure = s;
1462
1447
  }
1448
+ /**
1449
+ * Registers a custom module router in register.router.ts.
1450
+ * Must be preceded by setStructure("custom").
1451
+ */
1452
+ static async registerModuleRouter(moduleName) {
1453
+ await this.updateRegisterRouter(moduleName);
1454
+ this.structure = "clean";
1455
+ }
1463
1456
  /**
1464
1457
  * Creates the domain entity file.
1465
- * File: domain/entities/<featureName>.entity.ts
1466
1458
  */
1467
1459
  static async createEntity(featureName, entityDir) {
1468
1460
  const entityName = `${this.capitalize(featureName)}Entity`;
1469
1461
  const filePath = path2.join(entityDir, `${featureName}.entity.ts`);
1470
1462
  UFsModule.createFile(filePath, generateEntityTemplate(entityName, featureName));
1471
- console.log(colors6.green(`\u2705 Entity created: ${filePath}`));
1463
+ console.log(colors5.green(`\u2705 Entity created: ${filePath}`));
1472
1464
  }
1473
1465
  /**
1474
1466
  * Creates the domain event file.
1475
- * File: domain/events/<featureName>.event.ts
1476
1467
  */
1477
1468
  static async createEvent(featureName, eventDir) {
1478
1469
  const filePath = path2.join(eventDir, `${featureName}.event.ts`);
1479
1470
  UFsModule.createFile(filePath, generateEventTemplate(featureName));
1480
- console.log(colors6.green(`\u2705 Event created: ${filePath}`));
1471
+ console.log(colors5.green(`\u2705 Event created: ${filePath}`));
1481
1472
  }
1482
1473
  /**
1483
1474
  * Creates the domain exception file.
1484
- * File: domain/exceptions/<featureName>.exception.ts
1485
1475
  */
1486
1476
  static async createException(featureName, exceptionDir) {
1487
1477
  const filePath = path2.join(exceptionDir, `${featureName}.exception.ts`);
1488
1478
  UFsModule.createFile(filePath, generateExceptionTemplate(featureName));
1489
- console.log(colors6.green(`\u2705 Exception created: ${filePath}`));
1479
+ console.log(colors5.green(`\u2705 Exception created: ${filePath}`));
1490
1480
  }
1491
1481
  /**
1492
1482
  * Creates the repository port interface.
1493
- * File: application/ports/repositories/<featureName>.repository.interface.ts
1494
1483
  */
1495
1484
  static async createRepositoryInterface(featureName, interfaceDir) {
1496
1485
  const interfaceName = `I${this.capitalize(featureName)}Repository`;
1497
1486
  const filePath = path2.join(interfaceDir, `${featureName}.repository.interface.ts`);
1498
1487
  UFsModule.createFile(filePath, generateRepositoryInterfaceTemplate(interfaceName, featureName));
1499
- console.log(colors6.green(`\u2705 Repository interface created: ${filePath}`));
1488
+ console.log(colors5.green(`\u2705 Repository interface created: ${filePath}`));
1500
1489
  }
1501
1490
  /**
1502
1491
  * Creates the presenter port interface.
1503
- * File: application/ports/presenters/<featureName>.presenter.interface.ts
1504
1492
  */
1505
1493
  static async createPresenterInterface(featureName, presenterInterfaceDir) {
1506
1494
  const filePath = path2.join(presenterInterfaceDir, `${featureName}.presenter.interface.ts`);
1507
1495
  UFsModule.createFile(filePath, generatePresenterInterfaceTemplate(featureName));
1508
- console.log(colors6.green(`\u2705 Presenter interface created: ${filePath}`));
1496
+ console.log(colors5.green(`\u2705 Presenter interface created: ${filePath}`));
1509
1497
  }
1510
1498
  /**
1511
1499
  * Creates the application service port interface.
1512
- * File: application/ports/services/<featureName>.service.ts
1513
1500
  */
1514
1501
  static async createService(featureName, serviceDir) {
1515
1502
  const filePath = path2.join(serviceDir, `${featureName}.service.ts`);
1516
1503
  UFsModule.createFile(filePath, generateServiceTemplate(featureName));
1517
- console.log(colors6.green(`\u2705 Service interface created: ${filePath}`));
1504
+ console.log(colors5.green(`\u2705 Service interface created: ${filePath}`));
1518
1505
  }
1519
1506
  /**
1520
1507
  * Creates the DTO file (input + output shapes + mapper).
1521
- * File: application/dtos/<featureName>.dto.ts
1522
1508
  */
1523
1509
  static async createDto(featureName, dtoDir) {
1524
1510
  const filePath = path2.join(dtoDir, `${featureName}.dto.ts`);
1525
1511
  UFsModule.createFile(filePath, generateDtoTemplate(featureName));
1526
- console.log(colors6.green(`\u2705 DTO created: ${filePath}`));
1512
+ console.log(colors5.green(`\u2705 DTO created: ${filePath}`));
1527
1513
  }
1528
1514
  /**
1529
1515
  * Creates the use case file.
1530
- * File: application/use-cases/<featureName>.usecase.ts
1531
1516
  */
1532
1517
  static async createUseCase(featureName, useCaseDir) {
1533
1518
  const useCaseName = `${this.capitalize(featureName)}UseCase`;
1534
1519
  const filePath = path2.join(useCaseDir, `${featureName}.usecase.ts`);
1535
1520
  UFsModule.createFile(filePath, generateUseCaseTemplate(useCaseName, featureName));
1536
- console.log(colors6.green(`\u2705 UseCase created: ${filePath}`));
1521
+ console.log(colors5.green(`\u2705 UseCase created: ${filePath}`));
1537
1522
  }
1538
- // =========================================================================
1539
- // INFRASTRUCTURE LAYER
1540
- // =========================================================================
1541
1523
  /**
1542
1524
  * Creates the concrete repository implementation.
1543
- * File: infrastructure/adapters/repositories/<featureName>.repository.ts
1544
1525
  */
1545
1526
  static async createRepositoryImpl(featureName, repositoryImplDir) {
1546
1527
  const interfaceImportPath = `../../../application/ports/repositories/${featureName}.repository.interface`;
1547
1528
  const filePath = path2.join(repositoryImplDir, `${featureName}.repository.ts`);
1548
1529
  UFsModule.createFile(filePath, generateRepositoryImplTemplate(featureName, interfaceImportPath));
1549
- console.log(colors6.green(`\u2705 Repository implementation created: ${filePath}`));
1530
+ console.log(colors5.green(`\u2705 Repository implementation created: ${filePath}`));
1550
1531
  }
1551
1532
  /**
1552
1533
  * Creates the concrete presenter implementation.
1553
- * File: infrastructure/adapters/presenters/<featureName>.presenter.ts
1554
1534
  */
1555
1535
  static async createPresenterImpl(featureName, presenterImplDir) {
1556
1536
  const filePath = path2.join(presenterImplDir, `${featureName}.presenter.ts`);
1557
1537
  UFsModule.createFile(filePath, generatePresenterImplTemplate(featureName));
1558
- console.log(colors6.green(`\u2705 Presenter implementation created: ${filePath}`));
1538
+ console.log(colors5.green(`\u2705 Presenter implementation created: ${filePath}`));
1559
1539
  }
1560
1540
  /**
1561
1541
  * Creates the HTTP controller.
1562
1542
  * Prompts the user for method names, then generates the file.
1563
- * File: infrastructure/adapters/controllers/<featureName>.controller.ts
1564
1543
  *
1565
1544
  * @returns The controller class name and the list of chosen methods.
1566
1545
  */
@@ -1585,39 +1564,35 @@ export class ${controllerName} {}
1585
1564
  `;
1586
1565
  }
1587
1566
  UFsModule.createFile(filePath, content);
1588
- console.log(colors6.green(`\u2705 Controller created: ${filePath}`));
1567
+ console.log(colors5.green(`\u2705 Controller created: ${filePath}`));
1589
1568
  return { controllerName, methods };
1590
1569
  }
1591
1570
  /**
1592
1571
  * Creates the data model file (simple component).
1593
- * File: models/<featureName>.model.ts
1594
1572
  */
1595
1573
  static async createModel(featureName, modelDir) {
1596
1574
  const filePath = path2.join(modelDir, `${featureName}.model.ts`);
1597
1575
  UFsModule.createFile(filePath, generateModelTemplate(featureName));
1598
- console.log(colors6.green(`\u2705 Model created: ${filePath}`));
1576
+ console.log(colors5.green(`\u2705 Model created: ${filePath}`));
1599
1577
  }
1600
1578
  /**
1601
1579
  * Creates the simple repository (no interface, uses the model directly).
1602
- * File: repositories/<featureName>.repository.ts
1603
1580
  */
1604
1581
  static async createSimpleRepository(featureName, repositoryDir) {
1605
1582
  const filePath = path2.join(repositoryDir, `${featureName}.repository.ts`);
1606
1583
  UFsModule.createFile(filePath, generateSimpleRepositoryTemplate(featureName));
1607
- console.log(colors6.green(`\u2705 Repository created: ${filePath}`));
1584
+ console.log(colors5.green(`\u2705 Repository created: ${filePath}`));
1608
1585
  }
1609
1586
  /**
1610
1587
  * Creates the simple concrete service (no interface, wires the repository).
1611
- * File: services/<featureName>.service.ts
1612
1588
  */
1613
1589
  static async createSimpleService(featureName, serviceDir) {
1614
1590
  const filePath = path2.join(serviceDir, `${featureName}.service.ts`);
1615
1591
  UFsModule.createFile(filePath, generateSimpleServiceTemplate(featureName));
1616
- console.log(colors6.green(`\u2705 Service created: ${filePath}`));
1592
+ console.log(colors5.green(`\u2705 Service created: ${filePath}`));
1617
1593
  }
1618
1594
  /**
1619
1595
  * Creates the simple controller (delegates to service, no use-case/presenter).
1620
- * File: controllers/<featureName>.controller.ts
1621
1596
  */
1622
1597
  static async createSimpleController(featureName, controllerDir) {
1623
1598
  const controllerName = `${this.capitalize(featureName)}Controller`;
@@ -1640,31 +1615,29 @@ export class ${controllerName} {}
1640
1615
  `;
1641
1616
  }
1642
1617
  UFsModule.createFile(filePath, content);
1643
- console.log(colors6.green(`\u2705 Controller created: ${filePath}`));
1618
+ console.log(colors5.green(`\u2705 Controller created: ${filePath}`));
1644
1619
  return { controllerName, methods };
1645
1620
  }
1646
1621
  /**
1647
1622
  * Creates the route handler (HTTP verb + path → controller method bindings).
1648
- * File: infrastructure/routes/<featureName>.router.handler.ts (clean)
1649
- * routes/<featureName>.router.handler.ts (simple)
1623
+ *
1650
1624
  */
1651
1625
  static async createRouterHandler(featureName, routerHandlerDir, controllerMethods) {
1652
1626
  const filePath = path2.join(routerHandlerDir, `${featureName}.router.handler.ts`);
1653
1627
  const content = this.structure === "simple" ? generateSimpleRouterHandlerTemplate(featureName, controllerMethods) : generateRouterHandlerTemplate(featureName, controllerMethods);
1654
1628
  UFsModule.createFile(filePath, content);
1655
- console.log(colors6.green(`\u2705 Router Handler created: ${filePath}`));
1629
+ console.log(colors5.green(`\u2705 Router Handler created: ${filePath}`));
1656
1630
  }
1657
1631
  /**
1658
1632
  * Creates the feature router and registers it in register.router.ts.
1659
- * File: infrastructure/routes/<featureName>.router.ts (clean)
1660
- * routes/<featureName>.router.ts (simple)
1633
+ *
1661
1634
  */
1662
1635
  static async createRouter(featureName, routerDir) {
1663
1636
  const filePath = path2.join(routerDir, `${featureName}.router.ts`);
1664
1637
  const content = this.structure === "simple" ? generateSimpleRouterTemplate(featureName) : generateRouterTemplate(featureName);
1665
1638
  UFsModule.createFile(filePath, content);
1666
1639
  await this.updateRegisterRouter(featureName);
1667
- console.log(colors6.green(`\u2705 Router created: ${filePath}`));
1640
+ console.log(colors5.green(`\u2705 Router created: ${filePath}`));
1668
1641
  }
1669
1642
  static capitalize(str) {
1670
1643
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -1674,18 +1647,18 @@ export class ${controllerName} {}
1674
1647
  */
1675
1648
  static async updateRegisterRouter(featureName) {
1676
1649
  const routerName = `${this.capitalize(featureName)}Router`;
1677
- const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1650
+ const routerPath = this.structure === "simple" ? `../../features/${featureName}/routes/${featureName}.router` : this.structure === "custom" ? `../../features/${featureName}/${featureName}.router` : `../../features/${featureName}/infrastructure/routes/${featureName}.router`;
1678
1651
  const importStatement = `import { ${routerName} } from "${routerPath}"`;
1679
1652
  try {
1680
1653
  if (!fs3.existsSync(this.REGISTER_ROUTER_PATH)) {
1681
1654
  console.log(
1682
- colors6.yellow(`\u26A0\uFE0F register.router.ts not found at ${this.REGISTER_ROUTER_PATH}, skipping auto-registration.`)
1655
+ colors5.yellow(`\u26A0\uFE0F register.router.ts not found at ${this.REGISTER_ROUTER_PATH}, skipping auto-registration.`)
1683
1656
  );
1684
1657
  return;
1685
1658
  }
1686
1659
  let content = fs3.readFileSync(this.REGISTER_ROUTER_PATH, "utf-8");
1687
1660
  if (content.includes(`import { ${routerName} } from`)) {
1688
- console.log(colors6.cyan(`\u2139\uFE0F ${routerName} is already registered.`));
1661
+ console.log(colors5.cyan(`\u2139\uFE0F ${routerName} is already registered.`));
1689
1662
  return;
1690
1663
  }
1691
1664
  const lines = content.split("\n");
@@ -1703,12 +1676,12 @@ export class ${controllerName} {}
1703
1676
  const ARRAY_CLOSE = "]);";
1704
1677
  const startIdx = content.indexOf(ARRAY_OPEN);
1705
1678
  if (startIdx === -1) {
1706
- console.log(colors6.yellow(`\u26A0\uFE0F Could not find 'registered([' in register.router.ts.`));
1679
+ console.log(colors5.yellow(`\u26A0\uFE0F Could not find 'registered([' in register.router.ts.`));
1707
1680
  return;
1708
1681
  }
1709
1682
  const endIdx = content.indexOf(ARRAY_CLOSE, startIdx + ARRAY_OPEN.length);
1710
1683
  if (endIdx === -1) {
1711
- console.log(colors6.yellow(`\u26A0\uFE0F Could not find matching ']);' in register.router.ts.`));
1684
+ console.log(colors5.yellow(`\u26A0\uFE0F Could not find matching ']);' in register.router.ts.`));
1712
1685
  return;
1713
1686
  }
1714
1687
  const arrayContent = content.substring(startIdx + ARRAY_OPEN.length, endIdx);
@@ -1721,9 +1694,9 @@ export class ${controllerName} {}
1721
1694
  ${indentedRoutes}
1722
1695
  ` + content.substring(endIdx);
1723
1696
  fs3.writeFileSync(this.REGISTER_ROUTER_PATH, newContent, "utf-8");
1724
- console.log(colors6.green(`\u2705 register.router.ts updated with ${routerName}.`));
1697
+ console.log(colors5.green(`\u2705 register.router.ts updated with ${routerName}.`));
1725
1698
  } catch (error) {
1726
- console.error(colors6.red(`\u274C Failed to update register.router.ts: ${error.message}`));
1699
+ console.error(colors5.red(`\u274C Failed to update register.router.ts: ${error.message}`));
1727
1700
  }
1728
1701
  }
1729
1702
  };
@@ -1757,8 +1730,8 @@ var simpleComponentTemplate = async (featureName) => {
1757
1730
  await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routesDir, controller.methods);
1758
1731
  await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routesDir);
1759
1732
  console.log(
1760
- colors7.bgGreen(
1761
- colors7.white(`
1733
+ colors6.bgGreen(
1734
+ colors6.white(`
1762
1735
  \u{1F389} Feature "${featureName}" scaffolded with Simple Component!
1763
1736
  `)
1764
1737
  )
@@ -1767,21 +1740,18 @@ var simpleComponentTemplate = async (featureName) => {
1767
1740
 
1768
1741
  // src/templates/fullCleanComponent/fullCleanComponent.template.ts
1769
1742
  import path4 from "path";
1770
- import colors8 from "ansi-colors";
1743
+ import colors7 from "ansi-colors";
1771
1744
  var fullCleanComponentTemplate = async (featureName) => {
1772
1745
  const basePath = path4.join("src", "features", featureName);
1773
1746
  const dirs = [
1774
- // Domain
1775
1747
  path4.join(basePath, "domain", "entities"),
1776
1748
  path4.join(basePath, "domain", "events"),
1777
1749
  path4.join(basePath, "domain", "exceptions"),
1778
- // Application
1779
1750
  path4.join(basePath, "application", "use-cases"),
1780
1751
  path4.join(basePath, "application", "dtos"),
1781
1752
  path4.join(basePath, "application", "ports", "repositories"),
1782
1753
  path4.join(basePath, "application", "ports", "presenters"),
1783
1754
  path4.join(basePath, "application", "ports", "services"),
1784
- // Infrastructure
1785
1755
  path4.join(basePath, "infrastructure", "adapters", "controllers"),
1786
1756
  path4.join(basePath, "infrastructure", "adapters", "repositories"),
1787
1757
  path4.join(basePath, "infrastructure", "adapters", "presenters"),
@@ -1789,17 +1759,14 @@ var fullCleanComponentTemplate = async (featureName) => {
1789
1759
  ];
1790
1760
  dirs.forEach((dir) => UFsModule.createDirectoryRecursively(dir));
1791
1761
  const paths = {
1792
- // Domain
1793
1762
  entityDir: path4.join(basePath, "domain", "entities"),
1794
1763
  eventDir: path4.join(basePath, "domain", "events"),
1795
1764
  exceptionDir: path4.join(basePath, "domain", "exceptions"),
1796
- // Application
1797
1765
  repositoryInterfaceDir: path4.join(basePath, "application", "ports", "repositories"),
1798
1766
  presenterInterfaceDir: path4.join(basePath, "application", "ports", "presenters"),
1799
1767
  serviceDir: path4.join(basePath, "application", "ports", "services"),
1800
1768
  dtoDir: path4.join(basePath, "application", "dtos"),
1801
1769
  useCaseDir: path4.join(basePath, "application", "use-cases"),
1802
- // Infrastructure
1803
1770
  repositoryImplDir: path4.join(basePath, "infrastructure", "adapters", "repositories"),
1804
1771
  presenterImplDir: path4.join(basePath, "infrastructure", "adapters", "presenters"),
1805
1772
  controllerDir: path4.join(basePath, "infrastructure", "adapters", "controllers"),
@@ -1822,8 +1789,8 @@ var fullCleanComponentTemplate = async (featureName) => {
1822
1789
  await FeatureComponentGeneratorUtils.createRouterHandler(featureName, paths.routerDir, controller.methods);
1823
1790
  await FeatureComponentGeneratorUtils.createRouter(featureName, paths.routerDir);
1824
1791
  console.log(
1825
- colors8.bgGreen(
1826
- colors8.white(`
1792
+ colors7.bgGreen(
1793
+ colors7.white(`
1827
1794
  \u{1F389} Feature "${featureName}" scaffolded with Clean Architecture!
1828
1795
  `)
1829
1796
  )
@@ -1832,11 +1799,12 @@ var fullCleanComponentTemplate = async (featureName) => {
1832
1799
 
1833
1800
  // src/templates/cleanComponentByStep/cleanComponentByStep.template.ts
1834
1801
  import path5 from "path";
1835
- import colors9 from "ansi-colors";
1802
+ import colors8 from "ansi-colors";
1836
1803
  import { isCancel as isCancel2 } from "@clack/prompts";
1837
1804
  var cleanComponentByStepTemplate = async (featureName) => {
1838
1805
  const base = path5.join("src", "features", featureName);
1839
1806
  let created = 0;
1807
+ let controller = { controllerName: "", methods: [] };
1840
1808
  const ask = async (label) => {
1841
1809
  const answer = await UPrompt.confirm(label, false);
1842
1810
  if (isCancel2(answer)) {
@@ -1845,48 +1813,74 @@ var cleanComponentByStepTemplate = async (featureName) => {
1845
1813
  }
1846
1814
  return answer;
1847
1815
  };
1848
- const write = (dir, filename) => {
1849
- UFsModule.createDirectoryRecursively(dir);
1850
- const filePath = path5.join(dir, filename);
1851
- UFsModule.createFile(filePath, "");
1852
- console.log(colors9.green(`\u2705 Created: ${filePath}`));
1816
+ const track = () => {
1853
1817
  created++;
1854
1818
  };
1855
- console.log(colors9.bold(colors9.cyan("\n\u2500\u2500 Domain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1856
- if (await ask(`Entity \u2192 ${featureName}.entity.ts`))
1857
- write(path5.join(base, "domain", "entities"), `${featureName}.entity.ts`);
1858
- if (await ask(`Event \u2192 ${featureName}.event.ts`))
1859
- write(path5.join(base, "domain", "events"), `${featureName}.event.ts`);
1860
- if (await ask(`Exception \u2192 ${featureName}.exception.ts`))
1861
- write(path5.join(base, "domain", "exceptions"), `${featureName}.exception.ts`);
1862
- console.log(colors9.bold(colors9.cyan("\n\u2500\u2500 Application \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1863
- if (await ask(`Repo Interface \u2192 ${featureName}.repository.interface.ts`))
1864
- write(path5.join(base, "application", "ports", "repositories"), `${featureName}.repository.interface.ts`);
1865
- if (await ask(`Presenter Port \u2192 ${featureName}.presenter.interface.ts`))
1866
- write(path5.join(base, "application", "ports", "presenters"), `${featureName}.presenter.interface.ts`);
1867
- if (await ask(`Service Port \u2192 ${featureName}.service.ts`))
1868
- write(path5.join(base, "application", "ports", "services"), `${featureName}.service.ts`);
1869
- if (await ask(`DTO \u2192 ${featureName}.dto.ts`))
1870
- write(path5.join(base, "application", "dtos"), `${featureName}.dto.ts`);
1871
- if (await ask(`Use Case \u2192 ${featureName}.usecase.ts`))
1872
- write(path5.join(base, "application", "use-cases"), `${featureName}.usecase.ts`);
1873
- console.log(colors9.bold(colors9.cyan("\n\u2500\u2500 Infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1874
- if (await ask(`Repo Impl \u2192 ${featureName}.repository.ts`))
1875
- write(path5.join(base, "infrastructure", "adapters", "repositories"), `${featureName}.repository.ts`);
1876
- if (await ask(`Presenter Impl \u2192 ${featureName}.presenter.ts`))
1877
- write(path5.join(base, "infrastructure", "adapters", "presenters"), `${featureName}.presenter.ts`);
1878
- if (await ask(`Controller \u2192 ${featureName}.controller.ts`))
1879
- write(path5.join(base, "infrastructure", "adapters", "controllers"), `${featureName}.controller.ts`);
1880
- if (await ask(`Router Handler \u2192 ${featureName}.router.handler.ts`))
1881
- write(path5.join(base, "infrastructure", "routes"), `${featureName}.router.handler.ts`);
1882
- if (await ask(`Router \u2192 ${featureName}.router.ts`))
1883
- write(path5.join(base, "infrastructure", "routes"), `${featureName}.router.ts`);
1819
+ console.log(colors8.bold(colors8.cyan("\n\u2500\u2500 Domain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1820
+ const mkDir = (dir) => {
1821
+ UFsModule.createDirectoryRecursively(dir);
1822
+ return dir;
1823
+ };
1824
+ if (await ask(`Entity - ${featureName}.entity.ts`)) {
1825
+ await FeatureComponentGeneratorUtils.createEntity(featureName, mkDir(path5.join(base, "domain", "entities")));
1826
+ track();
1827
+ }
1828
+ if (await ask(`Event - ${featureName}.event.ts`)) {
1829
+ await FeatureComponentGeneratorUtils.createEvent(featureName, mkDir(path5.join(base, "domain", "events")));
1830
+ track();
1831
+ }
1832
+ if (await ask(`Exception - ${featureName}.exception.ts`)) {
1833
+ await FeatureComponentGeneratorUtils.createException(featureName, mkDir(path5.join(base, "domain", "exceptions")));
1834
+ track();
1835
+ }
1836
+ console.log(colors8.bold(colors8.cyan("\n\u2500\u2500 Application \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1837
+ if (await ask(`Repo Interface - ${featureName}.repository.interface.ts`)) {
1838
+ await FeatureComponentGeneratorUtils.createRepositoryInterface(featureName, mkDir(path5.join(base, "application", "ports", "repositories")));
1839
+ track();
1840
+ }
1841
+ if (await ask(`Presenter Port - ${featureName}.presenter.interface.ts`)) {
1842
+ await FeatureComponentGeneratorUtils.createPresenterInterface(featureName, mkDir(path5.join(base, "application", "ports", "presenters")));
1843
+ track();
1844
+ }
1845
+ if (await ask(`Service Port - ${featureName}.service.ts`)) {
1846
+ await FeatureComponentGeneratorUtils.createService(featureName, mkDir(path5.join(base, "application", "ports", "services")));
1847
+ track();
1848
+ }
1849
+ if (await ask(`DTO - ${featureName}.dto.ts`)) {
1850
+ await FeatureComponentGeneratorUtils.createDto(featureName, mkDir(path5.join(base, "application", "dtos")));
1851
+ track();
1852
+ }
1853
+ if (await ask(`Use Case - ${featureName}.usecase.ts`)) {
1854
+ await FeatureComponentGeneratorUtils.createUseCase(featureName, mkDir(path5.join(base, "application", "use-cases")));
1855
+ track();
1856
+ }
1857
+ console.log(colors8.bold(colors8.cyan("\n\u2500\u2500 Infrastructure \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")));
1858
+ if (await ask(`Repo Impl - ${featureName}.repository.ts`)) {
1859
+ await FeatureComponentGeneratorUtils.createRepositoryImpl(featureName, mkDir(path5.join(base, "infrastructure", "adapters", "repositories")));
1860
+ track();
1861
+ }
1862
+ if (await ask(`Presenter Impl - ${featureName}.presenter.ts`)) {
1863
+ await FeatureComponentGeneratorUtils.createPresenterImpl(featureName, mkDir(path5.join(base, "infrastructure", "adapters", "presenters")));
1864
+ track();
1865
+ }
1866
+ if (await ask(`Controller - ${featureName}.controller.ts`)) {
1867
+ controller = await FeatureComponentGeneratorUtils.createController(featureName, mkDir(path5.join(base, "infrastructure", "adapters", "controllers")));
1868
+ track();
1869
+ }
1870
+ if (await ask(`Router Handler - ${featureName}.router.handler.ts`)) {
1871
+ await FeatureComponentGeneratorUtils.createRouterHandler(featureName, mkDir(path5.join(base, "infrastructure", "routes")), controller.methods);
1872
+ track();
1873
+ }
1874
+ if (await ask(`Router - ${featureName}.router.ts`)) {
1875
+ await FeatureComponentGeneratorUtils.createRouter(featureName, mkDir(path5.join(base, "infrastructure", "routes")));
1876
+ track();
1877
+ }
1884
1878
  if (created === 0) {
1885
- console.log(colors9.yellow(`
1879
+ console.log(colors8.yellow(`
1886
1880
  \u26A0\uFE0F No files were created for feature "${featureName}".`));
1887
1881
  } else {
1888
1882
  console.log(
1889
- colors9.bgGreen(colors9.white(`
1883
+ colors8.bgGreen(colors8.white(`
1890
1884
  \u{1F389} Feature "${featureName}" \u2014 ${created} file(s) created step by step.
1891
1885
  `))
1892
1886
  );
@@ -1921,30 +1915,412 @@ var createComponentApplication = async (featureDirectory, featureName) => {
1921
1915
  }
1922
1916
  };
1923
1917
 
1918
+ // src/templates/customModule/customModuleScaffold.template.ts
1919
+ import path6 from "path";
1920
+ import fs4 from "fs";
1921
+ import colors9 from "ansi-colors";
1922
+ import { isCancel as isCancel4 } from "@clack/prompts";
1923
+
1924
+ // src/core/domains/constants/resolveRoute.constant.ts
1925
+ var CResolveRoute = (methodName, moduleName) => {
1926
+ const lower = methodName.toLowerCase();
1927
+ if (lower.includes("findall") || lower.includes("getall")) return { httpMethod: "get", path: `/${moduleName}` };
1928
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return { httpMethod: "get", path: `/${moduleName}/:id` };
1929
+ if (lower.includes("create") || lower.includes("add")) return { httpMethod: "post", path: `/${moduleName}` };
1930
+ if (lower.includes("update") || lower.includes("edit")) return { httpMethod: "put", path: `/${moduleName}/:id` };
1931
+ if (lower.includes("delete") || lower.includes("remove")) return { httpMethod: "delete", path: `/${moduleName}/:id` };
1932
+ return { httpMethod: "get", path: `/${moduleName}/${lower}` };
1933
+ };
1934
+
1935
+ // src/templates/customModule/customModule.template.ts
1936
+ var capitalize17 = (s) => s.charAt(0).toUpperCase() + s.slice(1);
1937
+ var generateCustomControllerTemplate = (controllerName, moduleName, methods) => {
1938
+ const cap = capitalize17(moduleName);
1939
+ const serviceName = `${cap}Service`;
1940
+ const body = methods.length === 0 ? ` // TODO: add your handler methods here` : methods.map((m) => generateMethod3(m, cap, serviceName)).join("\n");
1941
+ const serviceImport = methods.length === 0 ? `` : `import { ${serviceName} } from "./${moduleName}.service";
1942
+ `;
1943
+ return `import { Request, Response } from "opticore-express";
1944
+ import { ResponseHandler, HttpStatusCode, IResponseHandlerSuccessData } from "opticore-http-response";
1945
+ ${serviceImport}
1946
+ export class ${controllerName} {${methods.length > 0 ? `
1947
+ private static buildService(): ${serviceName} {
1948
+ return new ${serviceName}();
1949
+ }
1950
+ ` : ""}
1951
+ ${body}
1952
+ private static handleError(error: unknown) {
1953
+ const message = error instanceof Error ? error.message : "Internal server error";
1954
+ return ResponseHandler.error(message, HttpStatusCode.INTERNAL_SERVER_ERROR);
1955
+ }
1956
+ }`.trimEnd();
1957
+ };
1958
+ var generateMethod3 = (method, cap, serviceName) => {
1959
+ const lower = method.toLowerCase();
1960
+ if (lower.includes("findall") || lower.includes("getall")) return methodFindAll(method, cap);
1961
+ if (lower.includes("findbyid") || lower.includes("getbyid") || lower.includes("getone")) return methodFindById(method, cap);
1962
+ if (lower.includes("create") || lower.includes("add")) return methodCreate(method, cap);
1963
+ if (lower.includes("update") || lower.includes("edit")) return methodUpdate(method, cap);
1964
+ if (lower.includes("delete") || lower.includes("remove")) return methodDelete(method, cap);
1965
+ return `
1966
+ static async ${method}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1967
+ try {
1968
+ const service = ${cap}Controller.buildService();
1969
+ // TODO: implement ${method}
1970
+ return ResponseHandler.success({}, "success", HttpStatusCode.OK);
1971
+ } catch (error) {
1972
+ return ${cap}Controller.handleError(error);
1973
+ }
1974
+ }`;
1975
+ };
1976
+ var methodFindAll = (m, cap) => `
1977
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1978
+ try {
1979
+ const service = ${cap}Controller.buildService();
1980
+ const results = await service.findAll();
1981
+ return ResponseHandler.success(results, "success", HttpStatusCode.OK);
1982
+ } catch (error) {
1983
+ return ${cap}Controller.handleError(error);
1984
+ }
1985
+ }`;
1986
+ var methodFindById = (m, cap) => `
1987
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
1988
+ try {
1989
+ const { id } = req.params;
1990
+ const service = ${cap}Controller.buildService();
1991
+ const result = await service.findById(id);
1992
+ if (!result) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
1993
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
1994
+ } catch (error) {
1995
+ return ${cap}Controller.handleError(error);
1996
+ }
1997
+ }`;
1998
+ var methodCreate = (m, cap) => `
1999
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2000
+ try {
2001
+ const service = ${cap}Controller.buildService();
2002
+ const result = await service.create(req.body);
2003
+ return ResponseHandler.success(result, "created", HttpStatusCode.CREATED);
2004
+ } catch (error) {
2005
+ return ${cap}Controller.handleError(error);
2006
+ }
2007
+ }`;
2008
+ var methodUpdate = (m, cap) => `
2009
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2010
+ try {
2011
+ const { id } = req.params;
2012
+ const service = ${cap}Controller.buildService();
2013
+ const result = await service.update(id, req.body);
2014
+ if (!result) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2015
+ return ResponseHandler.success(result, "success", HttpStatusCode.OK);
2016
+ } catch (error) {
2017
+ return ${cap}Controller.handleError(error);
2018
+ }
2019
+ }`;
2020
+ var methodDelete = (m, cap) => `
2021
+ static async ${m}(req: Request, res: Response): Promise<IResponseHandlerSuccessData | ReturnType<typeof ResponseHandler.error>> {
2022
+ try {
2023
+ const { id } = req.params;
2024
+ const service = ${cap}Controller.buildService();
2025
+ const deleted = await service.delete(id);
2026
+ if (!deleted) return ResponseHandler.error(\`Not found: \${id}\`, HttpStatusCode.NOT_FOUND);
2027
+ return ResponseHandler.success(null, "deleted", HttpStatusCode.NO_CONTENT);
2028
+ } catch (error) {
2029
+ return ${cap}Controller.handleError(error);
2030
+ }
2031
+ }`;
2032
+ var generateCustomServiceTemplate = (moduleName) => {
2033
+ const cap = capitalize17(moduleName);
2034
+ return `export class ${cap}Service {
2035
+ async findAll(): Promise<unknown[]> {
2036
+ // TODO: implement
2037
+ return [];
2038
+ }
2039
+ async findById(id: string): Promise<unknown | null> {
2040
+ // TODO: implement
2041
+ return null;
2042
+ }
2043
+ async create(data: Record<string, unknown>): Promise<unknown> {
2044
+ // TODO: implement
2045
+ return data;
2046
+ }
2047
+ async update(id: string, data: Record<string, unknown>): Promise<unknown | null> {
2048
+ // TODO: implement
2049
+ return null;
2050
+ }
2051
+ async delete(id: string): Promise<boolean> {
2052
+ // TODO: implement
2053
+ return false;
2054
+ }
2055
+ }`.trimEnd();
2056
+ };
2057
+ var generateCustomRepositoryTemplate = (moduleName) => {
2058
+ const cap = capitalize17(moduleName);
2059
+ const name = moduleName.toLowerCase();
2060
+ const isAuthRelated = name.includes("auth") || name.includes("authenticate") || name.includes("authentication") || name.includes("login") || name.includes("security") || name.includes("session") || name.includes("connect") || name.includes("connexion") || name.includes("connection");
2061
+ if (isAuthRelated) {
2062
+ return `export class ${cap}Repository {
2063
+ // TODO: inject your data source (ORM, DB client, etc.)
2064
+
2065
+ async findByEmail(email: string): Promise<User | null> {
2066
+ throw new Error("Not implemented");
2067
+ }
2068
+
2069
+ async findByUsernameOrEmail(identity: string): Promise<User | null> {
2070
+ throw new Error("Not implemented");
2071
+ }
2072
+
2073
+ async updateLastLogin(userId: string, date: Date): Promise<void> {
2074
+ throw new Error("Not implemented");
2075
+ }
2076
+
2077
+ async incrementLoginAttempts(userId: string): Promise<number> {
2078
+ throw new Error("Not implemented");
2079
+ }
2080
+
2081
+ async resetLoginAttempts(userId: string): Promise<void> {
2082
+ throw new Error("Not implemented");
2083
+ }
2084
+
2085
+ async saveToken(userId: string, token: string): Promise<void> {
2086
+ throw new Error("Not implemented");
2087
+ }
2088
+ }`.trimEnd();
2089
+ }
2090
+ return `export class ${cap}Repository {
2091
+ // TODO: inject your data source (ORM, DB client, etc.)
2092
+ async findAll(): Promise<unknown[]> { throw new Error("Not implemented"); }
2093
+ async findById(id: string): Promise<unknown | null> { throw new Error("Not implemented"); }
2094
+ async create(data: Record<string, unknown>): Promise<unknown> { throw new Error("Not implemented"); }
2095
+ async update(id: string, data: Record<string, unknown>): Promise<unknown | null> { throw new Error("Not implemented"); }
2096
+ async delete(id: string): Promise<boolean> { throw new Error("Not implemented"); }
2097
+ }`.trimEnd();
2098
+ };
2099
+ var generateCustomModelTemplate = (moduleName) => {
2100
+ const cap = capitalize17(moduleName);
2101
+ return `export class ${cap}Model {
2102
+ constructor(
2103
+ public readonly id: string,
2104
+ // TODO: add your fields here
2105
+ ) {}
2106
+ }`.trimEnd();
2107
+ };
2108
+ var generateCustomTypesTemplate = (moduleName) => `// ${moduleName} types
2109
+ export type ${capitalize17(moduleName)}Id = string;
2110
+ // TODO: add your domain types here`.trimEnd();
2111
+ var generateCustomMiddlewareTemplate = (moduleName) => {
2112
+ const cap = capitalize17(moduleName);
2113
+ return `import { Request, Response, NextFunction } from "opticore-express";
2114
+
2115
+ export function ${moduleName}Middleware(req: Request, res: Response, next: NextFunction): void {
2116
+ // TODO: implement ${cap} middleware logic
2117
+ next();
2118
+ }`.trimEnd();
2119
+ };
2120
+ var generateCustomUtilsTemplate = (moduleName) => `// ${moduleName} utilities
2121
+ // TODO: add helper functions here`.trimEnd();
2122
+ var generateCustomErrorsTemplate = (moduleName) => {
2123
+ const cap = capitalize17(moduleName);
2124
+ return `export class ${cap}NotFoundError extends Error {
2125
+ constructor(id: string) {
2126
+ super(\`${cap} not found: \${id}\`);
2127
+ this.name = "${cap}NotFoundError";
2128
+ }
2129
+ }
2130
+ export class ${cap}ValidationError extends Error {
2131
+ constructor(message: string) {
2132
+ super(message);
2133
+ this.name = "${cap}ValidationError";
2134
+ }
2135
+ }`.trimEnd();
2136
+ };
2137
+ var generateCustomConstantsTemplate = (moduleName) => `// ${moduleName} constants
2138
+ export const ${moduleName.toUpperCase()}_PREFIX = "/${moduleName}";
2139
+ // TODO: add your constants here`.trimEnd();
2140
+ var generateCustomRouterHandlerTemplate = (moduleName, controllerMethods) => {
2141
+ const cap = capitalize17(moduleName);
2142
+ const handlerName = `${cap}HandlerRouter`;
2143
+ const controllerName = `${cap}Controller`;
2144
+ const routesCode = (controllerMethods || []).map((method) => {
2145
+ const { httpMethod, path: path8 } = CResolveRoute(method, moduleName);
2146
+ return `{
2147
+ path: \`${path8}\`,
2148
+ method: "${httpMethod}",
2149
+ middlewares: [],
2150
+ handler: async (ctx: ICustomContext) => await ${controllerName}.${method}(ctx.req, ctx.res)
2151
+ }`;
2152
+ }).join(",\n");
2153
+ return `import { OpticoreRouting, ICustomContext, IMultipleRouteDefinition } from "opticore-router";
2154
+ import { ${controllerName} } from "../../app/${moduleName}.controller";
2155
+
2156
+ export const ${handlerName}: () => IMultipleRouteDefinition = () => {
2157
+ return OpticoreRouting.routes(
2158
+ ${controllerName},
2159
+ [
2160
+ ${routesCode ? routesCode.replace(/\n/g, "\n ") : ""}
2161
+ ]
2162
+ );
2163
+ };`.trimEnd();
2164
+ };
2165
+ var generateCustomRouterTemplate = (moduleName) => {
2166
+ const cap = capitalize17(moduleName);
2167
+ const handlerName = `${cap}HandlerRouter`;
2168
+ const routerName = `${cap}Router`;
2169
+ return `import { ${handlerName} } from "./core/handler/${moduleName}.router.handler";
2170
+ import { TFeatureRoutes } from "opticore-router";
2171
+
2172
+ export const ${routerName}: TFeatureRoutes = {
2173
+ routes: [
2174
+ {
2175
+ path: ${handlerName}().path,
2176
+ handler: ${handlerName}().handler
2177
+ }
2178
+ ]
2179
+ };`.trimEnd();
2180
+ };
2181
+
2182
+ // src/templates/customModule/customModuleScaffold.template.ts
2183
+ var ok = (msg) => {
2184
+ console.log(colors9.green(`\u2705 ${msg}`));
2185
+ };
2186
+ var tip = (msg) => {
2187
+ console.log(colors9.yellow(` ${msg}`));
2188
+ };
2189
+ var capitalize18 = (s) => s.charAt(0).toUpperCase() + s.slice(1);
2190
+ async function askFile(label) {
2191
+ const answer = await UPrompt.confirm(`Create ${label}?`, false);
2192
+ return answer === true;
2193
+ }
2194
+ async function askMethods() {
2195
+ const wantsMethods = await UPrompt.confirm("Do you want to add methods to the controller?");
2196
+ if (!wantsMethods) return [];
2197
+ const raw = await UPrompt.text(
2198
+ "Enter the method names (comma separated):",
2199
+ "create, findAll, findById, update, delete",
2200
+ (v) => v ? void 0 : "Enter at least one method name."
2201
+ );
2202
+ if (isCancel4(raw)) return [];
2203
+ return raw.split(",").map((m) => m.trim()).filter(Boolean);
2204
+ }
2205
+ var customModuleScaffoldTemplate = async () => {
2206
+ const rawName = await _featureName();
2207
+ if (isCancel4(rawName)) {
2208
+ console.log(colors9.bgRed(colors9.white("Operation cancelled.")));
2209
+ process.exit(0);
2210
+ }
2211
+ const moduleName = rawName.toString();
2212
+ const moduleDir = path6.join(process.cwd(), "src", "features", moduleName);
2213
+ if (fs4.existsSync(moduleDir)) {
2214
+ console.log(colors9.bgCyan(colors9.white(`Module "${moduleName}" already exists.`)));
2215
+ process.exit(0);
2216
+ }
2217
+ const appDir = path6.join(moduleDir, "app");
2218
+ const coreMiddlewaresDir = path6.join(moduleDir, "core", "middlewares");
2219
+ const coreHandlerDir = path6.join(moduleDir, "core", "handler");
2220
+ const sharedUtilsDir = path6.join(moduleDir, "shared", "utils");
2221
+ const sharedErrorsDir = path6.join(moduleDir, "shared", "errors");
2222
+ const sharedConstantsDir = path6.join(moduleDir, "shared", "constants");
2223
+ UFsModule.createDirectoryRecursively(appDir);
2224
+ UFsModule.createDirectoryRecursively(coreHandlerDir);
2225
+ UFsModule.createDirectoryRecursively(sharedUtilsDir);
2226
+ UFsModule.createDirectoryRecursively(sharedErrorsDir);
2227
+ UFsModule.createDirectoryRecursively(sharedConstantsDir);
2228
+ let controllerMethods = [];
2229
+ if (await askFile(`${moduleName}.controller.ts`)) {
2230
+ controllerMethods = await askMethods();
2231
+ UFsModule.createFile(
2232
+ path6.join(appDir, `${moduleName}.controller.ts`),
2233
+ generateCustomControllerTemplate(`${capitalize18(moduleName)}Controller`, moduleName, controllerMethods)
2234
+ );
2235
+ ok(`app/${moduleName}.controller.ts`);
2236
+ }
2237
+ if (await askFile(`${moduleName}.service.ts`)) {
2238
+ UFsModule.createFile(path6.join(appDir, `${moduleName}.service.ts`), generateCustomServiceTemplate(moduleName));
2239
+ ok(`app/${moduleName}.service.ts`);
2240
+ }
2241
+ if (await askFile(`${moduleName}.repository.ts`)) {
2242
+ UFsModule.createFile(path6.join(appDir, `${moduleName}.repository.ts`), generateCustomRepositoryTemplate(moduleName));
2243
+ ok(`app/${moduleName}.repository.ts`);
2244
+ }
2245
+ if (await askFile(`${moduleName}.model.ts`)) {
2246
+ UFsModule.createFile(path6.join(appDir, `${moduleName}.model.ts`), generateCustomModelTemplate(moduleName));
2247
+ ok(`app/${moduleName}.model.ts`);
2248
+ }
2249
+ if (await askFile(`${moduleName}.types.ts`)) {
2250
+ UFsModule.createFile(path6.join(appDir, `${moduleName}.types.ts`), generateCustomTypesTemplate(moduleName));
2251
+ ok(`app/${moduleName}.types.ts`);
2252
+ }
2253
+ const wantsMiddleware = await UPrompt.confirm("Does your module need a middleware?", false);
2254
+ if (wantsMiddleware === true) {
2255
+ UFsModule.createDirectoryRecursively(coreMiddlewaresDir);
2256
+ UFsModule.createFile(
2257
+ path6.join(coreMiddlewaresDir, `${moduleName}.middleware.ts`),
2258
+ generateCustomMiddlewareTemplate(moduleName)
2259
+ );
2260
+ ok(`core/middlewares/${moduleName}.middleware.ts`);
2261
+ }
2262
+ UFsModule.createFile(
2263
+ path6.join(coreHandlerDir, `${moduleName}.router.handler.ts`),
2264
+ generateCustomRouterHandlerTemplate(moduleName, controllerMethods)
2265
+ );
2266
+ ok(`core/handler/${moduleName}.router.handler.ts`);
2267
+ UFsModule.createFile(path6.join(sharedUtilsDir, `${moduleName}.utils.ts`), generateCustomUtilsTemplate(moduleName));
2268
+ UFsModule.createFile(path6.join(sharedErrorsDir, `${moduleName}.errors.ts`), generateCustomErrorsTemplate(moduleName));
2269
+ UFsModule.createFile(path6.join(sharedConstantsDir, `${moduleName}.constants.ts`), generateCustomConstantsTemplate(moduleName));
2270
+ ok(`shared/utils/${moduleName}.utils.ts`);
2271
+ ok(`shared/errors/${moduleName}.errors.ts`);
2272
+ ok(`shared/constants/${moduleName}.constants.ts`);
2273
+ UFsModule.createFile(path6.join(moduleDir, `${moduleName}.router.ts`), generateCustomRouterTemplate(moduleName));
2274
+ ok(`${moduleName}.router.ts`);
2275
+ FeatureComponentGeneratorUtils.setStructure("custom");
2276
+ await FeatureComponentGeneratorUtils.registerModuleRouter(moduleName);
2277
+ tip(`src/features/${moduleName}/`);
2278
+ tip(`\u251C\u2500\u2500 app/`);
2279
+ tip(`\u251C\u2500\u2500 core/`);
2280
+ tip(`\u2502 \u251C\u2500\u2500 middlewares/`);
2281
+ tip(`\u2502 \u2514\u2500\u2500 handler/`);
2282
+ tip(`\u251C\u2500\u2500 shared/`);
2283
+ tip(`\u2502 \u251C\u2500\u2500 utils/`);
2284
+ tip(`\u2502 \u251C\u2500\u2500 errors/`);
2285
+ tip(`\u2502 \u2514\u2500\u2500 constants/`);
2286
+ tip(`\u2514\u2500\u2500 ${moduleName}.router.ts`);
2287
+ console.log(`
2288
+ ${colors9.bgGreen(colors9.white(`
2289
+ \u{1F389} Custom module "${moduleName}" scaffolded successfully!
2290
+ `))}`);
2291
+ };
2292
+
1924
2293
  // src/core/core.ts
1925
2294
  (async () => {
1926
2295
  UWelcomeMessage();
1927
2296
  const featureChoice = await choiceFeatureCleanModule();
1928
- featureChoice !== "opticore_clean_module" || featureChoice === void 0 ? customFeatureInfo() : UCustomFeatureInfo(featureChoice);
2297
+ if (featureChoice === "custom_feature") {
2298
+ await customModuleScaffoldTemplate();
2299
+ return;
2300
+ }
2301
+ if (!featureChoice || featureChoice !== "opticore_clean_module") {
2302
+ process3.exit(0);
2303
+ }
2304
+ UCustomFeatureInfo(featureChoice);
1929
2305
  const featureName = await _featureName();
1930
- const directory = path6.join(process4.cwd(), "src", "features");
1931
- const featureDirectory = path6.join(directory, featureName.toString());
2306
+ const directory = path7.join(process3.cwd(), "src", "features");
2307
+ const featureDirectory = path7.join(directory, featureName.toString());
1932
2308
  const featureCleanModule = BaseService._isSubdirectoryExists(featureName);
1933
2309
  if (featureCleanModule) {
1934
2310
  BaseService._featureFounded(featureName);
1935
2311
  }
1936
- if (isCancel4(featureName)) {
2312
+ if (isCancel5(featureName)) {
1937
2313
  BaseService._cancelOperation(featureDirectory);
1938
2314
  } else {
1939
2315
  try {
1940
- if (fs4.existsSync(directory)) {
2316
+ if (fs5.existsSync(directory)) {
1941
2317
  await createComponentApplication(featureDirectory, featureName);
1942
2318
  } else {
1943
2319
  BaseService._checkFeatureDir();
1944
2320
  }
1945
2321
  } catch (err) {
1946
2322
  console.error(err.message);
1947
- process4.exit();
2323
+ process3.exit();
1948
2324
  }
1949
2325
  }
1950
2326
  })();