nestcraftx 0.1.6 → 0.1.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestcraftx",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI to generate scalable NestJS projects with Clean Architecture and Clean Code",
5
5
  "main": "bin/nestcraft.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -10,6 +10,7 @@ const { createProject } = require("./utils/setups/projectSetup");
10
10
  const { setupAuth } = require("./utils/setups/setupAuth");
11
11
  const { setupSwagger } = require("./utils/setups/setupSwagger");
12
12
  const { setupDatabase } = require("./utils/setups/setupDatabase");
13
+ const { setupBootstrapLogger } = require("./utils/setups/setupLogger");
13
14
 
14
15
  async function main() {
15
16
  const inputs = await getUserInputs2();
@@ -17,8 +18,12 @@ async function main() {
17
18
  await createProject(inputs);
18
19
  await setupCleanArchitecture(inputs);
19
20
 
20
- if (inputs.useAuth) await setupAuth();
21
- if (inputs.useSwagger) await setupSwagger(inputs.swaggerInputs);
21
+ if (inputs.useAuth) await setupAuth(inputs);
22
+ if (inputs.useSwagger) {
23
+ await setupSwagger(inputs.swaggerInputs);
24
+ } else {
25
+ setupBootstrapLogger();
26
+ }
22
27
  if (inputs.useDocker) await configureDocker(inputs);
23
28
 
24
29
  // await setupPrisma(inputs);
package/unutils.txt CHANGED
@@ -1,173 +1,431 @@
1
- `import { Injectable } from '@nestjs/common';
2
- import { ${capitalize(
3
- entity
4
- )}Entity } from '${entityPath}/domain/entities/${entity}.entity';
5
- /* a decommenter apres migration des schema prisma */
6
- /* import { ${capitalize(entity)} as Prisma${capitalize(
7
- entity
8
- )} } from '@prisma/client'; */
9
- import { Create${capitalize(entity)}Dto,Update${capitalize(
10
- entity
11
- )}Dto } from 'src/${entity}/application/dtos/${entity}.dto';
12
-
13
- @Injectable()
14
- export class ${capitalize(entity)}Mapper {
15
- toDomain(data: any/* Prisma${capitalize(entity)} */): ${capitalize(
16
- entity
17
- )}Entity {
18
- return new ${capitalize(entity)}Entity(
19
- data.id,
20
- // ajoute ici tous les autres champs selon l'ordre de l'entité
21
- // exemple:
22
- // data.name,
23
- // data.description,
24
- // ...
25
- data.createdAt,
26
- data.updatedAt,
1
+ /*ancien input*/
2
+ import * as readline from "readline-sync";
3
+ import * as fs from "fs";
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"], // Choix des ORM supportés
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: "mysql",
48
+ label: "MySQL / MariaDB",
49
+ ormOptions: ["prisma", "typeorm"], // Choix des ORM supportés
50
+ required: [
51
+ {
52
+ title: "Nom d’utilisateur MySQL",
53
+ envVar: "MYSQL_USER",
54
+ defaultValue: "root",
55
+ hideEchoBack: false,
56
+ },
57
+ {
58
+ title: "Mot de passe MySQL",
59
+ envVar: "MYSQL_PASSWORD",
60
+ defaultValue: null,
61
+ hideEchoBack: true,
62
+ },
63
+ {
64
+ title: "Nom de la base de données",
65
+ envVar: "MYSQL_DB",
66
+ defaultValue: "mydb",
67
+ hideEchoBack: false,
68
+ },
69
+ {
70
+ title: "Hôte MySQL",
71
+ envVar: "MYSQL_HOST",
72
+ defaultValue: "localhost",
73
+ hideEchoBack: false,
74
+ },
75
+ {
76
+ title: "Port MySQL",
77
+ envVar: "MYSQL_PORT",
78
+ defaultValue: "3306",
79
+ hideEchoBack: false,
80
+ },
81
+ ],
82
+ },
83
+ {
84
+ name: "mongodb",
85
+ label: "MongoDB",
86
+ ormOptions: ["mongoose"], // Choix d'ORM pour MongoDB
87
+ required: [
88
+ {
89
+ title: "URL de connexion MongoDB",
90
+ envVar: "MONGO_URI",
91
+ defaultValue: "mongodb://localhost:27017",
92
+ hideEchoBack: false,
93
+ },
94
+ {
95
+ title: "Nom de la base de données",
96
+ envVar: "MONGO_DB",
97
+ defaultValue: "mydb",
98
+ hideEchoBack: false,
99
+ },
100
+ ],
101
+ },
102
+ {
103
+ name: "sqlite",
104
+ label: "SQLite",
105
+ ormOptions: ["prisma", "sequelize"], // Choix des ORM supportés
106
+ required: [
107
+ {
108
+ title: "Chemin du fichier SQLite",
109
+ envVar: "SQLITE_PATH",
110
+ defaultValue: "./data/sqlite.db",
111
+ hideEchoBack: false,
112
+ },
113
+ ],
114
+ },
115
+ {
116
+ name: "firebase",
117
+ label: "Firebase Firestore",
118
+ ormOptions: ["firebase"], // Firebase n'a pas d'ORM traditionnel
119
+ required: [
120
+ {
121
+ title: "Chemin vers le fichier de configuration Firebase (JSON)",
122
+ envVar: "FIREBASE_CONFIG_PATH",
123
+ defaultValue: "./firebase-config.json",
124
+ hideEchoBack: false,
125
+ },
126
+ ],
127
+ },
128
+ {
129
+ name: "redis",
130
+ label: "Redis",
131
+ ormOptions: ["redis"], // Redis n'a pas d'ORM traditionnel
132
+ required: [
133
+ {
134
+ title: "URL de connexion Redis",
135
+ envVar: "REDIS_URL",
136
+ defaultValue: "redis://localhost:6379",
137
+ hideEchoBack: false,
138
+ },
139
+ ],
140
+ }, */
141
+ ];
142
+
143
+ const projectName = readline.question("Nom du projet: ");
144
+ // const databaseName = readline.question("Nom de la base de données: ");
145
+
146
+ let usedDB = readline.question(
147
+ `Quelle base de donnée voulez-vous utiliser ? (${dataBases
148
+ .map((db) => db.name)
149
+ .join(", ")}) : `,
150
+ { defaultInput: "postgresql" }
151
+ );
152
+
153
+ let selectedDB = dataBases.find(
154
+ (db) => db.name.toLowerCase() === usedDB.toLowerCase()
155
+ );
156
+
157
+ while (!selectedDB) {
158
+ console.log("❌ Base de données non reconnue.");
159
+
160
+ usedDB = readline.question(
161
+ `Quelle base de donnée voulez-vous utiliser ? (${dataBases
162
+ .map((db) => db.name)
163
+ .join(", ")}) : `
27
164
  );
28
- }
29
165
 
30
- toPersistence(dto: Create${capitalize(entity)}Dto): any {
31
- return {
32
- // ajoute ici les champs correspondants au DTO de création
33
- // exemple:
34
- // name: dto.name,
35
- // description: dto.description,
36
- // ...
37
- };
166
+ selectedDB = dataBases.find(
167
+ (db) => db.name.toLowerCase() === usedDB.toLowerCase()
168
+ );
38
169
  }
39
170
 
40
- toUpdatePersistence(dto: Update${capitalize(entity)}Dto): any {
41
- const data: any = {};
42
- // Logique conditionnelle pour ne mettre que les champs fournis
43
- // exemple:
44
- // if (dto.name) data.name = dto.name;
45
- // if (dto.description) data.description = dto.description;
46
- return data;
171
+ const dbConfig = {};
172
+
173
+ // Configuration de la base de données (champ utilisateur)
174
+ selectedDB.required.forEach((field) => {
175
+ const answer = readline.question(
176
+ `${field.title} (par défaut: ${field.defaultValue}) : `,
177
+ { hideEchoBack: field.hideEchoBack }
178
+ );
179
+ dbConfig[field.envVar] = answer || field.defaultValue;
180
+ });
181
+
182
+ // Ajout du choix de l'ORM dans la configuration
183
+ if (selectedDB.ormOptions && selectedDB.ormOptions.length > 0) {
184
+ const ormChoice = readline.question(
185
+ `Choisissez un ORM pour ${selectedDB.label} (${selectedDB.ormOptions.join(
186
+ ", "
187
+ )}): `
188
+ );
189
+ dbConfig.orm = ormChoice || selectedDB.ormOptions[0]; // Par défaut, choisir le premier ORM
47
190
  }
48
- }
49
- `
50
-
51
- // 📌 5. DTOs
52
- const DtoFileContent = generateDto(entity);
53
- await createFile({
54
- path: `${entityPath}/application/dtos/${entity}.dto.ts`,
55
- contente: `
56
- // Importer les décorateurs nécessaires pour la validation et la documentation (Swagger)
57
- // import { ApiProperty } from '@nestjs/swagger';
58
- // import { IsString, IsOptional, IsInt, IsDate } from 'class-validator'; // Dépendances pour la validation
59
-
60
- // Classe DTO pour la création de l'entité ${capitalize(entity)}
61
- export class Create${capitalize(entity)}Dto {
62
- // Exemple de propriété pour une chaîne de caractères
63
- /* @ApiProperty({
64
- description: 'Le nom complet de l\'utilisateur',
65
- example: 'John Doe', // Exemple d'un nom complet
66
- })
67
- @IsString()
68
- name: string; // Le nom de l'utilisateur, représenté par une chaîne de caractères */
69
-
70
- // Exemple de propriété pour un nombre entier
71
- /* @ApiProperty({
72
- description: 'L\'âge de l\'utilisateur',
73
- example: 25, // Exemple d'âge
74
- })
75
- @IsInt()
76
- @IsOptional() // L'âge est optionnel lors de la création
77
- age?: number; // Âge de l'utilisateur, un nombre entier */
78
-
79
- // Exemple de propriété pour une date
80
- /* @ApiProperty({
81
- description: 'Date de naissance de l\'utilisateur',
82
- example: '1996-05-20', // Exemple de date au format ISO
83
- })
84
- @IsDate()
85
- birthdate: Date; // Date de naissance de l'utilisateur */
191
+
192
+ const useYarn = readline.keyInYNStrict("Utiliser Yarn ?");
193
+ const useDocker = readline.keyInYNStrict(
194
+ "Voulez-vous générer un fichier Docker?"
195
+ );
196
+ const useAuth = readline.keyInYNStrict(
197
+ "Voulez-vous ajouter une authentification JWT?"
198
+ );
199
+ const useSwagger = readline.keyInYNStrict("Voulez-vous installer Swagger?");
200
+ const packageManager = useYarn ? "yarn" : "npm";
201
+
202
+ // 🧱 Saisie des entités et champs
203
+ const entitiesData = {
204
+ entities: [],
205
+ relations: [],
206
+ };
207
+
208
+ if (useAuth) {
209
+ console.log("🔐 Auth activé : ajout automatique de l'entité 'User'");
210
+
211
+ entitiesData.entities.push({
212
+ name: "user",
213
+ fields: [
214
+ { name: "email", type: "string" },
215
+ { name: "password", type: "string" },
216
+ { name: "isActive", type: "boolean" },
217
+ ],
218
+ });
86
219
  }
87
220
 
88
- // Classe DTO pour la mise à jour de l'entité ${capitalize(entity)}
89
- export class Update${capitalize(entity)}Dto {
90
- // Exemple de propriété optionnelle pour un changement de nom
91
- /* @ApiProperty({
92
- description: 'Nom mis à jour de l\'utilisateur',
93
- example: 'Jane Smith', // Exemple de nouveau nom
94
- })
95
- @IsString()
96
- @IsOptional()
97
- name?: string; // Nom mis à jour, chaîne de caractères optionnelle */
98
-
99
- // Exemple de propriété optionnelle pour une mise à jour d'âge
100
- /* @ApiProperty({
101
- description: 'Âge mis à jour de l\'utilisateur',
102
- example: 26, // Exemple d'âge mis à jour
103
- })
104
- @IsInt()
105
- @IsOptional()
106
- age?: number; // Âge mis à jour, nombre entier optionnel */
221
+ let addEntity = readline.keyInYNStrict(
222
+ "Voulez-vous ajouter une autre entité ?"
223
+ );
224
+
225
+ while (addEntity) {
226
+ const name = readline.question(`Nom de l'entité : `);
227
+ const fields = [];
228
+
229
+ while (true) {
230
+ const fname = readline.question(
231
+ ` ➤ Nom du champ (vide pour terminer) : `
232
+ );
233
+ if (!fname) break;
234
+
235
+ const ftype = readline.question(
236
+ ` Type du champ "${fname}" (ex: string, number, boolean, Date, enum, etc.) : `
237
+ );
238
+ fields.push({ name: fname, type: ftype });
239
+ }
240
+
241
+ entitiesData.entities.push({ name, fields });
242
+
243
+ addEntity = readline.keyInYNStrict(
244
+ "Voulez-vous ajouter une autre entité ?"
245
+ );
107
246
  }
108
- `,
109
- });
110
247
 
248
+ // 🔗 Gestion des relations
249
+ const wantsRelation = readline.keyInYNStrict(
250
+ "Souhaites-tu ajouter des relations entre les entités ?"
251
+ );
111
252
 
253
+ if (wantsRelation) {
254
+ while (true) {
255
+ console.log("\n🧩 Entités disponibles :");
256
+ entitiesData.entities.forEach((ent, index) =>
257
+ console.log(` [${index}] ${ent.name}`)
258
+ );
112
259
 
260
+ const fromIndex = parseInt(
261
+ readline.question("Depuis quelle entité ? (index) : "),
262
+ 10
263
+ );
264
+ const toIndex = parseInt(
265
+ readline.question("Vers quelle entité ? (index) : "),
266
+ 10
267
+ );
268
+ const relType = readline.question(
269
+ "Type de relation ? (1-1 / 1-n / n-n) : "
270
+ );
113
271
 
272
+ const from = entitiesData.entities[fromIndex];
273
+ const to = entitiesData.entities[toIndex];
114
274
 
115
- `import { Injectable, NotFoundException } from '@nestjs/common';
116
- import { PrismaService } from 'src/prisma/prisma.service';
117
- import { Create${entityNameCapitalized}Dto,Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
118
- import { I${entityNameCapitalized}Repository } from 'src/${entityNameLower}/application/interfaces/${entityNameLower}.repository.interface';
119
- import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
120
- import { ${entityNameCapitalized}Mapper } from 'src/${entityNameLower}/domain/mappers/${entityNameLower}.mapper';
275
+ if (!from || !to) {
276
+ console.log("❌ Indice invalide, réessaye !");
277
+ continue;
278
+ }
121
279
 
122
- @Injectable()
123
- export class ${entityNameCapitalized}Repository implements I${entityNameCapitalized}Repository {
124
- constructor(
125
- private readonly prisma: PrismaService,
126
- private readonly mapper: ${entityNameCapitalized}Mapper,
127
- ) {}
280
+ entitiesData.relations.push({
281
+ from: from.name,
282
+ to: to.name,
283
+ type: relType,
284
+ });
128
285
 
129
- // create
130
- async create(data: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
131
- const toPersist = this.mapper.toPersistence(data);
132
- const created = await this.prisma.${entityNameLower}.create({ data: toPersist });
133
- return this.mapper.toDomain(created);
286
+ const again = readline.keyInYNStrict("Ajouter une autre relation ?");
287
+ if (!again) break;
288
+ }
134
289
  }
135
290
 
136
- // find by id
137
- async findById(id: string): Promise<${entityNameCapitalized}Entity> {
138
- const record = await this.prisma.${entityNameLower}.findUnique({
139
- where: { id },
140
- });
291
+ // Swagger (facultatif)
292
+ let swaggerInputs;
293
+ if (useSwagger) {
294
+ swaggerInputs = getUserInputsSwagger();
295
+ }
296
+
297
+ return {
298
+ projectName: projectName,
299
+ useYarn: useYarn,
300
+ useDocker: useDocker,
301
+ useAuth: useAuth,
302
+ useSwagger: useSwagger,
303
+ swaggerInputs: swaggerInputs,
304
+ packageManager: packageManager,
305
+ entitiesData: entitiesData,
306
+ selectedDB: selectedDB.name,
307
+ dbConfig: dbConfig,
308
+ };
309
+ }
310
+
311
+ export function getUserInputsSwagger() {
312
+ console.log("\n🔹 Configuration de Swagger 🔹");
141
313
 
142
- if (!record) {
143
- throw new NotFoundException(\`${entityNameCapitalized}Entity with id \${id} not found\`);
314
+ const title = readline.question("Titre de l'API ? (ex: Mon API) ", {
315
+ defaultInput: "Mon API",
316
+ });
317
+
318
+ const description = readline.question(
319
+ "Description de l'API ? (ex: API de gestion) ",
320
+ {
321
+ defaultInput: "API de gestion",
144
322
  }
323
+ );
324
+
325
+ const version = readline.question("Version de l'API ? (ex: 1.0.0) ", {
326
+ defaultInput: "1.0.0",
327
+ });
145
328
 
146
- return this.mapper.toDomain(record);
329
+ const endpoint = readline.question("Endpoint Swagger (ex: api/docs) ", {
330
+ defaultInput: "api/docs",
331
+ });
332
+
333
+ return { title, description, version, endpoint };
334
+ }
335
+ export async function createDirectory(directoryPath) {
336
+ try {
337
+ if (!fs.existsSync(directoryPath)) {
338
+ fs.mkdirSync(directoryPath, { recursive: true });
339
+ // console.log(`Dossier créé : ${directoryPath}`);
340
+ } else {
341
+ console.log(`Le dossier existe déjà : ${directoryPath}`);
342
+ }
343
+ } catch (error) {
344
+ console.error(
345
+ `Erreur lors de la création du dossier ${directoryPath}:`,
346
+ error
347
+ );
147
348
  }
349
+ }
148
350
 
149
- // update
150
- async update(id: string, data: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
151
- const toUpdate = this.mapper.toUpdatePersistence(data);
152
- const updated = await this.prisma.${entityNameLower}.update({
153
- where: { id },
154
- data: toUpdate,
155
- });
351
+ export async function createFile(fileData) {
352
+ // console.log("creating file....");
353
+ try {
354
+ if (!fs.existsSync(fileData.path)) {
355
+ fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
356
+ } else {
357
+ console.log(`Le fichier existe déjà : ${fileData.path}`);
358
+ fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
359
+ }
360
+ } catch (error) {
361
+ console.error(
362
+ `Erreur lors de la création du fichier ${fileData.path}:`,
363
+ error
364
+ );
365
+ }
366
+ }
367
+
368
+ export async function updateFile({ path, pattern, replacement }) {
369
+ try {
370
+ let mainTs = fs.readFileSync(path, "utf8");
371
+
372
+ // Si pattern est une string, on le convertit en RegExp
373
+ /* const regexPattern =
374
+ typeof pattern === "string" ? new RegExp(pattern, "g") : pattern;
375
+
376
+ if (!regexPattern.test(mainTs)) {
377
+ console.warn(`Pattern not found in ${path}. No changes made.`);
378
+ return;
379
+ } */
156
380
 
157
- return this.mapper.toDomain(updated);
381
+ const updatedContent = mainTs.replace(pattern, replacement);
382
+ await fs.writeFileSync(path, updatedContent, "utf-8");
383
+ // console.log(`✅ Updated file: ${path}`);
384
+ } catch (error) {
385
+ console.error(`❌ Error updating file ${path}:`, error);
158
386
  }
387
+ }
388
+
389
+ export async function safeUpdateAppModule(entity) {
390
+ const filePath = "src/app.module.ts";
391
+ const moduleName = `${capitalize(entity)}Module`;
392
+
393
+ const content = await fs.readFileSync(filePath, "utf-8");
394
+
395
+ const importLine = `import {${moduleName}} from 'src/${entity}/${entity}.module';`;
159
396
 
160
- // find all
161
- async findAll(): Promise<${entityNameCapitalized}Entity[]> {
162
- const records = await this.prisma.${entityNameLower}.findMany();
163
- return records.map(record => this.mapper.toDomain(record));
397
+ const importPatern = `import { ConfigModule } from '@nestjs/config';`;
398
+
399
+ // Ajout de l'import s'il n'existe pas
400
+ if (!content.includes(importLine)) {
401
+ await updateFile({
402
+ path: filePath,
403
+ pattern: importPatern,
404
+ replacement: `${importPatern}\n${importLine}`,
405
+ });
164
406
  }
165
407
 
166
- // delete
167
- async delete(id: string): Promise<void> {
168
- await this.prisma.${entityNameLower}.delete({
169
- where: { id },
408
+ // Regex qui capte le contenu de imports: [ ... ]
409
+ const importRegexPatern = `imports: [
410
+ ConfigModule.forRoot({
411
+ isGlobal: true, // Rendre ConfigModule accessible globalement
412
+ envFilePath: '.env', // Charger les variables d'environnement
413
+ }),`;
414
+ // const contentRegex = await fs.readFileSync(filePath, "utf-8");
415
+
416
+ if (!content.includes(moduleName)) {
417
+ await updateFile({
418
+ path: filePath,
419
+ pattern: importRegexPatern,
420
+ replacement: `${importRegexPatern}\n${moduleName},\n`,
170
421
  });
171
422
  }
172
423
  }
173
- `
424
+
425
+ export function capitalize(str) {
426
+ return str.charAt(0).toUpperCase() + str.slice(1);
427
+ }
428
+
429
+ export function decapitalize(str) {
430
+ return str.charAt(0).toLowerCase() + str.slice(1);
431
+ }
@@ -8,7 +8,7 @@ async function configureDocker(inputs) {
8
8
  const dockerfileContent = `FROM node:18\nWORKDIR /app\nCOPY . .\nRUN ${inputs.packageManager} install\nCMD ["${inputs.packageManager}", "run", "start"]`;
9
9
  fs.writeFileSync("Dockerfile", dockerfileContent);
10
10
 
11
- const dockerComposeContent = `version: '3'\nservices:\n db:\n image: postgres\n restart: always\n environment:\n POSTGRES_USER: ${inputs.dbUser}\n POSTGRES_PASSWORD: ${inputs.dbPassword}\n POSTGRES_DB: ${inputs.databaseName}\n ports:\n - "5432:5432"`;
11
+ const dockerComposeContent = `version: '3'\nservices:\n db:\n image: postgres\n restart: always\n environment:\n POSTGRES_USER: ${inputs.dbConfig.POSTGRES_USER}\n POSTGRES_PASSWORD: ${inputs.dbConfig.POSTGRES_PASSWORD}\n POSTGRES_DB: ${inputs.dbConfig.POSTGRES_DB}\n ports:\n - "5432:5432"`;
12
12
  fs.writeFileSync("docker-compose.yml", dockerComposeContent);
13
13
 
14
14
  logSuccess("Docker Configuré avec succès");