nestcraftx 0.2.5 → 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 (40) hide show
  1. package/CLI_USAGE.fr.md +331 -331
  2. package/CLI_USAGE.md +364 -364
  3. package/LICENSE +21 -21
  4. package/bin/nestcraft.js +84 -64
  5. package/commands/demo.js +333 -330
  6. package/commands/generate.js +93 -0
  7. package/commands/generateConf.js +91 -0
  8. package/commands/info.js +48 -48
  9. package/commands/new.js +338 -335
  10. package/commands/start.js +19 -19
  11. package/commands/test.js +7 -7
  12. package/package.json +1 -1
  13. package/utils/cliParser.js +133 -76
  14. package/utils/colors.js +62 -62
  15. package/utils/configs/configureDocker.js +120 -120
  16. package/utils/configs/setupCleanArchitecture.js +15 -13
  17. package/utils/configs/setupLightArchitecture.js +15 -9
  18. package/utils/file-utils/saveProjectConfig.js +36 -0
  19. package/utils/fullModeInput.js +607 -607
  20. package/utils/generators/application/dtoUpdater.js +54 -0
  21. package/utils/generators/cleanModuleGenerator.js +475 -0
  22. package/utils/generators/database/setupDatabase.js +31 -0
  23. package/utils/generators/domain/entityUpdater.js +78 -0
  24. package/utils/generators/infrastructure/mapperUpdater.js +65 -0
  25. package/utils/generators/lightModuleGenerator.js +131 -0
  26. package/utils/generators/relation/relation.engine.js +64 -0
  27. package/utils/interactive/askEntityInputs.js +165 -0
  28. package/utils/loggers/logError.js +7 -7
  29. package/utils/loggers/logInfo.js +7 -7
  30. package/utils/loggers/logSuccess.js +7 -7
  31. package/utils/loggers/logWarning.js +7 -7
  32. package/utils/setups/orms/typeOrmSetup.js +630 -630
  33. package/utils/setups/setupAuth.js +28 -15
  34. package/utils/setups/setupDatabase.js +75 -75
  35. package/utils/setups/setupPrisma.js +802 -630
  36. package/utils/shell.js +32 -32
  37. package/utils/spinner.js +57 -57
  38. package/utils/systemCheck.js +124 -124
  39. package/utils/userInput.js +421 -421
  40. package/utils/utils.js +27 -27
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.2.5",
3
+ "version": "0.2.6",
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": {
@@ -1,76 +1,133 @@
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
-
47
- function parseFlag(arg) {
48
- if (arg.includes('=')) {
49
- const [key, ...valueParts] = arg.slice(2).split('=');
50
- return [key, valueParts.join('=')];
51
- }
52
- return [arg.slice(2), null];
53
- }
54
-
55
- function isValidProjectName(name) {
56
- return /^[a-zA-Z0-9_-]+$/.test(name);
57
- }
58
-
59
- function validateFlags(parsed) {
60
- const validOrms = ['prisma', 'typeorm', 'mongoose'];
61
- const validModes = ['full', 'light'];
62
-
63
- if (parsed.flags.orm && !validOrms.includes(parsed.flags.orm)) {
64
- parsed.errors.push(`ORM invalide: "${parsed.flags.orm}". Valeurs acceptées: ${validOrms.join(', ')}`);
65
- }
66
-
67
- if (parsed.flags.mode && !validModes.includes(parsed.flags.mode)) {
68
- parsed.errors.push(`Mode invalide: "${parsed.flags.mode}". Valeurs acceptées: ${validModes.join(', ')}`);
69
- }
70
-
71
- if (parsed.flags.full && parsed.flags.light) {
72
- parsed.errors.push('Les flags --full et --light sont mutuellement exclusifs.');
73
- }
74
- }
75
-
76
- module.exports = { parseCliArgs };
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
+
47
+ function parseCliArgs(args) {
48
+ const parsed = {
49
+ command: null,
50
+ projectName: null, // Pour 'new'
51
+ subCommand: null, // Pour 'generate' (module, auth, etc.)
52
+ targetName: null, // Pour 'generate' (User, Product, etc.)
53
+ flags: {},
54
+ positional: [],
55
+ errors: [],
56
+ };
57
+
58
+ for (let i = 2; i < args.length; i++) {
59
+ const arg = args[i];
60
+
61
+ // Commande principale (new, g, info...)
62
+ if (i === 2 && !arg.startsWith("--")) {
63
+ parsed.command = arg;
64
+ continue;
65
+ }
66
+
67
+ if (arg.startsWith("--")) {
68
+ const [key, value] = parseFlag(arg);
69
+ // ... logique des flags inchangée ...
70
+ const nextArg = args[i + 1];
71
+ if (value !== null) {
72
+ parsed.flags[key] = value;
73
+ } else if (nextArg && !nextArg.startsWith("--")) {
74
+ parsed.flags[key] = nextArg;
75
+ i++;
76
+ } else {
77
+ parsed.flags[key] = true;
78
+ }
79
+ } else {
80
+ parsed.positional.push(arg);
81
+ }
82
+ }
83
+
84
+ // Distribution des arguments selon la commande
85
+ if (parsed.command === "new") {
86
+ parsed.projectName = parsed.positional[0] || null;
87
+ if (parsed.projectName && !isValidProjectName(parsed.projectName)) {
88
+ parsed.errors.push(`Nom de projet invalide: "${parsed.projectName}".`);
89
+ }
90
+ } else if (parsed.command === "generate" || parsed.command === "g") {
91
+ parsed.subCommand = parsed.positional[0] || null; // ex: module
92
+ parsed.targetName = parsed.positional[1] || null; // ex: User
93
+ }
94
+
95
+ validateFlags(parsed);
96
+ return parsed;
97
+ }
98
+ function parseFlag(arg) {
99
+ if (arg.includes("=")) {
100
+ const [key, ...valueParts] = arg.slice(2).split("=");
101
+ return [key, valueParts.join("=")];
102
+ }
103
+ return [arg.slice(2), null];
104
+ }
105
+
106
+ function isValidProjectName(name) {
107
+ return /^[a-zA-Z0-9_-]+$/.test(name);
108
+ }
109
+
110
+ function validateFlags(parsed) {
111
+ const validOrms = ["prisma", "typeorm", "mongoose"];
112
+ const validModes = ["full", "light"];
113
+
114
+ if (parsed.flags.orm && !validOrms.includes(parsed.flags.orm)) {
115
+ parsed.errors.push(
116
+ `ORM invalide: "${parsed.flags.orm}". Valeurs acceptées: ${validOrms.join(", ")}`,
117
+ );
118
+ }
119
+
120
+ if (parsed.flags.mode && !validModes.includes(parsed.flags.mode)) {
121
+ parsed.errors.push(
122
+ `Mode invalide: "${parsed.flags.mode}". Valeurs acceptées: ${validModes.join(", ")}`,
123
+ );
124
+ }
125
+
126
+ if (parsed.flags.full && parsed.flags.light) {
127
+ parsed.errors.push(
128
+ "Les flags --full et --light sont mutuellement exclusifs.",
129
+ );
130
+ }
131
+ }
132
+
133
+ 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
+ };
@@ -1,120 +1,120 @@
1
- const fs = require("fs");
2
- const { logInfo } = require("../loggers/logInfo");
3
- const { logSuccess } = require("../loggers/logSuccess");
4
- const { createFile } = require("../userInput");
5
-
6
- async function configureDocker(inputs) {
7
- logInfo("Generating Docker files...");
8
-
9
- const dockerfileContent = `
10
- # ------------------ Stage 1: Build & Dependencies ------------------
11
- FROM node:20-alpine AS builder
12
-
13
- # Set working directory
14
- WORKDIR /app
15
-
16
- # Copy package files first to leverage Docker cache
17
- COPY package.json package-lock.json ./
18
-
19
- # Install dependencies (Node dependencies and global tools like Prisma if needed)
20
- # The 'production' flag ensures only production dependencies are installed if applicable.
21
- # 'npm ci' is recommended for CI/CD/Docker builds instead of 'npm install'
22
- RUN ${
23
- inputs.packageManager === "npm"
24
- ? "npm ci"
25
- : `${inputs.packageManager} install --frozen-lockfile`
26
- }
27
-
28
- # Copy the rest of the application source code
29
- COPY . .
30
-
31
- # Build the NestJS application (if TypeScript is used)
32
- RUN ${inputs.packageManager} run build
33
-
34
- # ------------------ Stage 2: Production Runtime ------------------
35
- FROM node:20-alpine AS production
36
-
37
- # Set working directory
38
- WORKDIR /app
39
-
40
- # Copy production node_modules from the builder stage
41
- COPY --from=builder /app/node_modules ./node_modules
42
-
43
- # Copy built application and start scripts (dist and package.json)
44
- COPY --from=builder /app/dist ./dist
45
- COPY --from=builder /app/package.json ./package.json
46
-
47
- # Expose the application port (usually 3000 for NestJS)
48
- EXPOSE 3000
49
-
50
- # Run the application using the built files (production environment)
51
- # Use 'start:prod' if available, otherwise fall back to 'start'
52
- CMD [ "${inputs.packageManager}", "run", "start:prod" ]
53
- `;
54
- await createFile({
55
- path: "Dockerfile",
56
- contente: dockerfileContent.trim(),
57
- });
58
- const dockerComposeContent = `
59
- version: '3.8'
60
-
61
- services:
62
- # Application Service (NestJS)
63
- app:
64
- build:
65
- context: .
66
- dockerfile: Dockerfile
67
- # Links the container to the internal network
68
- networks:
69
- - backend_network
70
- # Maps internal port 3000 (EXPOSE in Dockerfile) to external port 3000
71
- ports:
72
- - "3000:3000"
73
- # Mount .env file (for local dev, not recommended for prod)
74
- env_file:
75
- - .env
76
- # Restart policy
77
- restart: always
78
- # Wait for the DB to be ready (requires 'wait-for-it' or similar)
79
- depends_on:
80
- - db
81
-
82
- # Database Service (PostgreSQL - generic image for example)
83
- db:
84
- image: postgres:15-alpine
85
- container_name: ${inputs.projectName}_db
86
- networks:
87
- - backend_network
88
- environment:
89
- POSTGRES_USER: ${inputs.dbConfig.POSTGRES_USER || "postgres"}
90
- POSTGRES_PASSWORD: ${inputs.dbConfig.POSTGRES_PASSWORD || "secret"}
91
- POSTGRES_DB: ${inputs.dbConfig.POSTGRES_DB || "mydatabase"}
92
- # Optional: Set timezone
93
- TZ: Europe/Paris
94
- # For dev purposes: map DB port externally (optional)
95
- ports:
96
- - "5432:5432"
97
- # Persist database data
98
- volumes:
99
- - postgres_data:/var/lib/postgresql/data
100
- restart: always
101
-
102
- # Networks definition
103
- networks:
104
- backend_network:
105
- driver: bridge
106
-
107
- # Volumes definition
108
- volumes:
109
- postgres_data:
110
- `;
111
-
112
- await createFile({
113
- path: "docker-compose.yml",
114
- contente: dockerComposeContent.trim(),
115
- });
116
-
117
- logSuccess("Docker successfully configured with enhanced settings");
118
- }
119
-
120
- module.exports = { configureDocker };
1
+ const fs = require("fs");
2
+ const { logInfo } = require("../loggers/logInfo");
3
+ const { logSuccess } = require("../loggers/logSuccess");
4
+ const { createFile } = require("../userInput");
5
+
6
+ async function configureDocker(inputs) {
7
+ logInfo("Generating Docker files...");
8
+
9
+ const dockerfileContent = `
10
+ # ------------------ Stage 1: Build & Dependencies ------------------
11
+ FROM node:20-alpine AS builder
12
+
13
+ # Set working directory
14
+ WORKDIR /app
15
+
16
+ # Copy package files first to leverage Docker cache
17
+ COPY package.json package-lock.json ./
18
+
19
+ # Install dependencies (Node dependencies and global tools like Prisma if needed)
20
+ # The 'production' flag ensures only production dependencies are installed if applicable.
21
+ # 'npm ci' is recommended for CI/CD/Docker builds instead of 'npm install'
22
+ RUN ${
23
+ inputs.packageManager === "npm"
24
+ ? "npm ci"
25
+ : `${inputs.packageManager} install --frozen-lockfile`
26
+ }
27
+
28
+ # Copy the rest of the application source code
29
+ COPY . .
30
+
31
+ # Build the NestJS application (if TypeScript is used)
32
+ RUN ${inputs.packageManager} run build
33
+
34
+ # ------------------ Stage 2: Production Runtime ------------------
35
+ FROM node:20-alpine AS production
36
+
37
+ # Set working directory
38
+ WORKDIR /app
39
+
40
+ # Copy production node_modules from the builder stage
41
+ COPY --from=builder /app/node_modules ./node_modules
42
+
43
+ # Copy built application and start scripts (dist and package.json)
44
+ COPY --from=builder /app/dist ./dist
45
+ COPY --from=builder /app/package.json ./package.json
46
+
47
+ # Expose the application port (usually 3000 for NestJS)
48
+ EXPOSE 3000
49
+
50
+ # Run the application using the built files (production environment)
51
+ # Use 'start:prod' if available, otherwise fall back to 'start'
52
+ CMD [ "${inputs.packageManager}", "run", "start:prod" ]
53
+ `;
54
+ await createFile({
55
+ path: "Dockerfile",
56
+ contente: dockerfileContent.trim(),
57
+ });
58
+ const dockerComposeContent = `
59
+ version: '3.8'
60
+
61
+ services:
62
+ # Application Service (NestJS)
63
+ app:
64
+ build:
65
+ context: .
66
+ dockerfile: Dockerfile
67
+ # Links the container to the internal network
68
+ networks:
69
+ - backend_network
70
+ # Maps internal port 3000 (EXPOSE in Dockerfile) to external port 3000
71
+ ports:
72
+ - "3000:3000"
73
+ # Mount .env file (for local dev, not recommended for prod)
74
+ env_file:
75
+ - .env
76
+ # Restart policy
77
+ restart: always
78
+ # Wait for the DB to be ready (requires 'wait-for-it' or similar)
79
+ depends_on:
80
+ - db
81
+
82
+ # Database Service (PostgreSQL - generic image for example)
83
+ db:
84
+ image: postgres:15-alpine
85
+ container_name: ${inputs.projectName}_db
86
+ networks:
87
+ - backend_network
88
+ environment:
89
+ POSTGRES_USER: ${inputs.dbConfig.POSTGRES_USER || "postgres"}
90
+ POSTGRES_PASSWORD: ${inputs.dbConfig.POSTGRES_PASSWORD || "secret"}
91
+ POSTGRES_DB: ${inputs.dbConfig.POSTGRES_DB || "mydatabase"}
92
+ # Optional: Set timezone
93
+ TZ: Europe/Paris
94
+ # For dev purposes: map DB port externally (optional)
95
+ ports:
96
+ - "5432:5432"
97
+ # Persist database data
98
+ volumes:
99
+ - postgres_data:/var/lib/postgresql/data
100
+ restart: always
101
+
102
+ # Networks definition
103
+ networks:
104
+ backend_network:
105
+ driver: bridge
106
+
107
+ # Volumes definition
108
+ volumes:
109
+ postgres_data:
110
+ `;
111
+
112
+ await createFile({
113
+ path: "docker-compose.yml",
114
+ contente: dockerComposeContent.trim(),
115
+ });
116
+
117
+ logSuccess("Docker successfully configured with enhanced settings");
118
+ }
119
+
120
+ module.exports = { configureDocker };