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.
- package/README.md +214 -173
- package/dist/{core-ED52ORJA.js → core-GNR24GXE.js} +614 -238
- package/dist/index.cjs +647 -252
- package/dist/index.js +1 -1
- package/package.json +42 -40
|
@@ -1,15 +1,34 @@
|
|
|
1
1
|
// src/core/core.ts
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { isCancel as
|
|
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
|
|
8
|
+
import cfonts from "cfonts";
|
|
9
|
+
import chalk from "chalk";
|
|
9
10
|
function UWelcomeMessage() {
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
141
|
+
import colors3 from "ansi-colors";
|
|
131
142
|
function UCustomFeatureInfo(choice) {
|
|
132
|
-
console.log(
|
|
133
|
-
`
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`)
|
|
145
|
-
|
|
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
|
|
169
|
-
import
|
|
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(
|
|
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(`${
|
|
183
|
-
|
|
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(`${
|
|
187
|
-
|
|
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(`${
|
|
191
|
-
|
|
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(`${
|
|
220
|
+
console.log(`${colors4.bgRed(`${colors4.white("Operation cancelled.")}`)}`);
|
|
195
221
|
UFsModule.removeDir(controllerPath, true, true);
|
|
196
|
-
|
|
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 ${
|
|
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
|
|
279
|
+
import colors6 from "ansi-colors";
|
|
254
280
|
|
|
255
281
|
// src/utils/featureComponentGenerator.utils.ts
|
|
256
282
|
import path2 from "path";
|
|
257
|
-
import
|
|
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
|
|
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
|
-
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
-
//
|
|
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:
|
|
880
|
+
const { httpMethod, path: path8 } = resolveRoute(method, featureName);
|
|
895
881
|
return `{
|
|
896
|
-
path: \`${
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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"))
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
if (lower.includes("
|
|
1318
|
-
|
|
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:
|
|
1400
|
+
const { httpMethod, path: path8 } = resolveRoute2(method, featureName);
|
|
1416
1401
|
return `{
|
|
1417
|
-
path: \`${
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
*
|
|
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(
|
|
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
|
-
*
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1697
|
+
console.log(colors5.green(`\u2705 register.router.ts updated with ${routerName}.`));
|
|
1725
1698
|
} catch (error) {
|
|
1726
|
-
console.error(
|
|
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
|
-
|
|
1761
|
-
|
|
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
|
|
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
|
-
|
|
1826
|
-
|
|
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
|
|
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
|
|
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(
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
if (await ask(`
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
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(
|
|
1879
|
+
console.log(colors8.yellow(`
|
|
1886
1880
|
\u26A0\uFE0F No files were created for feature "${featureName}".`));
|
|
1887
1881
|
} else {
|
|
1888
1882
|
console.log(
|
|
1889
|
-
|
|
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
|
|
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 =
|
|
1931
|
-
const featureDirectory =
|
|
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 (
|
|
2312
|
+
if (isCancel5(featureName)) {
|
|
1937
2313
|
BaseService._cancelOperation(featureDirectory);
|
|
1938
2314
|
} else {
|
|
1939
2315
|
try {
|
|
1940
|
-
if (
|
|
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
|
-
|
|
2323
|
+
process3.exit();
|
|
1948
2324
|
}
|
|
1949
2325
|
}
|
|
1950
2326
|
})();
|