nestcraftx 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/AUDIT_ROADMAP.md +798 -0
  2. package/CHANGELOG.fr.md +22 -0
  3. package/CHANGELOG.md +22 -0
  4. package/CLI_USAGE.fr.md +331 -331
  5. package/CLI_USAGE.md +364 -364
  6. package/LICENSE +21 -21
  7. package/PROGRESS.md +258 -0
  8. package/bin/nestcraft.js +84 -64
  9. package/commands/demo.js +337 -330
  10. package/commands/generate.js +94 -0
  11. package/commands/generateConf.js +91 -0
  12. package/commands/help.js +29 -2
  13. package/commands/info.js +48 -48
  14. package/commands/new.js +340 -335
  15. package/commands/start.js +19 -19
  16. package/commands/test.js +7 -7
  17. package/package.json +1 -1
  18. package/utils/app-module.updater.js +96 -0
  19. package/utils/cliParser.js +88 -76
  20. package/utils/colors.js +62 -62
  21. package/utils/configs/configureDocker.js +120 -120
  22. package/utils/configs/setupCleanArchitecture.js +17 -15
  23. package/utils/configs/setupLightArchitecture.js +15 -9
  24. package/utils/file-system.js +75 -0
  25. package/utils/file-utils/saveProjectConfig.js +36 -0
  26. package/utils/fullModeInput.js +607 -607
  27. package/utils/generators/application/dtoGenerator.js +251 -0
  28. package/utils/generators/application/dtoUpdater.js +54 -0
  29. package/utils/generators/cleanModuleGenerator.js +475 -0
  30. package/utils/generators/database/setupDatabase.js +49 -0
  31. package/utils/generators/domain/entityGenerator.js +126 -0
  32. package/utils/generators/domain/entityUpdater.js +78 -0
  33. package/utils/generators/infrastructure/mapperGenerator.js +114 -0
  34. package/utils/generators/infrastructure/mapperUpdater.js +65 -0
  35. package/utils/generators/infrastructure/middlewareGenerator.js +645 -0
  36. package/utils/generators/infrastructure/mongooseSchemaGenerator.js +196 -0
  37. package/utils/generators/infrastructure/repositoryGenerator.js +334 -0
  38. package/utils/generators/lightModuleGenerator.js +131 -0
  39. package/utils/generators/presentation/controllerGenerator.js +142 -0
  40. package/utils/generators/relation/relation.engine.js +64 -0
  41. package/utils/helpers.js +109 -0
  42. package/utils/interactive/askEntityInputs.js +165 -0
  43. package/utils/loggers/logError.js +7 -7
  44. package/utils/loggers/logInfo.js +7 -7
  45. package/utils/loggers/logSuccess.js +7 -7
  46. package/utils/loggers/logWarning.js +7 -7
  47. package/utils/setups/orms/typeOrmSetup.js +630 -630
  48. package/utils/setups/setupAuth.js +33 -20
  49. package/utils/setups/setupDatabase.js +76 -75
  50. package/utils/setups/setupMongoose.js +22 -5
  51. package/utils/setups/setupPrisma.js +748 -630
  52. package/utils/shell.js +112 -32
  53. package/utils/spinner.js +57 -57
  54. package/utils/systemCheck.js +124 -124
  55. package/utils/userInput.js +357 -421
  56. package/utils/utils.js +29 -2164
