nestgen-cli 0.1.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/cli/doctor-output.js +45 -0
  2. package/dist/cli/output.js +65 -0
  3. package/dist/engine/file-transaction.js +166 -0
  4. package/dist/engine/generated-code.js +26 -0
  5. package/dist/engine/generation-manifest.js +118 -0
  6. package/dist/engine/migration-plan.js +78 -0
  7. package/dist/engine/module-generator.js +80 -65
  8. package/dist/engine/project-config.js +5 -2
  9. package/dist/engine/project-path.js +25 -0
  10. package/dist/engine/project-preflight.js +96 -0
  11. package/dist/engine/resource-generator.js +1104 -68
  12. package/dist/engine/resource-spec.js +254 -11
  13. package/examples/store-api/README.md +76 -0
  14. package/examples/store-api/definitions/customers.resource.json +14 -0
  15. package/examples/store-api/definitions/order-items.resource.json +26 -0
  16. package/examples/store-api/definitions/orders.resource.json +22 -0
  17. package/examples/store-api/definitions/products.resource.json +19 -0
  18. package/examples/store-api/nestgen.example.json +7 -0
  19. package/nestgen.js +488 -84
  20. package/nestjs-generator/features/configure_bootstrap.mjs +5 -2
  21. package/nestjs-generator/features/docker.sh +6 -4
  22. package/nestjs-generator/features/git.sh +1 -1
  23. package/nestjs-generator/features/inject_module_to_app.sh +8 -6
  24. package/nestjs-generator/features/logger.sh +8 -4
  25. package/nestjs-generator/features/operational_foundation.mjs +210 -0
  26. package/nestjs-generator/features/operations.sh +18 -0
  27. package/nestjs-generator/features/preflight.mjs +2 -44
  28. package/nestjs-generator/features/prisma.sh +3 -3
  29. package/nestjs-generator/features/typeorm.sh +3 -3
  30. package/nestjs-generator/features/utils.sh +5 -3
  31. package/nestjs-generator/generate_project.sh +15 -9
  32. package/package.json +11 -4
  33. package/readme.md +234 -13
@@ -1,18 +1,21 @@
1
1
  import fs from 'node:fs';
2
2
 
3
- const [mainPath, swaggerEnabled] = process.argv.slice(2);
3
+ const [mainPath, swaggerEnabled, operationsEnabled] = process.argv.slice(2);
4
4
 
