nestcraftx 0.2.6 → 0.4.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/PROGRESS.md +259 -0
- package/commands/demo.js +2 -42
- package/commands/generate.js +5 -4
- package/commands/help.js +29 -2
- package/commands/new.js +6 -49
- package/package.json +1 -1
- package/utils/app-module.updater.js +102 -0
- package/utils/cliParser.js +0 -45
- package/utils/configs/setupCleanArchitecture.js +2 -2
- package/utils/envGenerator.js +6 -0
- package/utils/file-system.js +90 -0
- package/utils/file-utils/packageJsonUtils.js +6 -0
- package/utils/file-utils/saveProjectConfig.js +6 -0
- package/utils/fullModeInput.js +8 -229
- package/utils/generators/application/dtoGenerator.js +252 -0
- package/utils/generators/application/dtoUpdater.js +5 -0
- package/utils/generators/database/setupDatabase.js +32 -14
- package/utils/generators/domain/entityGenerator.js +126 -0
- package/utils/generators/domain/entityUpdater.js +5 -0
- package/utils/generators/infrastructure/mapperGenerator.js +114 -0
- package/utils/generators/infrastructure/mapperUpdater.js +5 -0
- package/utils/generators/infrastructure/middlewareGenerator.js +645 -0
- package/utils/generators/infrastructure/mongooseSchemaGenerator.js +229 -0
- package/utils/generators/infrastructure/repositoryGenerator.js +334 -0
- package/utils/generators/presentation/controllerGenerator.js +142 -0
- package/utils/helpers.js +115 -0
- package/utils/interactive/askEntityInputs.js +5 -68
- package/utils/interactive/entityBuilder.js +400 -0
- package/utils/lightModeInput.js +13 -246
- package/utils/setups/orms/typeOrmSetup.js +31 -4
- package/utils/setups/projectSetup.js +78 -8
- package/utils/setups/setupAuth.js +6 -7
- package/utils/setups/setupDatabase.js +2 -1
- package/utils/setups/setupMongoose.js +96 -30
- package/utils/setups/setupPrisma.js +118 -134
- package/utils/setups/setupSwagger.js +12 -8
- package/utils/shell.js +125 -10
- package/utils/userInput.js +37 -101
- package/utils/utils.js +29 -2164
package/utils/cliParser.js
CHANGED
|
@@ -1,48 +1,3 @@
|
|
|
1
|
-
/* function parseCliArgs(args) {
|
|
2
|
-
const parsed = {
|
|
3
|
-
command: null,
|
|
4
|
-
projectName: null,
|
|
5
|
-
flags: {},
|
|
6
|
-
positional: [],
|
|
7
|
-
errors: []
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
for (let i = 2; i < args.length; i++) {
|
|
11
|
-
const arg = args[i];
|
|
12
|
-
|
|
13
|
-
if (i === 2 && !arg.startsWith('--')) {
|
|
14
|
-
parsed.command = arg;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (i === 3 && !arg.startsWith('--') && parsed.command === 'new') {
|
|
19
|
-
if (!isValidProjectName(arg)) {
|
|
20
|
-
parsed.errors.push(`Nom de projet invalide: "${arg}". Utilisez uniquement des lettres, chiffres, tirets et underscores.`);
|
|
21
|
-
}
|
|
22
|
-
parsed.projectName = arg;
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
if (arg.startsWith('--')) {
|
|
27
|
-
const [key, value] = parseFlag(arg);
|
|
28
|
-
const nextArg = args[i + 1];
|
|
29
|
-
|
|
30
|
-
if (value !== null) {
|
|
31
|
-
parsed.flags[key] = value;
|
|
32
|
-
} else if (nextArg && !nextArg.startsWith('--')) {
|
|
33
|
-
parsed.flags[key] = nextArg;
|
|
34
|
-
i++;
|
|
35
|
-
} else {
|
|
36
|
-
parsed.flags[key] = true;
|
|
37
|
-
}
|
|
38
|
-
} else {
|
|
39
|
-
parsed.positional.push(arg);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
validateFlags(parsed);
|
|
44
|
-
return parsed;
|
|
45
|
-
} */
|
|
46
1
|
|
|
47
2
|
function parseCliArgs(args) {
|
|
48
3
|
const parsed = {
|
|
@@ -128,7 +128,7 @@ async function setupCleanArchitecture(inputs) {
|
|
|
128
128
|
|
|
129
129
|
// 4. Use Cases
|
|
130
130
|
const useCases = ["Create", "GetById", "GetAll", "Update", "Delete"];
|
|
131
|
-
|
|
131
|
+
for (const useCase of useCases) {
|
|
132
132
|
let content = "";
|
|
133
133
|
const entityName = capitalize(entity.name);
|
|
134
134
|
const entityNameLower = decapitalize(entity.name);
|
|
@@ -302,7 +302,7 @@ export class Delete${entityName}UseCase {
|
|
|
302
302
|
}.use-case.ts`,
|
|
303
303
|
contente: content.trim(),
|
|
304
304
|
});
|
|
305
|
-
}
|
|
305
|
+
}
|
|
306
306
|
|
|
307
307
|
// 5. DTOs
|
|
308
308
|
const DtoFileContent = await generateDto(entity, useSwagger);
|
package/utils/envGenerator.js
CHANGED
|
@@ -85,6 +85,12 @@ function buildDatabaseUrl(user, password, host, port, database, orm) {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function writeEnvFile(envContent, projectPath = ".") {
|
|
88
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
89
|
+
if (isDryRun) {
|
|
90
|
+
console.log(`[DRY-RUN] Simulated: write env file to ${path.join(projectPath, ".env")}`);
|
|
91
|
+
console.log(`[DRY-RUN] Simulated: write env example file to ${path.join(projectPath, ".env.example")}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
88
94
|
// Écriture du .env (avec secrets)
|
|
89
95
|
const envPath = path.join(projectPath, ".env");
|
|
90
96
|
fs.writeFileSync(envPath, envContent, "utf8");
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils/file-system.js
|
|
3
|
+
*
|
|
4
|
+
* Fonctions d'accès au système de fichiers utilisées par tous les générateurs.
|
|
5
|
+
* Extraites de userInput.js pour centraliser les opérations I/O.
|
|
6
|
+
*
|
|
7
|
+
* Les fichiers qui importent depuis userInput.js continuent de fonctionner
|
|
8
|
+
* sans modification grâce aux ré-exports dans userInput.js.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Crée un répertoire de manière récursive s'il n'existe pas déjà.
|
|
15
|
+
* @param {string} directoryPath - Le chemin du répertoire à créer.
|
|
16
|
+
*/
|
|
17
|
+
async function createDirectory(directoryPath) {
|
|
18
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
19
|
+
if (isDryRun) {
|
|
20
|
+
console.log(`[DRY-RUN] Simulated: create directory ${directoryPath}`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
if (!fs.existsSync(directoryPath)) {
|
|
25
|
+
fs.mkdirSync(directoryPath, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error(
|
|
29
|
+
`Erreur lors de la création du dossier ${directoryPath}:`,
|
|
30
|
+
error,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Crée un fichier avec le contenu fourni.
|
|
37
|
+
* Si le fichier existe déjà :
|
|
38
|
+
* - Si `fileData.overwrite === false` : skip silencieusement
|
|
39
|
+
* - Sinon : écrase le fichier existant
|
|
40
|
+
*
|
|
41
|
+
* @param {{ path: string, contente: string, overwrite?: boolean }} fileData
|
|
42
|
+
*/
|
|
43
|
+
async function createFile(fileData) {
|
|
44
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
45
|
+
if (isDryRun) {
|
|
46
|
+
console.log(`[DRY-RUN] Simulated: create/write file ${fileData.path}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
if (!fs.existsSync(fileData.path)) {
|
|
51
|
+
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
52
|
+
} else {
|
|
53
|
+
if (fileData.overwrite === false) {
|
|
54
|
+
console.log(`File already exists, skipping: ${fileData.path}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
console.log(`Existing file : ${fileData.path}`);
|
|
58
|
+
fs.writeFileSync(`${fileData.path}`, `${fileData.contente}`);
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`Erreur creating file ${fileData.path}:`, error);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Met à jour un fichier existant en remplaçant un pattern par un remplacement.
|
|
67
|
+
* Utilise `String.prototype.replace` : supporte les chaînes et les RegExp.
|
|
68
|
+
*
|
|
69
|
+
* @param {{ path: string, pattern: string|RegExp, replacement: string }} options
|
|
70
|
+
*/
|
|
71
|
+
async function updateFile({ path, pattern, replacement }) {
|
|
72
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
73
|
+
if (isDryRun) {
|
|
74
|
+
console.log(`[DRY-RUN] Simulated: update file ${path}`);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
let mainTs = fs.readFileSync(path, "utf8");
|
|
79
|
+
const updatedContent = mainTs.replace(pattern, replacement);
|
|
80
|
+
fs.writeFileSync(path, updatedContent, "utf-8");
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error(` Error updating file ${path}:`, error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
createDirectory,
|
|
88
|
+
createFile,
|
|
89
|
+
updateFile,
|
|
90
|
+
};
|
|
@@ -13,6 +13,12 @@ async function updatePackageJson(inputs, scripts, devDependencies = {}) {
|
|
|
13
13
|
const projectPath = process.cwd();
|
|
14
14
|
const packageJsonPath = path.join(projectPath, "package.json");
|
|
15
15
|
|
|
16
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
17
|
+
if (isDryRun) {
|
|
18
|
+
console.log(`[DRY-RUN] Simulated: update package.json scripts and devDependencies for ${inputs.projectName}`);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
try {
|
|
17
23
|
// 1. Lire le contenu actuel
|
|
18
24
|
const fileContent = fs.readFileSync(packageJsonPath, "utf8");
|
|
@@ -23,6 +23,12 @@ async function saveProjectConfig(inputs) {
|
|
|
23
23
|
generatedAt: new Date().toISOString(),
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
+
const isDryRun = process.argv.includes("--dry-run");
|
|
27
|
+
if (isDryRun) {
|
|
28
|
+
console.log(`[DRY-RUN] Simulated: save project config to ${configFile}`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
26
32
|
try {
|
|
27
33
|
if (!fs.existsSync(configDir)) {
|
|
28
34
|
fs.mkdirSync(configDir, { recursive: true });
|
package/utils/fullModeInput.js
CHANGED
|
@@ -4,6 +4,7 @@ const inquirer = require("inquirer");
|
|
|
4
4
|
const { capitalize } = require("./userInput");
|
|
5
5
|
const { logWarning } = require("./loggers/logWarning");
|
|
6
6
|
const { getPackageManager } = require("./utils");
|
|
7
|
+
const { buildEntityInteractive, buildRelationsInteractive } = require("./interactive/entityBuilder");
|
|
7
8
|
const actualInquirer = inquirer.default || inquirer;
|
|
8
9
|
|
|
9
10
|
async function getFullModeInputs(projectName, flags) {
|
|
@@ -334,97 +335,13 @@ async function getFullModeInputs(projectName, flags) {
|
|
|
334
335
|
|
|
335
336
|
let addEntity = readline.keyInYNStrict(`${info("[?]")} Add an entity?`);
|
|
336
337
|
while (addEntity) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const fields = [];
|
|
345
|
-
|
|
346
|
-
console.log(` Fields for "${name}" :`);
|
|
347
|
-
while (true) {
|
|
348
|
-
let fname = readline.question(" Field name (leave empty to finish) : ");
|
|
349
|
-
if (!fname) break;
|
|
350
|
-
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(fname)) {
|
|
351
|
-
logWarning("Invalid field name.");
|
|
352
|
-
continue;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
const baseTypeChoices = [
|
|
356
|
-
"string",
|
|
357
|
-
"text",
|
|
358
|
-
"number",
|
|
359
|
-
"decimal",
|
|
360
|
-
"boolean",
|
|
361
|
-
"Date",
|
|
362
|
-
"uuid",
|
|
363
|
-
"json",
|
|
364
|
-
"enum",
|
|
365
|
-
"array",
|
|
366
|
-
"object",
|
|
367
|
-
];
|
|
368
|
-
|
|
369
|
-
const typeQuestion = {
|
|
370
|
-
type: "list",
|
|
371
|
-
name: "ftype",
|
|
372
|
-
message: `Type for "${fname}"`,
|
|
373
|
-
default: "string",
|
|
374
|
-
choices: baseTypeChoices,
|
|
375
|
-
transformer: () => "",
|
|
376
|
-
};
|
|
377
|
-
const typeAnswer = await actualInquirer.prompt([typeQuestion]);
|
|
378
|
-
let ftype = typeAnswer.ftype;
|
|
379
|
-
process.stdout.write("\x1B[1A");
|
|
380
|
-
process.stdout.write("\x1B[K");
|
|
381
|
-
|
|
382
|
-
if (ftype === "array") {
|
|
383
|
-
const arrayInnerQuestion = {
|
|
384
|
-
type: "list",
|
|
385
|
-
name: "innerType",
|
|
386
|
-
message: `Type of elements for "${fname}[]"`,
|
|
387
|
-
default: "string",
|
|
388
|
-
choices: baseTypeChoices.filter(
|
|
389
|
-
(c) => c !== "array" && c !== "object"
|
|
390
|
-
),
|
|
391
|
-
transformer: () => "",
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
const innerAnswer = await actualInquirer.prompt([arrayInnerQuestion]);
|
|
395
|
-
ftype = `${innerAnswer.innerType}[]`;
|
|
396
|
-
} else if (ftype === "enum") {
|
|
397
|
-
const enumName = capitalize(fname) + "Enum";
|
|
398
|
-
console.log(
|
|
399
|
-
` ${info(
|
|
400
|
-
"[INFO]"
|
|
401
|
-
)} Enum type selected. Consider defining ${enumName} in your code.`
|
|
402
|
-
);
|
|
403
|
-
ftype = enumName;
|
|
404
|
-
} else if (ftype === "object") {
|
|
405
|
-
const objectNameQuestion = {
|
|
406
|
-
type: "input",
|
|
407
|
-
name: "objectName",
|
|
408
|
-
|
|
409
|
-
message: `Complex type name (DTO/Class or leave 'json') :`,
|
|
410
|
-
default: "json",
|
|
411
|
-
transformer: () => "",
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
const objectAnswer = await actualInquirer.prompt([objectNameQuestion]);
|
|
415
|
-
ftype = capitalize(objectAnswer.objectName.trim() || "json");
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
console.log(` Type for "${fname}" : ${ftype} ${success("[✓]")}`);
|
|
419
|
-
|
|
420
|
-
fields.push({ name: fname, type: ftype });
|
|
338
|
+
const entity = await buildEntityInteractive();
|
|
339
|
+
if (entity) {
|
|
340
|
+
entitiesData.entities.push(entity);
|
|
341
|
+
console.log(
|
|
342
|
+
`${success("[✓]")} Entity "${entity.name}" added with ${entity.fields.length} field(s)`
|
|
343
|
+
);
|
|
421
344
|
}
|
|
422
|
-
|
|
423
|
-
entitiesData.entities.push({ name, fields });
|
|
424
|
-
console.log(
|
|
425
|
-
`${success("[✓]")} Entity "${name}" added with ${fields.length} field(s)`
|
|
426
|
-
);
|
|
427
|
-
|
|
428
345
|
addEntity = readline.keyInYNStrict(`${info("[?]")} Add another entity?`);
|
|
429
346
|
}
|
|
430
347
|
|
|
@@ -432,145 +349,7 @@ async function getFullModeInputs(projectName, flags) {
|
|
|
432
349
|
`${info("[?]")} Add relationships between entities?`
|
|
433
350
|
);
|
|
434
351
|
if (wantsRelation) {
|
|
435
|
-
|
|
436
|
-
console.log(`\n${info("[INFO]")} Configuring relationships`);
|
|
437
|
-
|
|
438
|
-
let configuring = true;
|
|
439
|
-
while (configuring) {
|
|
440
|
-
const entityNames = entitiesData.entities.map((e) => e.name);
|
|
441
|
-
|
|
442
|
-
// 1. Select entities first
|
|
443
|
-
const selection = await actualInquirer.prompt([
|
|
444
|
-
{
|
|
445
|
-
type: "list",
|
|
446
|
-
name: "fromName",
|
|
447
|
-
message: "From which entity? (Source)",
|
|
448
|
-
choices: entityNames,
|
|
449
|
-
},
|
|
450
|
-
{
|
|
451
|
-
type: "list",
|
|
452
|
-
name: "toName",
|
|
453
|
-
message: (prev) =>
|
|
454
|
-
`To which entity should ${prev.fromName} be linked? (Target)`,
|
|
455
|
-
choices: (prev) =>
|
|
456
|
-
entityNames.filter((name) => name !== prev.fromName),
|
|
457
|
-
},
|
|
458
|
-
]);
|
|
459
|
-
|
|
460
|
-
// --- VERIFICATION: Check if link already exists (A->B or B->A) ---
|
|
461
|
-
const alreadyExists = entitiesData.relations.find(
|
|
462
|
-
(rel) =>
|
|
463
|
-
(rel.from === selection.fromName && rel.to === selection.toName) ||
|
|
464
|
-
(rel.from === selection.toName && rel.to === selection.fromName)
|
|
465
|
-
);
|
|
466
|
-
|
|
467
|
-
if (alreadyExists) {
|
|
468
|
-
logWarning(
|
|
469
|
-
`A relationship already exists between ${selection.fromName} and ${selection.toName} (${alreadyExists.type}).`
|
|
470
|
-
);
|
|
471
|
-
|
|
472
|
-
const { tryAgain } = await actualInquirer.prompt([
|
|
473
|
-
{
|
|
474
|
-
type: "confirm",
|
|
475
|
-
name: "tryAgain",
|
|
476
|
-
message: "Do you want to choose different entities?",
|
|
477
|
-
default: true,
|
|
478
|
-
},
|
|
479
|
-
]);
|
|
480
|
-
|
|
481
|
-
if (!tryAgain) break;
|
|
482
|
-
continue; // Restart selection
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// 2. Select Relationship type only if verification passed
|
|
486
|
-
const typeAnswer = await actualInquirer.prompt([
|
|
487
|
-
{
|
|
488
|
-
type: "list",
|
|
489
|
-
name: "relType",
|
|
490
|
-
message: "Relationship type:",
|
|
491
|
-
choices: [
|
|
492
|
-
{
|
|
493
|
-
name: `1-1 (One-to-One) : ${selection.fromName} has one ${selection.toName}`,
|
|
494
|
-
value: "1-1",
|
|
495
|
-
},
|
|
496
|
-
{
|
|
497
|
-
name: `1-n (One-to-Many) : ${selection.fromName} has many ${selection.toName}s`,
|
|
498
|
-
value: "1-n",
|
|
499
|
-
},
|
|
500
|
-
{
|
|
501
|
-
name: `n-1 (Many-to-One) : Many ${selection.fromName}s belong to one ${selection.toName}`,
|
|
502
|
-
value: "n-1",
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
name: `n-n (Many-to-Many) : Many ${selection.fromName}s linked to many ${selection.toName}s`,
|
|
506
|
-
value: "n-n",
|
|
507
|
-
},
|
|
508
|
-
],
|
|
509
|
-
},
|
|
510
|
-
]);
|
|
511
|
-
|
|
512
|
-
const from = entitiesData.entities.find(
|
|
513
|
-
(e) => e.name === selection.fromName
|
|
514
|
-
);
|
|
515
|
-
const to = entitiesData.entities.find(
|
|
516
|
-
(e) => e.name === selection.toName
|
|
517
|
-
);
|
|
518
|
-
const relType = typeAnswer.relType;
|
|
519
|
-
|
|
520
|
-
// Register Relationship
|
|
521
|
-
entitiesData.relations.push({
|
|
522
|
-
from: from.name,
|
|
523
|
-
to: to.name,
|
|
524
|
-
type: relType,
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
const fromLow = from.name.toLowerCase();
|
|
528
|
-
const toLow = to.name.toLowerCase();
|
|
529
|
-
|
|
530
|
-
// --- Add fields logic ---
|
|
531
|
-
if (relType === "1-1") {
|
|
532
|
-
from.fields.push(
|
|
533
|
-
{ name: `${toLow}Id`, type: "string" },
|
|
534
|
-
{ name: toLow, type: to.name }
|
|
535
|
-
);
|
|
536
|
-
} else if (relType === "1-n") {
|
|
537
|
-
from.fields.push({ name: `${toLow}s`, type: `${to.name}[]` });
|
|
538
|
-
to.fields.push(
|
|
539
|
-
{ name: `${fromLow}Id`, type: "string" },
|
|
540
|
-
{ name: fromLow, type: from.name }
|
|
541
|
-
);
|
|
542
|
-
} else if (relType === "n-1") {
|
|
543
|
-
from.fields.push(
|
|
544
|
-
{ name: `${toLow}Id`, type: "string" },
|
|
545
|
-
{ name: toLow, type: to.name }
|
|
546
|
-
);
|
|
547
|
-
to.fields.push({ name: `${fromLow}s`, type: `${from.name}[]` });
|
|
548
|
-
} else if (relType === "n-n") {
|
|
549
|
-
from.fields.push({ name: `${toLow}s`, type: `${to.name}[]` });
|
|
550
|
-
to.fields.push({ name: `${fromLow}s`, type: `${from.name}[]` });
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
console.log(
|
|
554
|
-
`\n${success("[✓]")} Relationship added: ${from.name} ${relType} ${
|
|
555
|
-
to.name
|
|
556
|
-
}`
|
|
557
|
-
);
|
|
558
|
-
|
|
559
|
-
const { addMore } = await actualInquirer.prompt([
|
|
560
|
-
{
|
|
561
|
-
type: "confirm",
|
|
562
|
-
name: "addMore",
|
|
563
|
-
message: "Add another relationship?",
|
|
564
|
-
default: false,
|
|
565
|
-
},
|
|
566
|
-
]);
|
|
567
|
-
configuring = addMore;
|
|
568
|
-
}
|
|
569
|
-
} else {
|
|
570
|
-
logWarning(
|
|
571
|
-
"At least two entities are required to configure a relationship."
|
|
572
|
-
);
|
|
573
|
-
}
|
|
352
|
+
await buildRelationsInteractive(entitiesData);
|
|
574
353
|
}
|
|
575
354
|
|
|
576
355
|
return {
|