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.
- package/CLI_USAGE.fr.md +331 -331
- package/CLI_USAGE.md +364 -364
- package/LICENSE +21 -21
- 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/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 +1 -1
- package/utils/cliParser.js +133 -76
- package/utils/colors.js +62 -62
- package/utils/configs/configureDocker.js +120 -120
- package/utils/configs/setupCleanArchitecture.js +15 -13
- package/utils/configs/setupLightArchitecture.js +15 -9
- 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/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 +28 -15
- package/utils/setups/setupDatabase.js +75 -75
- package/utils/setups/setupPrisma.js +802 -630
- 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 +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.
|
|
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": {
|
package/utils/cliParser.js
CHANGED
|
@@ -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
|
|
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
|
+
/* 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 };
|