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
package/commands/start.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
const { execSync } = require("child_process");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
|
|
4
|
-
// 🔐 Forcer UTF-8 uniquement sur Windows
|
|
5
|
-
if (process.platform === "win32") {
|
|
6
|
-
try {
|
|
7
|
-
execSync("chcp 65001", { stdio: "ignore" }); // Changer l'encodage de la console
|
|
8
|
-
} catch (err) {
|
|
9
|
-
const message = `⚠️ Impossible de forcer UTF-8 dans le terminal Windows: ${err}`;
|
|
10
|
-
console.warn(message);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
module.exports = function () {
|
|
15
|
-
console.log("🚀 Lancement de la génération NestJS...");
|
|
16
|
-
|
|
17
|
-
const setupPath = path.join(__dirname, "..", "setup.js");
|
|
18
|
-
require(setupPath);
|
|
19
|
-
};
|
|
1
|
+
const { execSync } = require("child_process");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
|
|
4
|
+
// 🔐 Forcer UTF-8 uniquement sur Windows
|
|
5
|
+
if (process.platform === "win32") {
|
|
6
|
+
try {
|
|
7
|
+
execSync("chcp 65001", { stdio: "ignore" }); // Changer l'encodage de la console
|
|
8
|
+
} catch (err) {
|
|
9
|
+
const message = `⚠️ Impossible de forcer UTF-8 dans le terminal Windows: ${err}`;
|
|
10
|
+
console.warn(message);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = function () {
|
|
15
|
+
console.log("🚀 Lancement de la génération NestJS...");
|
|
16
|
+
|
|
17
|
+
const setupPath = path.join(__dirname, "..", "setup.js");
|
|
18
|
+
require(setupPath);
|
|
19
|
+
};
|
package/commands/test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const { displaySystemCheck } = require('../utils/systemCheck');
|
|
2
|
-
|
|
3
|
-
async function testCommand() {
|
|
4
|
-
displaySystemCheck();
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
module.exports = testCommand;
|
|
1
|
+
const { displaySystemCheck } = require('../utils/systemCheck');
|
|
2
|
+
|
|
3
|
+
async function testCommand() {
|
|
4
|
+
displaySystemCheck();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
module.exports = testCommand;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestcraftx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Modern CLI to generate scalable NestJS projects with Clean Architecture (FULL) and MVP mode (LIGHT) - Enhanced with auto-generated .env secrets, complete ORM support, and entity relations",
|
|
5
5
|
"main": "bin/nestcraft.js",
|
|
6
6
|
"bin": {
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils/app-module.updater.js
|
|
3
|
+
*
|
|
4
|
+
* Gestion de la mise à jour sécurisée du fichier app.module.ts.
|
|
5
|
+
* Extrait de userInput.js pour isoler cette responsabilité spécifique.
|
|
6
|
+
*
|
|
7
|
+
* Les fichiers qui importent `safeUpdateAppModule` depuis userInput.js
|
|
8
|
+
* continuent de fonctionner sans modification grâce aux ré-exports.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require("fs");
|
|
12
|
+
const { updateFile } = require("./file-system");
|
|
13
|
+
const { logInfo } = require("./loggers/logInfo");
|
|
14
|
+
const { capitalize } = require("./helpers");
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Ajoute un module NestJS dans le fichier `src/app.module.ts` de manière idempotente.
|
|
18
|
+
*
|
|
19
|
+
* - Ajoute la ligne `import { XModule } from 'src/x/x.module'` si absente
|
|
20
|
+
* - Ajoute `XModule` dans le tableau `imports: []` du décorateur @Module si absent
|
|
21
|
+
* - Utilise un parsing par comptage de crochets pour gérer les imports imbriqués
|
|
22
|
+
* (ex: ConfigModule.forRoot({ ... }))
|
|
23
|
+
*
|
|
24
|
+
* @param {string} entity - Le nom de l'entité en minuscule (ex: "user", "post")
|
|
25
|
+
*/
|
|
26
|
+
async function safeUpdateAppModule(entity) {
|
|
27
|
+
const filePath = "src/app.module.ts";
|
|
28
|
+
const moduleName = `${capitalize(entity)}Module`;
|
|
29
|
+
const importLine = `import { ${moduleName} } from 'src/${entity}/${entity}.module';`;
|
|
30
|
+
|
|
31
|
+
let content = fs.readFileSync(filePath, "utf-8");
|
|
32
|
+
|
|
33
|
+
// Étape 1 : Ajout de l'import en haut du fichier si nécessaire
|
|
34
|
+
if (!content.includes(importLine)) {
|
|
35
|
+
const importMarker = `import { ConfigModule } from '@nestjs/config';`;
|
|
36
|
+
|
|
37
|
+
if (content.includes(importMarker)) {
|
|
38
|
+
await updateFile({
|
|
39
|
+
path: filePath,
|
|
40
|
+
pattern: importMarker,
|
|
41
|
+
replacement: `${importMarker}\n${importLine}`,
|
|
42
|
+
});
|
|
43
|
+
content = fs.readFileSync(filePath, "utf-8");
|
|
44
|
+
} else {
|
|
45
|
+
logInfo(
|
|
46
|
+
" Impossible de trouver le point d'insertion de l'import (ConfigModule manquant)",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Étape 2 : Vérifier le bloc des imports du @Module avec gestion des crochets imbriqués
|
|
52
|
+
const importsHeaderMatch = content.match(/imports:\s*\[/);
|
|
53
|
+
if (!importsHeaderMatch) {
|
|
54
|
+
logInfo(" Impossible de trouver le bloc 'imports' dans AppModule.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const startIndex =
|
|
59
|
+
importsHeaderMatch.index + importsHeaderMatch[0].length - 1; // index du '['
|
|
60
|
+
let depth = 1;
|
|
61
|
+
let endIndex = -1;
|
|
62
|
+
for (let i = startIndex + 1; i < content.length; i++) {
|
|
63
|
+
if (content[i] === "[") {
|
|
64
|
+
depth++;
|
|
65
|
+
} else if (content[i] === "]") {
|
|
66
|
+
depth--;
|
|
67
|
+
if (depth === 0) {
|
|
68
|
+
endIndex = i;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (endIndex === -1) {
|
|
75
|
+
logInfo(
|
|
76
|
+
" Impossible de trouver le crochet de fermeture du bloc 'imports' dans AppModule.",
|
|
77
|
+
);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const currentImportsBlock = content.substring(startIndex + 1, endIndex);
|
|
82
|
+
const isAlreadyImportedInModule = currentImportsBlock.includes(moduleName);
|
|
83
|
+
|
|
84
|
+
if (!isAlreadyImportedInModule) {
|
|
85
|
+
const updatedBlock = currentImportsBlock.trim().endsWith(",")
|
|
86
|
+
? `${currentImportsBlock.trim()} ${moduleName},`
|
|
87
|
+
: `${currentImportsBlock.trim()}, ${moduleName},`;
|
|
88
|
+
|
|
89
|
+
const beforeBlock = content.substring(0, startIndex + 1);
|
|
90
|
+
const afterBlock = content.substring(endIndex);
|
|
91
|
+
const newContent = beforeBlock + updatedBlock + afterBlock;
|
|
92
|
+
fs.writeFileSync(filePath, newContent, "utf-8");
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = { safeUpdateAppModule };
|
package/utils/cliParser.js
CHANGED
|
@@ -1,76 +1,88 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
1
|
+
|
|
2
|
+
function parseCliArgs(args) {
|
|
3
|
+
const parsed = {
|
|
4
|
+
command: null,
|
|
5
|
+
projectName: null, // Pour 'new'
|
|
6
|
+
subCommand: null, // Pour 'generate' (module, auth, etc.)
|
|
7
|
+
targetName: null, // Pour 'generate' (User, Product, etc.)
|
|
8
|
+
flags: {},
|
|
9
|
+
positional: [],
|
|
10
|
+
errors: [],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
for (let i = 2; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
|
|
16
|
+
// Commande principale (new, g, info...)
|
|
17
|
+
if (i === 2 && !arg.startsWith("--")) {
|
|
18
|
+
parsed.command = arg;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (arg.startsWith("--")) {
|
|
23
|
+
const [key, value] = parseFlag(arg);
|
|
24
|
+
// ... logique des flags inchangée ...
|
|
25
|
+
const nextArg = args[i + 1];
|
|
26
|
+
if (value !== null) {
|
|
27
|
+
parsed.flags[key] = value;
|
|
28
|
+
} else if (nextArg && !nextArg.startsWith("--")) {
|
|
29
|
+
parsed.flags[key] = nextArg;
|
|
30
|
+
i++;
|
|
31
|
+
} else {
|
|
32
|
+
parsed.flags[key] = true;
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
parsed.positional.push(arg);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Distribution des arguments selon la commande
|
|
40
|
+
if (parsed.command === "new") {
|
|
41
|
+
parsed.projectName = parsed.positional[0] || null;
|
|
42
|
+
if (parsed.projectName && !isValidProjectName(parsed.projectName)) {
|
|
43
|
+
parsed.errors.push(`Nom de projet invalide: "${parsed.projectName}".`);
|
|
44
|
+
}
|
|
45
|
+
} else if (parsed.command === "generate" || parsed.command === "g") {
|
|
46
|
+
parsed.subCommand = parsed.positional[0] || null; // ex: module
|
|
47
|
+
parsed.targetName = parsed.positional[1] || null; // ex: User
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
validateFlags(parsed);
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
function parseFlag(arg) {
|
|
54
|
+
if (arg.includes("=")) {
|
|
55
|
+
const [key, ...valueParts] = arg.slice(2).split("=");
|
|
56
|
+
return [key, valueParts.join("=")];
|
|
57
|
+
}
|
|
58
|
+
return [arg.slice(2), null];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isValidProjectName(name) {
|
|
62
|
+
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function validateFlags(parsed) {
|
|
66
|
+
const validOrms = ["prisma", "typeorm", "mongoose"];
|
|
67
|
+
const validModes = ["full", "light"];
|
|
68
|
+
|
|
69
|
+
if (parsed.flags.orm && !validOrms.includes(parsed.flags.orm)) {
|
|
70
|
+
parsed.errors.push(
|
|
71
|
+
`ORM invalide: "${parsed.flags.orm}". Valeurs acceptées: ${validOrms.join(", ")}`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (parsed.flags.mode && !validModes.includes(parsed.flags.mode)) {
|
|
76
|
+
parsed.errors.push(
|
|
77
|
+
`Mode invalide: "${parsed.flags.mode}". Valeurs acceptées: ${validModes.join(", ")}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (parsed.flags.full && parsed.flags.light) {
|
|
82
|
+
parsed.errors.push(
|
|
83
|
+
"Les flags --full et --light sont mutuellement exclusifs.",
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { parseCliArgs };
|
package/utils/colors.js
CHANGED
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
const colors = {
|
|
2
|
-
reset: '\x1b[0m',
|
|
3
|
-
bold: '\x1b[1m',
|
|
4
|
-
dim: '\x1b[2m',
|
|
5
|
-
|
|
6
|
-
black: '\x1b[30m',
|
|
7
|
-
red: '\x1b[31m',
|
|
8
|
-
green: '\x1b[32m',
|
|
9
|
-
yellow: '\x1b[33m',
|
|
10
|
-
blue: '\x1b[34m',
|
|
11
|
-
magenta: '\x1b[35m',
|
|
12
|
-
cyan: '\x1b[36m',
|
|
13
|
-
white: '\x1b[37m',
|
|
14
|
-
|
|
15
|
-
bgBlack: '\x1b[40m',
|
|
16
|
-
bgRed: '\x1b[41m',
|
|
17
|
-
bgGreen: '\x1b[42m',
|
|
18
|
-
bgYellow: '\x1b[43m',
|
|
19
|
-
bgBlue: '\x1b[44m',
|
|
20
|
-
bgMagenta: '\x1b[45m',
|
|
21
|
-
bgCyan: '\x1b[46m',
|
|
22
|
-
bgWhite: '\x1b[47m'
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
function colorize(text, color) {
|
|
26
|
-
return `${colors[color]}${text}${colors.reset}`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function success(text) {
|
|
30
|
-
return colorize(text, 'green');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function error(text) {
|
|
34
|
-
return colorize(text, 'red');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function warning(text) {
|
|
38
|
-
return colorize(text, 'yellow');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function info(text) {
|
|
42
|
-
return colorize(text, 'cyan');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function bold(text) {
|
|
46
|
-
return `${colors.bold}${text}${colors.reset}`;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function dim(text) {
|
|
50
|
-
return `${colors.dim}${text}${colors.reset}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = {
|
|
54
|
-
colors,
|
|
55
|
-
colorize,
|
|
56
|
-
success,
|
|
57
|
-
error,
|
|
58
|
-
warning,
|
|
59
|
-
info,
|
|
60
|
-
bold,
|
|
61
|
-
dim
|
|
62
|
-
};
|
|
1
|
+
const colors = {
|
|
2
|
+
reset: '\x1b[0m',
|
|
3
|
+
bold: '\x1b[1m',
|
|
4
|
+
dim: '\x1b[2m',
|
|
5
|
+
|
|
6
|
+
black: '\x1b[30m',
|
|
7
|
+
red: '\x1b[31m',
|
|
8
|
+
green: '\x1b[32m',
|
|
9
|
+
yellow: '\x1b[33m',
|
|
10
|
+
blue: '\x1b[34m',
|
|
11
|
+
magenta: '\x1b[35m',
|
|
12
|
+
cyan: '\x1b[36m',
|
|
13
|
+
white: '\x1b[37m',
|
|
14
|
+
|
|
15
|
+
bgBlack: '\x1b[40m',
|
|
16
|
+
bgRed: '\x1b[41m',
|
|
17
|
+
bgGreen: '\x1b[42m',
|
|
18
|
+
bgYellow: '\x1b[43m',
|
|
19
|
+
bgBlue: '\x1b[44m',
|
|
20
|
+
bgMagenta: '\x1b[45m',
|
|
21
|
+
bgCyan: '\x1b[46m',
|
|
22
|
+
bgWhite: '\x1b[47m'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function colorize(text, color) {
|
|
26
|
+
return `${colors[color]}${text}${colors.reset}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function success(text) {
|
|
30
|
+
return colorize(text, 'green');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function error(text) {
|
|
34
|
+
return colorize(text, 'red');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function warning(text) {
|
|
38
|
+
return colorize(text, 'yellow');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function info(text) {
|
|
42
|
+
return colorize(text, 'cyan');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function bold(text) {
|
|
46
|
+
return `${colors.bold}${text}${colors.reset}`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function dim(text) {
|
|
50
|
+
return `${colors.dim}${text}${colors.reset}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
colors,
|
|
55
|
+
colorize,
|
|
56
|
+
success,
|
|
57
|
+
error,
|
|
58
|
+
warning,
|
|
59
|
+
info,
|
|
60
|
+
bold,
|
|
61
|
+
dim
|
|
62
|
+
};
|