nestgen-cli 0.0.7
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/LICENSE +21 -0
- package/nestgen.js +218 -0
- package/nestjs-generator/features/add_module.sh +234 -0
- package/nestjs-generator/features/docker.sh +59 -0
- package/nestjs-generator/features/git.sh +12 -0
- package/nestjs-generator/features/inject_module_to_app.sh +99 -0
- package/nestjs-generator/features/logger.sh +24 -0
- package/nestjs-generator/features/path.sh +9 -0
- package/nestjs-generator/features/prisma.sh +33 -0
- package/nestjs-generator/features/swagger.sh +27 -0
- package/nestjs-generator/features/typeorm.sh +19 -0
- package/nestjs-generator/features/utils.sh +26 -0
- package/nestjs-generator/generate_project.sh +143 -0
- package/package.json +33 -0
- package/readme.md +175 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Aristide Kamara
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/nestgen.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
import inquirer from 'inquirer';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import { fileURLToPath } from 'url';
|
|
9
|
+
|
|
10
|
+
// ────── Resolve __dirname compatible ES Modules
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// ────── Définition du chemin du générateur
|
|
15
|
+
const ROOT_PATH = process.env.NESTGEN_ROOT || path.resolve(__dirname, './nestjs-generator');
|
|
16
|
+
const GENERATE_SCRIPT = path.join(ROOT_PATH, 'generate_project.sh');
|
|
17
|
+
const ADD_MODULE_SCRIPT = path.join(ROOT_PATH, './features/add_module.sh');
|
|
18
|
+
|
|
19
|
+
// ────── Logo CLI
|
|
20
|
+
function printLogo() {
|
|
21
|
+
console.log(chalk.magentaBright(`
|
|
22
|
+
███╗ ██╗███████╗███████╗████████╗ ██████╗ ███████╗███╗ ██╗
|
|
23
|
+
████╗ ██║██╔════╝██╔════╝╚══██╔══╝██╔════╝ ██╔════╝████╗ ██║
|
|
24
|
+
██╔██╗ ██║█████╗ ███████╗ ██║ ██║ ███╗█████╗ ██╔██╗ ██║
|
|
25
|
+
██║╚██╗██║██╔══╝ ╚════██║ ██║ ██║ ██║██╔══╝ ██║╚██╗██║
|
|
26
|
+
██║ ╚████║███████╗███████║ ██║ ╚██████╔╝███████╗██║ ╚████║
|
|
27
|
+
╚═╝ ╚═══╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝
|
|
28
|
+
`));
|
|
29
|
+
console.log(chalk.cyan.bold('✨ NestGen CLI — Générateur modulaire NestJS'));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ────── Helpers
|
|
33
|
+
function isNestProject() {
|
|
34
|
+
return fs.existsSync(path.resolve('./src/app.module.ts'));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function parseModuleArgs(args) {
|
|
38
|
+
const moduleName = args[1];
|
|
39
|
+
const ormArg = args.find(arg => arg.startsWith('--orm='));
|
|
40
|
+
const orm = ormArg ? ormArg.split('=')[1] : 'typeorm';
|
|
41
|
+
return { moduleName, orm };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ────── Commande : INIT
|
|
45
|
+
async function askInitQuestions() {
|
|
46
|
+
return await inquirer.prompt([
|
|
47
|
+
{
|
|
48
|
+
type: 'input',
|
|
49
|
+
name: 'projectName',
|
|
50
|
+
message: '📛 Nom du projet :',
|
|
51
|
+
default: 'my-app',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'input',
|
|
55
|
+
name: 'projectPath',
|
|
56
|
+
message: '📁 Dossier cible :',
|
|
57
|
+
default: './',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'list',
|
|
61
|
+
name: 'packageManager',
|
|
62
|
+
message: '📦 Package manager :',
|
|
63
|
+
choices: ['pnpm', 'yarn', 'npm'],
|
|
64
|
+
default: 'pnpm',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'list',
|
|
68
|
+
name: 'orm',
|
|
69
|
+
message: '🧠 ORM :',
|
|
70
|
+
choices: ['typeorm', 'prisma'],
|
|
71
|
+
default: 'typeorm',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'confirm',
|
|
75
|
+
name: 'withSwagger',
|
|
76
|
+
message: '📚 Activer Swagger ?',
|
|
77
|
+
default: true,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: 'confirm',
|
|
81
|
+
name: 'withDocker',
|
|
82
|
+
message: '🐳 Activer Docker ?',
|
|
83
|
+
default: false,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
type: 'confirm',
|
|
87
|
+
name: 'withGit',
|
|
88
|
+
message: '🔃 Initialiser Git ?',
|
|
89
|
+
default: true,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
type: 'input',
|
|
93
|
+
name: 'modules',
|
|
94
|
+
message: '📦 Modules à générer (séparés par des espaces) :',
|
|
95
|
+
default: 'user',
|
|
96
|
+
filter: (input) => input.split(' ').map(s => s.trim()).filter(Boolean),
|
|
97
|
+
}
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function runInteractiveInit() {
|
|
102
|
+
printLogo();
|
|
103
|
+
|
|
104
|
+
if (!fs.existsSync(GENERATE_SCRIPT)) {
|
|
105
|
+
console.log(chalk.red(`❌ Script introuvable : ${GENERATE_SCRIPT}`));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const answers = await askInitQuestions();
|
|
110
|
+
const {
|
|
111
|
+
projectName,
|
|
112
|
+
projectPath,
|
|
113
|
+
packageManager,
|
|
114
|
+
orm,
|
|
115
|
+
withSwagger,
|
|
116
|
+
withDocker,
|
|
117
|
+
withGit,
|
|
118
|
+
modules,
|
|
119
|
+
} = answers;
|
|
120
|
+
|
|
121
|
+
const env = {
|
|
122
|
+
APP_NAME: projectName,
|
|
123
|
+
PROJECT_PATH: path.resolve(projectPath),
|
|
124
|
+
PM: packageManager,
|
|
125
|
+
ORM: orm,
|
|
126
|
+
WITH_SWAGGER: withSwagger ? 'y' : 'n',
|
|
127
|
+
WITH_DOCKER: withDocker ? 'y' : 'n',
|
|
128
|
+
WITH_GIT: withGit ? 'y' : 'n',
|
|
129
|
+
MODULES: modules.join(' '),
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const envExport = Object.entries(env)
|
|
133
|
+
.map(([key, val]) => `${key}="${val}"`)
|
|
134
|
+
.join(' ');
|
|
135
|
+
|
|
136
|
+
console.log('\n🚀 Lancement de la génération du projet...\n');
|
|
137
|
+
try {
|
|
138
|
+
execSync(`env ${envExport} bash "${GENERATE_SCRIPT}"`, {
|
|
139
|
+
stdio: 'inherit',
|
|
140
|
+
});
|
|
141
|
+
} catch (err) {
|
|
142
|
+
console.error(chalk.red('❌ Une erreur est survenue pendant la génération.'));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ────── Commande : MODULE
|
|
148
|
+
async function runModuleGeneration(args) {
|
|
149
|
+
printLogo();
|
|
150
|
+
|
|
151
|
+
let moduleName, orm;
|
|
152
|
+
|
|
153
|
+
if (args.length > 1) {
|
|
154
|
+
({ moduleName, orm } = parseModuleArgs(args));
|
|
155
|
+
if (!moduleName) {
|
|
156
|
+
console.log(chalk.red('❌ Tu dois fournir un nom de module.'));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
const answers = await inquirer.prompt([
|
|
161
|
+
{
|
|
162
|
+
type: 'input',
|
|
163
|
+
name: 'moduleName',
|
|
164
|
+
message: '📦 Nom du module :',
|
|
165
|
+
validate: input => !!input || 'Le nom du module est requis',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
type: 'list',
|
|
169
|
+
name: 'orm',
|
|
170
|
+
message: '🧠 ORM utilisé :',
|
|
171
|
+
choices: ['typeorm', 'prisma'],
|
|
172
|
+
default: 'typeorm',
|
|
173
|
+
},
|
|
174
|
+
]);
|
|
175
|
+
moduleName = answers.moduleName;
|
|
176
|
+
orm = answers.orm;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (!isNestProject()) {
|
|
180
|
+
console.log(chalk.red('❌ Aucun projet NestJS détecté dans ce dossier.'));
|
|
181
|
+
console.log('👉 Lance cette commande depuis un projet généré avec `nestgen init`.');
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
console.log(chalk.cyan(`\n⚙️ Génération du module ${moduleName}...\n`));
|
|
187
|
+
execSync(`bash "${ADD_MODULE_SCRIPT}" "${moduleName}" "${orm}"`, {
|
|
188
|
+
stdio: 'inherit',
|
|
189
|
+
});
|
|
190
|
+
} catch (err) {
|
|
191
|
+
console.error(chalk.red('❌ Une erreur est survenue pendant la génération du module.'));
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ────── Entrée CLI
|
|
197
|
+
const args = process.argv.slice(2);
|
|
198
|
+
const command = args[0];
|
|
199
|
+
|
|
200
|
+
switch (command) {
|
|
201
|
+
case 'init':
|
|
202
|
+
await runInteractiveInit();
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'module':
|
|
206
|
+
await runModuleGeneration(args);
|
|
207
|
+
break;
|
|
208
|
+
|
|
209
|
+
default:
|
|
210
|
+
printLogo();
|
|
211
|
+
console.log(chalk.gray(`
|
|
212
|
+
📘 Commandes disponibles :
|
|
213
|
+
▸ nestgen init → Génère un projet complet NestJS (interactive)
|
|
214
|
+
▸ nestgen module [nom] [--orm=xxx] → Génère un module (interactive ou CLI)
|
|
215
|
+
▸ nestgen doctor → Diagnostic de l’installation CLI
|
|
216
|
+
`));
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
RAW_NAME=$1
|
|
4
|
+
ORM=$2
|
|
5
|
+
|
|
6
|
+
# ────── Charger les helpers ──────
|
|
7
|
+
FEATURES_PATH="$(dirname "$0")"
|
|
8
|
+
source "$FEATURES_PATH/utils.sh"
|
|
9
|
+
source "$FEATURES_PATH/logger.sh"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if [ -z "$RAW_NAME" ]; then
|
|
13
|
+
echo "❌ Tu dois passer un nom de module."
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
if [ -z "$ORM" ]; then
|
|
18
|
+
echo "❌ ORM manquant (typeorm ou prisma)."
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
# ────── Formatage ──────
|
|
23
|
+
NAME=$(echo "$RAW_NAME" | tr '[:upper:]' '[:lower:]')
|
|
24
|
+
PASCAL=$(echo "$NAME" | sed -E 's/(^|[-_])([a-z])/\u\2/g' | sed -E 's/[-_]//g')
|
|
25
|
+
|
|
26
|
+
NAME=$(echo "$RAW_NAME" | tr '[:upper:]' '[:lower:]')
|
|
27
|
+
PASCAL=$(echo "$NAME" | sed -E 's/(^|[-_])([a-z])/\U\2/g' | sed -E 's/[-_]//g')
|
|
28
|
+
|
|
29
|
+
CAMEL=$(echo "$PASCAL" | sed -E 's/^([A-Z])/\L\1/')
|
|
30
|
+
|
|
31
|
+
MODULE_DIR="src/app/$NAME"
|
|
32
|
+
mkdir -p "$MODULE_DIR"/{core/{application/{commands,events,queries},domain/{entities,ports}},infrastructure/{adapters,persistences/repositories},interfaces/{controllers,dtos}}
|
|
33
|
+
|
|
34
|
+
# ────── Entity ──────
|
|
35
|
+
cat > "$MODULE_DIR/core/domain/entities/${NAME}.entity.ts" <<EOF
|
|
36
|
+
export class $PASCAL {
|
|
37
|
+
constructor(
|
|
38
|
+
public readonly id: string,
|
|
39
|
+
public name: string,
|
|
40
|
+
public email: string,
|
|
41
|
+
) {}
|
|
42
|
+
}
|
|
43
|
+
EOF
|
|
44
|
+
|
|
45
|
+
# ────── Port ──────
|
|
46
|
+
cat > "$MODULE_DIR/core/domain/ports/${NAME}.repository.ts" <<EOF
|
|
47
|
+
import { $PASCAL } from '../entities/${NAME}.entity';
|
|
48
|
+
|
|
49
|
+
export interface ${PASCAL}RepositoryPort {
|
|
50
|
+
save(${CAMEL}: $PASCAL): Promise<$PASCAL>;
|
|
51
|
+
findById(id: string): Promise<$PASCAL | null>;
|
|
52
|
+
}
|
|
53
|
+
EOF
|
|
54
|
+
|
|
55
|
+
# ────── Command ──────
|
|
56
|
+
cat > "$MODULE_DIR/core/application/commands/create-${NAME}.command.ts" <<EOF
|
|
57
|
+
export class Create${PASCAL}Command {
|
|
58
|
+
constructor(public readonly name: string, public readonly email: string) {}
|
|
59
|
+
}
|
|
60
|
+
EOF
|
|
61
|
+
|
|
62
|
+
# ────── Command Handler ──────
|
|
63
|
+
cat > "$MODULE_DIR/core/application/commands/create-${NAME}.handler.ts" <<EOF
|
|
64
|
+
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
|
65
|
+
import { Create${PASCAL}Command } from './create-${NAME}.command';
|
|
66
|
+
import { $PASCAL } from '../../domain/entities/${NAME}.entity';
|
|
67
|
+
import { ${PASCAL}RepositoryPort } from '../../domain/ports/${NAME}.repository';
|
|
68
|
+
|
|
69
|
+
@CommandHandler(Create${PASCAL}Command)
|
|
70
|
+
export class Create${PASCAL}Handler implements ICommandHandler<Create${PASCAL}Command> {
|
|
71
|
+
constructor(private readonly repo: ${PASCAL}RepositoryPort) {}
|
|
72
|
+
|
|
73
|
+
async execute(command: Create${PASCAL}Command): Promise<string> {
|
|
74
|
+
const $CAMEL = new $PASCAL(Date.now().toString(), command.name, command.email);
|
|
75
|
+
const saved = await this.repo.save($CAMEL);
|
|
76
|
+
return saved.id;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
EOF
|
|
80
|
+
|
|
81
|
+
# ────── DTO ──────
|
|
82
|
+
cat > "$MODULE_DIR/interfaces/dtos/create-${NAME}.dto.ts" <<EOF
|
|
83
|
+
import { IsEmail, IsString } from 'class-validator';
|
|
84
|
+
|
|
85
|
+
export class Create${PASCAL}Dto {
|
|
86
|
+
@IsString()
|
|
87
|
+
name: string;
|
|
88
|
+
|
|
89
|
+
@IsEmail()
|
|
90
|
+
email: string;
|
|
91
|
+
}
|
|
92
|
+
EOF
|
|
93
|
+
|
|
94
|
+
# ────── Controller ──────
|
|
95
|
+
cat > "$MODULE_DIR/interfaces/controllers/${NAME}.controller.ts" <<EOF
|
|
96
|
+
import { Controller, Post, Body } from '@nestjs/common';
|
|
97
|
+
import { CommandBus } from '@nestjs/cqrs';
|
|
98
|
+
import { Create${PASCAL}Command } from '../../core/application/commands/create-${NAME}.command';
|
|
99
|
+
import { Create${PASCAL}Dto } from '../dtos/create-${NAME}.dto';
|
|
100
|
+
|
|
101
|
+
@Controller('${NAME}s')
|
|
102
|
+
export class ${PASCAL}Controller {
|
|
103
|
+
constructor(private readonly commandBus: CommandBus) {}
|
|
104
|
+
|
|
105
|
+
@Post()
|
|
106
|
+
async create(@Body() dto: Create${PASCAL}Dto) {
|
|
107
|
+
const id = await this.commandBus.execute(
|
|
108
|
+
new Create${PASCAL}Command(dto.name, dto.email)
|
|
109
|
+
);
|
|
110
|
+
return { id };
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
EOF
|
|
114
|
+
|
|
115
|
+
# ────── Repository ORM ──────
|
|
116
|
+
REPO_CLASS=""
|
|
117
|
+
ENTITY_IMPORT=""
|
|
118
|
+
REPO_PROVIDER=""
|
|
119
|
+
|
|
120
|
+
if [ "$ORM" == "typeorm" ]; then
|
|
121
|
+
cat > "$MODULE_DIR/infrastructure/persistences/repositories/${NAME}.orm.ts" <<EOF
|
|
122
|
+
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
|
|
123
|
+
|
|
124
|
+
@Entity('${NAME}s')
|
|
125
|
+
export class ${PASCAL}Entity {
|
|
126
|
+
@PrimaryGeneratedColumn('uuid')
|
|
127
|
+
id: string;
|
|
128
|
+
|
|
129
|
+
@Column()
|
|
130
|
+
name: string;
|
|
131
|
+
|
|
132
|
+
@Column({ unique: true })
|
|
133
|
+
email: string;
|
|
134
|
+
}
|
|
135
|
+
EOF
|
|
136
|
+
|
|
137
|
+
cat > "$MODULE_DIR/infrastructure/persistences/repositories/${NAME}.typeorm.repository.ts" <<EOF
|
|
138
|
+
import { Injectable } from '@nestjs/common';
|
|
139
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
140
|
+
import { Repository } from 'typeorm';
|
|
141
|
+
import { ${PASCAL} } from '../../../core/domain/entities/${NAME}.entity';
|
|
142
|
+
import { ${PASCAL}RepositoryPort } from '../../../core/domain/ports/${NAME}.repository';
|
|
143
|
+
import { ${PASCAL}Entity } from './${NAME}.orm';
|
|
144
|
+
|
|
145
|
+
@Injectable()
|
|
146
|
+
export class ${PASCAL}TypeOrmRepository implements ${PASCAL}RepositoryPort {
|
|
147
|
+
constructor(@InjectRepository(${PASCAL}Entity) private readonly repo: Repository<${PASCAL}Entity>) {}
|
|
148
|
+
|
|
149
|
+
async save(${CAMEL}: $PASCAL): Promise<$PASCAL> {
|
|
150
|
+
const entity = this.repo.create(${CAMEL});
|
|
151
|
+
const saved = await this.repo.save(entity);
|
|
152
|
+
return new $PASCAL(saved.id, saved.name, saved.email);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async findById(id: string): Promise<$PASCAL | null> {
|
|
156
|
+
const found = await this.repo.findOneBy({ id });
|
|
157
|
+
return found ? new $PASCAL(found.id, found.name, found.email) : null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
EOF
|
|
161
|
+
|
|
162
|
+
REPO_CLASS="${PASCAL}TypeOrmRepository"
|
|
163
|
+
ENTITY_IMPORT="TypeOrmModule.forFeature([${PASCAL}Entity])"
|
|
164
|
+
REPO_PROVIDER="{
|
|
165
|
+
provide: '${PASCAL}RepositoryPort',
|
|
166
|
+
useClass: ${PASCAL}TypeOrmRepository,
|
|
167
|
+
}"
|
|
168
|
+
|
|
169
|
+
elif [ "$ORM" == "prisma" ]; then
|
|
170
|
+
cat > "$MODULE_DIR/infrastructure/persistences/repositories/${NAME}.prisma.repository.ts" <<EOF
|
|
171
|
+
import { Injectable } from '@nestjs/common';
|
|
172
|
+
import { PrismaService } from '@/prisma.service';
|
|
173
|
+
import { ${PASCAL} } from '../../../core/domain/entities/${NAME}.entity';
|
|
174
|
+
import { ${PASCAL}RepositoryPort } from '../../../core/domain/ports/${NAME}.repository';
|
|
175
|
+
|
|
176
|
+
@Injectable()
|
|
177
|
+
export class ${PASCAL}PrismaRepository implements ${PASCAL}RepositoryPort {
|
|
178
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
179
|
+
|
|
180
|
+
async save(${CAMEL}: ${PASCAL}): Promise<${PASCAL}> {
|
|
181
|
+
const saved = await this.prisma.${NAME}.create({
|
|
182
|
+
data: {
|
|
183
|
+
id: ${CAMEL}.id,
|
|
184
|
+
name: ${CAMEL}.name,
|
|
185
|
+
email: ${CAMEL}.email,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
return new ${PASCAL}(saved.id, saved.name, saved.email);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async findById(id: string): Promise<${PASCAL} | null> {
|
|
192
|
+
const found = await this.prisma.${NAME}.findUnique({ where: { id } });
|
|
193
|
+
return found ? new ${PASCAL}(found.id, found.name, found.email) : null;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
EOF
|
|
197
|
+
|
|
198
|
+
REPO_CLASS="${PASCAL}PrismaRepository"
|
|
199
|
+
ENTITY_IMPORT=""
|
|
200
|
+
REPO_PROVIDER="{
|
|
201
|
+
provide: '${PASCAL}RepositoryPort',
|
|
202
|
+
useClass: ${PASCAL}PrismaRepository,
|
|
203
|
+
}"
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# ────── Module.ts ──────
|
|
207
|
+
IMPORTS="import { Module } from '@nestjs/common';
|
|
208
|
+
import { CqrsModule } from '@nestjs/cqrs';
|
|
209
|
+
import { ${PASCAL}Controller } from './interfaces/controllers/${NAME}.controller';
|
|
210
|
+
import { Create${PASCAL}Handler } from './core/application/commands/create-${NAME}.handler';
|
|
211
|
+
import { $REPO_CLASS } from './infrastructure/persistences/repositories/${NAME}.${ORM}.repository';"
|
|
212
|
+
|
|
213
|
+
if [ "$ORM" = "typeorm" ]; then
|
|
214
|
+
IMPORTS="$IMPORTS
|
|
215
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
216
|
+
import { ${PASCAL}Entity } from './infrastructure/persistences/repositories/${NAME}.orm';"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
echo "$IMPORTS
|
|
220
|
+
|
|
221
|
+
@Module({
|
|
222
|
+
imports: [CqrsModule${ENTITY_IMPORT:+, $ENTITY_IMPORT}],
|
|
223
|
+
controllers: [${PASCAL}Controller],
|
|
224
|
+
providers: [
|
|
225
|
+
Create${PASCAL}Handler,
|
|
226
|
+
$REPO_CLASS,
|
|
227
|
+
$REPO_PROVIDER,
|
|
228
|
+
],
|
|
229
|
+
})
|
|
230
|
+
export class ${PASCAL}Module {}
|
|
231
|
+
" > "$MODULE_DIR/${NAME}.module.ts"
|
|
232
|
+
|
|
233
|
+
bash "$(dirname "$0")/inject_module_to_app.sh" "$NAME" "$ORM"
|
|
234
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
# ────── Charger les helpers ──────
|
|
5
|
+
FEATURES_PATH="$(dirname "$0")/features"
|
|
6
|
+
source "$FEATURES_PATH/utils.sh"
|
|
7
|
+
source "$FEATURES_PATH/logger.sh"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
APP_NAME=$1
|
|
11
|
+
|
|
12
|
+
cat > Dockerfile <<EOF
|
|
13
|
+
FROM node:18-alpine
|
|
14
|
+
WORKDIR /usr/src/app
|
|
15
|
+
COPY package*.json ./
|
|
16
|
+
RUN npm install
|
|
17
|
+
COPY . .
|
|
18
|
+
RUN npm run build
|
|
19
|
+
CMD ["node", "dist/main"]
|
|
20
|
+
EOF
|
|
21
|
+
|
|
22
|
+
cat > docker-compose.yml <<EOF
|
|
23
|
+
version: '3.9'
|
|
24
|
+
|
|
25
|
+
services:
|
|
26
|
+
postgres:
|
|
27
|
+
image: postgres:15
|
|
28
|
+
restart: always
|
|
29
|
+
environment:
|
|
30
|
+
POSTGRES_USER: postgres
|
|
31
|
+
POSTGRES_PASSWORD: postgres
|
|
32
|
+
POSTGRES_DB: appdb
|
|
33
|
+
ports:
|
|
34
|
+
- "5432:5432"
|
|
35
|
+
volumes:
|
|
36
|
+
- pgdata:/var/lib/postgresql/data
|
|
37
|
+
|
|
38
|
+
app:
|
|
39
|
+
build: .
|
|
40
|
+
ports:
|
|
41
|
+
- "3000:3000"
|
|
42
|
+
depends_on:
|
|
43
|
+
- postgres
|
|
44
|
+
env_file:
|
|
45
|
+
- .env
|
|
46
|
+
volumes:
|
|
47
|
+
- .:/usr/src/app
|
|
48
|
+
command: npm run start:dev
|
|
49
|
+
|
|
50
|
+
volumes:
|
|
51
|
+
pgdata:
|
|
52
|
+
EOF
|
|
53
|
+
|
|
54
|
+
cat > .env <<EOF
|
|
55
|
+
DATABASE_URL=postgresql://postgres:postgres@postgres:5432/appdb
|
|
56
|
+
PORT=3000
|
|
57
|
+
EOF
|
|
58
|
+
|
|
59
|
+
echo "✅ Dockerfile, docker-compose et .env générés !"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ────── Charger les helpers ──────
|
|
4
|
+
FEATURES_PATH="$(dirname "$0")/features"
|
|
5
|
+
source "$FEATURES_PATH/utils.sh"
|
|
6
|
+
source "$FEATURES_PATH/logger.sh"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
git init
|
|
10
|
+
git add .
|
|
11
|
+
git commit -m "🚀 Initial commit (NestJS starter clean architecture)"
|
|
12
|
+
echo "✅ Git initialisé et commité !"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
RAW_NAME=$1
|
|
4
|
+
ORM=$2
|
|
5
|
+
APP_MODULE="src/app.module.ts"
|
|
6
|
+
MODULE_FILE="src/app/${RAW_NAME}/${RAW_NAME}.module.ts"
|
|
7
|
+
|
|
8
|
+
if [ -z "$RAW_NAME" ]; then
|
|
9
|
+
echo "❌ Tu dois fournir un nom de module."
|
|
10
|
+
exit 1
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [ ! -f "$MODULE_FILE" ]; then
|
|
14
|
+
echo "❌ Le fichier $MODULE_FILE est introuvable."
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 🧠 Extraire le vrai nom de la classe exportée
|
|
19
|
+
MODULE_CLASS=$(grep -oE 'export class [A-Za-z0-9_]+' "$MODULE_FILE" | awk '{print $3}')
|
|
20
|
+
|
|
21
|
+
if [ -z "$MODULE_CLASS" ]; then
|
|
22
|
+
echo "❌ Impossible de détecter la classe du module dans $MODULE_FILE"
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
MODULE_PATH="./app/${RAW_NAME}/${RAW_NAME}.module"
|
|
27
|
+
|
|
28
|
+
# 🔧 Créer app.module.ts s'il n'existe pas
|
|
29
|
+
if [ ! -f "$APP_MODULE" ]; then
|
|
30
|
+
echo "📄 Création de $APP_MODULE"
|
|
31
|
+
mkdir -p src
|
|
32
|
+
cat > "$APP_MODULE" <<EOF
|
|
33
|
+
import { Module } from '@nestjs/common';
|
|
34
|
+
|
|
35
|
+
@Module({
|
|
36
|
+
imports: [],
|
|
37
|
+
})
|
|
38
|
+
export class AppModule {}
|
|
39
|
+
EOF
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 🔌 Ajouter l'import du module s'il est absent
|
|
43
|
+
IMPORT_LINE="import { $MODULE_CLASS } from '${MODULE_PATH}';"
|
|
44
|
+
if ! grep -q "$IMPORT_LINE" "$APP_MODULE"; then
|
|
45
|
+
echo "🔧 Insertion de $MODULE_CLASS dans les imports du haut"
|
|
46
|
+
sed -i '' "1s|^|$IMPORT_LINE\n|" "$APP_MODULE"
|
|
47
|
+
else
|
|
48
|
+
echo "ℹ️ Import $MODULE_CLASS déjà présent"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# ➕ Ajouter dans imports: []
|
|
52
|
+
if grep -q "imports: \[" "$APP_MODULE"; then
|
|
53
|
+
if ! grep -q "$MODULE_CLASS" "$APP_MODULE"; then
|
|
54
|
+
sed -i '' "s|imports: \[|imports: [$MODULE_CLASS, |" "$APP_MODULE"
|
|
55
|
+
echo "✅ $MODULE_CLASS injecté dans imports"
|
|
56
|
+
else
|
|
57
|
+
echo "ℹ️ $MODULE_CLASS déjà dans imports"
|
|
58
|
+
fi
|
|
59
|
+
else
|
|
60
|
+
echo "❌ Aucun bloc imports: [] trouvé dans $APP_MODULE"
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# ➕ Ajouter CqrsModule si absent
|
|
64
|
+
if ! grep -q "CqrsModule" "$APP_MODULE"; then
|
|
65
|
+
sed -i '' "1i\\
|
|
66
|
+
import { CqrsModule } from '@nestjs/cqrs';
|
|
67
|
+
" "$APP_MODULE"
|
|
68
|
+
sed -i '' "s|imports: \[|imports: [CqrsModule, |" "$APP_MODULE"
|
|
69
|
+
echo "✅ CqrsModule injecté"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ➕ Ajouter TypeOrmModule si ORM = typeorm
|
|
73
|
+
if [ "$ORM" = "typeorm" ]; then
|
|
74
|
+
if ! grep -q "TypeOrmModule" "$APP_MODULE"; then
|
|
75
|
+
sed -i '' "1i\\
|
|
76
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
77
|
+
" "$APP_MODULE"
|
|
78
|
+
echo "✅ Import de TypeOrmModule ajouté"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if ! grep -q "TypeOrmModule.forRoot" "$APP_MODULE"; then
|
|
82
|
+
sed -i '' -E "s|(imports: \[)|\1\
|
|
83
|
+
TypeOrmModule.forRoot({\
|
|
84
|
+
type: 'postgres',\
|
|
85
|
+
host: 'localhost',\
|
|
86
|
+
port: 5432,\
|
|
87
|
+
username: 'postgres',\
|
|
88
|
+
password: 'postgres',\
|
|
89
|
+
database: 'appdb',\
|
|
90
|
+
synchronize: true,\
|
|
91
|
+
autoLoadEntities: true,\
|
|
92
|
+
}), |" "$APP_MODULE"
|
|
93
|
+
echo "✅ TypeOrmModule.forRoot injecté"
|
|
94
|
+
else
|
|
95
|
+
echo "ℹ️ TypeOrmModule.forRoot déjà présent"
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
echo "🎯 Module $MODULE_CLASS injecté dans app.module.ts avec succès 🧩"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# 🎨 Couleurs
|
|
4
|
+
RED='\033[0;31m'
|
|
5
|
+
GREEN='\033[0;32m'
|
|
6
|
+
YELLOW='\033[1;33m'
|
|
7
|
+
BLUE='\033[1;34m'
|
|
8
|
+
NC='\033[0m' # No Color
|
|
9
|
+
|
|
10
|
+
log_info() {
|
|
11
|
+
echo -e "${BLUE}ℹ️ $1${NC}"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
log_success() {
|
|
15
|
+
echo -e "${GREEN}✅ $1${NC}"
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
log_warn() {
|
|
19
|
+
echo -e "${YELLOW}⚠️ $1${NC}"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
log_error() {
|
|
23
|
+
echo -e "${RED}❌ $1${NC}"
|
|
24
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ────── Charger les helpers ──────
|
|
4
|
+
FEATURES_PATH="$(dirname "$0")/features"
|
|
5
|
+
source "$FEATURES_PATH/utils.sh"
|
|
6
|
+
source "$FEATURES_PATH/logger.sh"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PM=$1
|
|
10
|
+
APP_NAME=$2
|
|
11
|
+
|
|
12
|
+
$PM install prisma --save-dev
|
|
13
|
+
$PM install @prisma/client
|
|
14
|
+
npx prisma init
|
|
15
|
+
|
|
16
|
+
cat > .env <<EOF
|
|
17
|
+
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/$APP_NAME"
|
|
18
|
+
EOF
|
|
19
|
+
|
|
20
|
+
cat > prisma/schema.prisma <<EOF
|
|
21
|
+
generator client {
|
|
22
|
+
provider = "prisma-client-js"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
datasource db {
|
|
26
|
+
provider = "postgresql"
|
|
27
|
+
url = env("DATABASE_URL")
|
|
28
|
+
}
|
|
29
|
+
EOF
|
|
30
|
+
|
|
31
|
+
npx prisma generate
|
|
32
|
+
|
|
33
|
+
echo "✅ Prisma initialisé avec succès !"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ────── Charger les helpers ──────
|
|
4
|
+
FEATURES_PATH="$(dirname "$0")/features"
|
|
5
|
+
source "$FEATURES_PATH/utils.sh"
|
|
6
|
+
source "$FEATURES_PATH/logger.sh"
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
PM=$1
|
|
10
|
+
|
|
11
|
+
$PM install @nestjs/swagger swagger-ui-express
|
|
12
|
+
|
|
13
|
+
# Ajout dans main.ts (à faire manuellement ou via automatisation)
|
|
14
|
+
echo ""
|
|
15
|
+
echo "📘 Pour activer Swagger, ajoute ceci dans main.ts :"
|
|
16
|
+
echo ""
|
|
17
|
+
cat <<'DOC'
|
|
18
|
+
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
19
|
+
|
|
20
|
+
const config = new DocumentBuilder()
|
|
21
|
+
.setTitle('API Docs')
|
|
22
|
+
.setDescription('The API description')
|
|
23
|
+
.setVersion('1.0')
|
|
24
|
+
.build();
|
|
25
|
+
const document = SwaggerModule.createDocument(app, config);
|
|
26
|
+
SwaggerModule.setup('api', app, document);
|
|
27
|
+
DOC
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
PM=$1
|
|
4
|
+
APP_NAME=$2
|
|
5
|
+
|
|
6
|
+
# ────── Charger les helpers ──────
|
|
7
|
+
FEATURES_PATH="$(dirname "$0")"
|
|
8
|
+
source "$FEATURES_PATH/utils.sh"
|
|
9
|
+
source "$FEATURES_PATH/logger.sh"
|
|
10
|
+
|
|
11
|
+
INSTALL_CMD=$(get_install_cmd "$PM")
|
|
12
|
+
|
|
13
|
+
log_info "📦 Installation de TypeORM et PostgreSQL..."
|
|
14
|
+
|
|
15
|
+
$PM $INSTALL_CMD @nestjs/typeorm typeorm pg
|
|
16
|
+
|
|
17
|
+
log_success "✅ TypeORM installé avec succès"
|
|
18
|
+
|
|
19
|
+
log_info "💡 TypeORM sera automatiquement configuré dans app.module.ts"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ────── Commande d'installation adaptée au package manager ──────
|
|
4
|
+
get_install_cmd() {
|
|
5
|
+
local PM=$1
|
|
6
|
+
case "$PM" in
|
|
7
|
+
yarn) echo "add" ;;
|
|
8
|
+
pnpm|npm) echo "install" ;;
|
|
9
|
+
*) echo "install" ;;
|
|
10
|
+
esac
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
# ────── Vérifie si une commande est installée ──────
|
|
14
|
+
assert_command_exists() {
|
|
15
|
+
local CMD=$1
|
|
16
|
+
if ! command -v "$CMD" &> /dev/null; then
|
|
17
|
+
echo "❌ Commande introuvable : $CMD"
|
|
18
|
+
echo "👉 Installe-la ou vérifie ton PATH"
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# ────── Formatage ──────
|
|
24
|
+
to_pascal_case() {
|
|
25
|
+
echo "$1" | sed -E 's/(^|-|_)([a-z])/\U\2/g'
|
|
26
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# ────── INIT ──────
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
FEATURES_PATH="$(dirname "$0")/features"
|
|
7
|
+
source "$FEATURES_PATH/utils.sh"
|
|
8
|
+
source "$FEATURES_PATH/logger.sh"
|
|
9
|
+
|
|
10
|
+
# ────── DEBUG MODE ──────
|
|
11
|
+
DEBUG=false
|
|
12
|
+
for arg in "$@"; do
|
|
13
|
+
[[ "$arg" == "--debug" ]] && DEBUG=true
|
|
14
|
+
done
|
|
15
|
+
|
|
16
|
+
debug_log() {
|
|
17
|
+
if [ "$DEBUG" = true ]; then
|
|
18
|
+
echo "🐞 [DEBUG] $1" | tee /dev/tty
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
debug_log "Script lancé avec les arguments : $*"
|
|
23
|
+
debug_log "Chemin du script courant : $(pwd)"
|
|
24
|
+
debug_log "Features path : $FEATURES_PATH"
|
|
25
|
+
|
|
26
|
+
# ────── INFOS UTILISATEUR ──────
|
|
27
|
+
APP_NAME=${APP_NAME:-$(read -p "📛 Nom du projet : " tmp && echo "$tmp")}
|
|
28
|
+
PROJECT_PATH=${PROJECT_PATH:-$(read -p "📁 Chemin d’installation (vide = ici) : " tmp && echo "${tmp:-$(pwd)}")}
|
|
29
|
+
FULL_PATH="$PROJECT_PATH/$APP_NAME"
|
|
30
|
+
PM=${PM:-$(read -p "📦 Package manager (npm/yarn/pnpm) : " tmp && echo "$tmp")}
|
|
31
|
+
ORM=${ORM:-$(read -p "🧠 ORM ? (typeorm/prisma) : " tmp && echo "$tmp")}
|
|
32
|
+
|
|
33
|
+
INSTALL_CMD=$(get_install_cmd "$PM")
|
|
34
|
+
debug_log "Install command : $PM $INSTALL_CMD"
|
|
35
|
+
|
|
36
|
+
# ────── Vérification de Nest CLI ──────
|
|
37
|
+
if ! command -v nest &> /dev/null; then
|
|
38
|
+
log_warn "Nest CLI non installée. Installation avec npm..."
|
|
39
|
+
npm install -g @nestjs/cli
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# ────── Création du projet ──────
|
|
43
|
+
log_info "Création du projet à $FULL_PATH"
|
|
44
|
+
mkdir -p "$FULL_PATH"
|
|
45
|
+
cd "$FULL_PATH" || {
|
|
46
|
+
log_error "Erreur : impossible de se déplacer dans $FULL_PATH"
|
|
47
|
+
exit 1
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
nest new . --package-manager "$PM" --skip-git || {
|
|
51
|
+
log_error "Échec de nest new"
|
|
52
|
+
exit 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
log_info "Installation des packages communs..."
|
|
56
|
+
$PM $INSTALL_CMD @nestjs/cqrs class-validator class-transformer @nestjs/config
|
|
57
|
+
|
|
58
|
+
# ────── ORM SETUP ──────
|
|
59
|
+
case "$ORM" in
|
|
60
|
+
typeorm)
|
|
61
|
+
debug_log "Appel de typeorm.sh"
|
|
62
|
+
bash "$FEATURES_PATH/typeorm.sh" "$PM" "$APP_NAME"
|
|
63
|
+
;;
|
|
64
|
+
prisma)
|
|
65
|
+
debug_log "Appel de prisma.sh"
|
|
66
|
+
bash "$FEATURES_PATH/prisma.sh" "$PM" "$APP_NAME"
|
|
67
|
+
;;
|
|
68
|
+
*)
|
|
69
|
+
log_error "❌ ORM non reconnu : $ORM"
|
|
70
|
+
exit 1
|
|
71
|
+
;;
|
|
72
|
+
esac
|
|
73
|
+
|
|
74
|
+
# ────── Docker, Swagger, Git ──────
|
|
75
|
+
WITH_DOCKER=${WITH_DOCKER:-$(read -p "🐳 Activer Docker ? (y/n) : " tmp && echo "$tmp")}
|
|
76
|
+
[ "$WITH_DOCKER" = "y" ] && bash "$FEATURES_PATH/docker.sh" "$APP_NAME"
|
|
77
|
+
|
|
78
|
+
WITH_SWAGGER=${WITH_SWAGGER:-$(read -p "📚 Activer Swagger ? (y/n) : " tmp && echo "$tmp")}
|
|
79
|
+
[ "$WITH_SWAGGER" = "y" ] && bash "$FEATURES_PATH/swagger.sh" "$PM"
|
|
80
|
+
|
|
81
|
+
WITH_GIT=${WITH_GIT:-$(read -p "🔃 Initialiser Git ? (y/n) : " tmp && echo "$tmp")}
|
|
82
|
+
[ "$WITH_GIT" = "y" ] && bash "$FEATURES_PATH/git.sh"
|
|
83
|
+
|
|
84
|
+
# ────── Modules à générer ──────
|
|
85
|
+
MODULES=${MODULES:-$(read -p "👤 Modules à générer (séparés par espaces) : " tmp && echo "$tmp")}
|
|
86
|
+
for MODULE in $MODULES; do
|
|
87
|
+
debug_log "Génération du module $MODULE"
|
|
88
|
+
bash "$FEATURES_PATH/add_module.sh" "$MODULE" "$ORM"
|
|
89
|
+
done
|
|
90
|
+
|
|
91
|
+
# ────── Injection dans app.module.ts ──────
|
|
92
|
+
APP_MODULE="src/app.module.ts"
|
|
93
|
+
|
|
94
|
+
# CqrsModule
|
|
95
|
+
if ! grep -q "CqrsModule" "$APP_MODULE"; then
|
|
96
|
+
sed -i '' "1i\\
|
|
97
|
+
import { CqrsModule } from '@nestjs/cqrs';
|
|
98
|
+
" "$APP_MODULE"
|
|
99
|
+
sed -i '' "s|imports: \[|imports: [CqrsModule, |" "$APP_MODULE"
|
|
100
|
+
echo "✅ CqrsModule injecté"
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
# TypeOrmModule
|
|
104
|
+
if [ "$ORM" = "typeorm" ]; then
|
|
105
|
+
if ! grep -q "TypeOrmModule" "$APP_MODULE"; then
|
|
106
|
+
sed -i '' "1i\\
|
|
107
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
108
|
+
" "$APP_MODULE"
|
|
109
|
+
echo "✅ Import de TypeOrmModule ajouté"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if ! grep -q "TypeOrmModule.forRoot" "$APP_MODULE"; then
|
|
113
|
+
sed -i '' -E 's/(imports: \[[^]]*)(])/\1\
|
|
114
|
+
TypeOrmModule.forRoot({\
|
|
115
|
+
type: '\''postgres'\'',\
|
|
116
|
+
host: '\''localhost'\'',\
|
|
117
|
+
port: 5432,\
|
|
118
|
+
username: '\''postgres'\'',\
|
|
119
|
+
password: '\''postgres'\'',\
|
|
120
|
+
database: '\''appdb'\'',\
|
|
121
|
+
synchronize: true,\
|
|
122
|
+
autoLoadEntities: true,\
|
|
123
|
+
}), \2/g' "$APP_MODULE"
|
|
124
|
+
echo "✅ TypeOrmModule.forRoot injecté"
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# Modules générés
|
|
129
|
+
for MODULE in $MODULES; do
|
|
130
|
+
debug_log "Injection de $MODULE dans app.module.ts"
|
|
131
|
+
bash "$FEATURES_PATH/inject_module_to_app.sh" "$MODULE" "$ORM"
|
|
132
|
+
done
|
|
133
|
+
|
|
134
|
+
# ────── Résumé final ──────
|
|
135
|
+
echo ""
|
|
136
|
+
log_success "✅ Projet NestJS \"$APP_NAME\" généré avec succès 🎉"
|
|
137
|
+
echo "📁 Localisation : $FULL_PATH"
|
|
138
|
+
echo "📦 Package manager : $PM"
|
|
139
|
+
echo "🧠 ORM : $ORM"
|
|
140
|
+
[ "$WITH_DOCKER" = "y" ] && echo "🐳 Docker activé"
|
|
141
|
+
[ "$WITH_SWAGGER" = "y" ] && echo "📚 Swagger activé"
|
|
142
|
+
[ "$WITH_GIT" = "y" ] && echo "🔃 Git initialisé"
|
|
143
|
+
[ -n "$MODULES" ] && echo "📦 Modules générés : $MODULES"
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nestgen-cli",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "NestGen CLI — Générateur modulaire NestJS (DDD, CQRS, Hexagonal)",
|
|
5
|
+
"files": [
|
|
6
|
+
"nestgen.js",
|
|
7
|
+
"nestjs-generator"
|
|
8
|
+
],
|
|
9
|
+
"bin": {
|
|
10
|
+
"nestgen": "nestgen.js"
|
|
11
|
+
},
|
|
12
|
+
"author": "Aristide kamara bakarykamara52@gmail.com",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"nestjs",
|
|
16
|
+
"cli",
|
|
17
|
+
"ddd",
|
|
18
|
+
"cqrs",
|
|
19
|
+
"scaffold",
|
|
20
|
+
"generator",
|
|
21
|
+
"hexagonal"
|
|
22
|
+
],
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/el-hack/nestgen-cli.git"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"licenses": "MIT",
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"chalk": "^5.3.0",
|
|
31
|
+
"inquirer": "^9.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# NestGen
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/outis25/nestgen-js/main/assets/logo.png" alt="NestGen Logo" width="200" />
|
|
5
|
+
<h3>Générateur modulaire autonome pour projets NestJS</h3>
|
|
6
|
+
<p>Architecture DDD • CQRS • Hexagonal • Prisma/TypeORM • Docker/Swagger</p>
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+

|
|
10
|
+

|
|
11
|
+

|
|
12
|
+

|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## 📋 Table des matières
|
|
16
|
+
|
|
17
|
+
- [Introduction](#-introduction)
|
|
18
|
+
- [Fonctionnalités](#-fonctionnalités)
|
|
19
|
+
- [Installation](#-installation)
|
|
20
|
+
- [Guide d'utilisation](#-guide-dutilisation)
|
|
21
|
+
- [Architecture générée](#-architecture-générée)
|
|
22
|
+
- [Roadmap](#-roadmap)
|
|
23
|
+
- [FAQ](#-faq)
|
|
24
|
+
- [Contribuer](#-contribuer)
|
|
25
|
+
- [Licence](#-licence)
|
|
26
|
+
|
|
27
|
+
## 🚀 Introduction
|
|
28
|
+
|
|
29
|
+
**NestGen** est un générateur de code 100% autonome pour NestJS qui vous permet de créer rapidement des projets et des modules respectant les principes d'architecture avancés. Ce package n'a aucune dépendance externe et embarque tous les templates nécessaires pour générer des applications NestJS suivant les modèles Domain-Driven Design (DDD), Command Query Responsibility Segregation (CQRS) et l'architecture hexagonale.
|
|
30
|
+
|
|
31
|
+
Idéal pour les développeurs qui souhaitent maintenir une architecture propre et évolutive, NestGen accélère la phase de setup et garantit la cohérence structurelle de votre application, le tout sans nécessiter d'installation ou de configuration supplémentaire.
|
|
32
|
+
|
|
33
|
+
## ✨ Fonctionnalités
|
|
34
|
+
|
|
35
|
+
- **100% autonome** - Aucune dépendance externe, tout est intégré
|
|
36
|
+
- **Zéro configuration** - Fonctionne immédiatement après installation
|
|
37
|
+
- **Génération complète** - Projets et modules prêts à l'emploi en quelques secondes
|
|
38
|
+
- **Architecture avancée** - Support intégré pour DDD, CQRS et architecture hexagonale
|
|
39
|
+
- **Templates embarqués** - Tous les templates sont inclus dans le package
|
|
40
|
+
- **Support multiple d'ORM** - TypeORM et Prisma intégrés nativement
|
|
41
|
+
- **DevOps ready** - Configurations Docker, Swagger et CI/CD incluses
|
|
42
|
+
- **Mode interactif** - Interface CLI intuitive avec prompts pour une configuration guidée
|
|
43
|
+
|
|
44
|
+
## 📦 Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @outis25/nestgen
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
C'est tout! Aucune configuration supplémentaire n'est nécessaire. Tous les templates et dépendances sont embarqués dans le package.
|
|
51
|
+
|
|
52
|
+
## 🔧 Guide d'utilisation
|
|
53
|
+
|
|
54
|
+
### Initialiser un nouveau projet
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
nestgen init
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Cette commande lance un assistant interactif qui vous guidera à travers les étapes de configuration :
|
|
61
|
+
- Nom du projet
|
|
62
|
+
- Répertoire d'installation
|
|
63
|
+
- Sélection de l'ORM (TypeORM ou Prisma)
|
|
64
|
+
- Modules à générer automatiquement
|
|
65
|
+
- Options additionnelles (Git, Swagger, Docker, etc.)
|
|
66
|
+
|
|
67
|
+
### Générer un nouveau module
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
nestgen module <nom-du-module> [options]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Options disponibles :
|
|
74
|
+
- `--orm=<typeorm|prisma>` - Spécifie l'ORM à utiliser pour ce module
|
|
75
|
+
- `--crud` - Génère les opérations CRUD de base
|
|
76
|
+
- `--path=<chemin>` - Définit un chemin personnalisé pour le module
|
|
77
|
+
|
|
78
|
+
Exemples :
|
|
79
|
+
```bash
|
|
80
|
+
nestgen module user --orm=typeorm
|
|
81
|
+
nestgen module transaction --orm=prisma --crud
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Vérifier l'installation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
nestgen doctor
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Cette commande vérifie que :
|
|
91
|
+
- Le package est correctement installé
|
|
92
|
+
- Tous les templates embarqués sont disponibles
|
|
93
|
+
- Aucune dépendance n'est manquante
|
|
94
|
+
|
|
95
|
+
## 📁 Architecture générée
|
|
96
|
+
|
|
97
|
+
NestGen génère une structure de projet suivant les meilleures pratiques d'architecture :
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
src/app/<module>/
|
|
101
|
+
├── core/ # Cœur du domaine métier
|
|
102
|
+
│ ├── application/ # Cas d'utilisation
|
|
103
|
+
│ │ ├── commands/ # Commandes CQRS
|
|
104
|
+
│ │ ├── events/ # Événements domaine
|
|
105
|
+
│ │ └── queries/ # Requêtes CQRS
|
|
106
|
+
│ └── domain/ # Modèle du domaine
|
|
107
|
+
│ ├── entities/ # Entités métier
|
|
108
|
+
│ └── ports/ # Interfaces pour l'hexagonal
|
|
109
|
+
├── infrastructure/ # Implémentations techniques
|
|
110
|
+
│ ├── adapters/ # Adaptateurs pour l'hexagonal
|
|
111
|
+
│ └── persistences/ # Couche de persistance
|
|
112
|
+
│ └── repositories/ # Implémentations des repos
|
|
113
|
+
└── interfaces/ # Points d'entrée de l'app
|
|
114
|
+
├── controllers/ # Contrôleurs REST
|
|
115
|
+
└── dtos/ # Objets de transfert
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Cette structure facilite :
|
|
119
|
+
- La séparation des préoccupations
|
|
120
|
+
- Les tests unitaires et d'intégration
|
|
121
|
+
- L'évolutivité du code
|
|
122
|
+
- Le remplacement des composants techniques
|
|
123
|
+
|
|
124
|
+
## 🔮 Roadmap
|
|
125
|
+
|
|
126
|
+
- ✅ Génération de projet complet
|
|
127
|
+
- ✅ Génération de module modulaire (DDD, CQRS, Repo)
|
|
128
|
+
- ✅ Support Prisma et TypeORM
|
|
129
|
+
- ✅ Mode interactif
|
|
130
|
+
- ✅ Docker, Swagger, Git
|
|
131
|
+
- ✅ Templates intégrés
|
|
132
|
+
- ✅ Package 100% autonome
|
|
133
|
+
- ⬜ `nestgen resource <name>` (CRUD complet)
|
|
134
|
+
- ⬜ `nestgen destroy module <name>`
|
|
135
|
+
- ⬜ `nestgen preset ecommerce` (Templates d'applications)
|
|
136
|
+
- ⬜ `nestgen --interactive` (menu CLI avancé)
|
|
137
|
+
- ⬜ Support pour MongoDB et Mongoose
|
|
138
|
+
- ⬜ Génération de tests unitaires et d'intégration
|
|
139
|
+
|
|
140
|
+
## ❓ FAQ
|
|
141
|
+
|
|
142
|
+
**Q: NestGen nécessite-t-il des dépendances externes ?**
|
|
143
|
+
R: Non, NestGen est 100% autonome. Tous les templates et outils nécessaires sont embarqués dans le package.
|
|
144
|
+
|
|
145
|
+
**Q: Puis-je utiliser NestGen avec un projet NestJS existant ?**
|
|
146
|
+
R: Oui, vous pouvez ajouter des modules générés par NestGen à un projet existant. La structure générée s'intègre parfaitement aux projets NestJS standards.
|
|
147
|
+
|
|
148
|
+
**Q: Comment puis-je personnaliser les templates générés ?**
|
|
149
|
+
R: Par défaut, NestGen utilise ses templates intégrés. Leurs structures sont optimisées pour les meilleures pratiques et ne nécessitent généralement pas de modifications.
|
|
150
|
+
|
|
151
|
+
**Q: NestGen supporte-t-il les microservices ?**
|
|
152
|
+
R: Pas encore nativement, mais c'est prévu dans les prochaines versions. En attendant, vous pouvez adapter manuellement la structure générée.
|
|
153
|
+
|
|
154
|
+
## 👥 Contribuer
|
|
155
|
+
|
|
156
|
+
Les contributions sont les bienvenues ! Pour contribuer :
|
|
157
|
+
|
|
158
|
+
1. Forkez le dépôt
|
|
159
|
+
2. Créez une branche pour votre fonctionnalité (`git checkout -b feature/amazing-feature`)
|
|
160
|
+
3. Commitez vos changements (`git commit -m 'feat: add amazing feature'`)
|
|
161
|
+
4. Poussez vers la branche (`git push origin feature/amazing-feature`)
|
|
162
|
+
5. Ouvrez une Pull Request
|
|
163
|
+
|
|
164
|
+
<!-- Veuillez consulter le fichier `CONTRIBUTING.md` pour plus de détails. -->
|
|
165
|
+
|
|
166
|
+
## 📄 Licence
|
|
167
|
+
|
|
168
|
+
NestGen est distribué sous licence MIT. Voir le fichier `LICENSE` pour plus d'informations.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
<div align="center">
|
|
173
|
+
<p>Développé avec ❤️ par <a href="https://github.com/el-hack">@el-hack</a></p>
|
|
174
|
+
<p>Si vous trouvez ce projet utile, n'hésitez pas à lui donner une ⭐️ sur GitHub !</p>
|
|
175
|
+
</div>
|