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,78 @@
1
+ // src/utils/generators/domain/entityUpdater.js
2
+ const { updateFile, capitalize } = require("../../userInput");
3
+ const fs = require("fs");
4
+
5
+ async function updateExistingEntityRelation(
6
+ targetName,
7
+ newEntityName,
8
+ relationType,
9
+ mode,
10
+ ) {
11
+ const targetLow = targetName.toLowerCase();
12
+ const newCap = capitalize(newEntityName);
13
+ const newLow = newEntityName.toLowerCase();
14
+
15
+ const entityPath =
16
+ mode === "full"
17
+ ? `src/${targetLow}/domain/entities/${targetLow}.entity.ts`
18
+ : `src/${targetLow}/entities/${targetLow}.entity.ts`;
19
+
20
+ if (!fs.existsSync(entityPath)) return;
21
+
22
+ // Déterminer le champ à ajouter selon la relation
23
+ let fieldToAdd = "";
24
+ let typeToAdd = "";
25
+
26
+ switch (relationType) {
27
+ case "n-1": // L'inverse d'un n-1 est un 1-n (donc une liste)
28
+ /* fieldToAdd = `${newLow}s`;
29
+ typeToAdd = `${newCap}Entity[]`; */
30
+ break;
31
+ case "1-n": // L'inverse d'un 1-n est un n-1 (donc un ID ou l'objet)
32
+ fieldToAdd = `${newLow}Id`;
33
+ typeToAdd = `string`;
34
+ break;
35
+ case "1-1":
36
+ fieldToAdd = `${newLow}Id`;
37
+ typeToAdd = `string`;
38
+ break;
39
+ case "n-n":
40
+ /* fieldToAdd = `${newLow}s`;
41
+ typeToAdd = `${newCap}Entity[]`; */
42
+ break;
43
+ }
44
+
45
+ // 1. Ajouter l'import de la nouvelle entité au début du fichier (si c'est un type complexe)
46
+ if (typeToAdd.includes("Entity")) {
47
+ const importLine = `import { ${newCap}Entity } from 'src/${newLow}/domain/entities/${newLow}.entity';\n`;
48
+ await updateFile({
49
+ path: entityPath,
50
+ pattern: /^/, // Début du fichier
51
+ replacement: importLine,
52
+ });
53
+ }
54
+
55
+ // 2. Injecter dans le constructeur (avant la parenthèse fermante)
56
+ await updateFile({
57
+ path: entityPath,
58
+ pattern: /(constructor\([\s\S]*?)\n\s+\)/m,
59
+ replacement: `$1\n private readonly ${fieldToAdd}?: ${typeToAdd}, \n )`,
60
+ });
61
+
62
+ // 3. Injecter le Getter (avant la méthode toJSON)
63
+ const getterMethod = `\n get${capitalize(fieldToAdd)}(): ${typeToAdd} | undefined {\n return this.${fieldToAdd};\n }\n`;
64
+ await updateFile({
65
+ path: entityPath,
66
+ pattern: /toJSON\(\)/,
67
+ replacement: `${getterMethod}\n toJSON()`,
68
+ });
69
+
70
+ // 4. Injecter dans toJSON
71
+ await updateFile({
72
+ path: entityPath,
73
+ pattern: /(toJSON\(\) \{[\s\S]*?return \{[\s\S]*?)\n\s+\};/m,
74
+ replacement: `$1\n ${fieldToAdd}: this.${fieldToAdd},}`,
75
+ });
76
+ }
77
+
78
+ module.exports = { updateExistingEntityRelation };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * utils/generators/infrastructure/mapperGenerator.js
3
+ *
4
+ * Générateur du Mapper infrastructure (Full/Clean Architecture uniquement).
5
+ * Extrait de utils.js — Phase 1B.
6
+ *
7
+ * Le mapper n'est JAMAIS utilisé en mode light :
8
+ * - light : le repository utilise une méthode `private toEntity()` inline
9
+ * - full : le mapper découple persistence <-> domaine <-> DTO
10
+ */
11
+
12
+ const { capitalize, decapitalize } = require("../../helpers");
13
+
14
+ /**
15
+ * Génère le contenu TypeScript du Mapper pour une entité.
16
+ *
17
+ * @param {object} entity - L'objet entité { name, fields[] }
18
+ * @returns {Promise<string>} Le code TypeScript du mapper
19
+ */
20
+ async function generateMapper(entity) {
21
+ const entityName = capitalize(entity.name);
22
+
23
+ // 1. Types scalaires / primitifs autorisés dans le mapper
24
+ const SCALAR_TYPES = [
25
+ "string",
26
+ "text",
27
+ "uuid",
28
+ "json",
29
+ "number",
30
+ "int",
31
+ "float",
32
+ "decimal",
33
+ "boolean",
34
+ "date",
35
+ "role",
36
+ ];
37
+
38
+ // 2. Filtrage cohérent : scalaires + IDs techniques
39
+ const filterDomainFields = (f) => {
40
+ const typeName = f.type.toLowerCase().replace("[]", "");
41
+ return (
42
+ SCALAR_TYPES.includes(typeName) || f.name.toLowerCase().endsWith("id")
43
+ );
44
+ };
45
+
46
+ // 3. Champs filtrés pour le constructeur de l'Entity
47
+ const filteredFields = entity.fields.filter(filterDomainFields);
48
+
49
+ const domainArgs = ["data.id"]
50
+ .concat(["data.createdAt", "data.updatedAt"])
51
+ .concat(filteredFields.map((f) => `data.${f.name}`))
52
+ .join(",\n ");
53
+
54
+ // 4. Champs pour la persistence
55
+ const toPersistenceFields = filteredFields
56
+ .map((f) => `${f.name}: dto.${f.name},`)
57
+ .join("\n ");
58
+
59
+ const toUpdateFields = filteredFields
60
+ .map(
61
+ (f) => `if (dto.${f.name} !== undefined) data.${f.name} = dto.${f.name};`,
62
+ )
63
+ .join("\n ");
64
+
65
+ return `/**
66
+ * ${entityName}Mapper transforms data between
67
+ * different layers (Persistence <-> Domain <-> DTO).
68
+ *
69
+ * Ensures that the internal database structure
70
+ * never leaks into the API responses.
71
+ */
72
+
73
+ import { Injectable } from '@nestjs/common';
74
+ import { ${entityName}Entity } from 'src/${decapitalize(
75
+ entity.name,
76
+ )}/domain/entities/${decapitalize(entity.name)}.entity';
77
+ import { Create${entityName}Dto, Update${entityName}Dto } from 'src/${decapitalize(
78
+ entity.name,
79
+ )}/application/dtos/${decapitalize(entity.name)}.dto';
80
+
81
+ @Injectable()
82
+ export class ${entityName}Mapper {
83
+
84
+ /**
85
+ * Transforme les données (Prisma/TypeORM) en Entité de Domaine
86
+ */
87
+ toDomain(data: any): ${entityName}Entity {
88
+ return new ${entityName}Entity(
89
+ ${domainArgs}
90
+ );
91
+ }
92
+
93
+ /**
94
+ * Transforme le DTO en objet pour la création en base de données
95
+ */
96
+ toPersistence(dto: Create${entityName}Dto): any {
97
+ return {
98
+ ${toPersistenceFields}
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Prépare l'objet de mise à jour partielle
104
+ */
105
+ toUpdatePersistence(dto: Update${entityName}Dto): any {
106
+ const data: any = {};
107
+ ${toUpdateFields}
108
+ return data;
109
+ }
110
+ }
111
+ `;
112
+ }
113
+
114
+ module.exports = { generateMapper };
@@ -0,0 +1,65 @@
1
+ const { updateFile } = require("../../userInput");
2
+
3
+ // src/utils/generators/infrastructure/mapperUpdater.js
4
+ async function patchMapperWithRelation(source, targetName, mode = "full") {
5
+ const targetLow = targetName.toLowerCase();
6
+
7
+ if (mode === "full") {
8
+ return patchCleanMapper(source, targetLow);
9
+ } else {
10
+ return patchLightRepositoryMapper(source, targetLow);
11
+ }
12
+ }
13
+
14
+ async function patchCleanMapper(source, targetName) {
15
+ const mapperPath = `src/${source.toLowerCase()}/infrastructure/mappers/${source.toLowerCase()}.mapper.ts`;
16
+ const targetLow = targetName.toLowerCase();
17
+
18
+ // 1. ToDomain : Ajouter l'argument au constructeur de l'entité
19
+ await updateFile({
20
+ path: mapperPath,
21
+ pattern:
22
+ /(toDomain\(data: any\): .* \{[\s\S]*?return new .*?\()([\s\S]*?)(\);)/m,
23
+ replacement: `$1$2 data.${targetLow}Id$3`,
24
+ });
25
+
26
+ // 2. ToPersistence : Ajouter le champ pour Prisma
27
+ await updateFile({
28
+ path: mapperPath,
29
+ pattern: /(toPersistence\(dto: .* \{[\s\S]*?return \{)/m,
30
+ replacement: `$1\n ${targetLow}Id: dto.${targetLow}Id,`,
31
+ });
32
+
33
+ // 3. PATCH toUpdatePersistence → add FK update condition
34
+ await updateFile({
35
+ path: mapperPath,
36
+ pattern: /(toUpdatePersistence\(dto: .*?\{\s*const data: any = \{\};)/m,
37
+ replacement: `$1\n\n if (dto.${targetLow}Id !== undefined) data.${targetLow}Id = dto.${targetLow}Id;`,
38
+ });
39
+ }
40
+
41
+ /**
42
+ * LIGHT MODE → patch repository.ts (toEntity + create/update)
43
+ */
44
+ async function patchLightRepositoryMapper(entityName, targetLow) {
45
+ const repoPath = `src/${entityName.toLowerCase()}/repositories/${entityName.toLowerCase()}.repository.ts`;
46
+ // const targetRepoPath = `src/${targetLow.toLowerCase()}/repositories/${targetLow.toLowerCase()}.repository.ts`;
47
+
48
+ // 1. Patch toEntity constructor
49
+ /* await updateFile({
50
+ path: repoPath,
51
+ pattern:
52
+ /(private toEntity\(raw: any\): .*?\{\s*return new .*?\([\s\S]*?)(\);\s*\})/m,
53
+ replacement: `$1,\n raw.${targetLow}Id$2`,
54
+ }); */
55
+
56
+ // 2. Patch toEntity constructor in target
57
+ await updateFile({
58
+ path: repoPath,
59
+ pattern:
60
+ /(private toEntity\(raw: any\): .*?\{\s*return new .*?\([\s\S]*?)(\);\s*\})/m,
61
+ replacement: `$1\n raw.${targetLow}Id$2`,
62
+ });
63
+ }
64
+
65
+ module.exports = { patchMapperWithRelation };