5
5
  if (!mainPath || !fs.existsSync(mainPath)) {
6
6
  console.error(`❌ Bootstrap Nest introuvable : ${mainPath ?? 'src/main.ts'}`);
7
7
  process.exitCode = 1;
8
8
  } else {
9
9
  const swagger = swaggerEnabled === 'y';
10
+ const operations = operationsEnabled === 'y';
10
11
  const source = `import { ValidationPipe } from '@nestjs/common';
11
12
  import { NestFactory } from '@nestjs/core';
12
13
  ${swagger ? "import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';\n" : ''}import { AppModule } from './app.module';
14
+ ${operations ? "import { StructuredLogger } from './operations/structured-logger.service';\n" : ''}
13
15
 
14
16
  async function bootstrap() {
15
- const app = await NestFactory.create(AppModule);
17
+ const app = await NestFactory.create(AppModule${operations ? ', { bufferLogs: true }' : ''});
18
+ ${operations ? ' app.useLogger(app.get(StructuredLogger));\n app.enableShutdownHooks();\n' : ''}
16
19
  app.useGlobalPipes(new ValidationPipe({
17
20
  whitelist: true,
18
21
  forbidNonWhitelisted: true,
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ source "$(dirname "${BASH_SOURCE[0]}")/logger.sh"
4
+
3
5
  set -euo pipefail
4
6
 
5
7
  APP_NAME=${1:?Nom_application_requis}
@@ -8,14 +10,14 @@ PM=${2:-npm}
8
10
  case "$PM" in
9
11
  npm|pnpm|yarn) ;;
10
12
  *)
11
- echo "❌ Package manager non supporté pour Docker : $PM."
13
+ log_error "Package manager non supporté pour Docker : $PM."
12
14
  exit 1
13
15
  ;;
14
16
  esac
15
17
 
16
18
  for file in Dockerfile compose.yaml .dockerignore; do
17
19
  if [ -e "$file" ]; then
18
- echo "❌ $file existe déjà. Aucune configuration Docker n'a été remplacée."
20
+ log_error "$file existe déjà. Aucune configuration Docker n'a été remplacée."
19
21
  exit 1
20
22
  fi
21
23
  done
@@ -186,7 +188,7 @@ POSTGRES_PORT=5432
186
188
  PORT=3000
187
189
  ENV
188
190
  else
189
- echo "ℹ️ .env.example existe déjà et a été conservé."
191
+ log_info ".env.example existe déjà et a été conservé."
190
192
  fi
191
193
 
192
- echo "✅ Docker de développement et image de production générés pour $APP_NAME ($PM)."
194
+ log_success "Docker de développement et image de production générés pour $APP_NAME ($PM)."
@@ -11,4 +11,4 @@ source "$FEATURES_PATH/logger.sh"
11
11
  git init
12
12
  git add .
13
13
  git commit -m "🚀 Initial commit (NestJS starter clean architecture)"
14
- echo "✅ Git initialisé et commité !"
14
+ log_success "Git initialisé et commité !"
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ source "$(dirname "${BASH_SOURCE[0]}")/logger.sh"
4
+
3
5
  set -euo pipefail
4
6
 
5
7
  RAW_NAME=${1:-}
@@ -9,31 +11,31 @@ MODULE_FILE="src/app/${RAW_NAME}/${RAW_NAME}.module.ts"
9
11
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
12
 
11
13
  if [[ -z "$RAW_NAME" ]]; then
12
- echo "❌ Tu dois fournir un nom de module." >&2
14
+ log_error "Tu dois fournir un nom de module."
13
15
  exit 1
14
16
  fi
15
17
 
16
18
  if [[ "$ORM" != "typeorm" && "$ORM" != "prisma" ]]; then
17
- echo "❌ ORM non supporté : ${ORM:-vide}." >&2
19
+ log_error "ORM non supporté : ${ORM:-vide}."
18
20
  exit 1
19
21
  fi
20
22
 
21
23
  if [[ ! -f "$MODULE_FILE" ]]; then
22
- echo "❌ Le fichier $MODULE_FILE est introuvable." >&2
24
+ log_error "Le fichier $MODULE_FILE est introuvable."
23
25
  exit 1
24
26
  fi
25
27
 
26
28
  MODULE_CLASS=$(grep -oE 'export class [A-Za-z0-9_]+' "$MODULE_FILE" | awk '{print $3}')
27
29
 
28
30
  if [[ -z "$MODULE_CLASS" ]]; then
29
- echo "❌ Impossible de détecter la classe du module dans $MODULE_FILE" >&2
31
+ log_error "Impossible de détecter la classe du module dans $MODULE_FILE"
30
32
  exit 1
31
33
  fi
32
34
 
33
35
  MODULE_PATH="./app/${RAW_NAME}/${RAW_NAME}.module"
34
36
 
35
37
  if [[ ! -f "$APP_MODULE" ]]; then
36
- echo "📄 Création de $APP_MODULE"
38
+ log_info "Création de $APP_MODULE"
37
39
  mkdir -p src
38
40
  cat > "$APP_MODULE" <<'EOF'
39
41
  import { Module } from '@nestjs/common';
@@ -49,4 +51,4 @@ fi
49
51
  # avoir validé le décorateur @Module et son tableau imports.
50
52
  node "$SCRIPT_DIR/update_app_module.mjs" "$APP_MODULE" "$MODULE_CLASS" "$MODULE_PATH" "$ORM"
51
53
 
52
- echo "🎯 Module $MODULE_CLASS injecté dans app.module.ts avec succès 🧩"
54
+ log_success "Module $MODULE_CLASS injecté dans app.module.ts avec succès"
@@ -7,18 +7,22 @@ YELLOW='\033[1;33m'
7
7
  BLUE='\033[1;34m'
8
8
  NC='\033[0m' # No Color
9
9
 
10
+ if [ ! -t 1 ] || [ -n "${NO_COLOR+x}" ] || [ "${TERM:-}" = "dumb" ]; then
11
+ RED='' GREEN='' YELLOW='' BLUE='' NC=''
12
+ fi
13
+
10
14
  log_info() {
11
- echo -e "${BLUE}ℹ️ $1${NC}"
15
+ printf ' %b›%b %s\n' "$BLUE" "$NC" "$1"
12
16
  }
13
17
 
14
18
  log_success() {
15
- echo -e "${GREEN}✅ $1${NC}"
19
+ printf ' %b✓%b %s\n' "$GREEN" "$NC" "$1"
16
20
  }
17
21
 
18
22
  log_warn() {
19
- echo -e "${YELLOW}⚠️ $1${NC}"
23
+ printf ' %b⚠%b %s\n' "$YELLOW" "$NC" "$1"
20
24
  }
21
25
 
22
26
  log_error() {
23
- echo -e "${RED}❌ $1${NC}"
27
+ printf ' %b✖%b %s\n' "$RED" "$NC" "$1" >&2
24
28
  }
@@ -0,0 +1,210 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const [sourceDirectory, orm] = process.argv.slice(2);
5
+
6
+ if (!sourceDirectory || !['typeorm', 'prisma'].includes(orm)) {
7
+ console.error('❌ Usage : operational_foundation.mjs <source-directory> <typeorm|prisma>');
8
+ process.exitCode = 1;
9
+ } else {
10
+ const operationsDirectory = path.join(sourceDirectory, 'operations');
11
+ const appModule = path.join(sourceDirectory, 'app.module.ts');
12
+
13
+ if (!fs.existsSync(appModule)) {
14
+ console.error(`❌ AppModule introuvable : ${appModule}`);
15
+ process.exitCode = 1;
16
+ } else if (fs.existsSync(operationsDirectory)) {
17
+ console.error(`❌ ${operationsDirectory} existe déjà. Le socle d’exploitation n’a pas été remplacé.`);
18
+ process.exitCode = 1;
19
+ } else {
20
+ const databaseHealth =
21
+ orm === 'typeorm'
22
+ ? `import { Injectable } from '@nestjs/common';
23
+ import { InjectDataSource } from '@nestjs/typeorm';
24
+ import type { DataSource } from 'typeorm';
25
+
26
+ @Injectable()
27
+ export class DatabaseHealthService {
28
+ constructor(@InjectDataSource() private readonly dataSource: DataSource) {}
29
+
30
+ async check(): Promise<{ status: 'up' | 'down' }> {
31
+ try {
32
+ if (!this.dataSource.isInitialized) return { status: 'down' };
33
+ await this.dataSource.query('SELECT 1');
34
+ return { status: 'up' };
35
+ } catch {
36
+ return { status: 'down' };
37
+ }
38
+ }
39
+ }
40
+ `
41
+ : `import { Injectable } from '@nestjs/common';
42
+ import { PrismaService } from '../prisma/prisma.service';
43
+
44
+ @Injectable()
45
+ export class DatabaseHealthService {
46
+ constructor(private readonly prisma: PrismaService) {}
47
+
48
+ async check(): Promise<{ status: 'up' | 'down' }> {
49
+ try {
50
+ await this.prisma.$queryRawUnsafe('SELECT 1');
51
+ return { status: 'up' };
52
+ } catch {
53
+ return { status: 'down' };
54
+ }
55
+ }
56
+ }
57
+ `;
58
+
59
+ const files = new Map([
60
+ [
61
+ 'request-context.ts',
62
+ `import { AsyncLocalStorage } from 'node:async_hooks';
63
+
64
+ export const requestContext = new AsyncLocalStorage<{ requestId: string }>();
65
+ `,
66
+ ],
67
+ [
68
+ 'request-id.middleware.ts',
69
+ `import { Injectable, type NestMiddleware } from '@nestjs/common';
70
+ import { randomUUID } from 'node:crypto';
71
+ import type { NextFunction, Request, Response } from 'express';
72
+ import { requestContext } from './request-context';
73
+
74
+ const REQUEST_ID = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
75
+
76
+ @Injectable()
77
+ export class RequestIdMiddleware implements NestMiddleware {
78
+ use(request: Request, response: Response, next: NextFunction): void {
79
+ const candidate = request.header('x-request-id');
80
+ const requestId = candidate && REQUEST_ID.test(candidate) ? candidate : randomUUID();
81
+ response.setHeader('x-request-id', requestId);
82
+ requestContext.run({ requestId }, next);
83
+ }
84
+ }
85
+ `,
86
+ ],
87
+ [
88
+ 'structured-logger.service.ts',
89
+ `import { Injectable, type LoggerService } from '@nestjs/common';
90
+ import { requestContext } from './request-context';
91
+
92
+ const SENSITIVE_KEY = /password|secret|token|authorization|cookie|api[-_]?key/i;
93
+
94
+ export function redact(value: unknown): unknown {
95
+ if (Array.isArray(value)) return value.map(redact);
96
+ if (!value || typeof value !== 'object') return value;
97
+ if (value instanceof Error) return { name: value.name, message: value.message };
98
+ return Object.fromEntries(
99
+ Object.entries(value).map(([key, nested]) => [key, SENSITIVE_KEY.test(key) ? '[REDACTED]' : redact(nested)]),
100
+ );
101
+ }
102
+
103
+ @Injectable()
104
+ export class StructuredLogger implements LoggerService {
105
+ log(message: unknown, ...optional: unknown[]): void {
106
+ this.write('log', message, optional);
107
+ }
108
+
109
+ error(message: unknown, ...optional: unknown[]): void {
110
+ this.write('error', message, optional);
111
+ }
112
+
113
+ warn(message: unknown, ...optional: unknown[]): void {
114
+ this.write('warn', message, optional);
115
+ }
116
+
117
+ debug(message: unknown, ...optional: unknown[]): void {
118
+ this.write('debug', message, optional);
119
+ }
120
+
121
+ verbose(message: unknown, ...optional: unknown[]): void {
122
+ this.write('verbose', message, optional);
123
+ }
124
+
125
+ private write(level: string, message: unknown, optional: unknown[]): void {
126
+ const context = requestContext.getStore();
127
+ const record = {
128
+ timestamp: new Date().toISOString(),
129
+ level,
130
+ message: typeof message === 'string' ? message : redact(message),
131
+ ...(context ? { requestId: context.requestId } : {}),
132
+ ...(optional.length > 0 ? { details: redact(optional) } : {}),
133
+ };
134
+ process[level === 'error' ? 'stderr' : 'stdout'].write(JSON.stringify(record) + '\\n');
135
+ }
136
+ }
137
+ `,
138
+ ],
139
+ ['database-health.service.ts', databaseHealth],
140
+ [
141
+ 'health.controller.ts',
142
+ `import { Controller, Get, ServiceUnavailableException } from '@nestjs/common';
143
+ import { DatabaseHealthService } from './database-health.service';
144
+
145
+ @Controller('health')
146
+ export class HealthController {
147
+ constructor(private readonly databaseHealth: DatabaseHealthService) {}
148
+
149
+ @Get('live')
150
+ live(): { status: 'ok'; checks: { application: 'up' } } {
151
+ return { status: 'ok', checks: { application: 'up' } };
152
+ }
153
+
154
+ @Get('ready')
155
+ async ready(): Promise<{ status: 'ok'; checks: { database: 'up' } }> {
156
+ const database = await this.databaseHealth.check();
157
+ if (database.status === 'down') {
158
+ throw new ServiceUnavailableException({ status: 'error', checks: { database: 'down' } });
159
+ }
160
+ return { status: 'ok', checks: { database: 'up' } };
161
+ }
162
+ }
163
+ `,
164
+ ],
165
+ [
166
+ 'operations.module.ts',
167
+ `import { MiddlewareConsumer, Module, type NestModule } from '@nestjs/common';
168
+ ${orm === 'prisma' ? "import { PrismaModule } from '../prisma/prisma.module';\n" : ''}import { DatabaseHealthService } from './database-health.service';
169
+ import { HealthController } from './health.controller';
170
+ import { RequestIdMiddleware } from './request-id.middleware';
171
+ import { StructuredLogger } from './structured-logger.service';
172
+
173
+ @Module({
174
+ imports: [${orm === 'prisma' ? 'PrismaModule' : ''}],
175
+ controllers: [HealthController],
176
+ providers: [DatabaseHealthService, RequestIdMiddleware, StructuredLogger],
177
+ exports: [StructuredLogger],
178
+ })
179
+ export class OperationsModule implements NestModule {
180
+ configure(consumer: MiddlewareConsumer): void {
181
+ consumer.apply(RequestIdMiddleware).forRoutes('*');
182
+ }
183
+ }
184
+ `,
185
+ ],
186
+ [
187
+ 'README.md',
188
+ `# Socle d’exploitation
189
+
190
+ Cette option est locale au projet et ne requiert aucun service externe. Elle expose :
191
+
192
+ - \`GET /health/live\` : le processus Nest peut répondre ; aucun appel de dépendance n’est fait.
193
+ - \`GET /health/ready\` : le processus et la base PostgreSQL sont prêts. Une base indisponible retourne \`503\` avec \`{ status: 'error', checks: { database: 'down' } }\`.
194
+
195
+ Chaque réponse reçoit \`x-request-id\`. Une valeur cliente sûre est conservée ; sinon NestGen en crée une. Le logger Nest produit un JSON par ligne avec cet identifiant, et masque récursivement les clés contenant \`password\`, \`secret\`, \`token\`, \`authorization\`, \`cookie\` ou \`apiKey\`.
196
+
197
+ \`main.ts\` active \`enableShutdownHooks()\`. Ainsi, \`SIGTERM\` et \`SIGINT\` ferment le contexte Nest ; TypeORM ferme sa connexion et Prisma exécute son \`onModuleDestroy\` existant. Les migrations restent explicites et ne sont jamais appliquées par ces endpoints.
198
+ `,
199
+ ],
200
+ ]);
201
+
202
+ fs.mkdirSync(operationsDirectory, { recursive: true });
203
+ try {
204
+ for (const [name, content] of files) fs.writeFileSync(path.join(operationsDirectory, name), content);
205
+ } catch (error) {
206
+ fs.rmSync(operationsDirectory, { recursive: true, force: true });
207
+ throw error;
208
+ }
209
+ }
210
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+
3
+ source "$(dirname "${BASH_SOURCE[0]}")/logger.sh"
4
+
5
+ set -euo pipefail
6
+
7
+ ORM=${1:?ORM requis}
8
+ FEATURES_PATH="$(dirname "$0")"
9
+
10
+ if [[ "$ORM" != "typeorm" && "$ORM" != "prisma" ]]; then
11
+ log_error "ORM non supporté : $ORM" >&2
12
+ exit 1
13
+ fi
14
+
15
+ node "$FEATURES_PATH/operational_foundation.mjs" src "$ORM"
16
+ node "$FEATURES_PATH/update_app_module.mjs" src/app.module.ts OperationsModule './operations/operations.module' "$ORM"
17
+
18
+ log_success "Socle d’exploitation configuré : /health/live et /health/ready."
@@ -1,48 +1,6 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
1
+ import { inspectProject } from '../../dist/engine/project-preflight.js';
3
2
 
