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,251 @@
1
+ /**
2
+ * utils/generators/application/dtoGenerator.js
3
+ *
4
+ * Générateur des DTOs (Data Transfer Objects) Create/Update.
5
+ * Extrait de utils.js — Phase 1B.
6
+ *
7
+ * Supporte les modes "full" et "light" via le paramètre `mode` :
8
+ * - full : import Role depuis src/user/domain/enums/role.enum
9
+ * - light : import Role depuis src/common/enums/role.enum
10
+ *
11
+ * Génère deux classes : Create{Entity}Dto et Update{Entity}Dto
12
+ * Gère optionnellement les décorateurs Swagger (@ApiProperty etc.)
13
+ */
14
+
15
+ const { capitalize, formatType } = require("../../helpers");
16
+
17
+ /**
18
+ * Génère le contenu TypeScript des DTOs pour une entité.
19
+ *
20
+ * @param {object} entity - L'objet entité { name, fields[] }
21
+ * @param {boolean} useSwagger - Inclure les décorateurs Swagger
22
+ * @param {boolean} isAuthDto - Mode Auth DTO (classe unique sans CRUD)
23
+ * @param {"full"|"light"} mode - Mode d'architecture (impacte l'import de Role)
24
+ * @returns {Promise<string>} Le code TypeScript des DTOs
25
+ */
26
+ async function generateDto(
27
+ entity,
28
+ useSwagger,
29
+ isAuthDto = false,
30
+ mode = "full",
31
+ ) {
32
+ const entityName = capitalize(entity.name);
33
+ const entityNameLower = entity.name.toLowerCase();
34
+
35
+ /* ===============================
36
+ ENUM IMPORT
37
+ =============================== */
38
+ let enumImport = "";
39
+ if (entityName === "User") {
40
+ enumImport =
41
+ mode === "light"
42
+ ? "\nimport { Role } from 'src/common/enums/role.enum';"
43
+ : "\nimport { Role } from 'src/user/domain/enums/role.enum';";
44
+ }
45
+
46
+ /* ===============================
47
+ SWAGGER HELPERS
48
+ =============================== */
49
+ const getFieldDescription = (f) => {
50
+ const name = f.name.toLowerCase();
51
+ if (name.includes("email")) return "The official email address of the user";
52
+ if (name.includes("password"))
53
+ return "Must contain at least 8 characters, one letter and one number";
54
+ if (name.includes("token")) return "Authentication token";
55
+ if (name.includes("id") && name !== "id")
56
+ return `Unique identifier of the related ${name.replace("id", "")}`;
57
+ return `The ${f.name} of the ${entityNameLower}`;
58
+ };
59
+
60
+ const getExampleForField = (f) => {
61
+ const fieldName = f.name.toLowerCase();
62
+ const isArray = f.type.endsWith("[]");
63
+ const cleanType = f.type.toLowerCase().replace("[]", "");
64
+
65
+ const getBaseExample = () => {
66
+ if (fieldName.includes("email")) return "user@example.com";
67
+ if (fieldName.includes("password")) return "SecurePass@2024";
68
+ if (fieldName.includes("token")) return "eyJhbGciOi...";
69
+ if (fieldName.includes("id") && fieldName !== "id")
70
+ return "550e8400-e29b-41d4-a716-446655440000";
71
+ if (fieldName.includes("title") || fieldName.includes("name"))
72
+ return `${capitalize(entityName)} Example`;
73
+ if (cleanType === "boolean") return true;
74
+ if (cleanType === "number" || cleanType === "int") return 42;
75
+ return `${f.name.toLowerCase()}_val`;
76
+ };
77
+
78
+ const base = getBaseExample();
79
+ return isArray ? [base, base] : base;
80
+ };
81
+
82
+ /* ===============================
83
+ FIELD GENERATOR
84
+ =============================== */
85
+ const generateFieldLine = (f, optional = false, forceNoSwagger = false) => {
86
+ if (entityName === "User" && f.name.toLowerCase() === "role") return null;
87
+
88
+ const name = f.name;
89
+ const cleanType = f.type.toLowerCase().replace("[]", "");
90
+ const isArray = f.type.endsWith("[]");
91
+
92
+ const SCALAR_TYPES = [
93
+ "string",
94
+ "text",
95
+ "uuid",
96
+ "json",
97
+ "number",
98
+ "decimal",
99
+ "int",
100
+ "float",
101
+ "boolean",
102
+ "date",
103
+ ];
104
+ if (!SCALAR_TYPES.includes(cleanType)) return null;
105
+
106
+ let validators = [];
107
+ if (optional) validators.push("@IsOptional()");
108
+ if (name.toLowerCase().includes("email")) validators.push("@IsEmail()");
109
+ else if (name.toLowerCase().includes("password"))
110
+ validators.push(
111
+ "@MinLength(8, { message: 'Password is too short (min 8 characters)' })",
112
+ );
113
+
114
+ switch (cleanType) {
115
+ case "string":
116
+ case "text":
117
+ validators.push(isArray ? "@IsString({ each: true })" : "@IsString()");
118
+ if (!isArray && !name.toLowerCase().includes("password"))
119
+ validators.push("@MinLength(2)");
120
+ break;
121
+ case "number":
122
+ case "int":
123
+ case "float":
124
+ validators.push(
125
+ isArray ? "@IsNumber({}, { each: true })" : "@IsNumber()",
126
+ );
127
+ break;
128
+ case "boolean":
129
+ validators.push(
130
+ isArray ? "@IsBoolean({ each: true })" : "@IsBoolean()",
131
+ );
132
+ break;
133
+ case "uuid":
134
+ validators.push("@IsUUID()");
135
+ break;
136
+ case "date":
137
+ validators.push("@IsDateString()");
138
+ break;
139
+ }
140
+ if (isArray) validators.push("@IsArray()");
141
+
142
+ let swaggerDecorator = "";
143
+ if (useSwagger && !forceNoSwagger) {
144
+ const decorator = optional ? "@ApiPropertyOptional" : "@ApiProperty";
145
+ const options = JSON.stringify(
146
+ {
147
+ example: getExampleForField(f),
148
+ description: getFieldDescription(f),
149
+ },
150
+ null,
151
+ 2,
152
+ ).replace(/"([^"]+)":/g, "$1:");
153
+ swaggerDecorator = `${decorator}(${options})\n `;
154
+ }
155
+
156
+ return `${swaggerDecorator}${validators.join("\n ")}\n ${name}${
157
+ optional ? "?" : "!"
158
+ }: ${formatType(f.type)};`;
159
+ };
160
+
161
+ /* ======================================================
162
+ AUTH DTO (Strict & Clean)
163
+ ====================================================== */
164
+ if (isAuthDto) {
165
+ const authFields = entity.fields
166
+ .map((f) => generateFieldLine(f, false))
167
+ .filter(Boolean)
168
+ .join("\n\n ");
169
+
170
+ return `import {
171
+ IsString, IsInt, IsBoolean, IsEmail, IsArray,
172
+ IsUUID, IsDateString, MinLength, IsOptional
173
+ } from 'class-validator';
174
+ ${useSwagger ? "import { ApiProperty } from '@nestjs/swagger';" : ""}
175
+
176
+ /**
177
+ * Auth DTO - Strict contract for authentication
178
+ */
179
+ export class ${entityName}Dto {
180
+ ${authFields}
181
+ }
182
+ `;
183
+ }
184
+
185
+ /* ======================================================
186
+ CRUD DTOs (Create / Update)
187
+ ====================================================== */
188
+ const createFields = entity.fields
189
+ .map((f) => generateFieldLine(f, false))
190
+ .filter(Boolean)
191
+ .join("\n\n ");
192
+
193
+ const updateFields = entity.fields
194
+ .map((f) => generateFieldLine(f, true))
195
+ .filter(Boolean)
196
+ .join("\n\n ");
197
+
198
+ // Imports Swagger dynamiques
199
+ let swaggerImports = ["ApiProperty", "ApiPropertyOptional", "PartialType"];
200
+ if (entityName === "User") swaggerImports.push("ApiHideProperty");
201
+
202
+ return `import {
203
+ IsOptional, IsString, IsInt, IsBoolean, IsEmail,
204
+ IsArray, IsUUID, IsDateString, MinLength, IsEnum
205
+ } from 'class-validator';
206
+ ${
207
+ useSwagger
208
+ ? `import { ${swaggerImports.join(", ")} } from '@nestjs/swagger';`
209
+ : ""
210
+ }
211
+ ${enumImport}
212
+
213
+ /**
214
+ * DTO for creating a ${entityName}
215
+ */
216
+ export class Create${entityName}Dto {
217
+ ${createFields}
218
+
219
+ ${
220
+ entityName === "User"
221
+ ? `
222
+ ${useSwagger ? "@ApiHideProperty()" : ""}
223
+ @IsOptional()
224
+ @IsEnum(Role)
225
+ role: Role = Role.USER;`
226
+ : ""
227
+ }
228
+ }
229
+
230
+ /**
231
+ * DTO for updating a ${entityName}
232
+ */
233
+ ${
234
+ useSwagger
235
+ ? `export class Update${entityName}Dto extends PartialType(Create${entityName}Dto) {}`
236
+ : `export class Update${entityName}Dto {
237
+ ${updateFields}
238
+
239
+ ${
240
+ entityName === "User"
241
+ ? `@IsEnum(Role)
242
+ @IsOptional()
243
+ role?: Role;`
244
+ : ""
245
+ }
246
+ }`
247
+ }
248
+ `;
249
+ }
250
+
251
+ module.exports = { generateDto };
@@ -0,0 +1,54 @@
1
+ // src/utils/generators/application/dtoUpdater.js
2
+ const fs = require("fs");
3
+ const { updateFile, capitalize } = require("../../userInput");
4
+
5
+ async function patchDtoWithRelation(source, targetName, useSwagger, mode) {
6
+ let dtoPath;
7
+ if (mode === "full") {
8
+ dtoPath = `src/${source.toLowerCase()}/application/dtos/${source.toLowerCase()}.dto.ts`;
9
+ } else {
10
+ dtoPath = `src/${source.toLowerCase()}/dtos/${source.toLowerCase()}.dto.ts`;
11
+ }
12
+ const targetLow = targetName.toLowerCase();
13
+ const targetCap = capitalize(targetName);
14
+
15
+ // 1. Préparation du bloc de code selon Swagger
16
+ let fieldCode = "";
17
+
18
+ if (useSwagger) {
19
+ fieldCode = `
20
+ @ApiProperty({
21
+ example: '550e8400-e29b-41d4-a716-446655440000',
22
+ description: 'The unique identifier of the related ${targetCap}',
23
+ })
24
+ @IsUUID()
25
+ ${targetLow}Id!: string;`;
26
+ } else {
27
+ fieldCode = `
28
+ @IsUUID()
29
+ ${targetLow}Id!: string;`;
30
+ }
31
+
32
+ // 2. Injection dans CreateDto
33
+ // On injecte juste après l'ouverture de la classe
34
+ await updateFile({
35
+ path: dtoPath,
36
+ pattern: new RegExp(`export class Create${capitalize(source)}Dto {`),
37
+ replacement: `export class Create${capitalize(source)}Dto {${fieldCode}\n`,
38
+ });
39
+
40
+ // 3. Injection dans UpdateDto (Optionnel mais recommandé si non géré par PartialType)
41
+ // Si ton UpdateDto n'utilise pas PartialType(CreateDto), il faut aussi patcher l'Update
42
+ const fileContent = fs.readFileSync(dtoPath, "utf8");
43
+ if (!fileContent.includes("extends PartialType")) {
44
+ const updateFieldCode = fieldCode
45
+ .replace("@IsUUID()", "@IsOptional()\n @IsUUID()")
46
+ .replace("Id!: string", "Id?: string");
47
+ await updateFile({
48
+ path: dtoPath,
49
+ pattern: new RegExp(`export class Update${capitalize(source)}Dto {`),
50
+ replacement: `export class Update${capitalize(source)}Dto {${updateFieldCode}\n`,
51
+ });
52
+ }
53
+ }
54
+ module.exports = { patchDtoWithRelation };