nestcraftx 0.2.5 → 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.
- package/AUDIT_ROADMAP.md +798 -0
- package/CHANGELOG.fr.md +22 -0
- package/CHANGELOG.md +22 -0
- package/CLI_USAGE.fr.md +331 -331
- package/CLI_USAGE.md +364 -364
- package/LICENSE +21 -21
- package/PROGRESS.md +258 -0
- package/bin/nestcraft.js +84 -64
- package/commands/demo.js +337 -330
- package/commands/generate.js +94 -0
- package/commands/generateConf.js +91 -0
- package/commands/help.js +29 -2
- package/commands/info.js +48 -48
- package/commands/new.js +340 -335
- package/commands/start.js +19 -19
- package/commands/test.js +7 -7
- package/package.json +1 -1
- package/utils/app-module.updater.js +96 -0
- package/utils/cliParser.js +88 -76
- package/utils/colors.js +62 -62
- package/utils/configs/configureDocker.js +120 -120
- package/utils/configs/setupCleanArchitecture.js +17 -15
- package/utils/configs/setupLightArchitecture.js +15 -9
- package/utils/file-system.js +75 -0
- package/utils/file-utils/saveProjectConfig.js +36 -0
- package/utils/fullModeInput.js +607 -607
- package/utils/generators/application/dtoGenerator.js +251 -0
- package/utils/generators/application/dtoUpdater.js +54 -0
- package/utils/generators/cleanModuleGenerator.js +475 -0
- package/utils/generators/database/setupDatabase.js +49 -0
- package/utils/generators/domain/entityGenerator.js +126 -0
- package/utils/generators/domain/entityUpdater.js +78 -0
- package/utils/generators/infrastructure/mapperGenerator.js +114 -0
- package/utils/generators/infrastructure/mapperUpdater.js +65 -0
- package/utils/generators/infrastructure/middlewareGenerator.js +645 -0
- package/utils/generators/infrastructure/mongooseSchemaGenerator.js +196 -0
- package/utils/generators/infrastructure/repositoryGenerator.js +334 -0
- package/utils/generators/lightModuleGenerator.js +131 -0
- package/utils/generators/presentation/controllerGenerator.js +142 -0
- package/utils/generators/relation/relation.engine.js +64 -0
- package/utils/helpers.js +109 -0
- package/utils/interactive/askEntityInputs.js +165 -0
- 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/setupAuth.js +33 -20
- package/utils/setups/setupDatabase.js +76 -75
- package/utils/setups/setupMongoose.js +22 -5
- package/utils/setups/setupPrisma.js +748 -630
- package/utils/shell.js +112 -32
- package/utils/spinner.js +57 -57
- package/utils/systemCheck.js +124 -124
- package/utils/userInput.js +357 -421
- package/utils/utils.js +29 -2164
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils/generators/presentation/controllerGenerator.js
|
|
3
|
+
*
|
|
4
|
+
* Générateur du Controller (présentation) pour le mode Full/Clean Architecture.
|
|
5
|
+
* Extrait de utils.js — Phase 1B.
|
|
6
|
+
*
|
|
7
|
+
* ATTENTION : Ce générateur est UNIQUEMENT pour le mode Full.
|
|
8
|
+
* Le mode Light a son propre générateur dans setupLightArchitecture.js
|
|
9
|
+
* avec des différences comportementales :
|
|
10
|
+
* - Full : chemins "presentation/controllers/", méthodes getAll()/getById()
|
|
11
|
+
* - Light : chemins "controllers/", méthodes findAll()/findById(), Logger intégré
|
|
12
|
+
*
|
|
13
|
+
* Ne jamais fusionner les deux.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { capitalize, decapitalize, pluralize } = require("../../helpers");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Génère le contenu TypeScript du Controller NestJS (Full Architecture).
|
|
20
|
+
*
|
|
21
|
+
* @param {string} entityName - Le nom de l'entité (ex: "Post")
|
|
22
|
+
* @param {string} entityPath - Le chemin de base (ex: "src/post")
|
|
23
|
+
* @param {boolean} useSwagger - Inclure les décorateurs Swagger
|
|
24
|
+
* @returns {Promise<string>} Le code TypeScript du controller
|
|
25
|
+
*/
|
|
26
|
+
async function generateController(entityName, entityPath, useSwagger) {
|
|
27
|
+
const entityNameLower = decapitalize(entityName);
|
|
28
|
+
const entityNameCapitalized = capitalize(entityName);
|
|
29
|
+
const pluralName = pluralize(entityNameLower);
|
|
30
|
+
|
|
31
|
+
const swaggerImports = useSwagger
|
|
32
|
+
? `import { ApiTags, ApiOperation, ApiResponse, ApiParam } from '@nestjs/swagger';`
|
|
33
|
+
: "";
|
|
34
|
+
|
|
35
|
+
const swaggerClassDecorator = useSwagger
|
|
36
|
+
? `@ApiTags('${capitalize(pluralName)}')`
|
|
37
|
+
: "";
|
|
38
|
+
|
|
39
|
+
return `
|
|
40
|
+
import { Controller, Get, Post, Body, Param, Patch, Delete, HttpCode, HttpStatus, Query } from '@nestjs/common';
|
|
41
|
+
${swaggerImports}
|
|
42
|
+
import { ${entityNameCapitalized}Service } from '${entityPath}/application/services/${entityNameLower}.service';
|
|
43
|
+
import { Create${entityNameCapitalized}Dto, Update${entityNameCapitalized}Dto } from 'src/${entityNameLower}/application/dtos/${entityNameLower}.dto';
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Controller for ${entityNameCapitalized} management.
|
|
48
|
+
* Handles incoming HTTP requests and delegates logic to the service layer.
|
|
49
|
+
*/
|
|
50
|
+
${swaggerClassDecorator}
|
|
51
|
+
@Controller('${pluralName}')
|
|
52
|
+
export class ${entityNameCapitalized}Controller {
|
|
53
|
+
constructor(private readonly service: ${entityNameCapitalized}Service) {}
|
|
54
|
+
|
|
55
|
+
${
|
|
56
|
+
entityNameLower !== "user"
|
|
57
|
+
? `
|
|
58
|
+
@Post()
|
|
59
|
+
@HttpCode(HttpStatus.CREATED)
|
|
60
|
+
${
|
|
61
|
+
useSwagger
|
|
62
|
+
? `
|
|
63
|
+
@ApiOperation({ summary: 'Create a new ${entityNameLower}', description: 'Creates a new record for ${entityNameLower} in the database.' })
|
|
64
|
+
@ApiResponse({ status: 201, description: 'The ${entityNameLower} has been successfully created.' })
|
|
65
|
+
@ApiResponse({ status: 400, description: 'Invalid input data.' })`
|
|
66
|
+
: ""
|
|
67
|
+
}
|
|
68
|
+
async create(@Body() dto: Create${entityNameCapitalized}Dto) {
|
|
69
|
+
const result = await this.service.create(dto);
|
|
70
|
+
return {
|
|
71
|
+
message: '${entityNameCapitalized} created successfully',
|
|
72
|
+
data: result
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
`
|
|
76
|
+
: ""
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Get()
|
|
80
|
+
${
|
|
81
|
+
useSwagger
|
|
82
|
+
? `
|
|
83
|
+
@ApiOperation({ summary: 'Get all ${pluralName}', description: 'Retrieves a list of all ${pluralName} available.' })
|
|
84
|
+
@ApiResponse({ status: 200, description: 'Return all ${pluralName}.' })`
|
|
85
|
+
: ""
|
|
86
|
+
}
|
|
87
|
+
async getAll() {
|
|
88
|
+
return await this.service.getAll();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@Get(':id')
|
|
92
|
+
${
|
|
93
|
+
useSwagger
|
|
94
|
+
? `
|
|
95
|
+
@ApiOperation({ summary: 'Get ${entityNameLower} by ID' })
|
|
96
|
+
@ApiParam({ name: 'id', description: 'The unique identifier of the ${entityNameLower}' })
|
|
97
|
+
@ApiResponse({ status: 200, description: 'The ${entityNameLower} has been found.' })
|
|
98
|
+
@ApiResponse({ status: 404, description: '${entityNameCapitalized} not found.' })`
|
|
99
|
+
: ""
|
|
100
|
+
}
|
|
101
|
+
async getById(@Param('id') id: string) {
|
|
102
|
+
return await this.service.getById(id);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@Patch(':id')
|
|
106
|
+
${
|
|
107
|
+
useSwagger
|
|
108
|
+
? `
|
|
109
|
+
@ApiOperation({ summary: 'Update an existing ${entityNameLower}' })
|
|
110
|
+
@ApiParam({ name: 'id', description: 'The unique identifier of the ${entityNameLower} to update' })
|
|
111
|
+
@ApiResponse({ status: 200, description: 'The ${entityNameLower} has been successfully updated.' })
|
|
112
|
+
@ApiResponse({ status: 404, description: '${entityNameCapitalized} not found.' })`
|
|
113
|
+
: ""
|
|
114
|
+
}
|
|
115
|
+
async update(
|
|
116
|
+
@Param('id') id: string,
|
|
117
|
+
@Body() dto: Update${entityNameCapitalized}Dto,
|
|
118
|
+
) {
|
|
119
|
+
await this.service.update(id, dto);
|
|
120
|
+
return { message: '${entityNameCapitalized} updated successfully' };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@Delete(':id')
|
|
124
|
+
@HttpCode(HttpStatus.NO_CONTENT)
|
|
125
|
+
${
|
|
126
|
+
useSwagger
|
|
127
|
+
? `
|
|
128
|
+
@ApiOperation({ summary: 'Delete a ${entityNameLower}' })
|
|
129
|
+
@ApiParam({ name: 'id', description: 'The unique identifier of the ${entityNameLower} to delete' })
|
|
130
|
+
@ApiResponse({ status: 204, description: 'The ${entityNameLower} has been successfully deleted.' })
|
|
131
|
+
@ApiResponse({ status: 404, description: '${entityNameCapitalized} not found.' })`
|
|
132
|
+
: ""
|
|
133
|
+
}
|
|
134
|
+
async delete(@Param('id') id: string) {
|
|
135
|
+
await this.service.delete(id);
|
|
136
|
+
return { message: '${entityNameCapitalized} deleted successfully' };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = { generateController };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const { patchDtoWithRelation } = require("../application/dtoUpdater");
|
|
2
|
+
const { patchMapperWithRelation } = require("../infrastructure/mapperUpdater");
|
|
3
|
+
|
|
4
|
+
function resolveForeignKeyOwner(newEntityName, target, relationType) {
|
|
5
|
+
switch (relationType) {
|
|
6
|
+
case "1-n":
|
|
7
|
+
// FK on the N side → target
|
|
8
|
+
return null /* {
|
|
9
|
+
ownerEntity: target,
|
|
10
|
+
relatedEntity: newEntityName,
|
|
11
|
+
} */;
|
|
12
|
+
|
|
13
|
+
case "n-1":
|
|
14
|
+
// FK on the N side → source
|
|
15
|
+
return {
|
|
16
|
+
ownerEntity: target,
|
|
17
|
+
relatedEntity: newEntityName,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
case "1-1":
|
|
21
|
+
// Convention: FK on source side
|
|
22
|
+
return {
|
|
23
|
+
ownerEntity: target,
|
|
24
|
+
relatedEntity: newEntityName,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
case "n-n":
|
|
28
|
+
// Pivot table → no FK field
|
|
29
|
+
return null;
|
|
30
|
+
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(`❌ Unknown relation type: ${relationType}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function applyRelationPatches(
|
|
37
|
+
newEntityName,
|
|
38
|
+
target,
|
|
39
|
+
relationType,
|
|
40
|
+
useSwagger,
|
|
41
|
+
mode = "full",
|
|
42
|
+
) {
|
|
43
|
+
const owner = resolveForeignKeyOwner(newEntityName, target, relationType);
|
|
44
|
+
|
|
45
|
+
if (!owner) {
|
|
46
|
+
// console.log("ℹ️ n-n relation detected → pivot table, skipping DTO patch");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Patch DTO
|
|
51
|
+
await patchDtoWithRelation(
|
|
52
|
+
owner.ownerEntity,
|
|
53
|
+
owner.relatedEntity,
|
|
54
|
+
useSwagger,
|
|
55
|
+
mode,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
await patchMapperWithRelation(owner.ownerEntity, owner.relatedEntity, mode);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
resolveForeignKeyOwner,
|
|
63
|
+
applyRelationPatches,
|
|
64
|
+
};
|
package/utils/helpers.js
ADDED
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/utils/interactive/askEntityInputs.js
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const readline = require("readline-sync");
|
|
5
|
+
const inquirer = require("inquirer");
|
|
6
|
+
const { info, success } = require("../colors");
|
|
7
|
+
const { logWarning } = require("../loggers/logWarning");
|
|
8
|
+
const { capitalize } = require("../userInput");
|
|
9
|
+
const actualInquirer = inquirer.default || inquirer;
|
|
10
|
+
|
|
11
|
+
async function askEntityInputs(targetName) {
|
|
12
|
+
const entity = { name: targetName, fields: [], relation: null };
|
|
13
|
+
|
|
14
|
+
console.log(
|
|
15
|
+
`\n${info("[ENTITY DESIGN]")} Define fields for "${targetName}" :`,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
while (true) {
|
|
19
|
+
let fname = readline.question(" Field name (leave empty to finish) : ");
|
|
20
|
+
if (!fname) break;
|
|
21
|
+
|
|
22
|
+
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(fname)) {
|
|
23
|
+
logWarning("Invalid field name.");
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const baseTypeChoices = [
|
|
28
|
+
"string",
|
|
29
|
+
"text",
|
|
30
|
+
"number",
|
|
31
|
+
"boolean",
|
|
32
|
+
"Date",
|
|
33
|
+
"uuid",
|
|
34
|
+
"json",
|
|
35
|
+
"enum",
|
|
36
|
+
"array",
|
|
37
|
+
"object",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
const typeAnswer = await actualInquirer.prompt([
|
|
41
|
+
{
|
|
42
|
+
type: "list",
|
|
43
|
+
name: "ftype",
|
|
44
|
+
message: `Type for "${fname}"`,
|
|
45
|
+
choices: baseTypeChoices,
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
let ftype = typeAnswer.ftype;
|
|
50
|
+
|
|
51
|
+
// Logique de raffinement des types (identique à ton script original)
|
|
52
|
+
if (ftype === "array") {
|
|
53
|
+
const inner = await actualInquirer.prompt([
|
|
54
|
+
{
|
|
55
|
+
type: "list",
|
|
56
|
+
name: "innerType",
|
|
57
|
+
message: `Type of elements for "${fname}[]"`,
|
|
58
|
+
choices: baseTypeChoices.filter(
|
|
59
|
+
(c) => c !== "array" && c !== "object",
|
|
60
|
+
),
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
ftype = `${inner.innerType}[]`;
|
|
64
|
+
} else if (ftype === "enum") {
|
|
65
|
+
ftype = capitalize(fname) + "Enum";
|
|
66
|
+
} else if (ftype === "object") {
|
|
67
|
+
const obj = await actualInquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: "input",
|
|
70
|
+
name: "val",
|
|
71
|
+
message: "Complex type name :",
|
|
72
|
+
default: "json",
|
|
73
|
+
},
|
|
74
|
+
]);
|
|
75
|
+
ftype = capitalize(obj.val);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
entity.fields.push({ name: fname, type: ftype });
|
|
79
|
+
console.log(` Type for "${fname}" : ${ftype} ${success("[✓]")}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const relationData = await askRelationInputs(entity.name);
|
|
83
|
+
entity.relation = relationData;
|
|
84
|
+
|
|
85
|
+
return entity;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function askRelationInputs(newEntityName) {
|
|
89
|
+
const srcPath = path.join(process.cwd(), "src");
|
|
90
|
+
|
|
91
|
+
// Sécurité : Vérifie si le dossier src existe
|
|
92
|
+
if (!fs.existsSync(srcPath)) return null;
|
|
93
|
+
|
|
94
|
+
const blacklist = [
|
|
95
|
+
"common",
|
|
96
|
+
"prisma",
|
|
97
|
+
"auth",
|
|
98
|
+
"mail",
|
|
99
|
+
"infrastructure",
|
|
100
|
+
"shared",
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const modules = fs.readdirSync(srcPath).filter((folder) => {
|
|
104
|
+
const fullPath = path.join(srcPath, folder);
|
|
105
|
+
|
|
106
|
+
// 1. D'abord vérifier si c'est un dossier (important pour éviter ENOTDIR)
|
|
107
|
+
const isDirectory = fs.lstatSync(fullPath).isDirectory();
|
|
108
|
+
if (!isDirectory) return false;
|
|
109
|
+
|
|
110
|
+
// 2. Ensuite les autres filtres
|
|
111
|
+
const isNotBlacklisted = !blacklist.includes(folder.toLowerCase());
|
|
112
|
+
const isNotCurrent = folder.toLowerCase() !== newEntityName.toLowerCase();
|
|
113
|
+
|
|
114
|
+
// 3. Enfin vérifier s'il y a un fichier .module.ts à l'intérieur
|
|
115
|
+
const hasModuleFile = fs
|
|
116
|
+
.readdirSync(fullPath)
|
|
117
|
+
.some((file) => file.endsWith(".module.ts"));
|
|
118
|
+
|
|
119
|
+
return isNotBlacklisted && isNotCurrent && hasModuleFile;
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (modules.length === 0) {
|
|
123
|
+
console.log(info("\n[INFO] No existing modules found for relations."));
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const { wantRelation } = await actualInquirer.prompt([
|
|
128
|
+
{
|
|
129
|
+
type: "confirm",
|
|
130
|
+
name: "wantRelation",
|
|
131
|
+
message: "Add relation with an existing module?",
|
|
132
|
+
default: false,
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
if (!wantRelation) return null;
|
|
137
|
+
|
|
138
|
+
// On demande les détails de la relation
|
|
139
|
+
const relation = await actualInquirer.prompt([
|
|
140
|
+
{
|
|
141
|
+
type: "list",
|
|
142
|
+
name: "target",
|
|
143
|
+
message: "Select the target module:",
|
|
144
|
+
choices: modules,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: "list",
|
|
148
|
+
name: "type",
|
|
149
|
+
message: "Relation type (NewEntity -> Target):",
|
|
150
|
+
choices: [
|
|
151
|
+
{ name: "1-n (One-to-Many: This has many of those)", value: "1-n" },
|
|
152
|
+
{
|
|
153
|
+
name: "n-1 (Many-to-One: This belongs to one of those)",
|
|
154
|
+
value: "n-1",
|
|
155
|
+
},
|
|
156
|
+
{ name: "1-1 (One-to-One)", value: "1-1" },
|
|
157
|
+
{ name: "n-n (Many-to-Many)", value: "n-n" },
|
|
158
|
+
],
|
|
159
|
+
},
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
return relation;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
module.exports = { askEntityInputs };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { error } = require("../colors");
|
|
2
|
-
|
|
3
|
-
function logError(message) {
|
|
4
|
-
console.error(`\n${error("❌[ERROR]")} ${message}`);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = { logError };
|
|
1
|
+
const { error } = require("../colors");
|
|
2
|
+
|
|
3
|
+
function logError(message) {
|
|
4
|
+
console.error(`\n${error("❌[ERROR]")} ${message}`);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = { logError };
|
package/utils/loggers/logInfo.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { info } = require("../colors");
|
|
2
|
-
|
|
3
|
-
function logInfo(message) {
|
|
4
|
-
console.log(`\n${info("[INFO]")} ${message}`);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = { logInfo };
|
|
1
|
+
const { info } = require("../colors");
|
|
2
|
+
|
|
3
|
+
function logInfo(message) {
|
|
4
|
+
console.log(`\n${info("[INFO]")} ${message}`);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = { logInfo };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { success } = require("../colors");
|
|
2
|
-
|
|
3
|
-
function logSuccess(message) {
|
|
4
|
-
console.log(`\n${success("✅[SUCCESS]")} ${message}`);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = { logSuccess };
|
|
1
|
+
const { success } = require("../colors");
|
|
2
|
+
|
|
3
|
+
function logSuccess(message) {
|
|
4
|
+
console.log(`\n${success("✅[SUCCESS]")} ${message}`);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = { logSuccess };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { warning } = require("../colors");
|
|
2
|
-
|
|
3
|
-
function logWarning(message) {
|
|
4
|
-
console.log(`\n${warning("⚠️[
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = { logWarning };
|
|
1
|
+
const { warning } = require("../colors");
|
|
2
|
+
|
|
3
|
+
function logWarning(message) {
|
|
4
|
+
console.log(`\n${warning("⚠️[Warning]")} ${message}`);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = { logWarning };
|