4
- function error(message) {
5
- throw new Error(`Préflight échoué : ${message}`);
6
- }
7
-
8
- export function inspectProject(projectRoot, orm) {
9
- const packagePath = path.join(projectRoot, 'package.json');
10
- const appModulePath = path.join(projectRoot, 'src', 'app.module.ts');
11
- const nestCliPath = path.join(projectRoot, 'nest-cli.json');
12
-
13
- if (!fs.existsSync(packagePath))
14
- error('package.json introuvable. Exécute la commande à la racine d’un projet NestJS.');
15
- if (!fs.existsSync(appModulePath))
16
- error(
17
- 'src/app.module.ts introuvable. Les workspaces et structures personnalisées ne sont pas encore supportés.',
18
- );
19
-
20
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
21
- const nestCli = fs.existsSync(nestCliPath) ? JSON.parse(fs.readFileSync(nestCliPath, 'utf8')) : null;
22
- if (nestCli?.monorepo) error('les workspaces Nest ne sont pas encore supportés. Cible une application autonome.');
23
-
24
- const dependencies = { ...packageJson.dependencies, ...packageJson.devDependencies };
25
- const missing = ['@nestjs/common', '@nestjs/core', '@nestjs/cqrs'];
26
- if (orm === 'typeorm') missing.push('@nestjs/typeorm', 'typeorm');
27
- if (orm === 'prisma') missing.push('@prisma/client');
28
- const unavailable = missing.filter((dependency) => !dependencies[dependency]);
29
- if (unavailable.length) error(`dépendances manquantes : ${unavailable.join(', ')}.`);
30
-
31
- const appModule = fs.readFileSync(appModulePath, 'utf8');
32
- if (!/@Module\s*\(/.test(appModule)) error('le décorateur @Module est introuvable dans src/app.module.ts.');
33
-
34
- return {
35
- appModulePath,
36
- packageManager: fs.existsSync(path.join(projectRoot, 'pnpm-lock.yaml'))
37
- ? 'pnpm'
38
- : fs.existsSync(path.join(projectRoot, 'yarn.lock'))
39
- ? 'yarn'
40
- : fs.existsSync(path.join(projectRoot, 'package-lock.json'))
41
- ? 'npm'
42
- : null,
43
- hasRootTypeOrmConnection: /\bTypeOrmModule\.forRoot\s*\(/.test(appModule),
44
- };
45
- }
3
+ export { inspectProject };
46
4
 
47
5
  if (import.meta.url === `file://${process.argv[1]}`) {
48
6
  try {
@@ -12,7 +12,7 @@ PM=${1:?Package manager requis}
12
12
  APP_NAME=${2:?Nom_application_requis}
13
13
 
14
14
  if [ -e prisma/schema.prisma ]; then
15
- echo "❌ prisma/schema.prisma existe déjà. Aucune configuration Prisma n'a été remplacée."
15
+ log_error "prisma/schema.prisma existe déjà. Aucune configuration Prisma n'a été remplacée."
16
16
  exit 1
17
17
  fi
18
18
 
@@ -25,7 +25,7 @@ cat > .env <<EOF
25
25
  DATABASE_URL="postgresql://postgres:postgres@localhost:5432/$APP_NAME"
26
26
  EOF
27
27
  else
28
- echo "ℹ️ .env existe déjà et a été conservé."
28
+ log_info ".env existe déjà et a été conservé."
29
29
  fi
30
30
 
31
31
  cat > prisma/schema.prisma <<EOF
@@ -70,4 +70,4 @@ EOF
70
70
 
71
71
  pm_exec "$PM" prisma generate
72
72
 
73
- echo "✅ Prisma initialisé avec succès !"
73
+ log_success "Prisma initialisé avec succès !"
@@ -10,7 +10,7 @@ FEATURES_PATH="$(dirname "$0")"
10
10
  source "$FEATURES_PATH/utils.sh"
11
11
  source "$FEATURES_PATH/logger.sh"
12
12
 
13
- log_info "📦 Installation de TypeORM et PostgreSQL..."
13
+ log_info "Installation de TypeORM et PostgreSQL..."
14
14
 
15
15
  pm_add "$PM" @nestjs/typeorm@12 typeorm@0.3 pg@8 dotenv@16
16
16
  pm_add_dev "$PM" ts-node@10
@@ -75,6 +75,6 @@ DATABASE_NAME=appdb
75
75
  EOF
76
76
  fi
77
77
 
78
- log_success "✅ TypeORM installé avec succès"
78
+ log_success "TypeORM installé avec succès"
79
79
 
80
- log_info "💡 TypeORM utilise src/database/typeorm.config.ts et src/database/data-source.ts pour des migrations explicites."
80
+ log_info "TypeORM utilise src/database/typeorm.config.ts et src/database/data-source.ts pour des migrations explicites."
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
 
3
+ source "$(dirname "${BASH_SOURCE[0]}")/logger.sh"
4
+
3
5
  NEST_CLI_VERSION="12.0.0"
4
6
 
5
7
  assert_package_manager() {
@@ -7,7 +9,7 @@ assert_package_manager() {
7
9
  case "$package_manager" in
8
10
  npm|pnpm|yarn) assert_command_exists "$package_manager" ;;
9
11
  *)
10
- echo "❌ Package manager non supporté : $package_manager. Valeurs acceptées : npm, pnpm, yarn."
12
+ log_error "Package manager non supporté : $package_manager. Valeurs acceptées : npm, pnpm, yarn."
11
13
  return 1
12
14
  ;;
13
15
  esac
@@ -57,8 +59,8 @@ nest_new() {
57
59
  assert_command_exists() {
58
60
  local CMD=$1
59
61
  if ! command -v "$CMD" &> /dev/null; then
60
- echo "❌ Commande introuvable : $CMD"
61
- echo "👉 Installe-la ou vérifie ton PATH"
62
+ log_error "Commande introuvable : $CMD"
63
+ log_info "Installe-la ou vérifie ton PATH"
62
64
  return 1
63
65
  fi
64
66
  }
@@ -87,7 +87,12 @@ if [ "$WITH_SWAGGER" = "y" ]; then
87
87
  run_step "Configuration Swagger" bash "$FEATURES_PATH/swagger.sh" "$PM"
88
88
  fi
89
89
 
90
- run_step "Configuration du bootstrap Nest" node "$FEATURES_PATH/configure_bootstrap.mjs" src/main.ts "$WITH_SWAGGER"
90
+ WITH_OPERATIONS=${WITH_OPERATIONS:-$(read -p "🩺 Activer le socle d’exploitation ? (y/n) : " tmp && echo "$tmp")}
91
+ if [ "$WITH_OPERATIONS" = "y" ]; then
92
+ run_step "Configuration du socle d’exploitation" bash "$FEATURES_PATH/operations.sh" "$ORM"
93
+ fi
94
+
95
+ run_step "Configuration du bootstrap Nest" node "$FEATURES_PATH/configure_bootstrap.mjs" src/main.ts "$WITH_SWAGGER" "$WITH_OPERATIONS"
91
96
 
92
97
  WITH_GIT=${WITH_GIT:-$(read -p "🔃 Initialiser Git ? (y/n) : " tmp && echo "$tmp")}
93
98
  if [ "$WITH_GIT" = "y" ]; then
@@ -103,11 +108,12 @@ done
103
108
 
104
109
  # ────── Résumé final ──────
105
110
  echo ""
106
- log_success "✅ Projet NestJS \"$APP_NAME\" généré avec succès 🎉"
107
- echo "📁 Localisation : $FULL_PATH"
108
- echo "📦 Package manager : $PM"
109
- echo "🧠 ORM : $ORM"
110
- if [ "$WITH_DOCKER" = "y" ]; then echo "🐳 Docker activé"; fi
111
- if [ "$WITH_SWAGGER" = "y" ]; then echo "📚 Swagger activé"; fi
112
- if [ "$WITH_GIT" = "y" ]; then echo "🔃 Git initialisé"; fi
113
- if [ -n "$MODULES" ]; then echo "📦 Modules générés : $MODULES"; fi
111
+ log_success "Projet NestJS \"$APP_NAME\" généré avec succès"
112
+ printf '\n %-24s %s\n' 'Localisation' "$FULL_PATH"
113
+ printf ' %-24s %s\n' 'Package manager' "$PM" 'ORM' "$ORM"
114
+ if [ "$WITH_DOCKER" = "y" ]; then printf ' %-24s %s\n' 'Docker' 'activé'; fi
115
+ if [ "$WITH_SWAGGER" = "y" ]; then printf ' %-24s %s\n' 'Swagger' 'activé'; fi
116
+ if [ "$WITH_OPERATIONS" = "y" ]; then printf ' %-24s %s\n' 'Socle d’exploitation' 'activé'; fi
117
+ if [ "$WITH_GIT" = "y" ]; then printf ' %-24s %s\n' 'Git' 'initialisé'; fi
118
+ if [ -n "$MODULES" ]; then printf ' %-24s %s\n' 'Modules générés' "$MODULES"; fi
119
+ printf '\n'
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "nestgen-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "NestGen CLI — Générateur modulaire NestJS (DDD, CQRS, Hexagonal)",
5
5
  "files": [
6
6
  "dist",
7
+ "examples",
7
8
  "nestgen.js",
8
9
  "nestjs-generator"
9
10
  ],
@@ -35,24 +36,30 @@
35
36
  "check": "npm run typecheck && eslint .",
36
37
  "format": "prettier --write .",
37
38
  "format:check": "prettier --check .",
38
- "prepack": "npm run build && npm run check && npm run format:check && npm test",
39
+ "prepack": "npm run build && npm run check && npm run docs:check && npm run landing:check && npm run format:check && npm test && npm run test:example",
39
40
  "test": "node --test",
40
41
  "test:package": "node scripts/package-smoke.mjs",
42
+ "test:package-manager": "node scripts/package-manager-lockfile-integration.mjs",
41
43
  "test:prisma": "node scripts/prisma-integration.mjs",
42
44
  "test:docker": "node scripts/docker-config-integration.mjs",
45
+ "test:example": "node scripts/example-store-api-integration.mjs",
43
46
  "test:typeorm": "node scripts/typeorm-http-integration.mjs",
47
+ "release:verify": "node scripts/release-verify.mjs",
48
+ "release:verify-remote": "node scripts/release-verify-remote.mjs",
49
+ "docs:check": "node scripts/docs-contract.mjs",
50
+ "landing:check": "node scripts/landing-contract.mjs",
44
51
  "typecheck": "tsc -p tsconfig.json --noEmit"
45
52
  },
46
53
  "licenses": "MIT",
47
54
  "dependencies": {
48
55
  "chalk": "^5.3.0",
49
56
  "inquirer": "^9.0.0",
57
+ "prettier": "^3.9.9",
50
58
  "typescript": "^5.9.3"
51
59
  },
52
60
  "devDependencies": {
53
61
  "@eslint/js": "^9.39.5",
54
62
  "@types/node": "^24.19.0",
55
- "eslint": "^9.39.5",
56
- "prettier": "^3.9.9"
63
+ "eslint": "^9.39.5"
57
64
  }
58
65
  }