@@ -0,0 +1,475 @@
1
+ const {
2
+ createDirectory,
3
+ createFile,
4
+ safeUpdateAppModule,
5
+ capitalize,
6
+ decapitalize,
7
+ } = require("../userInput");
8
+ const { logInfo } = require("../loggers/logInfo");
9
+ const { logSuccess } = require("../loggers/logSuccess");
10
+ const {
11
+ generateEntityFileContent,
12
+ generateDto,
13
+ generateMapper,
14
+ generateController,
15
+ generateRepository,
16
+ generateMongooseSchemaFileContent,
17
+ } = require("../utils");
18
+ const { updateExistingEntityRelation } = require("./domain/entityUpdater");
19
+ const { applyRelationPatches } = require("./relation/relation.engine");
20
+ const setupDatabase = require("./database/setupDatabase");
21
+
22
+ async function generateCleanModule(name, config, entityData) {
23
+ const entityNameCapitalized = capitalize(name);
24
+ const entityNameLower = decapitalize(name);
25
+ const entityPath = `src/${entityNameLower}`;
26
+ const mode = "full";
27
+
28
+ // Chemins de la Clean Architecture
29
+ const folders = [
30
+ "application/use-cases",
31
+ "application/dtos",
32
+ "domain/interfaces",
33
+ "domain/entities",
34
+ "infrastructure/mappers",
35
+ "infrastructure/repositories",
36
+ "application/services",
37
+ "infrastructure/adapters",
38
+ "presentation/controllers",
39
+ ];
40
+
41
+ logInfo(
42
+ ` Building Clean Architecture layers for module: ${entityNameCapitalized}`,
43
+ );
44
+
45
+ // 1. Création des dossiers
46
+ for (const folder of folders) {
47
+ await createDirectory(`${entityPath}/${folder}`);
48
+ }
49
+
50
+ // 2. Génération de l'Entité de Domaine
51
+ const entityContent = await generateEntityFileContent(entityData);
52
+ await createFile({
53
+ path: `${entityPath}/domain/entities/${entityNameLower}.entity.ts`,
54
+ contente: entityContent,
55
+ });
56
+
57
+ // 3. Génération de l'Interface Repository
58
+ await createFile({
59
+ path: `${entityPath}/domain/interfaces/${entityNameLower}.repository.interface.ts`,
60
+ contente: getRepositoryInterfaceTemplate(
61
+ entityNameCapitalized,
62
+ entityNameLower,
63
+ ),
64
+ });
65
+
66
+ // 4. Génération du Repository (Implementation)
67
+ await generateRepository(name, config.orm);
68
+
69
+ // 5. Génération des Use Cases (Create, Get, Update, Delete)
70
+ await generateUseCases(entityPath, entityNameCapitalized, entityNameLower);
71
+
72
+ // 6. Génération des DTOs
73
+ const dtoContent = await generateDto(entityData, config.swagger);
74
+ await createFile({
75
+ path: `${entityPath}/application/dtos/${entityNameLower}.dto.ts`,
76
+ contente: dtoContent,
77
+ });
78
+
79
+ // 7. Génération du Mapper
80
+ const mapperContent = await generateMapper(entityData);
81
+ await createFile({
82
+ path: `${entityPath}/infrastructure/mappers/${entityNameLower}.mapper.ts`,
83
+ contente: mapperContent,
84
+ });
85
+
86
+ // ÉTAPE RELATIONS : Patching des fichiers si une relation existe
87
+ if (
88
+ entityData.relation &&
89
+ (entityData.relation.type != "1-n" || entityData.relation.type != "n-n")
90
+ ) {
91
+ const { target, type } = entityData.relation;
92
+ logInfo(
93
+ `🔗 Linking ${entityNameCapitalized} with ${capitalize(target)} (${type})...`,
94
+ );
95
+
96
+ await updateExistingEntityRelation(target, name, type);
97
+
98
+ /* if (type === "n-1" || type === "1-1") {
99
+ await patchDtoWithRelation(name, target, config.swagger);
100
+ await patchMapperWithRelation(name, target);
101
+ } */
102
+
103
+ await applyRelationPatches(name, target, type, config.swagger, mode);
104
+ }
105
+
106
+ // 8. Génération du Service, Adapter et Controller
107
+ await generateServiceAndAdapter(
108
+ entityPath,
109
+ entityNameCapitalized,
110
+ entityNameLower,
111
+ );
112
+
113
+ const controllerContent = await generateController(
114
+ name,
115
+ entityPath,
116
+ config.swagger,
117
+ );
118
+ await createFile({
119
+ path: `${entityPath}/presentation/controllers/${entityNameLower}.controller.ts`,
120
+ contente: controllerContent,
121
+ });
122
+
123
+ // 9. Génération du Module NestJS
124
+ const moduleContent = getModuleTemplate(
125
+ entityNameCapitalized,
126
+ entityNameLower,
127
+ entityPath,
128
+ config,
129
+ );
130
+ await createFile({
131
+ path: `${entityPath}/${entityNameLower}.module.ts`,
132
+ contente: moduleContent,
133
+ });
134
+
135
+ // 10. Auto-enregistrement dans AppModule
136
+ await safeUpdateAppModule(entityNameLower);
137
+
138
+ // 11.
139
+ await setupDatabase(config, entityData);
140
+
141
+ logSuccess(
142
+ ` ✨ Module ${entityNameCapitalized} generated and registered successfully!`,
143
+ );
144
+ }
145
+
146
+ // --- HELPERS DE TEMPLATES (Extraits de ton setup original) ---
147
+
148
+ function getRepositoryInterfaceTemplate(cap, low) {
149
+ return `import { Create${cap}Dto, Update${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
150
+ import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
151
+
152
+ export const I${cap}RepositoryName = 'I${cap}Repository';
153
+
154
+ export interface I${cap}Repository {
155
+ create(data: Create${cap}Dto): Promise<${cap}Entity>;
156
+ findById(id: string): Promise<${cap}Entity | null>;
157
+ findAll(): Promise<${cap}Entity[]>;
158
+ update(id: string, data: Update${cap}Dto): Promise<${cap}Entity | null>;
159
+ delete(id: string): Promise<void>;
160
+ }`;
161
+ }
162
+
163
+ ///////////////////
164
+ async function generateUseCases(entityPath, cap, low) {
165
+ const useCases = ["Create", "GetById", "GetAll", "Update", "Delete"];
166
+
167
+ for (const useCase of useCases) {
168
+ let content = "";
169
+ const fileName = `${decapitalize(useCase)}-${low}.use-case.ts`;
170
+ const fullPath = `${entityPath}/application/use-cases/${fileName}`;
171
+
172
+ switch (useCase) {
173
+ case "Create":
174
+ content = `
175
+ import { Inject, Logger } from '@nestjs/common';
176
+ import { Create${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
177
+ import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
178
+ import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
179
+
180
+ export class Create${cap}UseCase {
181
+ private readonly logger = new Logger(Create${cap}UseCase.name);
182
+
183
+ constructor(
184
+ @Inject(I${cap}RepositoryName)
185
+ private readonly repository: I${cap}Repository,
186
+ ) {}
187
+
188
+ async execute(data: Create${cap}Dto): Promise<${cap}Entity> {
189
+ this.logger.log('Starting creation process for ${cap}');
190
+ const result = await this.repository.create(data);
191
+ this.logger.log(\`Successfully created ${cap} (ID: \${result.getId()})\`);
192
+ return result;
193
+ }
194
+ }`;
195
+ break;
196
+
197
+ case "GetById":
198
+ content = `
199
+ import { Inject, Logger, NotFoundException } from '@nestjs/common';
200
+ import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
201
+ import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
202
+
203
+ export class GetById${cap}UseCase {
204
+ private readonly logger = new Logger(GetById${cap}UseCase.name);
205
+
206
+ constructor(
207
+ @Inject(I${cap}RepositoryName)
208
+ private readonly repository: I${cap}Repository,
209
+ ) {}
210
+
211
+ async execute(id: string): Promise<${cap}Entity> {
212
+ this.logger.log(\`Fetching ${cap} (ID: \${id})\`);
213
+ const result = await this.repository.findById(id);
214
+
215
+ if (!result) {
216
+ this.logger.warn(\`${cap} \${id} not found\`);
217
+ throw new NotFoundException(\`${cap} not found\`);
218
+ }
219
+
220
+ this.logger.log(\`Successfully retrieved ${cap}\`);
221
+ return result;
222
+ }
223
+ }`;
224
+ break;
225
+
226
+ case "GetAll":
227
+ content = `
228
+ import { Inject, Logger } from '@nestjs/common';
229
+ import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
230
+ import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
231
+
232
+ export class GetAll${cap}UseCase {
233
+ private readonly logger = new Logger(GetAll${cap}UseCase.name);
234
+
235
+ constructor(
236
+ @Inject(I${cap}RepositoryName)
237
+ private readonly repository: I${cap}Repository,
238
+ ) {}
239
+
240
+ async execute(): Promise<${cap}Entity[]> {
241
+ this.logger.log('Requesting all ${cap} records');
242
+ const results = await this.repository.findAll();
243
+ this.logger.log(\`Retrieved \${results.length} ${cap}(s)\`);
244
+ return results;
245
+ }
246
+ }`;
247
+ break;
248
+
249
+ case "Update":
250
+ content = `
251
+ import { Inject, Logger, NotFoundException } from '@nestjs/common';
252
+ import { Update${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
253
+ import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
254
+ import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
255
+
256
+ export class Update${cap}UseCase {
257
+ private readonly logger = new Logger(Update${cap}UseCase.name);
258
+
259
+ constructor(
260
+ @Inject(I${cap}RepositoryName)
261
+ private readonly repository: I${cap}Repository,
262
+ ) {}
263
+
264
+ async execute(id: string, data: Update${cap}Dto): Promise<${cap}Entity> {
265
+ this.logger.log(\`Updating ${cap} (ID: \${id})\`);
266
+ const existing = await this.repository.findById(id);
267
+ if (!existing) throw new NotFoundException(\`${cap} not found\`);
268
+
269
+ const result = await this.repository.update(id, data);
270
+ if (!result) {
271
+ this.logger.error(\`Update failed for ${cap} \${id} - entity disappeared\`);
272
+ throw new NotFoundException(\`${cap} update failed\`);
273
+ }
274
+
275
+ this.logger.log(\`Successfully updated ${cap} \${id}\`);
276
+ return result;
277
+ }
278
+ }`;
279
+ break;
280
+
281
+ case "Delete":
282
+ content = `
283
+ import { Inject, Logger, NotFoundException } from '@nestjs/common';
284
+ import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
285
+
286
+ export class Delete${cap}UseCase {
287
+ private readonly logger = new Logger(Delete${cap}UseCase.name);
288
+
289
+ constructor(
290
+ @Inject(I${cap}RepositoryName)
291
+ private readonly repository: I${cap}Repository,
292
+ ) {}
293
+
294
+ async execute(id: string): Promise<void> {
295
+ this.logger.log(\`Deleting ${cap} (ID: \${id})\`);
296
+ const existing = await this.repository.findById(id);
297
+ if (!existing) throw new NotFoundException(\`${cap} not found\`);
298
+
299
+ await this.repository.delete(id);
300
+ this.logger.log(\`Successfully deleted ${cap} \${id}\`);
301
+ }
302
+ }`;
303
+ break;
304
+ }
305
+
306
+ await createFile({
307
+ path: fullPath,
308
+ contente: content.trim(),
309
+ });
310
+ }
311
+ }
312
+
313
+ //////////////////
314
+ function getModuleTemplate(cap, low, entityPath, config) {
315
+ let importsBlock = [];
316
+ let providersBlock = [];
317
+ let extraImports = "";
318
+ let forwardRefImport = "";
319
+
320
+ // Gestion de l'ORM pour les imports du Module
321
+ if (config.orm === "prisma") {
322
+ extraImports += `import { PrismaModule } from 'src/prisma/prisma.module';\n`;
323
+ importsBlock.push("PrismaModule");
324
+ } else if (config.orm === "typeorm") {
325
+ extraImports += `import { ${cap} } from 'src/entities/${cap}.entity';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n`;
326
+ importsBlock.push(`TypeOrmModule.forFeature([${cap}])`);
327
+ } else if (config.orm === "mongoose") {
328
+ extraImports += `import { MongooseModule } from '@nestjs/mongoose';\nimport { ${cap}, ${cap}Schema } from 'src/${low}/infrastructure/persistence/mongoose/${low}.schema';\n`;
329
+ importsBlock.push(
330
+ `MongooseModule.forFeature([{ name: ${cap}.name, schema: ${cap}Schema }])`,
331
+ );
332
+ }
333
+
334
+ // Support Auth si nécessaire (User module)
335
+ if (low === "user" && config.auth) {
336
+ extraImports += "import { AuthModule } from 'src/auth/auth.module';\n";
337
+ importsBlock.push("forwardRef(() => AuthModule)");
338
+ forwardRefImport = " forwardRef,";
339
+ }
340
+
341
+ // Providers standard pour la Clean Arch
342
+ providersBlock.push(
343
+ `{ provide: 'I${cap}Repository', useClass: ${cap}Repository }`,
344
+ `${cap}Service`,
345
+ `Create${cap}UseCase`,
346
+ `Update${cap}UseCase`,
347
+ `GetById${cap}UseCase`,
348
+ `GetAll${cap}UseCase`,
349
+ `Delete${cap}UseCase`,
350
+ `${cap}Mapper`,
351
+ );
352
+
353
+ return `
354
+ import {${forwardRefImport} Module } from '@nestjs/common';
355
+ ${extraImports}
356
+ import { ${cap}Service } from 'src/${low}/application/services/${low}.service';
357
+ import { ${cap}Controller } from 'src/${low}/presentation/controllers/${low}.controller';
358
+ import { ${cap}Repository } from 'src/${low}/infrastructure/repositories/${low}.repository';
359
+ import { Create${cap}UseCase } from 'src/${low}/application/use-cases/create-${low}.use-case';
360
+ import { Update${cap}UseCase } from 'src/${low}/application/use-cases/update-${low}.use-case';
361
+ import { GetById${cap}UseCase } from 'src/${low}/application/use-cases/getById-${low}.use-case';
362
+ import { GetAll${cap}UseCase } from 'src/${low}/application/use-cases/getAll-${low}.use-case';
363
+ import { Delete${cap}UseCase } from 'src/${low}/application/use-cases/delete-${low}.use-case';
364
+ import { ${cap}Mapper } from 'src/${low}/infrastructure/mappers/${low}.mapper';
365
+
366
+ @Module({
367
+ imports: [
368
+ ${importsBlock.join(",\n ")}
369
+ ],
370
+ controllers: [
371
+ ${cap}Controller
372
+ ],
373
+ providers: [
374
+ ${providersBlock.join(",\n ")}
375
+ ],
376
+ exports: [
377
+ ${cap}Service,
378
+ 'I${cap}Repository'
379
+ ]
380
+ })
381
+ export class ${cap}Module {}
382
+ `.trim();
383
+ }
384
+
385
+ ////////////////
386
+ async function generateServiceAndAdapter(
387
+ entityPath,
388
+ entityNameCapitalized,
389
+ entityNameLower,
390
+ ) {
391
+ // 8. Service
392
+ await createFile({
393
+ path: `${entityPath}/application/services/${entityNameLower}.service.ts`,
394
+ contente: `/**
395
+ * PostService handles business logic
396
+ * for the Post entity.
397
+ *
398
+ * It acts as a bridge between the Controller and the Repository.
399
+ * Responsibilities include:
400
+ * - Data validation and transformation
401
+ * - Orchestrating use cases
402
+ * - Managing transactions
403
+ */
404
+
405
+ import { Injectable } from '@nestjs/common';
406
+ import { Create${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/create-${entityNameLower}.use-case';
407
+ import { Update${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/update-${entityNameLower}.use-case';
408
+ import { GetById${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/getById-${entityNameLower}.use-case';
409
+ import { GetAll${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/getAll-${entityNameLower}.use-case';
410
+ import { Delete${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/delete-${entityNameLower}.use-case';
411
+ import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
412
+ import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
413
+
414
+ @Injectable()
415
+ export class ${entityNameCapitalized}Service {
416
+ constructor(
417
+ private readonly createUseCase: Create${entityNameCapitalized}UseCase,
418
+ private readonly updateUseCase: Update${entityNameCapitalized}UseCase,
419
+ private readonly getByIdUseCase: GetById${entityNameCapitalized}UseCase,
420
+ private readonly getAllUseCase: GetAll${entityNameCapitalized}UseCase,
421
+ private readonly deleteUseCase: Delete${entityNameCapitalized}UseCase,
422
+ ) {}
423
+
424
+ async create(dto: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
425
+ return await this.createUseCase.execute(dto);
426
+ }
427
+ async update(id: string, dto: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity | null> {
428
+ return await this.updateUseCase.execute(id, dto);
429
+ }
430
+ async getById(id: string): Promise<${entityNameCapitalized}Entity | null> {
431
+ return await this.getByIdUseCase.execute(id);
432
+ }
433
+ async getAll(): Promise<${entityNameCapitalized}Entity[]> {
434
+ return await this.getAllUseCase.execute();
435
+ }
436
+ async delete(id: string): Promise<void> {
437
+ return await this.deleteUseCase.execute(id);
438
+ }
439
+ }
440
+ `.trim(),
441
+ });
442
+
443
+ // 9. Adapter
444
+ await createFile({
445
+ path: `${entityPath}/infrastructure/adapters/${entityNameLower}.adapter.ts`,
446
+ contente: `
447
+ // L'adaptateur permet de transformer ou d'adapter les données d'un format source vers un format cible.
448
+ // Cela est particulièrement utile lorsque nous devons interagir avec des API externes ou des services ayant des structures de données différentes.
449
+
450
+ export class ${entityNameCapitalized}Adapter {
451
+ // La méthode 'adapt' prend des données brutes d'un format spécifique et les transforme
452
+ // en un format qui est attendu par le système de notre domaine.
453
+ adapt(data: any) {
454
+ // Exemple d'adaptation des données - ceci est un exemple générique.
455
+ // Nous transformons les données pour que le format interne du système soit respecté.
456
+
457
+ const adaptedData = {
458
+ // Assurez-vous que vous mappez les propriétés nécessaires et les transformez.
459
+ id: data.id, // Mapping de l'ID de la donnée source à notre format interne
460
+ name: data.fullName || data.name, // Exemple de transformation de champ
461
+ description: data.details || data.description, // Gestion des données optionnelles
462
+ createdAt: new Date(data.createdAt), // Transformation de la date
463
+ updatedAt: new Date(data.updatedAt), // Idem pour la date de mise à jour
464
+ // Vous pouvez adapter d'autres champs en fonction des exigences spécifiques
465
+ };
466
+
467
+ // Retournez les données adaptées dans un format compréhensible pour le système
468
+ return adaptedData;
469
+ }
470
+ }
471
+ `,
472
+ });
473
+ }
474
+
475
+ module.exports = generateCleanModule;
@@ -0,0 +1,49 @@
1
+ const { logInfo } = require("../../loggers/logInfo");
2
+ const { updatePrismaSchema } = require("../../setups/setupPrisma");
3
+ const { createDirectory, createFile } = require("../../userInput");
4
+ const { generateMongooseSchemaFileContent } = require("../../utils");
5
+
6
+ async function setupDatabase(config, entityData) {
7
+ logInfo("Configuring the database...");
8
+
9
+ switch (config.orm) {
10
+ case "prisma":
11
+ await updatePrismaSchema(entityData);
12
+ break;
13
+ case "mongoose":
14
+ const entityNameLower = entityData.name.toLowerCase();
15
+ const isFull = config.mode === "full";
16
+ const schemaPath = isFull
17
+ ? `src/${entityNameLower}/infrastructure/persistence/mongoose`
18
+ : `src/${entityNameLower}/entities`;
19
+ await createDirectory(schemaPath);
20
+
21
+ // Reconstruct entitiesData with the single entity and relations for generateMongooseSchemaFileContent
22
+ const entitiesData = {
23
+ relations: entityData.relation ? [
24
+ {
25
+ from: entityData.name.toLowerCase(),
26
+ to: entityData.relation.target.toLowerCase(),
27
+ type: entityData.relation.type
28
+ }
29
+ ] : []
30
+ };
31
+
32
+ const mongooseSchemaContent = await generateMongooseSchemaFileContent(
33
+ entityData,
34
+ entitiesData,
35
+ config.mode
36
+ );
37
+
38
+ await createFile({
39
+ path: `${schemaPath}/${entityNameLower}.schema.ts`,
40
+ contente: mongooseSchemaContent,
41
+ });
42
+ break;
43
+ default:
44
+ // Other database types don't require database-level updates on module generation
45
+ break;
46
+ }
47
+ }
48
+
49
+ module.exports = setupDatabase;
@@ -0,0 +1,126 @@
1
+ /**
2
+ * utils/generators/domain/entityGenerator.js
3
+ *
4
+ * Générateur de l'Entité de Domaine (Domain Entity).
5
+ * Extrait de utils.js — Phase 1B.
6
+ *
7
+ * Supporte les modes "full" (Clean Architecture) et "light" (MVP).
8
+ * La différence entre les deux modes porte uniquement sur le chemin
9
+ * d'import de l'enum Role :
10
+ * - full : src/user/domain/enums/role.enum
11
+ * - light : src/common/enums/role.enum
12
+ */
13
+
14
+ const { capitalize, getFormattedType } = require("../../helpers");
15
+
16
+ /**
17
+ * Génère le contenu TypeScript de l'entité de domaine.
18
+ *
19
+ * @param {object} entity - L'objet entité { name, fields[] }
20
+ * @param {"full"|"light"} mode - Le mode d'architecture cible
21
+ * @returns {Promise<string>} Le code TypeScript de l'entité
22
+ */
23
+ async function generateEntityFileContent(entity, mode = "full") {
24
+ if (!entity || !entity.name) {
25
+ throw new Error("Nom de l'entité manquant !");
26
+ }
27
+
28
+ const entityName = capitalize(entity.name);
29
+ const className = `${entityName}Entity`;
30
+
31
+ const defaultFields = [
32
+ {
33
+ name: "id",
34
+ type: "string",
35
+ comment:
36
+ "L'identifiant unique de l'entité.\n * Utilisé pour retrouver de manière unique un enregistrement dans la base de données.\n *\n * Exemple : '123e4567-e89b-12d3-a456-426614174000'",
37
+ },
38
+ {
39
+ name: "createdAt",
40
+ type: "Date",
41
+ comment:
42
+ "La date de création de l'entité.\n * Définie lors de la création et ne change pas.\n *\n * Exemple : new Date('2022-01-01T10:00:00Z')",
43
+ },
44
+ {
45
+ name: "updatedAt",
46
+ type: "Date",
47
+ comment:
48
+ "La date de dernière mise à jour de l'entité.\n * Mise à jour à chaque modification.\n *\n * Exemple : new Date('2022-02-01T15:00:00Z')",
49
+ },
50
+ ];
51
+
52
+ // Types de base autorisés dans une Entité de Domaine
53
+ const DOMAIN_SCALAR_TYPES = [
54
+ "string",
55
+ "number",
56
+ "boolean",
57
+ "date",
58
+ "json",
59
+ "text",
60
+ "uuid",
61
+ "decimal",
62
+ "float",
63
+ "int",
64
+ "role",
65
+ ];
66
+
67
+ // Filtrage : on ne garde que les types scalaires ou les IDs techniques
68
+ const filteredFields = entity.fields.filter((f) => {
69
+ const typeLower = f.type.toLowerCase().replace("[]", "");
70
+ return DOMAIN_SCALAR_TYPES.includes(typeLower) || f.name.endsWith("Id");
71
+ });
72
+
73
+ const isUserEntityWithRole =
74
+ entity.name.toLowerCase() === "user" &&
75
+ entity.fields.some((f) => f.name === "role");
76
+
77
+ const allFields = [...defaultFields, ...filteredFields];
78
+
79
+ const constructorParams = allFields
80
+ .map((f) => `\n private readonly ${f.name}: ${getFormattedType(f)},`)
81
+ .join("");
82
+
83
+ const getters = allFields
84
+ .map(
85
+ (f) => `
86
+ get${capitalize(f.name)}(): ${getFormattedType(f)}
87
+ {
88
+ return this.${f.name};
89
+ }`,
90
+ )
91
+ .join("\n");
92
+
93
+ const jsonFields = allFields
94
+ .map((f) => ` ${f.name}: this.${f.name},`)
95
+ .join("\n");
96
+
97
+ let importStatements = "";
98
+ if (isUserEntityWithRole) {
99
+ importStatements +=
100
+ mode == "full"
101
+ ? `import { Role } from 'src/user/domain/enums/role.enum';\n\n`
102
+ : `import { Role } from 'src/common/enums/role.enum'; \n\n`;
103
+ }
104
+
105
+ return `${importStatements}/**
106
+ * ${className} représente l'entité principale de ${entityName} dans le domaine.
107
+ * Elle contient les propriétés de base nécessaires à la gestion des données liées à ${entityName}.
108
+ */
109
+ export class ${className} {
110
+ constructor(${constructorParams}
111
+ ) {}
112
+ ${getters}
113
+
114
+ /**
115
+ * transforme entity data to json
116
+ */
117
+ toJSON() {
118
+ return {
119
+ ${jsonFields}
120
+ };
121
+ }
122
+ }
123
+ `;
124
+ }
125
+
126
+ module.exports = { generateEntityFileContent };