nestcraftx 0.3.0 → 1.0.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/.github/workflows/ci.yml +35 -0
- package/CHANGELOG.fr.md +74 -0
- package/CHANGELOG.md +74 -0
- package/PROGRESS.md +70 -67
- package/README.fr.md +60 -69
- package/bin/nestcraft.js +8 -0
- package/commands/demo.js +12 -46
- package/commands/generate.js +551 -2
- package/commands/generateConf.js +4 -4
- package/commands/help.js +11 -0
- package/commands/info.js +2 -3
- package/commands/list.js +93 -0
- package/commands/new.js +28 -56
- package/jest.config.js +9 -0
- package/package.json +7 -2
- package/readme.md +59 -68
- package/tests/e2e/generator.spec.js +71 -0
- package/tests/unit/appModuleUpdater.spec.js +111 -0
- package/tests/unit/cliParser.spec.js +74 -0
- package/tests/unit/controllerGenerator.spec.js +145 -0
- package/tests/unit/dtoGenerator.spec.js +87 -0
- package/tests/unit/entityGenerator.spec.js +65 -0
- package/tests/unit/generateCommand.spec.js +100 -0
- package/tests/unit/health.spec.js +69 -0
- package/tests/unit/listCommand.spec.js +91 -0
- package/tests/unit/middlewareGenerator.spec.js +64 -0
- package/tests/unit/newCommand.spec.js +88 -0
- package/tests/unit/relationCommand.spec.js +202 -0
- package/tests/unit/throttler.spec.js +117 -0
- package/utils/app-module.updater.js +6 -0
- package/utils/configs/setupCleanArchitecture.js +1 -1
- package/utils/configs/setupLightArchitecture.js +98 -26
- package/utils/envGenerator.js +9 -1
- package/utils/file-system.js +15 -0
- package/utils/file-utils/packageJsonUtils.js +6 -0
- package/utils/file-utils/saveProjectConfig.js +9 -0
- package/utils/fullModeInput.js +12 -229
- package/utils/generators/application/dtoGenerator.js +26 -8
- package/utils/generators/application/dtoUpdater.js +5 -0
- package/utils/generators/cleanModuleGenerator.js +40 -11
- package/utils/generators/domain/entityUpdater.js +5 -0
- package/utils/generators/infrastructure/mapperUpdater.js +5 -0
- package/utils/generators/infrastructure/middlewareGenerator.js +112 -315
- package/utils/generators/infrastructure/mongooseSchemaGenerator.js +114 -4
- package/utils/generators/infrastructure/repositoryGenerator.js +114 -14
- package/utils/generators/lightModuleGenerator.js +14 -0
- package/utils/generators/presentation/controllerGenerator.js +70 -19
- package/utils/helpers.js +8 -2
- package/utils/interactive/askEntityInputs.js +5 -68
- package/utils/interactive/askRelationCommand.js +98 -0
- package/utils/interactive/entityBuilder.js +411 -0
- package/utils/lightModeInput.js +22 -241
- package/utils/setups/orms/typeOrmSetup.js +42 -8
- package/utils/setups/projectSetup.js +88 -10
- package/utils/setups/setupAuth.js +335 -20
- package/utils/setups/setupDatabase.js +4 -1
- package/utils/setups/setupHealth.js +74 -0
- package/utils/setups/setupMongoose.js +84 -32
- package/utils/setups/setupPrisma.js +151 -4
- package/utils/setups/setupSwagger.js +15 -8
- package/utils/setups/setupThrottler.js +92 -0
- package/utils/shell.js +61 -9
package/commands/help.js
CHANGED
|
@@ -59,6 +59,17 @@ async function helpCommand() {
|
|
|
59
59
|
--auth Adds JWT Auth
|
|
60
60
|
--swagger Adds Swagger UI
|
|
61
61
|
|
|
62
|
+
nestcraftx generate, g <subcommand> [target]
|
|
63
|
+
Generates modules or auth post-scaffolding
|
|
64
|
+
Subcommands:
|
|
65
|
+
module <name> Generates a new module (MVP or Clean Architecture)
|
|
66
|
+
auth Enables JWT Auth on an existing project
|
|
67
|
+
relation, r Creates a relation between two existing modules
|
|
68
|
+
(interactive: select source, target, and relation type)
|
|
69
|
+
|
|
70
|
+
nestcraftx list, l
|
|
71
|
+
Lists all scaffolded entities and their relationships
|
|
72
|
+
|
|
62
73
|
nestcraftx test
|
|
63
74
|
Checks system environment (Node, npm, Nest CLI, Docker, etc.)
|
|
64
75
|
|
package/commands/info.js
CHANGED
|
@@ -27,11 +27,10 @@ async function infoCommand() {
|
|
|
27
27
|
console.log(" " + packageJson.repository.url);
|
|
28
28
|
|
|
29
29
|
console.log("\n📅 Upcoming:");
|
|
30
|
-
console.log(" •
|
|
30
|
+
console.log(" • MySQL & SQLite support");
|
|
31
|
+
console.log(" • Custom templates from GitHub");
|
|
31
32
|
console.log(" • Microservices support");
|
|
32
|
-
console.log(" • CI/CD Templates");
|
|
33
33
|
console.log(" • GraphQL integration");
|
|
34
|
-
console.log(" • Automated Tests");
|
|
35
34
|
|
|
36
35
|
console.log("\n💡 Available Commands:");
|
|
37
36
|
console.log(" nestcraftx new <name> [options] Create a project");
|
package/commands/list.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { logError } = require("../utils/loggers/logError");
|
|
4
|
+
const { logInfo } = require("../utils/loggers/logInfo");
|
|
5
|
+
const { logWarning } = require("../utils/loggers/logWarning");
|
|
6
|
+
const { bold } = require("../utils/colors");
|
|
7
|
+
|
|
8
|
+
async function listCommand() {
|
|
9
|
+
const configPath = path.join(process.cwd(), ".nestcraftx", ".nestcraftxrc");
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(configPath)) {
|
|
12
|
+
logError("Aucun fichier de configuration NestcraftX trouvé.");
|
|
13
|
+
logInfo("Assurez-vous d'être à la racine d'un projet NestcraftX.");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let projectConfig;
|
|
18
|
+
try {
|
|
19
|
+
const rawData = fs.readFileSync(configPath, "utf8");
|
|
20
|
+
projectConfig = JSON.parse(rawData);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
logError("Erreur lors de la lecture du fichier .nestcraftxrc. Le JSON est peut-être corrompu.");
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const { name, mode, orm, database, entities = [], relations = [] } = projectConfig;
|
|
27
|
+
|
|
28
|
+
console.log("\n" + "=".repeat(74));
|
|
29
|
+
console.log(`📋 Project: ${bold(name)} | Mode: ${bold(mode ? mode.toUpperCase() : "FULL")} | ORM: ${bold(orm ? orm.toUpperCase() : "")} | DB: ${bold(database || "")}`);
|
|
30
|
+
console.log("=".repeat(74));
|
|
31
|
+
|
|
32
|
+
if (entities.length === 0) {
|
|
33
|
+
logWarning("Aucune entité configurée pour le moment.");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// 1. Draw Entities Table
|
|
38
|
+
console.log("\n" + bold("ENTITIES"));
|
|
39
|
+
console.log("┌" + "─".repeat(20) + "┬" + "─".repeat(50) + "┐");
|
|
40
|
+
console.log("│ " + "Entity Name".padEnd(18) + " │ " + "Fields (Type)".padEnd(48) + " │");
|
|
41
|
+
console.log("├" + "─".repeat(20) + "┼" + "─".repeat(50) + "┤");
|
|
42
|
+
|
|
43
|
+
entities.forEach(entity => {
|
|
44
|
+
const nameStr = entity.name || "";
|
|
45
|
+
const fieldsStr = (entity.fields || []).map(f => `${f.name} (${f.type})`).join(", ");
|
|
46
|
+
|
|
47
|
+
// Chunk fields if they are too long to prevent table layout breakage
|
|
48
|
+
if (fieldsStr.length <= 48) {
|
|
49
|
+
console.log("│ " + nameStr.padEnd(18) + " │ " + fieldsStr.padEnd(48) + " │");
|
|
50
|
+
} else {
|
|
51
|
+
// Print first line
|
|
52
|
+
let chunk = fieldsStr.substring(0, 48);
|
|
53
|
+
let lastSpace = chunk.lastIndexOf(", ");
|
|
54
|
+
if (lastSpace > 30) {
|
|
55
|
+
chunk = fieldsStr.substring(0, lastSpace + 1);
|
|
56
|
+
}
|
|
57
|
+
console.log("│ " + nameStr.padEnd(18) + " │ " + chunk.padEnd(48) + " │");
|
|
58
|
+
|
|
59
|
+
let remaining = fieldsStr.substring(chunk.length).trim();
|
|
60
|
+
while (remaining.length > 0) {
|
|
61
|
+
let nextChunk = remaining.substring(0, 48);
|
|
62
|
+
if (remaining.length > 48) {
|
|
63
|
+
let spaceIndex = nextChunk.lastIndexOf(", ");
|
|
64
|
+
if (spaceIndex > 30) {
|
|
65
|
+
nextChunk = remaining.substring(0, spaceIndex + 1);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
console.log("│ " + "".padEnd(18) + " │ " + nextChunk.padEnd(48) + " │");
|
|
69
|
+
remaining = remaining.substring(nextChunk.length).trim();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
console.log("└" + "─".repeat(20) + "┴" + "─".repeat(50) + "┘");
|
|
75
|
+
|
|
76
|
+
// 2. Draw Relations Table
|
|
77
|
+
if (relations && relations.length > 0) {
|
|
78
|
+
console.log("\n" + bold("RELATIONSHIPS"));
|
|
79
|
+
console.log("┌" + "─".repeat(40) + "┬" + "─".repeat(15) + "┐");
|
|
80
|
+
console.log("│ " + "Relation".padEnd(38) + " │ " + "Type".padEnd(13) + " │");
|
|
81
|
+
console.log("├" + "─".repeat(40) + "┼" + "─".repeat(15) + "┤");
|
|
82
|
+
|
|
83
|
+
relations.forEach(rel => {
|
|
84
|
+
const relationStr = `${rel.from} ──► ${rel.to}`;
|
|
85
|
+
const typeStr = rel.type || "";
|
|
86
|
+
console.log("│ " + relationStr.padEnd(38) + " │ " + typeStr.padEnd(13) + " │");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
console.log("└" + "─".repeat(40) + "┴" + "─".repeat(15) + "┘\n");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = listCommand;
|
package/commands/new.js
CHANGED
|
@@ -1,26 +1,12 @@
|
|
|
1
1
|
const { getFullModeInputs } = require("../utils/fullModeInput");
|
|
2
2
|
const { getLightModeInputs } = require("../utils/lightModeInput");
|
|
3
|
-
const {
|
|
4
|
-
const {
|
|
5
|
-
setupCleanArchitecture,
|
|
6
|
-
} = require("../utils/configs/setupCleanArchitecture");
|
|
7
|
-
const {
|
|
8
|
-
setupLightArchitecture,
|
|
9
|
-
} = require("../utils/configs/setupLightArchitecture");
|
|
10
|
-
const { setupAuth } = require("../utils/setups/setupAuth");
|
|
11
|
-
const { setupSwagger } = require("../utils/setups/setupSwagger");
|
|
12
|
-
const { setupDatabase } = require("../utils/setups/setupDatabase");
|
|
13
|
-
const { configureDocker } = require("../utils/configs/configureDocker");
|
|
14
|
-
const { setupBootstrapLogger } = require("../utils/setups/setupLogger");
|
|
3
|
+
const { runProjectSetupPipeline } = require("../utils/setups/projectSetup");
|
|
15
4
|
const { logSuccess } = require("../utils/loggers/logSuccess");
|
|
16
5
|
const { logInfo } = require("../utils/loggers/logInfo");
|
|
17
6
|
const { logError } = require("../utils/loggers/logError");
|
|
18
|
-
const { generateEnvFile, writeEnvFile } = require("../utils/envGenerator");
|
|
19
7
|
const readline = require("readline-sync");
|
|
20
8
|
const { info, warning } = require("../utils/colors");
|
|
21
9
|
const { logWarning } = require("../utils/loggers/logWarning");
|
|
22
|
-
const { saveProjectConfig } = require("../utils/file-utils/saveProjectConfig");
|
|
23
|
-
const { printSetupWarnings } = require("../utils/shell");
|
|
24
10
|
|
|
25
11
|
async function newCommand(projectName, flags = {}) {
|
|
26
12
|
if (!projectName) {
|
|
@@ -50,7 +36,11 @@ async function newCommand(projectName, flags = {}) {
|
|
|
50
36
|
}
|
|
51
37
|
}
|
|
52
38
|
|
|
53
|
-
const
|
|
39
|
+
const mode = determineMode(flags);
|
|
40
|
+
const inputs =
|
|
41
|
+
mode === "light"
|
|
42
|
+
? await buildLightModeInputs(currentProjectName, flags)
|
|
43
|
+
: await buildFullModeInputs(currentProjectName, flags);
|
|
54
44
|
return executeProjectSetup(inputs);
|
|
55
45
|
}
|
|
56
46
|
|
|
@@ -100,7 +90,8 @@ function hasAllLightModeFlags(flags) {
|
|
|
100
90
|
if (flags.interactive === true) {
|
|
101
91
|
return false;
|
|
102
92
|
}
|
|
103
|
-
|
|
93
|
+
const hasMode = flags.mode !== undefined || flags.light === true || flags.full === true;
|
|
94
|
+
return flags.orm !== undefined && hasMode;
|
|
104
95
|
}
|
|
105
96
|
|
|
106
97
|
function buildLightModeFromFlags(projectName, flags) {
|
|
@@ -111,13 +102,19 @@ function buildLightModeFromFlags(projectName, flags) {
|
|
|
111
102
|
logError(`Unrecognized ORM: ${orm}. Using Prisma by default.`);
|
|
112
103
|
}
|
|
113
104
|
|
|
105
|
+
let packageManager = "npm";
|
|
106
|
+
if (flags.pnpm) packageManager = "pnpm";
|
|
107
|
+
else if (flags.yarn) packageManager = "yarn";
|
|
108
|
+
else if (flags.packageManager) packageManager = flags.packageManager.toLowerCase();
|
|
109
|
+
|
|
114
110
|
const inputs = {
|
|
115
111
|
projectName,
|
|
116
112
|
mode: "light",
|
|
117
|
-
useYarn:
|
|
113
|
+
useYarn: packageManager === "yarn",
|
|
118
114
|
useDocker: flags.docker !== false,
|
|
119
115
|
useAuth: !!flags.auth,
|
|
120
116
|
useSwagger: !!flags.swagger,
|
|
117
|
+
useThrottler: !!flags.throttler,
|
|
121
118
|
swaggerInputs: flags.swagger
|
|
122
119
|
? {
|
|
123
120
|
title: `${projectName} API`,
|
|
@@ -126,7 +123,7 @@ function buildLightModeFromFlags(projectName, flags) {
|
|
|
126
123
|
endpoint: "api/docs",
|
|
127
124
|
}
|
|
128
125
|
: undefined,
|
|
129
|
-
packageManager:
|
|
126
|
+
packageManager: packageManager,
|
|
130
127
|
entitiesData: {
|
|
131
128
|
entities: [],
|
|
132
129
|
relations: [],
|
|
@@ -169,7 +166,8 @@ function hasAllFullModeFlags(flags) {
|
|
|
169
166
|
if (flags.interactive === true) {
|
|
170
167
|
return false;
|
|
171
168
|
}
|
|
172
|
-
|
|
169
|
+
const hasMode = flags.mode !== undefined || flags.light === true || flags.full === true;
|
|
170
|
+
return flags.orm !== undefined && hasMode;
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
function buildFullModeFromFlags(projectName, flags) {
|
|
@@ -178,7 +176,10 @@ function buildFullModeFromFlags(projectName, flags) {
|
|
|
178
176
|
const validOrms = ["prisma", "typeorm", "mongoose"];
|
|
179
177
|
const finalOrm = validOrms.includes(orm) ? orm : "prisma";
|
|
180
178
|
|
|
181
|
-
|
|
179
|
+
let packageManager = "npm";
|
|
180
|
+
if (flags.pnpm) packageManager = "pnpm";
|
|
181
|
+
else if (flags.yarn) packageManager = "yarn";
|
|
182
|
+
else if (flags.packageManager) packageManager = flags.packageManager.toLowerCase();
|
|
182
183
|
|
|
183
184
|
const defaultDBConfig =
|
|
184
185
|
finalOrm === "mongoose"
|
|
@@ -196,18 +197,20 @@ function buildFullModeFromFlags(projectName, flags) {
|
|
|
196
197
|
POSTGRES_PORT: flags.dbPort || "5432",
|
|
197
198
|
};
|
|
198
199
|
|
|
199
|
-
const useAuth = flags.auth
|
|
200
|
-
const useSwagger = flags.swagger
|
|
200
|
+
const useAuth = !!flags.auth;
|
|
201
|
+
const useSwagger = !!flags.swagger;
|
|
201
202
|
const useDocker = flags.docker !== false;
|
|
203
|
+
const useThrottler = !!flags.throttler;
|
|
202
204
|
|
|
203
205
|
const inputs = {
|
|
204
206
|
projectName,
|
|
205
207
|
mode: "full",
|
|
206
|
-
useYarn:
|
|
208
|
+
useYarn: packageManager === "yarn",
|
|
207
209
|
packageManager: packageManager,
|
|
208
210
|
useDocker: useDocker,
|
|
209
211
|
useAuth: useAuth,
|
|
210
212
|
useSwagger: useSwagger,
|
|
213
|
+
useThrottler: useThrottler,
|
|
211
214
|
swaggerInputs: useSwagger
|
|
212
215
|
? {
|
|
213
216
|
title: flags.swaggerTitle || `${projectName} API`,
|
|
@@ -240,40 +243,9 @@ function buildFullModeFromFlags(projectName, flags) {
|
|
|
240
243
|
|
|
241
244
|
async function executeProjectSetup(inputs) {
|
|
242
245
|
try {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
await createProject(inputs);
|
|
246
|
-
|
|
247
|
-
if (inputs.mode === "light") {
|
|
248
|
-
await setupLightArchitecture(inputs);
|
|
249
|
-
} else {
|
|
250
|
-
await setupCleanArchitecture(inputs);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (inputs.useAuth) await setupAuth(inputs);
|
|
254
|
-
|
|
255
|
-
if (inputs.useSwagger) {
|
|
256
|
-
await setupSwagger(inputs.swaggerInputs);
|
|
257
|
-
} else {
|
|
258
|
-
setupBootstrapLogger();
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (inputs.useDocker) await configureDocker(inputs);
|
|
262
|
-
|
|
263
|
-
await setupDatabase(inputs);
|
|
264
|
-
|
|
265
|
-
const envContent = await generateEnvFile(inputs);
|
|
266
|
-
writeEnvFile(envContent);
|
|
267
|
-
|
|
268
|
-
await saveProjectConfig(inputs);
|
|
269
|
-
|
|
270
|
-
// Show any non-critical warnings accumulated during generation
|
|
271
|
-
printSetupWarnings();
|
|
272
|
-
|
|
246
|
+
await runProjectSetupPipeline(inputs);
|
|
273
247
|
printSuccessSummary(inputs);
|
|
274
248
|
} catch (error) {
|
|
275
|
-
// Erreur lors de la creation du projet: ${error.message}
|
|
276
|
-
logError(`Error during project creation: ${error.message}`);
|
|
277
249
|
throw error;
|
|
278
250
|
}
|
|
279
251
|
}
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestcraftx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.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": {
|
|
7
7
|
"nestcraftx": "bin/nestcraft.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"test": "
|
|
10
|
+
"test": "jest --runInBand tests/unit",
|
|
11
|
+
"test:unit": "jest --runInBand tests/unit",
|
|
12
|
+
"test:e2e": "jest --runInBand tests/e2e"
|
|
11
13
|
},
|
|
12
14
|
"repository": {
|
|
13
15
|
"type": "git",
|
|
@@ -37,5 +39,8 @@
|
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"inquirer": "^12.10.0",
|
|
39
41
|
"readline-sync": "^1.4.10"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"jest": "^30.4.2"
|
|
40
45
|
}
|
|
41
46
|
}
|
package/readme.md
CHANGED
|
@@ -31,13 +31,13 @@ Key Features:
|
|
|
31
31
|
|
|
32
32
|
- Smart Config: Automated Swagger decorators, auto-documented .env files, and pre-configured database connections.
|
|
33
33
|
|
|
34
|
-
> **Version 0.
|
|
34
|
+
> **Version 1.0.0 (Stable Release):** Production-grade NestJS scaffolding with Clean Architecture, JWT authentication, relations updater, conditional RBAC guards, global rate limiting, health checks, advanced pagination/filtering/sorting, Swagger UI, and Docker compose files!
|
|
35
35
|
|
|
36
36
|
---
|
|
37
37
|
|
|
38
38
|
## Table of Contents
|
|
39
39
|
|
|
40
|
-
- [What's New in
|
|
40
|
+
- [What's New in v1.0.0](#whats-new-in-v100-stable-release)
|
|
41
41
|
- [Project Objective](#project-objective)
|
|
42
42
|
- [Prerequisites](#prerequisites)
|
|
43
43
|
- [Installation](#installation)
|
|
@@ -52,59 +52,32 @@ Key Features:
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
## What's New in
|
|
55
|
+
## What's New in v1.0.0 (Stable Release)
|
|
56
56
|
|
|
57
|
-
###
|
|
57
|
+
### 🔗 Standalone Relation Command (`nestcraftx g relation`)
|
|
58
|
+
- Create 1-n, n-1, 1-1, or n-n relations between **existing modules** at any time.
|
|
59
|
+
- Automatically updates domain entities (FK fields and getters), DTOs (Create and Update), and data mappers.
|
|
60
|
+
- Syncs database schemas (runs format, client generation, and migrations dev for Prisma; injects ObjectId references for Mongoose).
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
### 🛡️ Conditional RBAC Guards & Swagger Integration
|
|
63
|
+
- Mutation endpoints (`POST`, `PATCH`, `DELETE`) are protected with `JwtAuthGuard` and `RolesGuard` (`@Roles('ADMIN')`) only if Auth is enabled.
|
|
64
|
+
- Read endpoints (`GET`, `GET :id`) are decorated with `@Public()`.
|
|
65
|
+
- Generates complete API documentation using `@ApiBearerAuth()` and standard error responses when Swagger is enabled.
|
|
60
66
|
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
67
|
+
### 🚦 Global Rate Limiting (Throttler Module)
|
|
68
|
+
- Integrated `@nestjs/throttler` package with preconfigured global rate limiting policies (default: 10 requests per minute).
|
|
69
|
+
- Configurable via interactive prompts or CLI flag `--throttler`.
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
### 🏥 Health Check Endpoint (`/health`)
|
|
72
|
+
- Exposes a native `/health` check route reporting global status (`ok`), timestamp, and application uptime (`process.uptime()`).
|
|
73
|
+
- Fully documented in Swagger UI, optimized with zero third-party packages for fast compilation and maximum safety.
|
|
66
74
|
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
75
|
+
### 🚀 Advanced Query Params (Pagination, Filtering, Sorting)
|
|
76
|
+
- Auto-injects paginated data retrieval (e.g. `?page=1&limit=10&search=...&sortBy=createdAt&sortOrder=desc`) into controllers and repository layers.
|
|
77
|
+
- Seamlessly maps incoming query params to Prisma, TypeORM, and Mongoose queries.
|
|
70
78
|
|
|
71
|
-
###
|
|
72
|
-
|
|
73
|
-
- ✅ Flag options: `--light`, `--orm`, `--auth`, `--swagger`, `--docker`
|
|
74
|
-
- ✅ Interactive mode: only asks questions for missing flags
|
|
75
|
-
- ✅ Intelligent merging of flags and interactive responses
|
|
76
|
-
- ✅ 3 pre-configured entities with relationships
|
|
77
|
-
- ✅ Support for all ORMs (Prisma, TypeORM, Mongoose)
|
|
78
|
-
- ✅ Separate instructions in [Demo Documentation](./DEMO.md)
|
|
79
|
-
|
|
80
|
-
### Modern CLI with Flags
|
|
81
|
-
|
|
82
|
-
```bash
|
|
83
|
-
nestcraftx new <project-name> [options]
|
|
84
|
-
|
|
85
|
-
Options:
|
|
86
|
-
--light Simplified architecture mode
|
|
87
|
-
--full Complete architecture mode (default)
|
|
88
|
-
--db=<db> Database choice: postgresql|mongodb
|
|
89
|
-
--orm=<orm> ORM choice: prisma|typeorm|mongoose
|
|
90
|
-
--auth Enable JWT authentication
|
|
91
|
-
--swagger Enable Swagger documentation
|
|
92
|
-
--docker Enable Docker (default: true)
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Automatic Secret Generation
|
|
96
|
-
|
|
97
|
-
- Auto-generated JWT secrets (64 secure characters)
|
|
98
|
-
- Ready-to-use .env file
|
|
99
|
-
- DATABASE_URL automatically configured
|
|
100
|
-
- Sanitized .env.example file
|
|
101
|
-
|
|
102
|
-
### Improved UX
|
|
103
|
-
|
|
104
|
-
- Colored messages (info, success, error)
|
|
105
|
-
- Animated spinners for long operations
|
|
106
|
-
- Detailed post-generation summary
|
|
107
|
-
- Real-time validation of options
|
|
79
|
+
### 📋 Interactive Scaffolding Report
|
|
80
|
+
- Out-of-the-box CLI report with ASCII borders after each module generation summarizing files generated, database schema updates, relations established, and clear next steps.
|
|
108
81
|
|
|
109
82
|
### Quick Examples
|
|
110
83
|
|
|
@@ -178,6 +151,21 @@ npm install
|
|
|
178
151
|
npm link
|
|
179
152
|
```
|
|
180
153
|
|
|
154
|
+
### Running Tests
|
|
155
|
+
|
|
156
|
+
We use Jest to run unit tests and E2E generator compiler tests:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Run unit tests (fast check)
|
|
160
|
+
npm run test:unit
|
|
161
|
+
|
|
162
|
+
# Run E2E integration tests (scaffolds project, runs npm install & tsc type checks)
|
|
163
|
+
npm run test:e2e
|
|
164
|
+
|
|
165
|
+
# Run all tests
|
|
166
|
+
npm test
|
|
167
|
+
```
|
|
168
|
+
|
|
181
169
|
---
|
|
182
170
|
|
|
183
171
|
## Available Commands
|
|
@@ -548,31 +536,34 @@ open http://localhost:3000/api/docs
|
|
|
548
536
|
|
|
549
537
|
## Roadmap
|
|
550
538
|
|
|
551
|
-
### Version 0.
|
|
539
|
+
### Version 0.3.0 — Stabilization & UX (Completed)
|
|
540
|
+
- [x] CommonJS module system unification
|
|
541
|
+
- [x] Dynamic helper versioning and interactive flags checks
|
|
542
|
+
- [x] Dynamic package manager setup
|
|
552
543
|
|
|
553
|
-
|
|
554
|
-
- [x]
|
|
555
|
-
- [x]
|
|
556
|
-
- [
|
|
544
|
+
### Version 0.4.0 — Architecture Refactoring (Completed)
|
|
545
|
+
- [x] Split of monolithic files into dedicated generators
|
|
546
|
+
- [x] Interactive state-based `EntityBuilder` with revision menus
|
|
547
|
+
- [x] Dry-run simulation mode (`--dry-run`)
|
|
557
548
|
|
|
558
|
-
### Version 0.
|
|
549
|
+
### Version 0.5.0 — Quality & Security (Completed)
|
|
550
|
+
- [x] Persistent DB storage for OTPs & password reset tokens
|
|
551
|
+
- [x] Strict package version pinning
|
|
552
|
+
- [x] Contextual semantic Swagger DTO descriptions
|
|
559
553
|
|
|
560
|
-
|
|
561
|
-
- [
|
|
562
|
-
- [
|
|
554
|
+
### Version 0.6.0 — Tests & CI/CD (Completed)
|
|
555
|
+
- [x] Unit test coverage using Jest
|
|
556
|
+
- [x] E2E compiler checks & resolution of upstream peer dependency conflicts
|
|
557
|
+
- [x] Multi-OS Node.js matrix GitHub Actions CI/CD pipeline
|
|
563
558
|
|
|
564
|
-
### Version 0.
|
|
565
|
-
|
|
566
|
-
- [ ]
|
|
567
|
-
- [ ]
|
|
568
|
-
- [ ] Project presets (API Only / Auth / Full CRUD)
|
|
559
|
+
### Version 0.7.0 - 0.9.0 — Missing Features (In Progress)
|
|
560
|
+
- [ ] Interactive `generate auth` and complete non-interactive execution
|
|
561
|
+
- [ ] API Pagination, filtering, rate limiting, and RBAC guards
|
|
562
|
+
- [ ] MySQL/SQLite support
|
|
569
563
|
|
|
570
564
|
### Version 1.0.0 — Stable Release
|
|
571
|
-
|
|
572
|
-
- [ ] TypeScript-native CLI
|
|
573
|
-
- [ ] Enforced conventions & stable API contracts
|
|
574
565
|
- [ ] Official documentation website
|
|
575
|
-
- [ ]
|
|
566
|
+
- [ ] Production audit and LTS guarantees
|
|
576
567
|
|
|
577
568
|
---
|
|
578
569
|
|
|
@@ -629,7 +620,7 @@ Thanks to all contributors and the NestJS community!
|
|
|
629
620
|
|
|
630
621
|
---
|
|
631
622
|
|
|
632
|
-
**NestCraftX v0.
|
|
623
|
+
**NestCraftX v0.6.0** - Clean Architecture Made Simple
|
|
633
624
|
|
|
634
625
|
For more information:
|
|
635
626
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
4
|
+
const demoCommand = require('../../commands/demo');
|
|
5
|
+
|
|
6
|
+
describe('E2E Generator - Compilation Check', () => {
|
|
7
|
+
const e2eDir = path.join(__dirname, '..', 'e2e-output');
|
|
8
|
+
const projectDir = path.join(e2eDir, 'blog-demo');
|
|
9
|
+
const originalCwd = process.cwd();
|
|
10
|
+
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
// S'assurer que le dossier temporaire e2e-output existe et est vide
|
|
13
|
+
if (fs.existsSync(e2eDir)) {
|
|
14
|
+
fs.rmSync(e2eDir, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
fs.mkdirSync(e2eDir, { recursive: true });
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterAll(() => {
|
|
20
|
+
// Restaurer le CWD original et nettoyer les dossiers générés
|
|
21
|
+
process.chdir(originalCwd);
|
|
22
|
+
if (fs.existsSync(e2eDir)) {
|
|
23
|
+
try {
|
|
24
|
+
fs.rmSync(e2eDir, { recursive: true, force: true });
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.warn('Cleanup warning (some files might be locked):', err.message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should generate a project with Prisma, Auth, Swagger and compile successfully', async () => {
|
|
32
|
+
// Se déplacer dans le dossier e2e-output avant la génération
|
|
33
|
+
process.chdir(e2eDir);
|
|
34
|
+
|
|
35
|
+
console.log('Generating E2E project...');
|
|
36
|
+
// Exécuter demoCommand en passant les options pour éviter les prompts inquirer
|
|
37
|
+
await demoCommand({
|
|
38
|
+
light: false, // Full Clean Architecture
|
|
39
|
+
orm: 'prisma',
|
|
40
|
+
docker: false,
|
|
41
|
+
auth: true,
|
|
42
|
+
swagger: true,
|
|
43
|
+
packageManager: 'npm'
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Vérifier l'existence physique du projet et de fichiers clés
|
|
47
|
+
expect(fs.existsSync(projectDir)).toBe(true);
|
|
48
|
+
expect(fs.existsSync(path.join(projectDir, 'package.json'))).toBe(true);
|
|
49
|
+
expect(fs.existsSync(path.join(projectDir, 'prisma', 'schema.prisma'))).toBe(true);
|
|
50
|
+
expect(fs.existsSync(path.join(projectDir, 'src', 'app.module.ts'))).toBe(true);
|
|
51
|
+
|
|
52
|
+
// Se déplacer dans le dossier du projet généré
|
|
53
|
+
process.chdir(projectDir);
|
|
54
|
+
|
|
55
|
+
console.log('Running npm install in the generated project...');
|
|
56
|
+
// Lancer npm install avec --legacy-peer-deps pour installer les dépendances figées
|
|
57
|
+
execSync('npm install --legacy-peer-deps --no-audit --no-fund', { stdio: 'inherit' });
|
|
58
|
+
|
|
59
|
+
console.log('Running tsc --noEmit in the generated project...');
|
|
60
|
+
// Lancer la vérification de type TypeScript
|
|
61
|
+
let compilePassed = false;
|
|
62
|
+
try {
|
|
63
|
+
execSync('npx tsc --noEmit', { stdio: 'inherit' });
|
|
64
|
+
compilePassed = true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('TypeScript compilation failed:', error.message);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expect(compilePassed).toBe(true);
|
|
70
|
+
}, 300000); // 5 minutes timeout pour npm install + compilation
|
|
71
|
+
});
|