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.
Files changed (63) hide show
  1. package/.gitattributes +6 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. package/.github/ISSUE_TEMPLATE/pull_request_template.md +24 -0
  5. package/CHANGELOG.fr.md +97 -97
  6. package/CHANGELOG.md +98 -98
  7. package/CLI_USAGE.fr.md +331 -331
  8. package/CLI_USAGE.md +364 -364
  9. package/DEMO.fr.md +292 -292
  10. package/DEMO.md +294 -294
  11. package/LICENSE +21 -21
  12. package/MIGRATION_GUIDE.fr.md +127 -127
  13. package/MIGRATION_GUIDE.md +124 -124
  14. package/QUICK_START.fr.md +152 -152
  15. package/QUICK_START.md +169 -169
  16. package/README.fr.md +653 -659
  17. package/SECURITY.md +10 -0
  18. package/bin/nestcraft.js +84 -64
  19. package/commands/demo.js +333 -330
  20. package/commands/generate.js +93 -0
  21. package/commands/generateConf.js +91 -0
  22. package/commands/help.js +78 -78
  23. package/commands/info.js +48 -48
  24. package/commands/new.js +338 -335
  25. package/commands/start.js +19 -19
  26. package/commands/test.js +7 -7
  27. package/package.json +41 -41
  28. package/readme.md +638 -643
  29. package/utils/cliParser.js +133 -76
  30. package/utils/colors.js +62 -62
  31. package/utils/configs/configureDocker.js +120 -120
  32. package/utils/configs/setupCleanArchitecture.js +563 -557
  33. package/utils/configs/setupLightArchitecture.js +701 -660
  34. package/utils/envGenerator.js +122 -122
  35. package/utils/file-utils/packageJsonUtils.js +49 -55
  36. package/utils/file-utils/saveProjectConfig.js +36 -0
  37. package/utils/fullModeInput.js +607 -607
  38. package/utils/generators/application/dtoUpdater.js +54 -0
  39. package/utils/generators/cleanModuleGenerator.js +475 -0
  40. package/utils/generators/database/setupDatabase.js +31 -0
  41. package/utils/generators/domain/entityUpdater.js +78 -0
  42. package/utils/generators/infrastructure/mapperUpdater.js +65 -0
  43. package/utils/generators/lightModuleGenerator.js +131 -0
  44. package/utils/generators/relation/relation.engine.js +64 -0
  45. package/utils/interactive/askEntityInputs.js +165 -0
  46. package/utils/lightModeInput.js +460 -460
  47. package/utils/loggers/logError.js +7 -7
  48. package/utils/loggers/logInfo.js +7 -7
  49. package/utils/loggers/logSuccess.js +7 -7
  50. package/utils/loggers/logWarning.js +7 -7
  51. package/utils/setups/orms/typeOrmSetup.js +630 -630
  52. package/utils/setups/projectSetup.js +46 -46
  53. package/utils/setups/setupAuth.js +973 -926
  54. package/utils/setups/setupDatabase.js +75 -75
  55. package/utils/setups/setupLogger.js +69 -59
  56. package/utils/setups/setupMongoose.js +377 -432
  57. package/utils/setups/setupPrisma.js +802 -630
  58. package/utils/setups/setupSwagger.js +97 -88
  59. package/utils/shell.js +32 -32
  60. package/utils/spinner.js +57 -57
  61. package/utils/systemCheck.js +124 -124
  62. package/utils/userInput.js +421 -421
  63. package/utils/utils.js +2197 -1762
