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.
- package/AUDIT_ROADMAP.md +798 -0
- package/CHANGELOG.fr.md +22 -0
- package/CHANGELOG.md +22 -0
- package/CLI_USAGE.fr.md +331 -331
- package/CLI_USAGE.md +364 -364
- package/LICENSE +21 -21
- package/PROGRESS.md +258 -0
- package/bin/nestcraft.js +84 -64
- package/commands/demo.js +337 -330
- package/commands/generate.js +94 -0
- package/commands/generateConf.js +91 -0
- package/commands/help.js +29 -2
- package/commands/info.js +48 -48
- package/commands/new.js +340 -335
- package/commands/start.js +19 -19
- package/commands/test.js +7 -7
- package/package.json +1 -1
- package/utils/app-module.updater.js +96 -0
- package/utils/cliParser.js +88 -76
- package/utils/colors.js +62 -62
- package/utils/configs/configureDocker.js +120 -120
- package/utils/configs/setupCleanArchitecture.js +17 -15
- package/utils/configs/setupLightArchitecture.js +15 -9
- package/utils/file-system.js +75 -0
- package/utils/file-utils/saveProjectConfig.js +36 -0
- package/utils/fullModeInput.js +607 -607
- package/utils/generators/application/dtoGenerator.js +251 -0
- package/utils/generators/application/dtoUpdater.js +54 -0
- package/utils/generators/cleanModuleGenerator.js +475 -0
- package/utils/generators/database/setupDatabase.js +49 -0
- package/utils/generators/domain/entityGenerator.js +126 -0
- package/utils/generators/domain/entityUpdater.js +78 -0
- package/utils/generators/infrastructure/mapperGenerator.js +114 -0
- package/utils/generators/infrastructure/mapperUpdater.js +65 -0
- package/utils/generators/infrastructure/middlewareGenerator.js +645 -0
- package/utils/generators/infrastructure/mongooseSchemaGenerator.js +196 -0
- package/utils/generators/infrastructure/repositoryGenerator.js +334 -0
- package/utils/generators/lightModuleGenerator.js +131 -0
- package/utils/generators/presentation/controllerGenerator.js +142 -0
- package/utils/generators/relation/relation.engine.js +64 -0
- package/utils/helpers.js +109 -0
- package/utils/interactive/askEntityInputs.js +165 -0
- package/utils/loggers/logError.js +7 -7
- package/utils/loggers/logInfo.js +7 -7
- package/utils/loggers/logSuccess.js +7 -7
- package/utils/loggers/logWarning.js +7 -7
- package/utils/setups/orms/typeOrmSetup.js +630 -630
- package/utils/setups/setupAuth.js +33 -20
- package/utils/setups/setupDatabase.js +76 -75
- package/utils/setups/setupMongoose.js +22 -5
- package/utils/setups/setupPrisma.js +748 -630
- package/utils/shell.js +112 -32
- package/utils/spinner.js +57 -57
- package/utils/systemCheck.js +124 -124
- package/utils/userInput.js +357 -421
- 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 };
|