nestcraftx 0.2.4 → 0.2.6
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/.gitattributes +6 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- package/.github/ISSUE_TEMPLATE/pull_request_template.md +24 -0
- package/CHANGELOG.fr.md +97 -97
- package/CHANGELOG.md +98 -98
- package/CLI_USAGE.fr.md +331 -331
- package/CLI_USAGE.md +364 -364
- package/DEMO.fr.md +292 -292
- package/DEMO.md +294 -294
- package/LICENSE +21 -21
- package/MIGRATION_GUIDE.fr.md +127 -127
- package/MIGRATION_GUIDE.md +124 -124
- package/QUICK_START.fr.md +152 -152
- package/QUICK_START.md +169 -169
- package/README.fr.md +653 -659
- package/SECURITY.md +10 -0
- package/bin/nestcraft.js +84 -64
- package/commands/demo.js +333 -330
- package/commands/generate.js +93 -0
- package/commands/generateConf.js +91 -0
- package/commands/help.js +78 -78
- package/commands/info.js +48 -48
- package/commands/new.js +338 -335
- package/commands/start.js +19 -19
- package/commands/test.js +7 -7
- package/package.json +41 -41
- package/readme.md +638 -643
- package/utils/cliParser.js +133 -76
- package/utils/colors.js +62 -62
- package/utils/configs/configureDocker.js +120 -120
- package/utils/configs/setupCleanArchitecture.js +563 -557
- package/utils/configs/setupLightArchitecture.js +701 -660
- package/utils/envGenerator.js +122 -122
- package/utils/file-utils/packageJsonUtils.js +49 -55
- package/utils/file-utils/saveProjectConfig.js +36 -0
- package/utils/fullModeInput.js +607 -607
- package/utils/generators/application/dtoUpdater.js +54 -0
- package/utils/generators/cleanModuleGenerator.js +475 -0
- package/utils/generators/database/setupDatabase.js +31 -0
- package/utils/generators/domain/entityUpdater.js +78 -0
- package/utils/generators/infrastructure/mapperUpdater.js +65 -0
- package/utils/generators/lightModuleGenerator.js +131 -0
- package/utils/generators/relation/relation.engine.js +64 -0
- package/utils/interactive/askEntityInputs.js +165 -0
- package/utils/lightModeInput.js +460 -460
- 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/projectSetup.js +46 -46
- package/utils/setups/setupAuth.js +973 -926
- package/utils/setups/setupDatabase.js +75 -75
- package/utils/setups/setupLogger.js +69 -59
- package/utils/setups/setupMongoose.js +377 -432
- package/utils/setups/setupPrisma.js +802 -630
- package/utils/setups/setupSwagger.js +97 -88
- package/utils/shell.js +32 -32
- package/utils/spinner.js +57 -57
- package/utils/systemCheck.js +124 -124
- package/utils/userInput.js +421 -421
- package/utils/utils.js +2197 -1762
package/utils/userInput.js
CHANGED
|
@@ -1,421 +1,421 @@
|
|
|
1
|
-
import * as readline from "readline-sync";
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
import { logInfo } from "./loggers/logInfo.js";
|
|
4
|
-
|
|
5
|
-
export async function getUserInputs2() {
|
|
6
|
-
console.log("\n🔹🔹🔹 Configuration du projet 🔹🔹🔹\n");
|
|
7
|
-
|
|
8
|
-
const dataBases = [
|
|
9
|
-
{
|
|
10
|
-
name: "postgresql",
|
|
11
|
-
label: "PostgreSQL",
|
|
12
|
-
ormOptions: ["prisma", "typeorm"],
|
|
13
|
-
required: [
|
|
14
|
-
{
|
|
15
|
-
title: "Nom d’utilisateur PostgreSQL",
|
|
16
|
-
envVar: "POSTGRES_USER",
|
|
17
|
-
defaultValue: "postgres",
|
|
18
|
-
hideEchoBack: false,
|
|
19
|
-
},
|
|
20
|
-
{
|
|
21
|
-
title: "Mot de passe PostgreSQL",
|
|
22
|
-
envVar: "POSTGRES_PASSWORD",
|
|
23
|
-
defaultValue: null,
|
|
24
|
-
hideEchoBack: true,
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
title: "Nom de la base de données",
|
|
28
|
-
envVar: "POSTGRES_DB",
|
|
29
|
-
defaultValue: "mydb",
|
|
30
|
-
hideEchoBack: false,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
title: "Hôte PostgreSQL",
|
|
34
|
-
envVar: "POSTGRES_HOST",
|
|
35
|
-
defaultValue: "localhost",
|
|
36
|
-
hideEchoBack: false,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
title: "Port PostgreSQL",
|
|
40
|
-
envVar: "POSTGRES_PORT",
|
|
41
|
-
defaultValue: "5432",
|
|
42
|
-
hideEchoBack: false,
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
name: "mongodb",
|
|
48
|
-
label: "MongoDB",
|
|
49
|
-
ormOptions: ["mongoose"],
|
|
50
|
-
required: [
|
|
51
|
-
{
|
|
52
|
-
title: "URL de connexion MongoDB",
|
|
53
|
-
envVar: "MONGO_URI",
|
|
54
|
-
defaultValue: "mongodb://localhost:27017",
|
|
55
|
-
hideEchoBack: false,
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
title: "Nom de la base de données",
|
|
59
|
-
envVar: "MONGO_DB",
|
|
60
|
-
defaultValue: "mydb",
|
|
61
|
-
hideEchoBack: false,
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
// Validation du nom du projet
|
|
68
|
-
let projectName;
|
|
69
|
-
while (true) {
|
|
70
|
-
projectName = readline.question("Nom du projet: ");
|
|
71
|
-
if (/^[A-Za-z][A-Za-z0-9_-]*$/.test(projectName)) break;
|
|
72
|
-
console.log(
|
|
73
|
-
"❌ Nom de projet invalide. Lettres, chiffres, _ ou - uniquement, commencez par une lettre."
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Sélection de la base de données avec validation
|
|
78
|
-
let usedDB = readline.question(
|
|
79
|
-
`Quelle base de donnée voulez-vous utiliser ? (${dataBases
|
|
80
|
-
.map((db) => db.name)
|
|
81
|
-
.join(", ")}) : `,
|
|
82
|
-
{ defaultInput: "postgresql" }
|
|
83
|
-
);
|
|
84
|
-
let selectedDB = dataBases.find(
|
|
85
|
-
(db) => db.name.toLowerCase() === usedDB.toLowerCase()
|
|
86
|
-
);
|
|
87
|
-
while (!selectedDB) {
|
|
88
|
-
console.log("❌ Base de données non reconnue.");
|
|
89
|
-
usedDB = readline.question(
|
|
90
|
-
`Quelle base de donnée voulez-vous utiliser ? (${dataBases
|
|
91
|
-
.map((db) => db.name)
|
|
92
|
-
.join(", ")}) :
|
|
93
|
-
);
|
|
94
|
-
selectedDB = dataBases.find(
|
|
95
|
-
(db) => db.name.toLowerCase() === usedDB.toLowerCase()
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const dbConfig = {};
|
|
100
|
-
selectedDB.required.forEach((field) => {
|
|
101
|
-
let answer;
|
|
102
|
-
while (true) {
|
|
103
|
-
answer = readline.question(
|
|
104
|
-
`${field.title} (par défaut: ${field.defaultValue}) : `,
|
|
105
|
-
{ hideEchoBack: field.hideEchoBack }
|
|
106
|
-
);
|
|
107
|
-
if (answer || field.defaultValue !== null) break;
|
|
108
|
-
console.log("❌ Ce champ est requis.");
|
|
109
|
-
}
|
|
110
|
-
dbConfig[field.envVar] = answer || field.defaultValue;
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
// Choix de l'ORM avec validation
|
|
114
|
-
if (selectedDB.ormOptions && selectedDB.ormOptions.length > 0) {
|
|
115
|
-
let ormChoice;
|
|
116
|
-
while (true) {
|
|
117
|
-
ormChoice = readline.question(
|
|
118
|
-
`Choisissez un ORM pour ${
|
|
119
|
-
selectedDB.label
|
|
120
|
-
} (${selectedDB.ormOptions.join(", ")}):
|
|
121
|
-
);
|
|
122
|
-
if (!ormChoice) ormChoice = selectedDB.ormOptions[0];
|
|
123
|
-
if (selectedDB.ormOptions.includes(ormChoice.toLowerCase())) break;
|
|
124
|
-
console.log(
|
|
125
|
-
"❌ ORM non reconnu. Choix possibles :",
|
|
126
|
-
selectedDB.ormOptions.join(", ")
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
dbConfig.orm = ormChoice.toLowerCase();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const useYarn = readline.keyInYNStrict("Utiliser Yarn ?");
|
|
133
|
-
const useDocker = readline.keyInYNStrict(
|
|
134
|
-
"Voulez-vous générer un fichier Docker?"
|
|
135
|
-
);
|
|
136
|
-
const useAuth = readline.keyInYNStrict(
|
|
137
|
-
"Voulez-vous ajouter une authentification JWT?"
|
|
138
|
-
);
|
|
139
|
-
const useSwagger = readline.keyInYNStrict("Voulez-vous installer Swagger?");
|
|
140
|
-
const packageManager = useYarn ? "yarn" : "npm";
|
|
141
|
-
|
|
142
|
-
// Saisie des entités et champs avec validation
|
|
143
|
-
const entitiesData = {
|
|
144
|
-
entities: [],
|
|
145
|
-
relations: [],
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
if (useAuth) {
|
|
149
|
-
console.log("🔐 Auth activé : ajout automatique de l'entité 'User'");
|
|
150
|
-
entitiesData.entities.push({
|
|
151
|
-
name: "user",
|
|
152
|
-
fields: [
|
|
153
|
-
{ name: "email", type: "string" },
|
|
154
|
-
{ name: "password", type: "string" },
|
|
155
|
-
{ name: "isActive", type: "boolean" },
|
|
156
|
-
],
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let addEntity = readline.keyInYNStrict("Voulez-vous ajouter une entité ?");
|
|
161
|
-
while (addEntity) {
|
|
162
|
-
// Validation du nom d'entité
|
|
163
|
-
let name;
|
|
164
|
-
while (true) {
|
|
165
|
-
name = readline.question("Nom de l'entité (lettres, chiffres, _): ");
|
|
166
|
-
if (/^[A-Za-z][A-Za-z0-9_]*$/.test(name)) break;
|
|
167
|
-
console.log(
|
|
168
|
-
"❌ Nom invalide. Utilisez uniquement lettres, chiffres, _ et commencez par une lettre."
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Saisie des champs avec validation
|
|
173
|
-
const fields = [];
|
|
174
|
-
while (true) {
|
|
175
|
-
let fname = readline.question(" ➤ Nom du champ (vide pour terminer) : ");
|
|
176
|
-
if (!fname) break;
|
|
177
|
-
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(fname)) {
|
|
178
|
-
console.log(
|
|
179
|
-
"❌ Nom de champ invalide. Lettres, chiffres, _ uniquement, commencez par une lettre."
|
|
180
|
-
);
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
let ftype;
|
|
184
|
-
while (true) {
|
|
185
|
-
ftype = readline.question(
|
|
186
|
-
` Type du champ "${fname}" (string, number, boolean, Date, enum, etc.) :
|
|
187
|
-
);
|
|
188
|
-
if (ftype) break;
|
|
189
|
-
console.log("❌ Type de champ requis.");
|
|
190
|
-
}
|
|
191
|
-
fields.push({ name: fname, type: ftype });
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
entitiesData.entities.push({ name, fields });
|
|
195
|
-
|
|
196
|
-
addEntity = readline.keyInYNStrict(
|
|
197
|
-
"Voulez-vous ajouter une autre entité ?"
|
|
198
|
-
);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Gestion des relations avec validation
|
|
202
|
-
const wantsRelation = readline.keyInYNStrict(
|
|
203
|
-
"Souhaites-tu ajouter des relations entre les entités ?"
|
|
204
|
-
);
|
|
205
|
-
if (wantsRelation && entitiesData.entities.length > 1) {
|
|
206
|
-
while (true) {
|
|
207
|
-
console.log("\n🧩 Entités disponibles :");
|
|
208
|
-
entitiesData.entities.forEach((ent, index) =>
|
|
209
|
-
console.log(` [${index}] ${ent.name}`)
|
|
210
|
-
);
|
|
211
|
-
|
|
212
|
-
let fromIndex, toIndex;
|
|
213
|
-
while (true) {
|
|
214
|
-
fromIndex = parseInt(
|
|
215
|
-
readline.question("Depuis quelle entité ? (index) : "),
|
|
216
|
-
10
|
|
217
|
-
);
|
|
218
|
-
if (!isNaN(fromIndex) && entitiesData.entities[fromIndex]) break;
|
|
219
|
-
console.log("❌ Indice invalide, réessaye !");
|
|
220
|
-
}
|
|
221
|
-
while (true) {
|
|
222
|
-
toIndex = parseInt(
|
|
223
|
-
readline.question("Vers quelle entité ? (index) : "),
|
|
224
|
-
10
|
|
225
|
-
);
|
|
226
|
-
if (!isNaN(toIndex) && entitiesData.entities[toIndex]) break;
|
|
227
|
-
console.log("❌ Indice invalide, réessaye !");
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
let relType;
|
|
231
|
-
while (true) {
|
|
232
|
-
relType = readline.question("Type de relation ? (1-1 / 1-n / n-n) : ");
|
|
233
|
-
if (["1-1", "1-n", "n-n"].includes(relType)) break;
|
|
234
|
-
console.log(
|
|
235
|
-
"❌ Type de relation invalide. Choix possibles : 1-1, 1-n, n-n"
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const from = entitiesData.entities[fromIndex];
|
|
240
|
-
const to = entitiesData.entities[toIndex];
|
|
241
|
-
|
|
242
|
-
entitiesData.relations.push({
|
|
243
|
-
from: from.name,
|
|
244
|
-
to: to.name,
|
|
245
|
-
type: relType,
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
if (relType === "1-1") {
|
|
249
|
-
from.fields.push({
|
|
250
|
-
name: `${to.name.toLowerCase()}Id`,
|
|
251
|
-
type: "string",
|
|
252
|
-
});
|
|
253
|
-
to.fields.push({
|
|
254
|
-
name: `${from.name.toLowerCase()}Id`,
|
|
255
|
-
type: "string",
|
|
256
|
-
});
|
|
257
|
-
} else if (relType === "1-n") {
|
|
258
|
-
to.fields.push({
|
|
259
|
-
name: `${from.name.toLowerCase()}Id`,
|
|
260
|
-
type: "string",
|
|
261
|
-
}); // ex: video.userId
|
|
262
|
-
// pas de champ inverse dans from (user)
|
|
263
|
-
} else if (relType === "n-n") {
|
|
264
|
-
// pour n-n tu peux gérer ça plus tard avec une table pivot
|
|
265
|
-
from.fields.push({
|
|
266
|
-
name: `${to.name.toLowerCase()}Ids`,
|
|
267
|
-
type: "string[]",
|
|
268
|
-
});
|
|
269
|
-
to.fields.push({
|
|
270
|
-
name: `${from.name.toLowerCase()}Ids`,
|
|
271
|
-
type: "string[]",
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const again = readline.keyInYNStrict("Ajouter une autre relation ?");
|
|
276
|
-
if (!again) break;
|
|
277
|
-
}
|
|
278
|
-
} else if (wantsRelation) {
|
|
279
|
-
console.log("❌ Il faut au moins deux entités pour créer une relation.");
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Swagger (facultatif)
|
|
283
|
-
let swaggerInputs;
|
|
284
|
-
if (useSwagger) {
|
|
285
|
-
swaggerInputs = getUserInputsSwagger();
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return {
|
|
289
|
-
projectName: projectName,
|
|
290
|
-
useYarn: useYarn,
|
|
291
|
-
useDocker: useDocker,
|
|
292
|
-
useAuth: useAuth,
|
|
293
|
-
useSwagger: useSwagger,
|
|
294
|
-
swaggerInputs: swaggerInputs,
|
|
295
|
-
packageManager: packageManager,
|
|
296
|
-
entitiesData: entitiesData,
|
|
297
|
-
selectedDB: selectedDB.name,
|
|
298
|
-
dbConfig: dbConfig,
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
export function getUserInputsSwagger() {
|
|
303
|
-
console.log("\n🔹 Configuration de Swagger 🔹");
|
|
304
|
-
|
|
305
|
-
const title = readline.question("Titre de l'API ? (ex: Mon API) ", {
|
|
306
|
-
defaultInput: "Mon API",
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
const description = readline.question(
|
|
310
|
-
"Description de l'API ? (ex: API de gestion) ",
|
|
311
|
-
{
|
|
312
|
-
defaultInput: "API de gestion",
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
const version = readline.question("Version de l'API ? (ex: 1.0.0) ", {
|
|
317
|
-
defaultInput: "1.0.0",
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const endpoint = readline.question("Endpoint Swagger (ex: api/docs) ", {
|
|
321
|
-
defaultInput: "api/docs",
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
return { title, description, version, endpoint };
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
export async function createDirectory(directoryPath) {
|
|
328
|
-
try {
|
|
329
|
-
if (!fs.existsSync(directoryPath)) {
|
|
330
|
-
fs.mkdirSync(directoryPath, { recursive: true });
|
|
331
|
-
// console.log(`Dossier créé : ${directoryPath}`);
|
|
332
|
-
}
|
|
333
|
-
} catch (error) {
|
|
334
|
-
console.error(
|
|
335
|
-
`Erreur lors de la création du dossier ${directoryPath}:`,
|
|
336
|
-
error
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
export async function createFile(fileData) {
|
|
342
|
-
try {
|
|
343
|
-
if (!fs.existsSync(fileData.path)) {
|
|
344
|
-
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
345
|
-
} else {
|
|
346
|
-
console.log(`Existing file : ${fileData.path}`);
|
|
347
|
-
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
348
|
-
}
|
|
349
|
-
} catch (error) {
|
|
350
|
-
console.error(`Erreur creating file ${fileData.path}:`, error);
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export async function updateFile({ path, pattern, replacement }) {
|
|
355
|
-
try {
|
|
356
|
-
let mainTs = fs.readFileSync(path, "utf8");
|
|
357
|
-
const updatedContent = mainTs.replace(pattern, replacement);
|
|
358
|
-
fs.writeFileSync(path, updatedContent, "utf-8");
|
|
359
|
-
// console.log(` Updated file: ${path}`);
|
|
360
|
-
} catch (error) {
|
|
361
|
-
console.error(` Error updating file ${path}:`, error);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export async function safeUpdateAppModule(entity) {
|
|
366
|
-
const filePath = "src/app.module.ts";
|
|
367
|
-
const moduleName = `${capitalize(entity)}Module`;
|
|
368
|
-
const importLine = `import { ${moduleName} } from 'src/${entity}/${entity}.module';`;
|
|
369
|
-
|
|
370
|
-
let content = fs.readFileSync(filePath, "utf-8");
|
|
371
|
-
|
|
372
|
-
// Étape 1 : Ajout de l'import si nécessaire
|
|
373
|
-
if (!content.includes(importLine)) {
|
|
374
|
-
const importMarker = `import { ConfigModule } from '@nestjs/config';`;
|
|
375
|
-
|
|
376
|
-
if (content.includes(importMarker)) {
|
|
377
|
-
await updateFile({
|
|
378
|
-
path: filePath,
|
|
379
|
-
pattern: importMarker,
|
|
380
|
-
replacement: `${importMarker}\n${importLine}`,
|
|
381
|
-
});
|
|
382
|
-
content = fs.readFileSync(filePath, "utf-8");
|
|
383
|
-
} else {
|
|
384
|
-
logInfo(
|
|
385
|
-
" Impossible de trouver le point d'insertion de l'import (ConfigModule manquant)"
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// Étape 2 : Vérifier le bloc des imports du @Module
|
|
391
|
-
const importsBlockRegex = /imports:\s*\[((.|\n)*?)\]/m;
|
|
392
|
-
const match = content.match(importsBlockRegex);
|
|
393
|
-
|
|
394
|
-
if (!match) {
|
|
395
|
-
logInfo(" Impossible de trouver le bloc 'imports' dans AppModule.");
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
const currentImportsBlock = match[1];
|
|
400
|
-
const isAlreadyImportedInModule = currentImportsBlock.includes(moduleName);
|
|
401
|
-
|
|
402
|
-
if (!isAlreadyImportedInModule) {
|
|
403
|
-
const updatedBlock = currentImportsBlock.trim().endsWith(",")
|
|
404
|
-
? `${currentImportsBlock.trim()} ${moduleName},`
|
|
405
|
-
: `${currentImportsBlock.trim()}, ${moduleName},`;
|
|
406
|
-
|
|
407
|
-
const newContent = content.replace(
|
|
408
|
-
importsBlockRegex,
|
|
409
|
-
`imports: [${updatedBlock}]
|
|
410
|
-
);
|
|
411
|
-
fs.writeFileSync(filePath, newContent, "utf-8");
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
export function capitalize(str) {
|
|
416
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
export function decapitalize(str) {
|
|
420
|
-
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
421
|
-
}
|
|
1
|
+
import * as readline from "readline-sync";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import { logInfo } from "./loggers/logInfo.js";
|
|
4
|
+
|
|
5
|
+
export async function getUserInputs2() {
|
|
6
|
+
console.log("\n🔹🔹🔹 Configuration du projet 🔹🔹🔹\n");
|
|
7
|
+
|
|
8
|
+
const dataBases = [
|
|
9
|
+
{
|
|
10
|
+
name: "postgresql",
|
|
11
|
+
label: "PostgreSQL",
|
|
12
|
+
ormOptions: ["prisma", "typeorm"],
|
|
13
|
+
required: [
|
|
14
|
+
{
|
|
15
|
+
title: "Nom d’utilisateur PostgreSQL",
|
|
16
|
+
envVar: "POSTGRES_USER",
|
|
17
|
+
defaultValue: "postgres",
|
|
18
|
+
hideEchoBack: false,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: "Mot de passe PostgreSQL",
|
|
22
|
+
envVar: "POSTGRES_PASSWORD",
|
|
23
|
+
defaultValue: null,
|
|
24
|
+
hideEchoBack: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
title: "Nom de la base de données",
|
|
28
|
+
envVar: "POSTGRES_DB",
|
|
29
|
+
defaultValue: "mydb",
|
|
30
|
+
hideEchoBack: false,
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: "Hôte PostgreSQL",
|
|
34
|
+
envVar: "POSTGRES_HOST",
|
|
35
|
+
defaultValue: "localhost",
|
|
36
|
+
hideEchoBack: false,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: "Port PostgreSQL",
|
|
40
|
+
envVar: "POSTGRES_PORT",
|
|
41
|
+
defaultValue: "5432",
|
|
42
|
+
hideEchoBack: false,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "mongodb",
|
|
48
|
+
label: "MongoDB",
|
|
49
|
+
ormOptions: ["mongoose"],
|
|
50
|
+
required: [
|
|
51
|
+
{
|
|
52
|
+
title: "URL de connexion MongoDB",
|
|
53
|
+
envVar: "MONGO_URI",
|
|
54
|
+
defaultValue: "mongodb://localhost:27017",
|
|
55
|
+
hideEchoBack: false,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
title: "Nom de la base de données",
|
|
59
|
+
envVar: "MONGO_DB",
|
|
60
|
+
defaultValue: "mydb",
|
|
61
|
+
hideEchoBack: false,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// Validation du nom du projet
|
|
68
|
+
let projectName;
|
|
69
|
+
while (true) {
|
|
70
|
+
projectName = readline.question("Nom du projet: ");
|
|
71
|
+
if (/^[A-Za-z][A-Za-z0-9_-]*$/.test(projectName)) break;
|
|
72
|
+
console.log(
|
|
73
|
+
"❌ Nom de projet invalide. Lettres, chiffres, _ ou - uniquement, commencez par une lettre.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Sélection de la base de données avec validation
|
|
78
|
+
let usedDB = readline.question(
|
|
79
|
+
`Quelle base de donnée voulez-vous utiliser ? (${dataBases
|
|
80
|
+
.map((db) => db.name)
|
|
81
|
+
.join(", ")}) : `,
|
|
82
|
+
{ defaultInput: "postgresql" },
|
|
83
|
+
);
|
|
84
|
+
let selectedDB = dataBases.find(
|
|
85
|
+
(db) => db.name.toLowerCase() === usedDB.toLowerCase(),
|
|
86
|
+
);
|
|
87
|
+
while (!selectedDB) {
|
|
88
|
+
console.log("❌ Base de données non reconnue.");
|
|
89
|
+
usedDB = readline.question(
|
|
90
|
+
`Quelle base de donnée voulez-vous utiliser ? (${dataBases
|
|
91
|
+
.map((db) => db.name)
|
|
92
|
+
.join(", ")}) : `,
|
|
93
|
+
);
|
|
94
|
+
selectedDB = dataBases.find(
|
|
95
|
+
(db) => db.name.toLowerCase() === usedDB.toLowerCase(),
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const dbConfig = {};
|
|
100
|
+
selectedDB.required.forEach((field) => {
|
|
101
|
+
let answer;
|
|
102
|
+
while (true) {
|
|
103
|
+
answer = readline.question(
|
|
104
|
+
`${field.title} (par défaut: ${field.defaultValue}) : `,
|
|
105
|
+
{ hideEchoBack: field.hideEchoBack },
|
|
106
|
+
);
|
|
107
|
+
if (answer || field.defaultValue !== null) break;
|
|
108
|
+
console.log("❌ Ce champ est requis.");
|
|
109
|
+
}
|
|
110
|
+
dbConfig[field.envVar] = answer || field.defaultValue;
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Choix de l'ORM avec validation
|
|
114
|
+
if (selectedDB.ormOptions && selectedDB.ormOptions.length > 0) {
|
|
115
|
+
let ormChoice;
|
|
116
|
+
while (true) {
|
|
117
|
+
ormChoice = readline.question(
|
|
118
|
+
`Choisissez un ORM pour ${
|
|
119
|
+
selectedDB.label
|
|
120
|
+
} (${selectedDB.ormOptions.join(", ")}): `,
|
|
121
|
+
);
|
|
122
|
+
if (!ormChoice) ormChoice = selectedDB.ormOptions[0];
|
|
123
|
+
if (selectedDB.ormOptions.includes(ormChoice.toLowerCase())) break;
|
|
124
|
+
console.log(
|
|
125
|
+
"❌ ORM non reconnu. Choix possibles :",
|
|
126
|
+
selectedDB.ormOptions.join(", "),
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
dbConfig.orm = ormChoice.toLowerCase();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const useYarn = readline.keyInYNStrict("Utiliser Yarn ?");
|
|
133
|
+
const useDocker = readline.keyInYNStrict(
|
|
134
|
+
"Voulez-vous générer un fichier Docker?",
|
|
135
|
+
);
|
|
136
|
+
const useAuth = readline.keyInYNStrict(
|
|
137
|
+
"Voulez-vous ajouter une authentification JWT?",
|
|
138
|
+
);
|
|
139
|
+
const useSwagger = readline.keyInYNStrict("Voulez-vous installer Swagger?");
|
|
140
|
+
const packageManager = useYarn ? "yarn" : "npm";
|
|
141
|
+
|
|
142
|
+
// Saisie des entités et champs avec validation
|
|
143
|
+
const entitiesData = {
|
|
144
|
+
entities: [],
|
|
145
|
+
relations: [],
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (useAuth) {
|
|
149
|
+
console.log("🔐 Auth activé : ajout automatique de l'entité 'User'");
|
|
150
|
+
entitiesData.entities.push({
|
|
151
|
+
name: "user",
|
|
152
|
+
fields: [
|
|
153
|
+
{ name: "email", type: "string" },
|
|
154
|
+
{ name: "password", type: "string" },
|
|
155
|
+
{ name: "isActive", type: "boolean" },
|
|
156
|
+
],
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let addEntity = readline.keyInYNStrict("Voulez-vous ajouter une entité ?");
|
|
161
|
+
while (addEntity) {
|
|
162
|
+
// Validation du nom d'entité
|
|
163
|
+
let name;
|
|
164
|
+
while (true) {
|
|
165
|
+
name = readline.question("Nom de l'entité (lettres, chiffres, _): ");
|
|
166
|
+
if (/^[A-Za-z][A-Za-z0-9_]*$/.test(name)) break;
|
|
167
|
+
console.log(
|
|
168
|
+
"❌ Nom invalide. Utilisez uniquement lettres, chiffres, _ et commencez par une lettre.",
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Saisie des champs avec validation
|
|
173
|
+
const fields = [];
|
|
174
|
+
while (true) {
|
|
175
|
+
let fname = readline.question(" ➤ Nom du champ (vide pour terminer) : ");
|
|
176
|
+
if (!fname) break;
|
|
177
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(fname)) {
|
|
178
|
+
console.log(
|
|
179
|
+
"❌ Nom de champ invalide. Lettres, chiffres, _ uniquement, commencez par une lettre.",
|
|
180
|
+
);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
let ftype;
|
|
184
|
+
while (true) {
|
|
185
|
+
ftype = readline.question(
|
|
186
|
+
` Type du champ "${fname}" (string, number, boolean, Date, enum, etc.) : `,
|
|
187
|
+
);
|
|
188
|
+
if (ftype) break;
|
|
189
|
+
console.log("❌ Type de champ requis.");
|
|
190
|
+
}
|
|
191
|
+
fields.push({ name: fname, type: ftype });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
entitiesData.entities.push({ name, fields });
|
|
195
|
+
|
|
196
|
+
addEntity = readline.keyInYNStrict(
|
|
197
|
+
"Voulez-vous ajouter une autre entité ?",
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Gestion des relations avec validation
|
|
202
|
+
const wantsRelation = readline.keyInYNStrict(
|
|
203
|
+
"Souhaites-tu ajouter des relations entre les entités ?",
|
|
204
|
+
);
|
|
205
|
+
if (wantsRelation && entitiesData.entities.length > 1) {
|
|
206
|
+
while (true) {
|
|
207
|
+
console.log("\n🧩 Entités disponibles :");
|
|
208
|
+
entitiesData.entities.forEach((ent, index) =>
|
|
209
|
+
console.log(` [${index}] ${ent.name}`),
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
let fromIndex, toIndex;
|
|
213
|
+
while (true) {
|
|
214
|
+
fromIndex = parseInt(
|
|
215
|
+
readline.question("Depuis quelle entité ? (index) : "),
|
|
216
|
+
10,
|
|
217
|
+
);
|
|
218
|
+
if (!isNaN(fromIndex) && entitiesData.entities[fromIndex]) break;
|
|
219
|
+
console.log("❌ Indice invalide, réessaye !");
|
|
220
|
+
}
|
|
221
|
+
while (true) {
|
|
222
|
+
toIndex = parseInt(
|
|
223
|
+
readline.question("Vers quelle entité ? (index) : "),
|
|
224
|
+
10,
|
|
225
|
+
);
|
|
226
|
+
if (!isNaN(toIndex) && entitiesData.entities[toIndex]) break;
|
|
227
|
+
console.log("❌ Indice invalide, réessaye !");
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
let relType;
|
|
231
|
+
while (true) {
|
|
232
|
+
relType = readline.question("Type de relation ? (1-1 / 1-n / n-n) : ");
|
|
233
|
+
if (["1-1", "1-n", "n-n"].includes(relType)) break;
|
|
234
|
+
console.log(
|
|
235
|
+
"❌ Type de relation invalide. Choix possibles : 1-1, 1-n, n-n",
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const from = entitiesData.entities[fromIndex];
|
|
240
|
+
const to = entitiesData.entities[toIndex];
|
|
241
|
+
|
|
242
|
+
entitiesData.relations.push({
|
|
243
|
+
from: from.name,
|
|
244
|
+
to: to.name,
|
|
245
|
+
type: relType,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
if (relType === "1-1") {
|
|
249
|
+
from.fields.push({
|
|
250
|
+
name: `${to.name.toLowerCase()}Id`,
|
|
251
|
+
type: "string",
|
|
252
|
+
});
|
|
253
|
+
to.fields.push({
|
|
254
|
+
name: `${from.name.toLowerCase()}Id`,
|
|
255
|
+
type: "string",
|
|
256
|
+
});
|
|
257
|
+
} else if (relType === "1-n") {
|
|
258
|
+
to.fields.push({
|
|
259
|
+
name: `${from.name.toLowerCase()}Id`,
|
|
260
|
+
type: "string",
|
|
261
|
+
}); // ex: video.userId
|
|
262
|
+
// pas de champ inverse dans from (user)
|
|
263
|
+
} else if (relType === "n-n") {
|
|
264
|
+
// pour n-n tu peux gérer ça plus tard avec une table pivot
|
|
265
|
+
from.fields.push({
|
|
266
|
+
name: `${to.name.toLowerCase()}Ids`,
|
|
267
|
+
type: "string[]",
|
|
268
|
+
});
|
|
269
|
+
to.fields.push({
|
|
270
|
+
name: `${from.name.toLowerCase()}Ids`,
|
|
271
|
+
type: "string[]",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const again = readline.keyInYNStrict("Ajouter une autre relation ?");
|
|
276
|
+
if (!again) break;
|
|
277
|
+
}
|
|
278
|
+
} else if (wantsRelation) {
|
|
279
|
+
console.log("❌ Il faut au moins deux entités pour créer une relation.");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Swagger (facultatif)
|
|
283
|
+
let swaggerInputs;
|
|
284
|
+
if (useSwagger) {
|
|
285
|
+
swaggerInputs = getUserInputsSwagger();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
projectName: projectName,
|
|
290
|
+
useYarn: useYarn,
|
|
291
|
+
useDocker: useDocker,
|
|
292
|
+
useAuth: useAuth,
|
|
293
|
+
useSwagger: useSwagger,
|
|
294
|
+
swaggerInputs: swaggerInputs,
|
|
295
|
+
packageManager: packageManager,
|
|
296
|
+
entitiesData: entitiesData,
|
|
297
|
+
selectedDB: selectedDB.name,
|
|
298
|
+
dbConfig: dbConfig,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function getUserInputsSwagger() {
|
|
303
|
+
console.log("\n🔹 Configuration de Swagger 🔹");
|
|
304
|
+
|
|
305
|
+
const title = readline.question("Titre de l'API ? (ex: Mon API) ", {
|
|
306
|
+
defaultInput: "Mon API",
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const description = readline.question(
|
|
310
|
+
"Description de l'API ? (ex: API de gestion) ",
|
|
311
|
+
{
|
|
312
|
+
defaultInput: "API de gestion",
|
|
313
|
+
},
|
|
314
|
+
);
|
|
315
|
+
|
|
316
|
+
const version = readline.question("Version de l'API ? (ex: 1.0.0) ", {
|
|
317
|
+
defaultInput: "1.0.0",
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const endpoint = readline.question("Endpoint Swagger (ex: api/docs) ", {
|
|
321
|
+
defaultInput: "api/docs",
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
return { title, description, version, endpoint };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export async function createDirectory(directoryPath) {
|
|
328
|
+
try {
|
|
329
|
+
if (!fs.existsSync(directoryPath)) {
|
|
330
|
+
fs.mkdirSync(directoryPath, { recursive: true });
|
|
331
|
+
// console.log(`Dossier créé : ${directoryPath}`);
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error(
|
|
335
|
+
`Erreur lors de la création du dossier ${directoryPath}:`,
|
|
336
|
+
error,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export async function createFile(fileData) {
|
|
342
|
+
try {
|
|
343
|
+
if (!fs.existsSync(fileData.path)) {
|
|
344
|
+
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
345
|
+
} else {
|
|
346
|
+
console.log(`Existing file : ${fileData.path}`);
|
|
347
|
+
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
348
|
+
}
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error(`Erreur creating file ${fileData.path}:`, error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function updateFile({ path, pattern, replacement }) {
|
|
355
|
+
try {
|
|
356
|
+
let mainTs = fs.readFileSync(path, "utf8");
|
|
357
|
+
const updatedContent = mainTs.replace(pattern, replacement);
|
|
358
|
+
fs.writeFileSync(path, updatedContent, "utf-8");
|
|
359
|
+
// console.log(` Updated file: ${path}`);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error(` Error updating file ${path}:`, error);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export async function safeUpdateAppModule(entity) {
|
|
366
|
+
const filePath = "src/app.module.ts";
|
|
367
|
+
const moduleName = `${capitalize(entity)}Module`;
|
|
368
|
+
const importLine = `import { ${moduleName} } from 'src/${entity}/${entity}.module';`;
|
|
369
|
+
|
|
370
|
+
let content = fs.readFileSync(filePath, "utf-8");
|
|
371
|
+
|
|
372
|
+
// Étape 1 : Ajout de l'import si nécessaire
|
|
373
|
+
if (!content.includes(importLine)) {
|
|
374
|
+
const importMarker = `import { ConfigModule } from '@nestjs/config';`;
|
|
375
|
+
|
|
376
|
+
if (content.includes(importMarker)) {
|
|
377
|
+
await updateFile({
|
|
378
|
+
path: filePath,
|
|
379
|
+
pattern: importMarker,
|
|
380
|
+
replacement: `${importMarker}\n${importLine}`,
|
|
381
|
+
});
|
|
382
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
383
|
+
} else {
|
|
384
|
+
logInfo(
|
|
385
|
+
" Impossible de trouver le point d'insertion de l'import (ConfigModule manquant)",
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Étape 2 : Vérifier le bloc des imports du @Module
|
|
391
|
+
const importsBlockRegex = /imports:\s*\[((.|\n)*?)\]/m;
|
|
392
|
+
const match = content.match(importsBlockRegex);
|
|
393
|
+
|
|
394
|
+
if (!match) {
|
|
395
|
+
logInfo(" Impossible de trouver le bloc 'imports' dans AppModule.");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const currentImportsBlock = match[1];
|
|
400
|
+
const isAlreadyImportedInModule = currentImportsBlock.includes(moduleName);
|
|
401
|
+
|
|
402
|
+
if (!isAlreadyImportedInModule) {
|
|
403
|
+
const updatedBlock = currentImportsBlock.trim().endsWith(",")
|
|
404
|
+
? `${currentImportsBlock.trim()} ${moduleName},`
|
|
405
|
+
: `${currentImportsBlock.trim()}, ${moduleName},`;
|
|
406
|
+
|
|
407
|
+
const newContent = content.replace(
|
|
408
|
+
importsBlockRegex,
|
|
409
|
+
`imports: [${updatedBlock}]`,
|
|
410
|
+
);
|
|
411
|
+
fs.writeFileSync(filePath, newContent, "utf-8");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function capitalize(str) {
|
|
416
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function decapitalize(str) {
|
|
420
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
421
|
+
}
|