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
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/utils/generators/application/dtoUpdater.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const { updateFile, capitalize } = require("../../userInput");
|
|
4
|
+
|
|
5
|
+
async function patchDtoWithRelation(source, targetName, useSwagger, mode) {
|
|
6
|
+
let dtoPath;
|
|
7
|
+
if (mode === "full") {
|
|
8
|
+
dtoPath = `src/${source.toLowerCase()}/application/dtos/${source.toLowerCase()}.dto.ts`;
|
|
9
|
+
} else {
|
|
10
|
+
dtoPath = `src/${source.toLowerCase()}/dtos/${source.toLowerCase()}.dto.ts`;
|
|
11
|
+
}
|
|
12
|
+
const targetLow = targetName.toLowerCase();
|
|
13
|
+
const targetCap = capitalize(targetName);
|
|
14
|
+
|
|
15
|
+
// 1. Préparation du bloc de code selon Swagger
|
|
16
|
+
let fieldCode = "";
|
|
17
|
+
|
|
18
|
+
if (useSwagger) {
|
|
19
|
+
fieldCode = `
|
|
20
|
+
@ApiProperty({
|
|
21
|
+
example: '550e8400-e29b-41d4-a716-446655440000',
|
|
22
|
+
description: 'The unique identifier of the related ${targetCap}',
|
|
23
|
+
})
|
|
24
|
+
@IsUUID()
|
|
25
|
+
${targetLow}Id!: string;`;
|
|
26
|
+
} else {
|
|
27
|
+
fieldCode = `
|
|
28
|
+
@IsUUID()
|
|
29
|
+
${targetLow}Id!: string;`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 2. Injection dans CreateDto
|
|
33
|
+
// On injecte juste après l'ouverture de la classe
|
|
34
|
+
await updateFile({
|
|
35
|
+
path: dtoPath,
|
|
36
|
+
pattern: new RegExp(`export class Create${capitalize(source)}Dto {`),
|
|
37
|
+
replacement: `export class Create${capitalize(source)}Dto {${fieldCode}\n`,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 3. Injection dans UpdateDto (Optionnel mais recommandé si non géré par PartialType)
|
|
41
|
+
// Si ton UpdateDto n'utilise pas PartialType(CreateDto), il faut aussi patcher l'Update
|
|
42
|
+
const fileContent = fs.readFileSync(dtoPath, "utf8");
|
|
43
|
+
if (!fileContent.includes("extends PartialType")) {
|
|
44
|
+
const updateFieldCode = fieldCode
|
|
45
|
+
.replace("@IsUUID()", "@IsOptional()\n @IsUUID()")
|
|
46
|
+
.replace("Id!: string", "Id?: string");
|
|
47
|
+
await updateFile({
|
|
48
|
+
path: dtoPath,
|
|
49
|
+
pattern: new RegExp(`export class Update${capitalize(source)}Dto {`),
|
|
50
|
+
replacement: `export class Update${capitalize(source)}Dto {${updateFieldCode}\n`,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
module.exports = { patchDtoWithRelation };
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
const {
|
|
2
|
+
createDirectory,
|
|
3
|
+
createFile,
|
|
4
|
+
safeUpdateAppModule,
|
|
5
|
+
capitalize,
|
|
6
|
+
decapitalize,
|
|
7
|
+
} = require("../userInput");
|
|
8
|
+
const { logInfo } = require("../loggers/logInfo");
|
|
9
|
+
const { logSuccess } = require("../loggers/logSuccess");
|
|
10
|
+
const {
|
|
11
|
+
generateEntityFileContent,
|
|
12
|
+
generateDto,
|
|
13
|
+
generateMapper,
|
|
14
|
+
generateController,
|
|
15
|
+
generateRepository,
|
|
16
|
+
generateMongooseSchemaFileContent,
|
|
17
|
+
} = require("../utils");
|
|
18
|
+
const { updateExistingEntityRelation } = require("./domain/entityUpdater");
|
|
19
|
+
const { applyRelationPatches } = require("./relation/relation.engine");
|
|
20
|
+
const setupDatabase = require("./database/setupDatabase");
|
|
21
|
+
|
|
22
|
+
async function generateCleanModule(name, config, entityData) {
|
|
23
|
+
const entityNameCapitalized = capitalize(name);
|
|
24
|
+
const entityNameLower = decapitalize(name);
|
|
25
|
+
const entityPath = `src/${entityNameLower}`;
|
|
26
|
+
const mode = "full";
|
|
27
|
+
|
|
28
|
+
// Chemins de la Clean Architecture
|
|
29
|
+
const folders = [
|
|
30
|
+
"application/use-cases",
|
|
31
|
+
"application/dtos",
|
|
32
|
+
"domain/interfaces",
|
|
33
|
+
"domain/entities",
|
|
34
|
+
"infrastructure/mappers",
|
|
35
|
+
"infrastructure/repositories",
|
|
36
|
+
"application/services",
|
|
37
|
+
"infrastructure/adapters",
|
|
38
|
+
"presentation/controllers",
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
logInfo(
|
|
42
|
+
` Building Clean Architecture layers for module: ${entityNameCapitalized}`,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// 1. Création des dossiers
|
|
46
|
+
for (const folder of folders) {
|
|
47
|
+
await createDirectory(`${entityPath}/${folder}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Génération de l'Entité de Domaine
|
|
51
|
+
const entityContent = await generateEntityFileContent(entityData);
|
|
52
|
+
await createFile({
|
|
53
|
+
path: `${entityPath}/domain/entities/${entityNameLower}.entity.ts`,
|
|
54
|
+
contente: entityContent,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 3. Génération de l'Interface Repository
|
|
58
|
+
await createFile({
|
|
59
|
+
path: `${entityPath}/domain/interfaces/${entityNameLower}.repository.interface.ts`,
|
|
60
|
+
contente: getRepositoryInterfaceTemplate(
|
|
61
|
+
entityNameCapitalized,
|
|
62
|
+
entityNameLower,
|
|
63
|
+
),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// 4. Génération du Repository (Implementation)
|
|
67
|
+
await generateRepository(name, config.orm);
|
|
68
|
+
|
|
69
|
+
// 5. Génération des Use Cases (Create, Get, Update, Delete)
|
|
70
|
+
await generateUseCases(entityPath, entityNameCapitalized, entityNameLower);
|
|
71
|
+
|
|
72
|
+
// 6. Génération des DTOs
|
|
73
|
+
const dtoContent = await generateDto(entityData, config.swagger);
|
|
74
|
+
await createFile({
|
|
75
|
+
path: `${entityPath}/application/dtos/${entityNameLower}.dto.ts`,
|
|
76
|
+
contente: dtoContent,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// 7. Génération du Mapper
|
|
80
|
+
const mapperContent = await generateMapper(entityData);
|
|
81
|
+
await createFile({
|
|
82
|
+
path: `${entityPath}/infrastructure/mappers/${entityNameLower}.mapper.ts`,
|
|
83
|
+
contente: mapperContent,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// ÉTAPE RELATIONS : Patching des fichiers si une relation existe
|
|
87
|
+
if (
|
|
88
|
+
entityData.relation &&
|
|
89
|
+
(entityData.relation.type != "1-n" || entityData.relation.type != "n-n")
|
|
90
|
+
) {
|
|
91
|
+
const { target, type } = entityData.relation;
|
|
92
|
+
logInfo(
|
|
93
|
+
`🔗 Linking ${entityNameCapitalized} with ${capitalize(target)} (${type})...`,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await updateExistingEntityRelation(target, name, type);
|
|
97
|
+
|
|
98
|
+
/* if (type === "n-1" || type === "1-1") {
|
|
99
|
+
await patchDtoWithRelation(name, target, config.swagger);
|
|
100
|
+
await patchMapperWithRelation(name, target);
|
|
101
|
+
} */
|
|
102
|
+
|
|
103
|
+
await applyRelationPatches(name, target, type, config.swagger, mode);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 8. Génération du Service, Adapter et Controller
|
|
107
|
+
await generateServiceAndAdapter(
|
|
108
|
+
entityPath,
|
|
109
|
+
entityNameCapitalized,
|
|
110
|
+
entityNameLower,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const controllerContent = await generateController(
|
|
114
|
+
name,
|
|
115
|
+
entityPath,
|
|
116
|
+
config.swagger,
|
|
117
|
+
);
|
|
118
|
+
await createFile({
|
|
119
|
+
path: `${entityPath}/presentation/controllers/${entityNameLower}.controller.ts`,
|
|
120
|
+
contente: controllerContent,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// 9. Génération du Module NestJS
|
|
124
|
+
const moduleContent = getModuleTemplate(
|
|
125
|
+
entityNameCapitalized,
|
|
126
|
+
entityNameLower,
|
|
127
|
+
entityPath,
|
|
128
|
+
config,
|
|
129
|
+
);
|
|
130
|
+
await createFile({
|
|
131
|
+
path: `${entityPath}/${entityNameLower}.module.ts`,
|
|
132
|
+
contente: moduleContent,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 10. Auto-enregistrement dans AppModule
|
|
136
|
+
await safeUpdateAppModule(entityNameLower);
|
|
137
|
+
|
|
138
|
+
// 11.
|
|
139
|
+
await setupDatabase(config, entityData);
|
|
140
|
+
|
|
141
|
+
logSuccess(
|
|
142
|
+
` ✨ Module ${entityNameCapitalized} generated and registered successfully!`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// --- HELPERS DE TEMPLATES (Extraits de ton setup original) ---
|
|
147
|
+
|
|
148
|
+
function getRepositoryInterfaceTemplate(cap, low) {
|
|
149
|
+
return `import { Create${cap}Dto, Update${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
|
|
150
|
+
import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
|
|
151
|
+
|
|
152
|
+
export const I${cap}RepositoryName = 'I${cap}Repository';
|
|
153
|
+
|
|
154
|
+
export interface I${cap}Repository {
|
|
155
|
+
create(data: Create${cap}Dto): Promise<${cap}Entity>;
|
|
156
|
+
findById(id: string): Promise<${cap}Entity | null>;
|
|
157
|
+
findAll(): Promise<${cap}Entity[]>;
|
|
158
|
+
update(id: string, data: Update${cap}Dto): Promise<${cap}Entity | null>;
|
|
159
|
+
delete(id: string): Promise<void>;
|
|
160
|
+
}`;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
///////////////////
|
|
164
|
+
async function generateUseCases(entityPath, cap, low) {
|
|
165
|
+
const useCases = ["Create", "GetById", "GetAll", "Update", "Delete"];
|
|
166
|
+
|
|
167
|
+
for (const useCase of useCases) {
|
|
168
|
+
let content = "";
|
|
169
|
+
const fileName = `${decapitalize(useCase)}-${low}.use-case.ts`;
|
|
170
|
+
const fullPath = `${entityPath}/application/use-cases/${fileName}`;
|
|
171
|
+
|
|
172
|
+
switch (useCase) {
|
|
173
|
+
case "Create":
|
|
174
|
+
content = `
|
|
175
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
176
|
+
import { Create${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
|
|
177
|
+
import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
|
|
178
|
+
import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
|
|
179
|
+
|
|
180
|
+
export class Create${cap}UseCase {
|
|
181
|
+
private readonly logger = new Logger(Create${cap}UseCase.name);
|
|
182
|
+
|
|
183
|
+
constructor(
|
|
184
|
+
@Inject(I${cap}RepositoryName)
|
|
185
|
+
private readonly repository: I${cap}Repository,
|
|
186
|
+
) {}
|
|
187
|
+
|
|
188
|
+
async execute(data: Create${cap}Dto): Promise<${cap}Entity> {
|
|
189
|
+
this.logger.log('Starting creation process for ${cap}');
|
|
190
|
+
const result = await this.repository.create(data);
|
|
191
|
+
this.logger.log(\`Successfully created ${cap} (ID: \${result.getId()})\`);
|
|
192
|
+
return result;
|
|
193
|
+
}
|
|
194
|
+
}`;
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case "GetById":
|
|
198
|
+
content = `
|
|
199
|
+
import { Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
200
|
+
import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
|
|
201
|
+
import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
|
|
202
|
+
|
|
203
|
+
export class GetById${cap}UseCase {
|
|
204
|
+
private readonly logger = new Logger(GetById${cap}UseCase.name);
|
|
205
|
+
|
|
206
|
+
constructor(
|
|
207
|
+
@Inject(I${cap}RepositoryName)
|
|
208
|
+
private readonly repository: I${cap}Repository,
|
|
209
|
+
) {}
|
|
210
|
+
|
|
211
|
+
async execute(id: string): Promise<${cap}Entity> {
|
|
212
|
+
this.logger.log(\`Fetching ${cap} (ID: \${id})\`);
|
|
213
|
+
const result = await this.repository.findById(id);
|
|
214
|
+
|
|
215
|
+
if (!result) {
|
|
216
|
+
this.logger.warn(\`${cap} \${id} not found\`);
|
|
217
|
+
throw new NotFoundException(\`${cap} not found\`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.logger.log(\`Successfully retrieved ${cap}\`);
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
}`;
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case "GetAll":
|
|
227
|
+
content = `
|
|
228
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
229
|
+
import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
|
|
230
|
+
import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
|
|
231
|
+
|
|
232
|
+
export class GetAll${cap}UseCase {
|
|
233
|
+
private readonly logger = new Logger(GetAll${cap}UseCase.name);
|
|
234
|
+
|
|
235
|
+
constructor(
|
|
236
|
+
@Inject(I${cap}RepositoryName)
|
|
237
|
+
private readonly repository: I${cap}Repository,
|
|
238
|
+
) {}
|
|
239
|
+
|
|
240
|
+
async execute(): Promise<${cap}Entity[]> {
|
|
241
|
+
this.logger.log('Requesting all ${cap} records');
|
|
242
|
+
const results = await this.repository.findAll();
|
|
243
|
+
this.logger.log(\`Retrieved \${results.length} ${cap}(s)\`);
|
|
244
|
+
return results;
|
|
245
|
+
}
|
|
246
|
+
}`;
|
|
247
|
+
break;
|
|
248
|
+
|
|
249
|
+
case "Update":
|
|
250
|
+
content = `
|
|
251
|
+
import { Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
252
|
+
import { Update${cap}Dto } from 'src/${low}/application/dtos/${low}.dto';
|
|
253
|
+
import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
|
|
254
|
+
import { ${cap}Entity } from 'src/${low}/domain/entities/${low}.entity';
|
|
255
|
+
|
|
256
|
+
export class Update${cap}UseCase {
|
|
257
|
+
private readonly logger = new Logger(Update${cap}UseCase.name);
|
|
258
|
+
|
|
259
|
+
constructor(
|
|
260
|
+
@Inject(I${cap}RepositoryName)
|
|
261
|
+
private readonly repository: I${cap}Repository,
|
|
262
|
+
) {}
|
|
263
|
+
|
|
264
|
+
async execute(id: string, data: Update${cap}Dto): Promise<${cap}Entity> {
|
|
265
|
+
this.logger.log(\`Updating ${cap} (ID: \${id})\`);
|
|
266
|
+
const existing = await this.repository.findById(id);
|
|
267
|
+
if (!existing) throw new NotFoundException(\`${cap} not found\`);
|
|
268
|
+
|
|
269
|
+
const result = await this.repository.update(id, data);
|
|
270
|
+
if (!result) {
|
|
271
|
+
this.logger.error(\`Update failed for ${cap} \${id} - entity disappeared\`);
|
|
272
|
+
throw new NotFoundException(\`${cap} update failed\`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.logger.log(\`Successfully updated ${cap} \${id}\`);
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
}`;
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
case "Delete":
|
|
282
|
+
content = `
|
|
283
|
+
import { Inject, Logger, NotFoundException } from '@nestjs/common';
|
|
284
|
+
import { I${cap}RepositoryName, type I${cap}Repository } from 'src/${low}/domain/interfaces/${low}.repository.interface';
|
|
285
|
+
|
|
286
|
+
export class Delete${cap}UseCase {
|
|
287
|
+
private readonly logger = new Logger(Delete${cap}UseCase.name);
|
|
288
|
+
|
|
289
|
+
constructor(
|
|
290
|
+
@Inject(I${cap}RepositoryName)
|
|
291
|
+
private readonly repository: I${cap}Repository,
|
|
292
|
+
) {}
|
|
293
|
+
|
|
294
|
+
async execute(id: string): Promise<void> {
|
|
295
|
+
this.logger.log(\`Deleting ${cap} (ID: \${id})\`);
|
|
296
|
+
const existing = await this.repository.findById(id);
|
|
297
|
+
if (!existing) throw new NotFoundException(\`${cap} not found\`);
|
|
298
|
+
|
|
299
|
+
await this.repository.delete(id);
|
|
300
|
+
this.logger.log(\`Successfully deleted ${cap} \${id}\`);
|
|
301
|
+
}
|
|
302
|
+
}`;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
await createFile({
|
|
307
|
+
path: fullPath,
|
|
308
|
+
contente: content.trim(),
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
//////////////////
|
|
314
|
+
function getModuleTemplate(cap, low, entityPath, config) {
|
|
315
|
+
let importsBlock = [];
|
|
316
|
+
let providersBlock = [];
|
|
317
|
+
let extraImports = "";
|
|
318
|
+
let forwardRefImport = "";
|
|
319
|
+
|
|
320
|
+
// Gestion de l'ORM pour les imports du Module
|
|
321
|
+
if (config.orm === "prisma") {
|
|
322
|
+
extraImports += `import { PrismaModule } from 'src/prisma/prisma.module';\n`;
|
|
323
|
+
importsBlock.push("PrismaModule");
|
|
324
|
+
} else if (config.orm === "typeorm") {
|
|
325
|
+
extraImports += `import { ${cap} } from 'src/entities/${cap}.entity';\nimport { TypeOrmModule } from '@nestjs/typeorm';\n`;
|
|
326
|
+
importsBlock.push(`TypeOrmModule.forFeature([${cap}])`);
|
|
327
|
+
} else if (config.orm === "mongoose") {
|
|
328
|
+
extraImports += `import { MongooseModule } from '@nestjs/mongoose';\nimport { ${cap}, ${cap}Schema } from 'src/${low}/infrastructure/persistence/mongoose/${low}.schema';\n`;
|
|
329
|
+
importsBlock.push(
|
|
330
|
+
`MongooseModule.forFeature([{ name: ${cap}.name, schema: ${cap}Schema }])`,
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Support Auth si nécessaire (User module)
|
|
335
|
+
if (low === "user" && config.auth) {
|
|
336
|
+
extraImports += "import { AuthModule } from 'src/auth/auth.module';\n";
|
|
337
|
+
importsBlock.push("forwardRef(() => AuthModule)");
|
|
338
|
+
forwardRefImport = " forwardRef,";
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Providers standard pour la Clean Arch
|
|
342
|
+
providersBlock.push(
|
|
343
|
+
`{ provide: 'I${cap}Repository', useClass: ${cap}Repository }`,
|
|
344
|
+
`${cap}Service`,
|
|
345
|
+
`Create${cap}UseCase`,
|
|
346
|
+
`Update${cap}UseCase`,
|
|
347
|
+
`GetById${cap}UseCase`,
|
|
348
|
+
`GetAll${cap}UseCase`,
|
|
349
|
+
`Delete${cap}UseCase`,
|
|
350
|
+
`${cap}Mapper`,
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
return `
|
|
354
|
+
import {${forwardRefImport} Module } from '@nestjs/common';
|
|
355
|
+
${extraImports}
|
|
356
|
+
import { ${cap}Service } from 'src/${low}/application/services/${low}.service';
|
|
357
|
+
import { ${cap}Controller } from 'src/${low}/presentation/controllers/${low}.controller';
|
|
358
|
+
import { ${cap}Repository } from 'src/${low}/infrastructure/repositories/${low}.repository';
|
|
359
|
+
import { Create${cap}UseCase } from 'src/${low}/application/use-cases/create-${low}.use-case';
|
|
360
|
+
import { Update${cap}UseCase } from 'src/${low}/application/use-cases/update-${low}.use-case';
|
|
361
|
+
import { GetById${cap}UseCase } from 'src/${low}/application/use-cases/getById-${low}.use-case';
|
|
362
|
+
import { GetAll${cap}UseCase } from 'src/${low}/application/use-cases/getAll-${low}.use-case';
|
|
363
|
+
import { Delete${cap}UseCase } from 'src/${low}/application/use-cases/delete-${low}.use-case';
|
|
364
|
+
import { ${cap}Mapper } from 'src/${low}/infrastructure/mappers/${low}.mapper';
|
|
365
|
+
|
|
366
|
+
@Module({
|
|
367
|
+
imports: [
|
|
368
|
+
${importsBlock.join(",\n ")}
|
|
369
|
+
],
|
|
370
|
+
controllers: [
|
|
371
|
+
${cap}Controller
|
|
372
|
+
],
|
|
373
|
+
providers: [
|
|
374
|
+
${providersBlock.join(",\n ")}
|
|
375
|
+
],
|
|
376
|
+
exports: [
|
|
377
|
+
${cap}Service,
|
|
378
|
+
'I${cap}Repository'
|
|
379
|
+
]
|
|
380
|
+
})
|
|
381
|
+
export class ${cap}Module {}
|
|
382
|
+
`.trim();
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
////////////////
|
|
386
|
+
async function generateServiceAndAdapter(
|
|
387
|
+
entityPath,
|
|
388
|
+
entityNameCapitalized,
|
|
389
|
+
entityNameLower,
|
|
390
|
+
) {
|
|
391
|
+
// 8. Service
|
|
392
|
+
await createFile({
|
|
393
|
+
path: `${entityPath}/application/services/${entityNameLower}.service.ts`,
|
|
394
|
+
contente: `/**
|
|
395
|
+
* PostService handles business logic
|
|
396
|
+
* for the Post entity.
|
|
397
|
+
*
|
|
398
|
+
* It acts as a bridge between the Controller and the Repository.
|
|
399
|
+
* Responsibilities include:
|
|
400
|
+
* - Data validation and transformation
|
|
401
|
+
* - Orchestrating use cases
|
|
402
|
+
* - Managing transactions
|
|
403
|
+
*/
|
|
404
|
+
|
|
405
|
+
import { Injectable } from '@nestjs/common';
|
|
406
|
+
import { Create${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/create-${entityNameLower}.use-case';
|
|
407
|
+
import { Update${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/update-${entityNameLower}.use-case';
|
|
408
|
+
import { GetById${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/getById-${entityNameLower}.use-case';
|
|
409
|
+
import { GetAll${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/getAll-${entityNameLower}.use-case';
|
|
410
|
+
import { Delete${entityNameCapitalized}UseCase } from 'src/${entityNameLower}/application/use-cases/delete-${entityNameLower}.use-case';
|
|
411
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
412
|
+
import { ${entityNameCapitalized}Entity } from 'src/${entityNameLower}/domain/entities/${entityNameLower}.entity';
|
|
413
|
+
|
|
414
|
+
@Injectable()
|
|
415
|
+
export class ${entityNameCapitalized}Service {
|
|
416
|
+
constructor(
|
|
417
|
+
private readonly createUseCase: Create${entityNameCapitalized}UseCase,
|
|
418
|
+
private readonly updateUseCase: Update${entityNameCapitalized}UseCase,
|
|
419
|
+
private readonly getByIdUseCase: GetById${entityNameCapitalized}UseCase,
|
|
420
|
+
private readonly getAllUseCase: GetAll${entityNameCapitalized}UseCase,
|
|
421
|
+
private readonly deleteUseCase: Delete${entityNameCapitalized}UseCase,
|
|
422
|
+
) {}
|
|
423
|
+
|
|
424
|
+
async create(dto: Create${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity> {
|
|
425
|
+
return await this.createUseCase.execute(dto);
|
|
426
|
+
}
|
|
427
|
+
async update(id: string, dto: Update${entityNameCapitalized}Dto): Promise<${entityNameCapitalized}Entity | null> {
|
|
428
|
+
return await this.updateUseCase.execute(id, dto);
|
|
429
|
+
}
|
|
430
|
+
async getById(id: string): Promise<${entityNameCapitalized}Entity | null> {
|
|
431
|
+
return await this.getByIdUseCase.execute(id);
|
|
432
|
+
}
|
|
433
|
+
async getAll(): Promise<${entityNameCapitalized}Entity[]> {
|
|
434
|
+
return await this.getAllUseCase.execute();
|
|
435
|
+
}
|
|
436
|
+
async delete(id: string): Promise<void> {
|
|
437
|
+
return await this.deleteUseCase.execute(id);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
`.trim(),
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
// 9. Adapter
|
|
444
|
+
await createFile({
|
|
445
|
+
path: `${entityPath}/infrastructure/adapters/${entityNameLower}.adapter.ts`,
|
|
446
|
+
contente: `
|
|
447
|
+
// L'adaptateur permet de transformer ou d'adapter les données d'un format source vers un format cible.
|
|
448
|
+
// Cela est particulièrement utile lorsque nous devons interagir avec des API externes ou des services ayant des structures de données différentes.
|
|
449
|
+
|
|
450
|
+
export class ${entityNameCapitalized}Adapter {
|
|
451
|
+
// La méthode 'adapt' prend des données brutes d'un format spécifique et les transforme
|
|
452
|
+
// en un format qui est attendu par le système de notre domaine.
|
|
453
|
+
adapt(data: any) {
|
|
454
|
+
// Exemple d'adaptation des données - ceci est un exemple générique.
|
|
455
|
+
// Nous transformons les données pour que le format interne du système soit respecté.
|
|
456
|
+
|
|
457
|
+
const adaptedData = {
|
|
458
|
+
// Assurez-vous que vous mappez les propriétés nécessaires et les transformez.
|
|
459
|
+
id: data.id, // Mapping de l'ID de la donnée source à notre format interne
|
|
460
|
+
name: data.fullName || data.name, // Exemple de transformation de champ
|
|
461
|
+
description: data.details || data.description, // Gestion des données optionnelles
|
|
462
|
+
createdAt: new Date(data.createdAt), // Transformation de la date
|
|
463
|
+
updatedAt: new Date(data.updatedAt), // Idem pour la date de mise à jour
|
|
464
|
+
// Vous pouvez adapter d'autres champs en fonction des exigences spécifiques
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// Retournez les données adaptées dans un format compréhensible pour le système
|
|
468
|
+
return adaptedData;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
`,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
module.exports = generateCleanModule;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const { logInfo } = require("../../loggers/logInfo");
|
|
2
|
+
const { updatePrismaSchema } = require("../../setups/setupPrisma");
|
|
3
|
+
|
|
4
|
+
async function setupDatabase(config, entityData) {
|
|
5
|
+
logInfo("Configuring the database...");
|
|
6
|
+
|
|
7
|
+
switch (config.orm) {
|
|
8
|
+
case "prisma":
|
|
9
|
+
await updatePrismaSchema(entityData);
|
|
10
|
+
break;
|
|
11
|
+
case "typeorm":
|
|
12
|
+
await setupMySQL(inputs);
|
|
13
|
+
break;
|
|
14
|
+
case "mongoose":
|
|
15
|
+
await setupMongoDB(inputs); // MongoDB Configuration
|
|
16
|
+
break;
|
|
17
|
+
case "sqlite":
|
|
18
|
+
await setupSQLite(inputs); // SQLite Configuration
|
|
19
|
+
break;
|
|
20
|
+
case "firebase":
|
|
21
|
+
await setupFirebase(inputs); // Firebase Configuration
|
|
22
|
+
break;
|
|
23
|
+
case "redis":
|
|
24
|
+
await setupRedis(inputs); // Redis Configuration
|
|
25
|
+
break;
|
|
26
|
+
default:
|
|
27
|
+
throw new Error("Unsupported database.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = setupDatabase;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// src/utils/generators/domain/entityUpdater.js
|
|
2
|
+
const { updateFile, capitalize } = require("../../userInput");
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
|
|
5
|
+
async function updateExistingEntityRelation(
|
|
6
|
+
targetName,
|
|
7
|
+
newEntityName,
|
|
8
|
+
relationType,
|
|
9
|
+
mode,
|
|
10
|
+
) {
|
|
11
|
+
const targetLow = targetName.toLowerCase();
|
|
12
|
+
const newCap = capitalize(newEntityName);
|
|
13
|
+
const newLow = newEntityName.toLowerCase();
|
|
14
|
+
|
|
15
|
+
const entityPath =
|
|
16
|
+
mode === "full"
|
|
17
|
+
? `src/${targetLow}/domain/entities/${targetLow}.entity.ts`
|
|
18
|
+
: `src/${targetLow}/entities/${targetLow}.entity.ts`;
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(entityPath)) return;
|
|
21
|
+
|
|
22
|
+
// Déterminer le champ à ajouter selon la relation
|
|
23
|
+
let fieldToAdd = "";
|
|
24
|
+
let typeToAdd = "";
|
|
25
|
+
|
|
26
|
+
switch (relationType) {
|
|
27
|
+
case "n-1": // L'inverse d'un n-1 est un 1-n (donc une liste)
|
|
28
|
+
/* fieldToAdd = `${newLow}s`;
|
|
29
|
+
typeToAdd = `${newCap}Entity[]`; */
|
|
30
|
+
break;
|
|
31
|
+
case "1-n": // L'inverse d'un 1-n est un n-1 (donc un ID ou l'objet)
|
|
32
|
+
fieldToAdd = `${newLow}Id`;
|
|
33
|
+
typeToAdd = `string`;
|
|
34
|
+
break;
|
|
35
|
+
case "1-1":
|
|
36
|
+
fieldToAdd = `${newLow}Id`;
|
|
37
|
+
typeToAdd = `string`;
|
|
38
|
+
break;
|
|
39
|
+
case "n-n":
|
|
40
|
+
/* fieldToAdd = `${newLow}s`;
|
|
41
|
+
typeToAdd = `${newCap}Entity[]`; */
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 1. Ajouter l'import de la nouvelle entité au début du fichier (si c'est un type complexe)
|
|
46
|
+
if (typeToAdd.includes("Entity")) {
|
|
47
|
+
const importLine = `import { ${newCap}Entity } from 'src/${newLow}/domain/entities/${newLow}.entity';\n`;
|
|
48
|
+
await updateFile({
|
|
49
|
+
path: entityPath,
|
|
50
|
+
pattern: /^/, // Début du fichier
|
|
51
|
+
replacement: importLine,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 2. Injecter dans le constructeur (avant la parenthèse fermante)
|
|
56
|
+
await updateFile({
|
|
57
|
+
path: entityPath,
|
|
58
|
+
pattern: /(constructor\([\s\S]*?)\n\s+\)/m,
|
|
59
|
+
replacement: `$1\n private readonly ${fieldToAdd}?: ${typeToAdd}, \n )`,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 3. Injecter le Getter (avant la méthode toJSON)
|
|
63
|
+
const getterMethod = `\n get${capitalize(fieldToAdd)}(): ${typeToAdd} | undefined {\n return this.${fieldToAdd};\n }\n`;
|
|
64
|
+
await updateFile({
|
|
65
|
+
path: entityPath,
|
|
66
|
+
pattern: /toJSON\(\)/,
|
|
67
|
+
replacement: `${getterMethod}\n toJSON()`,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 4. Injecter dans toJSON
|
|
71
|
+
await updateFile({
|
|
72
|
+
path: entityPath,
|
|
73
|
+
pattern: /(toJSON\(\) \{[\s\S]*?return \{[\s\S]*?)\n\s+\};/m,
|
|
74
|
+
replacement: `$1\n ${fieldToAdd}: this.${fieldToAdd},}`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { updateExistingEntityRelation };
|