nestcraftx 0.2.6 → 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.
@@ -0,0 +1,109 @@
1
+ /**
2
+ * utils/helpers.js
3
+ *
4
+ * Fonctions utilitaires de manipulation de chaînes et de types.
5
+ * Partagées entre les générateurs full-mode et light-mode.
6
+ *
7
+ * Ces fonctions sont purement stateless : aucune dépendance externe,
8
+ * aucun effet de bord, aucune logique métier.
9
+ */
10
+
11
+ /**
12
+ * Met en majuscule la première lettre d'une chaîne.
13
+ * @param {string} str
14
+ * @returns {string}
15
+ */
16
+ function capitalize(str) {
17
+ if (!str) return str;
18
+ return str.charAt(0).toUpperCase() + str.slice(1);
19
+ }
20
+
21
+ /**
22
+ * Met en minuscule la première lettre d'une chaîne.
23
+ * @param {string} str
24
+ * @returns {string}
25
+ */
26
+ function decapitalize(str) {
27
+ if (!str) return str;
28
+ return str.charAt(0).toLowerCase() + str.slice(1);
29
+ }
30
+
31
+ /**
32
+ * Retourne le pluriel simple d'un nom.
33
+ * - si finit par 'y' → remplace par 'ies' (category → categories)
34
+ * - si finit déjà par 's' → retourne tel quel
35
+ * - sinon → ajoute 's'
36
+ * @param {string} name
37
+ * @returns {string}
38
+ */
39
+ function pluralize(name) {
40
+ if (name.endsWith("y")) {
41
+ return name.slice(0, -1) + "ies";
42
+ } else if (name.endsWith("s")) {
43
+ return name;
44
+ }
45
+ return name + "s";
46
+ }
47
+
48
+ /**
49
+ * Mappe le type de champ interne (sélectionné par l'utilisateur) au type
50
+ * TypeScript correspondant pour la génération de code.
51
+ *
52
+ * @param {string} type - Le type de base (string, number, boolean, Date, json, etc.)
53
+ * @returns {string} Le type formaté TypeScript
54
+ */
55
+ function formatType(type) {
56
+ switch (type.toLowerCase()) {
57
+ case "number":
58
+ case "decimal":
59
+ return "number";
60
+
61
+ case "boolean":
62
+ return "boolean";
63
+
64
+ case "date":
65
+ return "Date";
66
+
67
+ case "json":
68
+ return "Record<string, any>";
69
+
70
+ case "string":
71
+ case "text":
72
+ case "uuid":
73
+ return "string";
74
+ }
75
+
76
+ // Gère les tableaux (ex: 'string[]', 'number[]')
77
+ if (type.endsWith("[]")) {
78
+ return `${formatType(type.slice(0, -2))}[]`;
79
+ }
80
+
81
+ // Laisse passer les types complexes (DTO, Enum, classe custom)
82
+ if (type.match(/^[A-Za-z][A-Za-z0-9_]*$/)) {
83
+ return type;
84
+ }
85
+
86
+ return "any";
87
+ }
88
+
89
+ /**
90
+ * Gère les cas spéciaux avant l'application de formatType.
91
+ * Ex: le champ 'role' de type string est traité comme le type 'Role' (enum).
92
+ *
93
+ * @param {object} field - L'objet champ { name, type }
94
+ * @returns {string} Le type final formaté
95
+ */
96
+ function getFormattedType(field) {
97
+ if (field.name === "role" && field.type.toLowerCase().startsWith("string")) {
98
+ return "Role";
99
+ }
100
+ return formatType(field.type);
101
+ }
102
+
103
+ module.exports = {
104
+ capitalize,
105
+ decapitalize,
106
+ pluralize,
107
+ formatType,
108
+ getFormattedType,
109
+ };
@@ -13,12 +13,15 @@ async function setupAuth(inputs) {
13
13
  const isFull = mode === "full";
14
14
 
15
15
  // 1. INSTALLATION DES DÉPENDANCES
16
+ const pkgManager = inputs.packageManager || "npm";
17
+ const installCmd = pkgManager === "yarn" || pkgManager === "pnpm" ? `${pkgManager} add` : "npm install";
18
+
16
19
  await runCommand(
17
- `npm install @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt uuid`,
20
+ `${installCmd} @nestjs/jwt @nestjs/passport passport passport-jwt bcrypt uuid`,
18
21
  "Erreur install deps auth",
19
22
  );
20
23
  await runCommand(
21
- `npm install -D @types/passport-jwt @types/bcrypt @types/uuid`,
24
+ `${installCmd} -D @types/passport-jwt @types/bcrypt @types/uuid`,
22
25
  "Erreur install dev-deps auth",
23
26
  );
24
27
 
@@ -773,9 +776,6 @@ export class JwtAuthGuard extends AuthGuard('jwt') {
773
776
  const request = context.switchToHttp().getRequest();
774
777
  const user = request.user;
775
778
 
776
- console.log('🔍 Required Roles:', requiredRoles);
777
- console.log('👤 User Role:', user?.role);
778
-
779
779
  // Check if the user has one of the required roles
780
780
  return user && user.role && requiredRoles.includes(user.role);
781
781
  }
@@ -9,7 +9,8 @@ async function setupDatabase(inputs) {
9
9
 
10
10
  await runCommand(
11
11
  "npm install dotenv",
12
- "Error installing dotenv automatically, please run it manually now"
12
+ "Error installing dotenv automatically, please run it manually now",
13
+ { critical: false },
13
14
  );
14
15
 
15
16
  switch (inputs.selectedDB) {
@@ -1,5 +1,6 @@
1
1
  const { runCommand } = require("../shell");
2
2
  const path = require("path");
3
+ const fs = require("fs");
3
4
  const {
4
5
  createFile,
5
6
  updateFile,
@@ -68,11 +69,27 @@ async function setupMongoose(inputs) {
68
69
  );
69
70
 
70
71
  // --- Base Configuration (app.module.ts and .env) --- // Generating the .env file
71
- const envContent = `
72
- MONGO_URI=${inputs.dbConfig.MONGO_URI}
73
- MONGO_DB=${inputs.dbConfig.MONGO_DB}
74
- `.trim();
75
- await createFile({ path: ".env", contente: envContent });
72
+ const envPath = ".env";
73
+ const mongoUriLine = `MONGO_URI=${inputs.dbConfig.MONGO_URI}`;
74
+ const mongoDbLine = `MONGO_DB=${inputs.dbConfig.MONGO_DB}`;
75
+
76
+ if (!fs.existsSync(envPath)) {
77
+ const envContent = `${mongoUriLine}\n${mongoDbLine}\n`;
78
+ await createFile({ path: envPath, contente: envContent });
79
+ } else {
80
+ let content = fs.readFileSync(envPath, "utf8");
81
+ if (content.includes("MONGO_URI=")) {
82
+ content = content.replace(/MONGO_URI=.*/g, mongoUriLine);
83
+ } else {
84
+ content += `\n${mongoUriLine}`;
85
+ }
86
+ if (content.includes("MONGO_DB=")) {
87
+ content = content.replace(/MONGO_DB=.*/g, mongoDbLine);
88
+ } else {
89
+ content += `\n${mongoDbLine}`;
90
+ }
91
+ fs.writeFileSync(envPath, content, "utf8");
92
+ }
76
93
 
77
94
  const appModulePath = path.join("src", "app.module.ts");
78
95
  const mongooseImport = `import { MongooseModule } from '@nestjs/mongoose';`;
@@ -86,36 +86,41 @@ async function setupPrisma(inputs) {
86
86
  for (const relation of inputs.entitiesData.relations) {
87
87
  const fromLower = relation.from.toLowerCase();
88
88
  const toLower = relation.to.toLowerCase();
89
- const fromCapitalized = capitalize(relation.from);
90
- const toCapitalized = capitalize(relation.to); // 'from' side (source)
91
89
 
92
90
  if (relation.type === "1-n") {
93
91
  // 'One' side: exclude the name of the other entity's list (e.g., 'articles')
94
92
  fieldsToExcludeMap.get(fromLower).push(`${toLower}s`);
95
- } else if (relation.type === "n-1") {
96
- // 'Many' side: exclude the foreign key (e.g., 'articleId') and the relation name (e.g., 'article')
97
- fieldsToExcludeMap.get(fromLower).push(`${toLower}id`, toLower);
98
- } // Add other relation types (1-1, n-n) if necessary here... // 'to' side (target)
99
- if (relation.type === "1-n") {
100
93
  // 'Many' side: exclude the foreign key (e.g., 'userId') and the relation name (e.g., 'user')
101
94
  fieldsToExcludeMap.get(toLower).push(`${fromLower}id`, fromLower);
102
95
  } else if (relation.type === "n-1") {
96
+ // 'Many' side: exclude the foreign key (e.g., 'articleId') and the relation name (e.g., 'article')
97
+ fieldsToExcludeMap.get(fromLower).push(`${toLower}id`, toLower);
103
98
  // 'One' side: exclude the name of the other entity's list (e.g., 'comments')
104
99
  fieldsToExcludeMap.get(toLower).push(`${fromLower}s`);
100
+ } else if (relation.type === "1-1") {
101
+ // 'from' side (source of relation, holds the FK): exclude foreign key and relation name
102
+ fieldsToExcludeMap.get(fromLower).push(`${toLower}id`, toLower);
103
+ // 'to' side (target of relation): exclude relation name
104
+ fieldsToExcludeMap.get(toLower).push(fromLower);
105
+ } else if (relation.type === "n-n") {
106
+ // Both sides hold list of other entity: exclude plurals
107
+ fieldsToExcludeMap.get(fromLower).push(`${toLower}s`);
108
+ fieldsToExcludeMap.get(toLower).push(`${fromLower}s`);
105
109
  }
106
110
  }
107
111
  }
108
112
  // 2. Initial generation of models WITHOUT incorrect relationship fields
109
113
  for (const entity of inputs.entitiesData.entities) {
110
114
  const entityNameLower = entity.name.toLowerCase();
115
+ const entityNameCap = capitalize(entity.name);
111
116
  // On utilise un Set pour suivre les noms de champs déjà écrits dans ce modèle
112
117
  const addedFields = new Set(["id", "createdat", "updatedat"]);
113
118
 
114
119
  schemaContent += `
115
120
  /**
116
- * ${entity.name} Model
121
+ * ${entityNameCap} Model
117
122
  */
118
- model ${entity.name} {
123
+ model ${entityNameCap} {
119
124
  id String @id @default(uuid())
120
125
  createdAt DateTime @default(now())
121
126
  updatedAt DateTime @updatedAt`;
@@ -159,30 +164,35 @@ async function setupPrisma(inputs) {
159
164
  const to = relation.to;
160
165
  const type = relation.type;
161
166
 
167
+ const fromCap = capitalize(from);
168
+ const toCap = capitalize(to);
169
+ const fromLow = from.toLowerCase();
170
+ const toLow = to.toLowerCase();
171
+
162
172
  // The replacement must be done on the entire generated schemaContent // Using a replacement function to update the content of `schemaContent`
163
173
  if (type === "1-n") {
164
174
  // "One" side (source): adds the list (to[])
165
175
  schemaContent = schemaContent.replace(
166
- new RegExp(`model ${from} \{([\\s\\S]*?)\n\\}`, "m"),
176
+ new RegExp(`model ${fromCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
167
177
  (match, content) => {
168
- const fieldLine = ` ${to}s ${to}[]`;
178
+ const fieldLine = ` ${toLow}s ${toCap}[]`;
169
179
  return match.includes(fieldLine)
170
180
  ? match
171
- : `model ${from} {${content}\n${fieldLine}\n}`;
181
+ : `model ${fromCap} {${content}\n${fieldLine}\n}`;
172
182
  },
173
183
  );
174
184
 
175
185
  // "Many" side (target): adds the relation and the foreign key
176
186
  schemaContent = schemaContent.replace(
177
- new RegExp(`model ${to} \{([\\s\\S]*?)\n\\}`, "m"),
187
+ new RegExp(`model ${toCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
178
188
  (match, content) => {
179
- const relationLine = ` ${from} ${from} @relation(fields: [${from}Id], references: [id])`;
180
- const fkLine = ` ${from}Id String`;
189
+ const relationLine = ` ${fromLow} ${fromCap} @relation(fields: [${fromLow}Id], references: [id])`;
190
+ const fkLine = ` ${fromLow}Id String`;
181
191
  let result = match.includes(relationLine)
182
192
  ? content
183
193
  : `${content}\n${relationLine}`;
184
194
  result = result.includes(fkLine) ? result : `${result}\n${fkLine}`;
185
- return `model ${to} {${result}\n}`;
195
+ return `model ${toCap} {${result}\n}`;
186
196
  },
187
197
  );
188
198
  }
@@ -192,27 +202,26 @@ async function setupPrisma(inputs) {
192
202
 
193
203
  // "Many" side (source = from): adds the relation and the foreign key
194
204
  schemaContent = schemaContent.replace(
195
- new RegExp(`model ${from} \{([\\s\\S]*?)\n\\}`, "m"),
205
+ new RegExp(`model ${fromCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
196
206
  (match, content) => {
197
- const relationLine = ` ${to} ${to} @relation(fields: [${to}Id], references: [id])`;
198
- const fkLine = ` ${to}Id String`;
207
+ const relationLine = ` ${toLow} ${toCap} @relation(fields: [${toLow}Id], references: [id])`;
208
+ const fkLine = ` ${toLow}Id String`;
199
209
  let result = match.includes(relationLine)
200
210
  ? content
201
211
  : `${content}\n${relationLine}`;
202
212
  result = result.includes(fkLine) ? result : `${result}\n${fkLine}`;
203
- return `model ${from} {${result}\n}`;
213
+ return `model ${fromCap} {${result}\n}`;
204
214
  },
205
215
  );
206
216
 
207
217
  // "One" side (target = to): adds the list (from[])
208
218
  schemaContent = schemaContent.replace(
209
- new RegExp(`model ${to} \{([\\s\\S]*?)\n\\}`, "m"),
219
+ new RegExp(`model ${toCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
210
220
  (match, content) => {
211
- const fromCapitalized = capitalize(from);
212
- const fieldLine = ` ${from}s ${from}[]`;
221
+ const fieldLine = ` ${fromLow}s ${fromCap}[]`;
213
222
  return match.includes(fieldLine)
214
223
  ? match
215
- : `model ${to} {${content}\n${fieldLine}\n}`;
224
+ : `model ${toCap} {${content}\n${fieldLine}\n}`;
216
225
  },
217
226
  );
218
227
  }
@@ -220,28 +229,28 @@ async function setupPrisma(inputs) {
220
229
  if (type === "1-1") {
221
230
  // 'from' side (source): adds the relation, foreign key, and @unique attribute
222
231
  schemaContent = schemaContent.replace(
223
- new RegExp(`model ${from} \{([\\s\\S]*?)\n\\}`, "m"),
232
+ new RegExp(`model ${fromCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
224
233
  (match, content) => {
225
- const relationLine = ` ${to} ${to}? @relation(fields: [${to}Id], references: [id])`; // The foreign key must be unique in a 1-1 relationship, and optional for flexibility
226
- const fkLine = ` ${to}Id String? @unique`;
234
+ const relationLine = ` ${toLow} ${toCap}? @relation(fields: [${toLow}Id], references: [id])`; // The foreign key must be unique in a 1-1 relationship, and optional for flexibility
235
+ const fkLine = ` ${toLow}Id String? @unique`;
227
236
 
228
237
  let result = match.includes(relationLine)
229
238
  ? content
230
239
  : `${content}\n${relationLine}`;
231
240
  result = result.includes(fkLine) ? result : `${result}\n${fkLine}`;
232
- return `model ${from} {${result}\n}`;
241
+ return `model ${fromCap} {${result}\n}`;
233
242
  },
234
243
  );
235
244
 
236
245
  // 'to' side (target): adds the inverse relation (optional)
237
246
  schemaContent = schemaContent.replace(
238
- new RegExp(`model ${to} \{([\\s\\S]*?)\n\\}`, "m"),
247
+ new RegExp(`model ${toCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
239
248
  (match, content) => {
240
249
  // Inverse relation (optional because 'from' holds the FK)
241
- const fieldLine = ` ${from} ${from}?`;
250
+ const fieldLine = ` ${fromLow} ${fromCap}?`;
242
251
  return match.includes(fieldLine)
243
252
  ? match
244
- : `model ${to} {${content}\n${fieldLine}\n}`;
253
+ : `model ${toCap} {${content}\n${fieldLine}\n}`;
245
254
  },
246
255
  );
247
256
  }
@@ -249,23 +258,23 @@ async function setupPrisma(inputs) {
249
258
  if (type === "n-n") {
250
259
  // 'from' side (source): adds the list (to[])
251
260
  schemaContent = schemaContent.replace(
252
- new RegExp(`model ${from} \{([\\s\\S]*?)\n\\}`, "m"),
261
+ new RegExp(`model ${fromCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
253
262
  (match, content) => {
254
- const fieldLine = ` ${to}s ${to}[]`;
263
+ const fieldLine = ` ${toLow}s ${toCap}[]`;
255
264
  return match.includes(fieldLine)
256
265
  ? match
257
- : `model ${from} {${content}\n${fieldLine}\n}`;
266
+ : `model ${fromCap} {${content}\n${fieldLine}\n}`;
258
267
  },
259
268
  );
260
269
 
261
270
  // 'to' side (target): adds the list (from[])
262
271
  schemaContent = schemaContent.replace(
263
- new RegExp(`model ${to} \{([\\s\\S]*?)\n\\}`, "m"),
272
+ new RegExp(`model ${toCap} \\{([\\s\\S]*?)\\n\\}`, "m"),
264
273
  (match, content) => {
265
- const fieldLine = ` ${from}s ${from}[]`;
274
+ const fieldLine = ` ${fromLow}s ${fromCap}[]`;
266
275
  return match.includes(fieldLine)
267
276
  ? match
268
- : `model ${to} {${content}\n${fieldLine}\n}`;
277
+ : `model ${toCap} {${content}\n${fieldLine}\n}`;
269
278
  },
270
279
  );
271
280
  }
@@ -289,7 +298,7 @@ async function setupPrisma(inputs) {
289
298
  path: schemaPath,
290
299
  contente: baseSchema,
291
300
  });
292
- await runCommand(`npx prisma format`, "❌ Failed to format prisma schema");
301
+ await runCommand(`npx prisma format`, "❌ Failed to format prisma schema — run 'npx prisma format' manually", { critical: false });
293
302
 
294
303
  // 📁 Step 6: Creating the `src/prisma` structure
295
304
  const defaultPatch = "src/prisma";
@@ -344,6 +353,7 @@ async function setupPrisma(inputs) {
344
353
  await runCommand(
345
354
  `${inputs.packageManager} add dotenv`,
346
355
  "❌ Failed to install dotenv",
356
+ { critical: false },
347
357
  );
348
358
 
349
359
  // Creating prisma.config.ts file to load environment variables
@@ -362,7 +372,7 @@ async function setupPrisma(inputs) {
362
372
  }
363
373
 
364
374
  // Step 7: Generating the Prisma client
365
- await runCommand("npx prisma generate", "❌ Prisma generation failed");
375
+ await runCommand("npx prisma generate", "❌ Prisma client generation failed — run 'npx prisma generate' manually", { critical: false });
366
376
 
367
377
  // Step 8: Migration (ONLY in 'new' mode)
368
378
  if (inputs.isDemo) {
@@ -645,18 +655,18 @@ async function updatePrismaSchema(entityData) {
645
655
 
646
656
  switch (relation.type) {
647
657
  case "n-1": // "Many to One" : Le nouveau module appartient à une cible
648
- relationFields += `\n ${targetLow} ${targetLow} @relation(fields: [${targetLow}Id], references: [id])`;
658
+ relationFields += `\n ${targetLow} ${targetCap} @relation(fields: [${targetLow}Id], references: [id])`;
649
659
  relationFields += `\n ${targetLow}Id String`;
650
660
  break;
651
661
  case "1-n": // "One to Many" : Le nouveau module possède plusieurs cibles
652
- relationFields += `\n ${targetLow}s ${targetLow}[]`;
662
+ relationFields += `\n ${targetLow}s ${targetCap}[]`;
653
663
  break;
654
664
  case "1-1":
655
- relationFields += `\n ${targetLow} ${targetLow}? @relation(fields: [${targetLow}Id], references: [id])`;
665
+ relationFields += `\n ${targetLow} ${targetCap}? @relation(fields: [${targetLow}Id], references: [id])`;
656
666
  relationFields += `\n ${targetLow}Id String? @unique`;
657
667
  break;
658
668
  case "n-n":
659
- relationFields += `\n ${targetLow}s ${targetLow}[]`;
669
+ relationFields += `\n ${targetLow}s ${targetCap}[]`;
660
670
  break;
661
671
  }
662
672
  }
@@ -665,7 +675,7 @@ async function updatePrismaSchema(entityData) {
665
675
  let modelBlock = `\n/**
666
676
  * ${capitalizedName} Model
667
677
  */
668
- model ${lowercasedName} {
678
+ model ${capitalizedName} {
669
679
  id String @id @default(uuid())
670
680
  createdAt DateTime @default(now())
671
681
  updatedAt DateTime @updatedAt${relationFields}`;
@@ -686,117 +696,53 @@ model ${lowercasedName} {
686
696
  // 4️⃣ Injection de l'INVERSE dans le modèle cible (Target)
687
697
  if (relation) {
688
698
  const targetCap = capitalize(relation.target);
689
- const targetLow = capitalize(relation.target);
699
+ const targetLow = relation.target.toLowerCase();
690
700
  const newCap = capitalizedName;
691
701
  const newLow = name.toLowerCase();
692
702
 
693
703
  let inverseField = "";
694
704
  switch (relation.type) {
695
705
  case "n-1": // Inverse d'un Many-to-One est un One-to-Many
696
- inverseField = ` ${newLow}s ${newLow}[]`;
706
+ inverseField = ` ${newLow}s ${newCap}[]`;
697
707
  break;
698
708
  case "1-n": // Inverse d'un One-to-Many est un Many-to-One
699
- inverseField = ` ${newLow} ${newLow} @relation(fields: [${newLow}Id], references: [id])\n ${newLow}Id String`;
709
+ inverseField = ` ${newLow} ${newCap} @relation(fields: [${newLow}Id], references: [id])\n ${newLow}Id String`;
700
710
  break;
701
711
  case "1-1":
702
- inverseField = ` ${newLow} ${newLow}?`;
712
+ inverseField = ` ${newLow} ${newCap}?`;
703
713
  break;
704
714
  case "n-n":
705
- inverseField = ` ${newLow}s ${newLow}[]`;
715
+ inverseField = ` ${newLow}s ${newCap}[]`;
706
716
  break;
707
717
  }
708
718
 
709
719
  // On utilise une RegEx pour trouver le bloc du modèle cible et insérer avant la fermeture "}"
710
720
  await updateFile({
711
721
  path: schemaPath,
712
- pattern: new RegExp(`model ${targetLow} \\{([\\s\\S]*?)\\}`, "g"),
713
- replacement: `model ${targetLow} {$1\n${inverseField}\n}`,
722
+ pattern: new RegExp(`model ${targetCap} \\{([\\s\\S]*?)\\}`, "g"),
723
+ replacement: `model ${targetCap} {$1\n${inverseField}\n}`,
714
724
  });
715
725
  }
716
726
 
717
727
  // 3️⃣ Prisma Commands Sync (meilleur sur Windows)
718
- try {
719
- console.log(info("Running: prisma format..."));
720
- execSync("npx prisma format", { stdio: "pipe" });
721
-
722
- console.log(info("Running: prisma generate..."));
723
- execSync("npx prisma generate", { stdio: "pipe" });
724
-
725
- console.log(info("Running: prisma migrate dev..."));
726
- execSync(
727
- `npx prisma migrate dev --name add_module_${entityData.name} --force`,
728
- {
729
- stdio: "pipe",
730
- },
731
- );
732
-
733
- // console.log(success("🎉 Prisma migration completed successfully!"));
734
- } catch (err) {
735
- // 4️⃣ Gestion spécifique Windows EPERM
736
- if (
737
- err.message.includes("operation not permitted") ||
738
- err.message.includes("EPERM") ||
739
- err.message.includes("EBUSY")
740
- ) {
741
- logWarning("System Lock Detected!");
742
- console.log(
743
- `${info("Prisma Client binary is locked because your server is running.")}`,
744
- );
745
- console.log(
746
- `${info("ACTION:")} Stop NestJS (Ctrl+C) and run manually:\n`,
747
- );
748
- console.log(`${success("npx prisma generate")}`);
749
- console.log(`${success("npx prisma migrate dev")}\n`);
750
- } else {
751
- console.log(error("❌ Prisma error: " + err.message));
752
- }
753
- }
754
- }
755
-
756
- /* async function updatePrismaSchema(entityData) {
757
- const schemaPath = "prisma/schema.prisma";
758
-
759
- // Construction du bloc modèle
760
- let modelBlock = `\nmodel ${capitalize(entityData.name)} {
761
- id String @id @default(uuid())
762
- createdAt DateTime @default(now())
763
- updatedAt DateTime @updatedAt`;
764
-
765
- entityData.fields.forEach((field) => {
766
- // Note: On réutilise ta fonction mapTypeToPrisma ici
767
- modelBlock += `\n ${field.name} ${mapTypeToPrisma(field.type)}`;
768
- });
769
-
770
- modelBlock += `\n}\n`;
771
-
772
- // Injection à la fin du fichier
773
- await updateFile({
774
- path: schemaPath,
775
- pattern: /$/, // On ajoute à la fin
776
- replacement: modelBlock,
777
- });
728
+ await runCommand(
729
+ "npx prisma format",
730
+ "❌ Failed to format prisma schema — run 'npx prisma format' manually",
731
+ { critical: false }
732
+ );
778
733
 
779
- // ⚠️ IMPORTANT : On formate et on régénère le client pour supprimer les erreurs TS
780
- try {
781
- await runCommand("npx prisma format");
782
- await runCommand("npx prisma generate");
783
- await runCommand(
784
- `npx prisma migrate dev --name add_module_${entityData.name}`,
785
- );
786
- } catch (err) {
787
- if (err.message.includes("EPERM")) {
788
- console.log(
789
- `\n${warning("⚠️ System Lock:")} Could not update Prisma Client binary because your app is currently running.`,
790
- );
791
- console.log(
792
- `${info("ACTION REQUIRED:")} Stop your dev server (Ctrl+C) and run: ${success("| npx prisma generate | and | npx prisma migrate dev |")}\n`,
793
- );
794
- } else {
795
- logError("Prisma error: " + err.message);
796
- }
797
- }
734
+ await runCommand(
735
+ "npx prisma generate",
736
+ "❌ Failed to generate prisma client — run 'npx prisma generate' manually",
737
+ { critical: false }
738
+ );
798
739
 
799
- throw new Error(err);
800
- } */
740
+ await runCommand(
741
+ `npx prisma migrate dev --name add_module_${entityData.name} --force`,
742
+ `❌ Failed to run prisma migration — run 'npx prisma migrate dev --name add_module_${entityData.name}' manually`,
743
+ { critical: false }
744
+ );
745
+ }
801
746
 
802
747
  module.exports = { setupPrisma, updatePrismaSchema };
748
+