@@ -1,432 +1,377 @@
1
- const { runCommand } = require("../shell");
2
- const path = require("path");
3
- const {
4
- createFile,
5
- updateFile,
6
- createDirectory,
7
- capitalize,
8
- } = require("../userInput");
9
- const { logSuccess } = require("../loggers/logSuccess");
10
- const { logInfo } = require("../loggers/logInfo");
11
- const { updatePackageJson } = require("../file-utils/packageJsonUtils");
12
- const { logWarning } = require("../loggers/logWarning");
13
-
14
- /* async function setupMongoose(inputs) {
15
- logInfo("πŸ“¦ Installation de Mongoose et @nestjs/mongoose...");
16
- runCommand(
17
- "npm install @nestjs/mongoose mongoose",
18
- "Mongoose inetaller avec succes !"
19
- );
20
-
21
- // GΓ©nΓ©ration du fichier .env
22
- const envContent = `
23
- MONGO_URI=${inputs.dbConfig.MONGO_URI}
24
- MONGO_DB=${inputs.dbConfig.MONGO_DB}
25
- `.trim();
26
- await createFile({ path: ".env", contente: envContent });
27
-
28
- // Ajout de l'import et de la configuration Mongoose dans app.module.ts
29
- const appModulePath = path.join("src", "app.module.ts");
30
- const mongooseImport = `import { MongooseModule } from '@nestjs/mongoose';`;
31
-
32
- // Ajoute l'import si absent
33
- await updateFile({
34
- path: appModulePath,
35
- pattern: /import {[\s\S]*?} from '@nestjs\/config';/,
36
- replacement: (match) => `${match}\n${mongooseImport}`,
37
- });
38
-
39
- // Ajoute la configuration MongooseModule dans les imports
40
- const importsPattern =
41
- /imports:\s*\[[\s\S]*?ConfigModule\.forRoot\([\s\S]*?\),/;
42
- await updateFile({
43
- path: appModulePath,
44
- pattern: importsPattern,
45
- replacement: (match) =>
46
- `${match}
47
- MongooseModule.forRoot(process.env.MONGO_URI || " ", {
48
- dbName: process.env.MONGO_DB,
49
- }),`,
50
- });
51
-
52
- if (inputs.isDemo) {
53
- await setupMongooseSeeding(inputs);
54
- }
55
-
56
- logSuccess("Mongoose configurΓ© et injectΓ© dans app.module.ts !");
57
- } */
58
-
59
- // The utility function for mapping is necessary for setupMongoose
60
- /**
61
- * Maps generic entity types to Mongoose schema types.
62
- * @param {string} type - Generic type (e.g., 'string', 'number', 'Date', 'string[]', 'MonEnum')
63
- * @returns {string} The corresponding Mongoose type.
64
- */
65
- function mapTypeToMongoose(type) {
66
- // Handles the case of arrays (e.g., 'string[]')
67
- if (type.endsWith("[]")) {
68
- const innerType = type.slice(0, -2);
69
- const innerMapping = mapTypeToMongoose(innerType); // Recursive // Returns the type enclosed in brackets, Mongoose understands this // Note: Remove "mongoose.Schema.Types." for the array of Mixed
70
- return innerMapping.replace("mongoose.Schema.Types.", "") === "Mixed"
71
- ? "[mongoose.Schema.Types.Mixed]"
72
- : `[${innerMapping}]`;
73
- }
74
-
75
- const typeLower = type.toLowerCase();
76
-
77
- switch (typeLower) {
78
- case "string":
79
- case "text":
80
- case "uuid":
81
- return "String";
82
-
83
- case "number":
84
- case "decimal":
85
- case "int":
86
- return "Number";
87
-
88
- case "boolean":
89
- return "Boolean";
90
-
91
- case "date":
92
- return "Date";
93
-
94
- case "json":
95
- case "object": // Uses Mixed for unstructured JSON objects
96
- return "mongoose.Schema.Types.Mixed";
97
-
98
- default: // Case of a custom Enum type (e.g., 'StatusEnum')
99
- return "String";
100
- }
101
- }
102
-
103
- async function setupMongoose(inputs) {
104
- logWarning(
105
- `Mongoose integration is currently in Beta (v0.2.x). \nSome manual import fixes and corrections might be required in the generated files.`
106
- );
107
-
108
- logInfo("πŸ“¦ Installing Mongoose and @nestjs/mongoose...");
109
-
110
- await runCommand(
111
- `${inputs.packageManager} install @nestjs/mongoose mongoose`,
112
- "Mongoose and its dependencies successfully installed!"
113
- );
114
-
115
- // --- Base Configuration (app.module.ts and .env) --- // Generating the .env file
116
- const envContent = `
117
- MONGO_URI=${inputs.dbConfig.MONGO_URI}
118
- MONGO_DB=${inputs.dbConfig.MONGO_DB}
119
- `.trim();
120
- await createFile({ path: ".env", contente: envContent });
121
-
122
- const appModulePath = path.join("src", "app.module.ts");
123
- const mongooseImport = `import { MongooseModule } from '@nestjs/mongoose';`;
124
- const mongooseForRoot = `
125
- MongooseModule.forRoot(process.env.MONGO_URI || "mongodb://localhost/test", {
126
- dbName: process.env.MONGO_DB,
127
- }),`;
128
-
129
- // 1. Adding MongooseModule import
130
- await updateFile({
131
- path: appModulePath,
132
- pattern: /import {[\s\S]*?} from '@nestjs\/config';/,
133
- replacement: (match) => `${match}\n${mongooseImport}`,
134
- });
135
-
136
- // 2. Adding MongooseModule.forRoot() configuration
137
- const importsPattern =
138
- /imports:\s*\[[\s\S]*?ConfigModule\.forRoot\([\s\S]*?\),/;
139
- await updateFile({
140
- path: appModulePath,
141
- pattern: importsPattern,
142
- replacement: (match) => `${match}${mongooseForRoot}`,
143
- });
144
-
145
- // --- Generating Mongoose Entities (Schemas) ---
146
- logInfo("πŸ“ Generating Mongoose schemas (src/schemas)...");
147
- await createDirectory("src/schemas");
148
-
149
- let forFeatureImports = [];
150
-
151
- for (const entity of inputs.entitiesData.entities) {
152
- const entityName = capitalize(entity.name);
153
- const entityNameLower = entity.name.toLowerCase();
154
- const schemaName = `${entityName}Schema`;
155
- const interfaceName = `${entityName}Document`;
156
-
157
- let fieldsContent = "";
158
- let extraImports = "";
159
- let mongooseImportCode =
160
- "import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';\n";
161
-
162
- // Conditional addition of import * as mongoose
163
- if (
164
- entity.fields.some(
165
- (f) => mapTypeToMongoose(f.type) === "mongoose.Schema.Types.Mixed"
166
- )
167
- ) {
168
- mongooseImportCode += "import * as mongoose from 'mongoose';\n";
169
- }
170
-
171
- // --- Base Data Fields --- // Adding the role field if it's the User entity (assuming a Role enum exists)
172
- if (entityNameLower === "user") {
173
- extraImports += `import { Role } from 'src/user/domain/enums/role.enum';\n`;
174
- fieldsContent += `
175
- @Prop({ type: String, enum: Role, default: Role.USER })
176
- role: Role;
177
- `;
178
- }
179
-
180
- for (const field of entity.fields) {
181
- // We ignore fields if we know they are relationships
182
- // (Exclusion logic should be done, but here we simplify Mongoose)
183
-
184
- const mongooseType = mapTypeToMongoose(field.type);
185
- const tsType = field.type;
186
-
187
- const isRequired =
188
- field.name.toLowerCase() !== "isactive" &&
189
- field.name.toLowerCase() !== "password";
190
- const requiredOption = isRequired ? ", required: true" : "";
191
-
192
- if (
193
- mongooseType === "String" &&
194
- tsType.charAt(0) === tsType.charAt(0).toUpperCase() &&
195
- tsType !== "Role"
196
- ) {
197
- extraImports += `import { ${tsType} } from '../shared/enums/${tsType.toLowerCase()}.enum';\n`;
198
- }
199
-
200
- fieldsContent += `
201
- @Prop({ type: ${mongooseType}${requiredOption} })
202
- ${field.name}: ${tsType};
203
- `;
204
- }
205
-
206
- // --- Mongoose Relations (References) ---
207
- for (const relation of inputs.entitiesData.relations) {
208
- const relFrom = relation.from;
209
- const relTo = relation.to;
210
-
211
- if (relTo.toLowerCase() === entityNameLower) {
212
- const targetEntity = capitalize(relFrom);
213
- mongooseImportCode += "import * as mongoose from 'mongoose';\n";
214
- extraImports += `import { ${targetEntity} } from './${targetEntity}.schema';\n`;
215
-
216
- if (
217
- relation.type === "1-n" ||
218
- relation.type === "1-1" ||
219
- relation.type === "n-1"
220
- ) {
221
- const fkName = `${relFrom.toLowerCase()}Id`;
222
- fieldsContent += `
223
- // Reference to the ${targetEntity} entity
224
- @Prop({ type: mongoose.Schema.Types.ObjectId, ref: '${targetEntity}' })
225
- ${fkName}: ${targetEntity};
226
- `;
227
- }
228
-
229
- // n-n : array reference (non-standard Mongoose, but common)
230
- else if (relation.type === "n-n") {
231
- const collectionName = `${relFrom.toLowerCase()}s`;
232
- fieldsContent += `
233
- // Multiple references to the ${targetEntity} entity
234
- @Prop({ type: [{ type: mongoose.Schema.Types.ObjectId, ref: '${targetEntity}' }] })
235
- ${collectionName}: ${targetEntity}[];
236
- `;
237
- }
238
- }
239
- }
240
-
241
- // --- Final Generation of Schemas/Documents File ---
242
- const content = `${mongooseImportCode}
243
- ${extraImports}
244
-
245
- export type ${interfaceName} = ${entityName} & mongoose.Document;
246
-
247
- @Schema({ timestamps: true })
248
- export class ${entityName} {
249
- // Mongoose handles the ID (_id: ObjectId)
250
- ${fieldsContent}
251
- }
252
-
253
- export const ${schemaName} = SchemaFactory.createForClass(${entityName});
254
- `;
255
-
256
- await createFile({
257
- path: `src/schemas/${entityName}.schema.ts`,
258
- contente: content,
259
- });
260
-
261
- forFeatureImports.push(
262
- `{ name: ${entityName}.name, schema: ${schemaName} }`
263
- );
264
- }
265
-
266
- // --- Final Update of app.module.ts --- //
267
- // 3. Adding entity (schema) imports at the beginning of app.module.ts
268
- const schemaImports = inputs.entitiesData.entities
269
- .map((entity) => {
270
- const entityName = capitalize(entity.name);
271
- const schemaName = `${entityName}Schema`;
272
- return `import { ${entityName}, ${schemaName} } from './schemas/${entityName}.schema';`;
273
- })
274
- .join("\n");
275
-
276
- await updateFile({
277
- path: appModulePath,
278
- pattern: new RegExp(mongooseImport, "g"), // Targets the existing Mongoose import
279
- replacement: (match) => `${match}\n${schemaImports}`,
280
- }); // 4. Adding MongooseModule.forFeature([...])
281
-
282
- const forFeatureBlock = `MongooseModule.forFeature([${forFeatureImports.join(
283
- ", "
284
- )}]),`;
285
-
286
- await updateFile({
287
- path: appModulePath,
288
- pattern: new RegExp(mongooseForRoot.trim().replace(/[\n\r]/g, "\\s*"), "g"),
289
- replacement: (match) => `${match}\n\t${forFeatureBlock}`,
290
- });
291
-
292
- if (inputs.isDemo) {
293
- // The generateSampleData function must be adapted for 'new Date()' usage and Mongoose types
294
- await setupMongooseSeeding(inputs);
295
- }
296
-
297
- logSuccess(
298
- "Mongoose configuration complete. Schemas are generated in src/schemas!"
299
- );
300
- }
301
-
302
- async function setupMongooseSeeding(inputs) {
303
- logInfo("βš™οΈ Configuring seeding for Mongoose..."); // --- Dependencies ---
304
-
305
- const mongooseDevDeps = ["ts-node", "@types/node", "@types/bcrypt"];
306
- await runCommand(
307
- `${inputs.packageManager} add -D ${mongooseDevDeps.join(" ")}`,
308
- "❌ Failed to install Mongoose seeding dependencies"
309
- );
310
- await runCommand(
311
- `${inputs.packageManager} install bcrypt`,
312
- "❌ Failed to install bcrypt"
313
- );
314
-
315
- // --- Scripts in package.json ---
316
- const mongooseScripts = {
317
- seed: "ts-node src/database/seed.ts",
318
- };
319
- await updatePackageJson(inputs, mongooseScripts);
320
-
321
- // --- Creating seed.ts file ---
322
- await createDirectory("src/database");
323
- const seedTsContent = generateMongooseSeedContent(
324
- inputs.entitiesData.entities
325
- );
326
-
327
- await createFile({
328
- path: `src/database/seed.ts`,
329
- contente: seedTsContent,
330
- });
331
-
332
- logSuccess("Mongoose seeding configured.");
333
- }
334
-
335
- function generateMongooseSeedContent(entities) {
336
- return `/**
337
- * πŸš€ Mongoose Seeding Script
338
- * Automatically generated by NestCraftX
339
- * ------------------------------------
340
- * This script inserts sample data into the MongoDB database.
341
- * Command: npm run seed
342
- */
343
-
344
- import mongoose from 'mongoose';
345
- import bcrypt from 'bcrypt';
346
- ${entities
347
- .map(
348
- (e) =>
349
- `import { ${
350
- e.name
351
- }Schema } from '../modules/${e.name.toLowerCase()}/${e.name.toLowerCase()}.schema';`
352
- )
353
- .join("\n")}
354
-
355
- async function connectDB() {
356
- const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/${
357
- entities[0]?.name?.toLowerCase() || "app"
358
- }_db';
359
- await mongoose.connect(MONGO_URI);
360
- console.log('βœ… Connected to MongoDB');
361
- }
362
-
363
- async function seed() {
364
- try {
365
- await connectDB();
366
-
367
- ${entities
368
- .map((entity) => {
369
- const modelVar = `${entity.name}Model`;
370
- const dataVar = `${entity.name.toLowerCase()}Data`;
371
- return `
372
- // --- ${entity.name} ---
373
- const ${modelVar} = mongoose.model('${entity.name}', ${entity.name}Schema);
374
- const ${dataVar} = [
375
- ${generateSampleData(entity)}
376
- ];
377
- await ${modelVar}.insertMany(${dataVar});
378
- console.log('βœ… ${entity.name} data inserted');
379
- `;
380
- })
381
- .join("\n")}
382
-
383
- console.log('πŸŽ‰ Seeding finished successfully.');
384
- await mongoose.disconnect();
385
- } catch (err) {
386
- console.error('❌ Error during seeding:', err);
387
- process.exit(1);
388
- }
389
- }
390
-
391
- seed();
392
- `;
393
- }
394
-
395
- /**
396
- * Generates sample data for each entity, handling advanced types.
397
- */
398
- function generateSampleData(entity) {
399
- const fields = entity.fields || [];
400
- const sampleObj = fields
401
- .map((f) => {
402
- // For 'string[]', 'number[]', 'Date[]', etc. types
403
- if (f.type.endsWith("[]")) {
404
- const baseType = f.type.slice(0, -2);
405
- if (baseType === "string" || baseType === "text")
406
- return `${f.name}: ['${f.name}_item_1', '${f.name}_item_2']`;
407
- if (baseType === "number" || baseType === "decimal")
408
- return `${f.name}: [10, 20]`;
409
- if (baseType === "Date") return `${f.name}: [new Date(), new Date()]`;
410
- return `${f.name}: []`;
411
- }
412
-
413
- if (f.name.toLowerCase().includes("password")) {
414
- return `${f.name}: await bcrypt.hash('password123', 10)`;
415
- }
416
- if (["string", "text", "uuid"].includes(f.type.toLowerCase()))
417
- return `${f.name}: '${f.name}_example'`;
418
- if (["number", "decimal", "int"].includes(f.type.toLowerCase()))
419
- return `${f.name}: ${Math.floor(Math.random() * 100)}`;
420
- if (f.type === "boolean") return `${f.name}: true`;
421
- if (f.type === "date") return `${f.name}: new Date()`;
422
- if (["json", "object"].includes(f.type.toLowerCase()))
423
- return `${f.name}: { key: 'value', count: 42 }`;
424
-
425
- return `${f.name}: null`;
426
- })
427
- .join(",\n ");
428
-
429
- return `{ ${sampleObj} }`;
430
- }
431
-
432
- module.exports = { setupMongoose };
1
+ const { runCommand } = require("../shell");
2
+ const path = require("path");
3
+ const {
4
+ createFile,
5
+ updateFile,
6
+ createDirectory,
7
+ capitalize,
8
+ } = require("../userInput");
9
+ const { logSuccess } = require("../loggers/logSuccess");
10
+ const { logInfo } = require("../loggers/logInfo");
11
+ const { updatePackageJson } = require("../file-utils/packageJsonUtils");
12
+
13
+ /**
14
+ * Maps generic entity types to Mongoose schema types.
15
+ * @param {string} type - Generic type (e.g., 'string', 'number', 'Date', 'string[]', 'MonEnum')
16
+ * @returns {string} The corresponding Mongoose type.
17
+ */
18
+ function mapTypeToMongoose(type) {
19
+ // Handles the case of arrays (e.g., 'string[]')
20
+ if (type.endsWith("[]")) {
21
+ const innerType = type.slice(0, -2);
22
+ const innerMapping = mapTypeToMongoose(innerType); // Recursive // Returns the type enclosed in brackets, Mongoose understands this // Note: Remove "mongoose.Schema.Types." for the array of Mixed
23
+ return innerMapping.replace("mongoose.Schema.Types.", "") === "Mixed"
24
+ ? "[mongoose.Schema.Types.Mixed]"
25
+ : `[${innerMapping}]`;
26
+ }
27
+
28
+ const typeLower = type.toLowerCase();
29
+
30
+ switch (typeLower) {
31
+ case "string":
32
+ case "text":
33
+ case "uuid":
34
+ return "String";
35
+
36
+ case "number":
37
+ case "decimal":
38
+ case "int":
39
+ return "Number";
40
+
41
+ case "boolean":
42
+ return "Boolean";
43
+
44
+ case "date":
45
+ return "Date";
46
+
47
+ case "json":
48
+ case "object": // Uses Mixed for unstructured JSON objects
49
+ return "mongoose.Schema.Types.Mixed";
50
+
51
+ default: // Case of a custom Enum type (e.g., 'StatusEnum')
52
+ return "String";
53
+ }
54
+ }
55
+
56
+ async function setupMongoose(inputs) {
57
+ /* logWarning(
58
+ `Mongoose integration is currently in Beta (v0.2.x). \nSome manual import fixes and corrections might be required in the generated files.`
59
+ ); */
60
+
61
+ const isFull = inputs.mode === "full";
62
+
63
+ logInfo("πŸ“¦ Installing Mongoose and @nestjs/mongoose...");
64
+
65
+ await runCommand(
66
+ `${inputs.packageManager} install @nestjs/mongoose mongoose`,
67
+ "Mongoose and its dependencies successfully installed!"
68
+ );
69
+
70
+ // --- 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 });
76
+
77
+ const appModulePath = path.join("src", "app.module.ts");
78
+ const mongooseImport = `import { MongooseModule } from '@nestjs/mongoose';`;
79
+ const mongooseForRoot = `
80
+ MongooseModule.forRoot(process.env.MONGO_URI || "mongodb://localhost/test", {
81
+ dbName: process.env.MONGO_DB,
82
+ }),`;
83
+
84
+ // 1. Adding MongooseModule import
85
+ await updateFile({
86
+ path: appModulePath,
87
+ pattern: /import {[\s\S]*?} from '@nestjs\/config';/,
88
+ replacement: (match) => `${match}\n${mongooseImport}`,
89
+ });
90
+
91
+ // 2. Adding MongooseModule.forRoot() configuration
92
+ const importsPattern =
93
+ /imports:\s*\[[\s\S]*?ConfigModule\.forRoot\([\s\S]*?\),/;
94
+ await updateFile({
95
+ path: appModulePath,
96
+ pattern: importsPattern,
97
+ replacement: (match) => `${match}${mongooseForRoot}`,
98
+ });
99
+
100
+ // --- Generating Mongoose Entities (Schemas) ---
101
+ logInfo("πŸ“ Generating Mongoose schemas (src/schemas)...");
102
+
103
+ let forFeatureImports = [];
104
+
105
+ for (const entity of inputs.entitiesData.entities) {
106
+ const entityName = capitalize(entity.name);
107
+ const entityNameLower = entity.name.toLowerCase();
108
+ const schemaName = `${entityName}Schema`;
109
+ const interfaceName = `${entityName}Document`;
110
+
111
+ let fieldsContent = "";
112
+ let extraImports = "";
113
+ // On commence avec mongoose dΓ©jΓ  importΓ© pour Γ©viter les erreurs "namespace not found"
114
+ let mongooseImportCode =
115
+ "import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';\nimport * as mongoose from 'mongoose';\n";
116
+
117
+ // --- Gestion du Role (Γ‰viter le doublon) ---
118
+ // --- Gestion du Role (Γ‰viter le doublon) ---
119
+ if (entityNameLower === "user") {
120
+ extraImports += isFull
121
+ ? `import { Role } from 'src/user/domain/enums/role.enum';\n`
122
+ : `import { Role } from 'src/common/enums/role.enum';\n`;
123
+
124
+ fieldsContent += `
125
+ @Prop({ type: String, enum: Role, default: Role.USER })
126
+ role: Role;
127
+ `;
128
+ }
129
+
130
+ for (const field of entity.fields) {
131
+ // πŸ›‘οΈ CORRECTION : On saute le champ s'il a dΓ©jΓ  Γ©tΓ© traitΓ© manuellement (ex: role)
132
+ if (entityNameLower === "user" && field.name.toLowerCase() === "role")
133
+ continue;
134
+
135
+ // πŸ›‘οΈ CORRECTION : On Γ©vite de gΓ©nΓ©rer les champs de relations ici car ils sont gΓ©rΓ©s plus bas
136
+ // Si le champ se termine par 'Id' ou s'il a le mΓͺme nom qu'une entitΓ© connue
137
+ const isRelationField = inputs.entitiesData.entities.some(
138
+ (e) =>
139
+ field.name.toLowerCase() === e.name.toLowerCase() ||
140
+ field.name.toLowerCase() === e.name.toLowerCase() + "id"
141
+ );
142
+ if (isRelationField) continue;
143
+
144
+ const mongooseType = mapTypeToMongoose(field.type);
145
+ const tsType = field.type;
146
+
147
+ const isRequired =
148
+ field.name.toLowerCase() !== "isactive" &&
149
+ field.name.toLowerCase() !== "password";
150
+ const requiredOption = isRequired ? ", required: true" : "";
151
+
152
+ // Import des enums partagΓ©s si nΓ©cessaire
153
+ if (
154
+ mongooseType === "String" &&
155
+ tsType.charAt(0) === tsType.charAt(0).toUpperCase() &&
156
+ tsType !== "Role"
157
+ ) {
158
+ extraImports += `import { ${tsType} } from '../shared/enums/${tsType.toLowerCase()}.enum';\n`;
159
+ }
160
+
161
+ fieldsContent += `
162
+ @Prop({ type: ${mongooseType}${requiredOption} })
163
+ ${field.name}: ${tsType};
164
+ `;
165
+ }
166
+
167
+ // --- Final Generation ---
168
+ const content = `${mongooseImportCode}${extraImports}
169
+ export type ${interfaceName} = ${entityName} & mongoose.Document;
170
+
171
+ @Schema({ timestamps: true })
172
+ export class ${entityName} {
173
+ ${fieldsContent}
174
+ }
175
+
176
+ export const ${schemaName} = SchemaFactory.createForClass(${entityName});
177
+ `;
178
+
179
+ forFeatureImports.push(
180
+ `{ name: ${entityName}.name, schema: ${schemaName} }`
181
+ );
182
+ }
183
+ // --- Final Update of app.module.ts --- //
184
+ // 3. Adding MongooseModule.forFeature([...])
185
+ const forFeatureBlock = `MongooseModule.forFeature([${forFeatureImports.join(
186
+ ", "
187
+ )}]),`;
188
+
189
+ await updateFile({
190
+ path: appModulePath,
191
+ pattern: new RegExp(mongooseForRoot.trim().replace(/[\n\r]/g, "\\s*"), "g"),
192
+ replacement: (match) => `${match}\n\t${forFeatureBlock}`,
193
+ });
194
+
195
+ if (inputs.isDemo) {
196
+ // The generateSampleData function must be adapted for 'new Date()' usage and Mongoose types
197
+ await setupMongooseSeeding(inputs);
198
+ }
199
+
200
+ logSuccess(
201
+ "Mongoose configuration complete. Schemas are generated in src/schemas!"
202
+ );
203
+ }
204
+
205
+ async function setupMongooseSeeding(inputs) {
206
+ logInfo("βš™οΈ Configuring seeding for Mongoose..."); // --- Dependencies ---
207
+
208
+ const mongooseDevDeps = ["ts-node", "@types/node", "@types/bcrypt"];
209
+ await runCommand(
210
+ `${inputs.packageManager} add -D ${mongooseDevDeps.join(" ")}`,
211
+ "❌ Failed to install Mongoose seeding dependencies"
212
+ );
213
+ await runCommand(
214
+ `${inputs.packageManager} install bcrypt`,
215
+ "❌ Failed to install bcrypt"
216
+ );
217
+
218
+ // --- Scripts in package.json ---
219
+ const mongooseScripts = {
220
+ seed: "ts-node src/database/seed.ts",
221
+ };
222
+ await updatePackageJson(inputs, mongooseScripts);
223
+
224
+ // --- Creating seed.ts file ---
225
+ await createDirectory("src/database");
226
+ const seedTsContent = await generateMongooseSeedContent(
227
+ inputs.entitiesData.entities,
228
+ inputs.mode
229
+ );
230
+
231
+ await createFile({
232
+ path: `src/database/seed.ts`,
233
+ contente: seedTsContent,
234
+ });
235
+
236
+ logSuccess("Mongoose seeding configured.");
237
+ }
238
+
239
+ async function generateMongooseSeedContent(entities, mode) {
240
+ const hasUser = entities.some((e) => e.name.toLowerCase() === "user");
241
+ const hasPost = entities.some((e) => e.name.toLowerCase() === "post");
242
+ const hasComment = entities.some((e) => e.name.toLowerCase() === "comment");
243
+
244
+ return `
245
+ import mongoose from 'mongoose';
246
+ import * as bcrypt from 'bcrypt';
247
+ import * as dotenv from 'dotenv';
248
+ dotenv.config();
249
+
250
+ ${entities
251
+ .map((e) => {
252
+ const name = capitalize(e.name);
253
+ const lowName = e.name.toLowerCase();
254
+ let path;
255
+
256
+ if (lowName === "session") {
257
+ path =
258
+ mode === "full"
259
+ ? `../auth/infrastructure/persistence/mongoose/${lowName}.schema`
260
+ : `../auth/persistence/${lowName}.schema`;
261
+ } else {
262
+ path =
263
+ mode === "full"
264
+ ? `../${lowName}/infrastructure/persistence/mongoose/${lowName}.schema`
265
+ : `../${lowName}/entities/${lowName}.schema`;
266
+ }
267
+
268
+ return `import { ${name}Schema } from '${path}';`;
269
+ })
270
+ .join("\n")}
271
+
272
+ async function seed() {
273
+ const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/nestcraft_db';
274
+
275
+ try {
276
+ await mongoose.connect(MONGO_URI);
277
+ console.log('🌱 Starting Mongoose seeding...');
278
+
279
+ // --- MODELS ---
280
+ ${entities
281
+ .map(
282
+ (e) =>
283
+ `const ${capitalize(e.name)}Model = mongoose.model('${capitalize(
284
+ e.name
285
+ )}', ${capitalize(e.name)}Schema);`
286
+ )
287
+ .join("\n ")}
288
+
289
+ // --- CLEANUP ---
290
+ await Promise.all([${entities
291
+ .map((e) => `${capitalize(e.name)}Model.deleteMany({})`)
292
+ .join(", ")}]);
293
+
294
+ // --- 1. ADMIN & DEMO USERS ---
295
+ const salt = await bcrypt.genSalt(10);
296
+ const hashedPassword = await bcrypt.hash('password123', salt);
297
+
298
+ const users = await ${hasUser ? "UserModel" : "null"}.insertMany([
299
+ { email: 'admin@nestcraft.com', password: hashedPassword, username: 'NestCraftAdmin', role: 'SUPER_ADMIN', isActive: true },
300
+ { email: 'emma.jones@demo.com', password: hashedPassword, username: 'EmmaJones', isActive: true },
301
+ { email: 'lucas.martin@demo.com', password: hashedPassword, username: 'LucasMartin', isActive: true },
302
+ { email: 'sophia.bernard@demo.com', password: hashedPassword, username: 'SophiaBernard', isActive: true },
303
+ { email: 'alexandre.dubois@demo.com', password: hashedPassword, username: 'AlexandreDubois', isActive: true },
304
+ { email: 'chloe.moreau@demo.com', password: hashedPassword, username: 'ChloeMoreau', isActive: true },
305
+ ]);
306
+ console.log(\`πŸ‘₯ \${users.length} users created\`);
307
+
308
+ ${
309
+ hasPost
310
+ ? `
311
+ // --- 2. BLOG POSTS ---
312
+ const posts = await PostModel.insertMany([
313
+ {
314
+ title: 'The Basics of NestJS for Modern Developers',
315
+ content: 'Discover how to build a robust and maintainable API with NestJS...',
316
+ published: true,
317
+ userId: users[1]._id,
318
+ },
319
+ {
320
+ title: 'How to Secure Your API with JWT',
321
+ content: 'JWT authentication is a standard for securing APIs...',
322
+ published: true,
323
+ userId: users[2]._id,
324
+ },
325
+ {
326
+ title: 'Optimizing Node.js API Performance',
327
+ content: 'Discover best practices for improving performance...',
328
+ published: true,
329
+ userId: users[3]._id,
330
+ },
331
+ {
332
+ title: 'Introduction to Prisma ORM',
333
+ content: 'Prisma is a modern ORM that simplifies interactions with the database...',
334
+ published: true,
335
+ userId: users[4]._id,
336
+ },
337
+ {
338
+ title: 'Understanding Clean Architecture',
339
+ content: 'Clean Architecture helps separate business logic from the rest of the code...',
340
+ published: false,
341
+ userId: users[0]._id,
342
+ },
343
+ ]);
344
+ console.log('πŸ“ 5 Articles created');
345
+ `
346
+ : ""
347
+ }
348
+
349
+ ${
350
+ hasComment && hasPost
351
+ ? `
352
+ // --- 3. DEMO COMMENTS ---
353
+ await CommentModel.insertMany([
354
+ { content: 'Excellent article! I was able to apply these tips directly to my NestJS project.', postId: posts[0]._id, userId: users[2]._id },
355
+ { content: 'Very clear and well explained, thank you for sharing about Prisma πŸ‘', postId: posts[3]._id, userId: users[0]._id },
356
+ { content: "I didn't know about JWT before this article, it's a real revelation.", postId: posts[1]._id, userId: users[4]._id },
357
+ { content: 'Clean Architecture always seemed blurry to me, this article finally enlightened me.', postId: posts[4]._id, userId: users[1]._id },
358
+ { content: 'Thanks for the content! I would like to see a complete tutorial with NestJS + Prisma.', postId: posts[2]._id, userId: users[3]._id },
359
+ ]);
360
+ console.log('πŸ’¬ 5 Comments created');
361
+ `
362
+ : ""
363
+ }
364
+
365
+ console.log('βœ… Mongoose seeding finished successfully! πŸš€');
366
+ } catch (err) {
367
+ console.error('❌ Seeding error:', err);
368
+ } finally {
369
+ await mongoose.disconnect();
370
+ }
371
+ }
372
+
373
+ seed();
374
+ `;
375
+ }
376
+
377
+ module.exports = { setupMongoose };