nestgen-cli 0.2.1 → 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.
@@ -0,0 +1,45 @@
1
+ import chalk from 'chalk';
2
+ /** Human-readable presentation only; structured diagnostics remain unchanged. */
3
+ export function formatDoctorOutput(diagnostics, options = {}) {
4
+ const groups = [
5
+ { severity: 'ERROR', title: 'À corriger', marker: '✖', color: chalk.red },
6
+ { severity: 'WARNING', title: 'À vérifier', marker: '⚠', color: chalk.yellow },
7
+ { severity: 'INFO', title: 'Vérifications', marker: '✓', color: chalk.green },
8
+ ];
9
+ const lines = options.quiet
10
+ ? []
11
+ : [
12
+ '',
13
+ ` ${chalk.cyan.bold('◆ NestGen')} ${chalk.bold('Doctor')}`,
14
+ chalk.dim(' Diagnostic de votre environnement'),
15
+ '',
16
+ ];
17
+ for (const group of groups) {
18
+ if (options.quiet && group.severity === 'INFO')
19
+ continue;
20
+ const entries = diagnostics.filter((diagnostic) => diagnostic.severity === group.severity);
21
+ if (!entries.length)
22
+ continue;
23
+ if (!options.quiet)
24
+ lines.push(` ${group.color.bold(group.title)} ${chalk.dim(`(${entries.length})`)}`, '');
25
+ for (const diagnostic of entries) {
26
+ lines.push(` ${group.color(group.marker)} ${diagnostic.cause}`);
27
+ if (diagnostic.severity !== 'INFO' || (diagnostic.id === 'NESTGEN_CONFIG' && !diagnostic.detail))
28
+ lines.push(` → ${diagnostic.action}`);
29
+ if (options.verbose) {
30
+ lines.push(chalk.dim(` Code : ${diagnostic.id}`));
31
+ if (diagnostic.detail)
32
+ lines.push(chalk.dim(` Détail : ${diagnostic.detail}`));
33
+ }
34
+ }
35
+ if (!options.quiet)
36
+ lines.push('');
37
+ }
38
+ if (!options.quiet) {
39
+ const count = (severity) => diagnostics.filter((diagnostic) => diagnostic.severity === severity).length;
40
+ const errors = count('ERROR');
41
+ const warnings = count('WARNING');
42
+ lines.push(chalk.dim(' ────────────────────────────────────────'), ` ${errors ? chalk.red.bold('✖ Des corrections sont nécessaires') : warnings ? chalk.yellow.bold('⚠ Quelques points à vérifier') : chalk.green.bold('✓ Votre environnement est prêt')}`, ` ${chalk.red(`${errors} erreur${errors > 1 ? 's' : ''}`)} · ${chalk.yellow(`${warnings} avertissement${warnings > 1 ? 's' : ''}`)} · ${chalk.dim(`${count('INFO')} vérifications`)}`, '');
43
+ }
44
+ return lines.join('\n');
45
+ }
@@ -0,0 +1,65 @@
1
+ import chalk from 'chalk';
2
+ export function heading(title, subtitle) {
3
+ return `\n ${chalk.cyan.bold('◆ NestGen')} ${chalk.bold(title)}\n${subtitle ? `${chalk.dim(` ${subtitle}`)}\n` : ''}`;
4
+ }
5
+ export function success(message) {
6
+ return `\n ${chalk.green('✓')} ${chalk.bold(message)}\n`;
7
+ }
8
+ export function formatError(message) {
9
+ return `${heading('Erreur')} ${chalk.red('✖')} ${message.split('\n').join('\n ')}\n`;
10
+ }
11
+ export function formatConfig(config, subtitle) {
12
+ const rows = [
13
+ ['ORM', config.orm],
14
+ ['Architecture', config.profile],
15
+ ['Sources', config.sourceRoot],
16
+ ['Package manager', config.packageManager],
17
+ ['Version configuration', config.version],
18
+ ['Version templates', config.templateVersion],
19
+ ];
20
+ return `${heading('Configuration', subtitle)}${rows.map(([label, value]) => ` ${chalk.dim(label.padEnd(24))}${value}`).join('\n')}\n`;
21
+ }
22
+ export function formatMigration(migration) {
23
+ return [
24
+ ` ${chalk.bold('Migration à préparer')}`,
25
+ ` ${chalk.cyan(migration.command)}`,
26
+ chalk.dim(' Cette commande prépare la migration ; aucune migration appliquée.'),
27
+ ...migration.risks.map((risk) => ` ${risk.severity === 'danger' ? chalk.red('✖') : chalk.yellow('⚠')} ${risk.message}`),
28
+ '',
29
+ ].join('\n');
30
+ }
31
+ export function formatPlan(plan, verbose = false) {
32
+ const lines = [heading('Aperçu', `Génération ${plan.operation} · aucun fichier écrit`)];
33
+ for (const change of plan.changes) {
34
+ const label = change.status === 'create'
35
+ ? chalk.green('+ Créer')
36
+ : change.status === 'replace'
37
+ ? chalk.cyan('~ Modifier')
38
+ : chalk.red('✖ Conflit');
39
+ lines.push(` ${label} ${change.path}`);
40
+ if (change.reason)
41
+ lines.push(` ${chalk.yellow(change.reason)}`);
42
+ if (verbose && change.diff)
43
+ lines.push(chalk.dim(` ${change.diff.split('\n').join('\n ')}`));
44
+ }
45
+ for (const conflict of plan.conflicts.filter((conflict) => !conflict.path))
46
+ lines.push(` ${chalk.red('✖')} ${conflict.reason}`);
47
+ lines.push('', chalk.dim(' ────────────────────────────────────────'), ` ${plan.changes.length} fichiers · ${plan.conflicts.length} conflits`, '');
48
+ if (plan.migration)
49
+ lines.push(formatMigration(plan.migration));
50
+ return lines.join('\n');
51
+ }
52
+ /** Align help columns from the complete existing command/option descriptions. */
53
+ export function formatHelp(usage) {
54
+ return `${heading('Aide')}${usage
55
+ .split('\n')
56
+ .map((line) => {
57
+ if (!line)
58
+ return '';
59
+ if (!line.startsWith(' '))
60
+ return ` ${chalk.bold(line)}`;
61
+ const [label, description] = line.trim().split(/\s{2,}/);
62
+ return description ? ` ${chalk.cyan(label.padEnd(34))} ${description}` : ` ${line.trim()}`;
63
+ })
64
+ .join('\n')}\n`;
65
+ }
@@ -1,6 +1,6 @@
1
- # Exemple Store API — NestGen CLI 0.2.1
1
+ # Exemple Store API — NestGen CLI 0.2.2
2
2
 
3
- Cette application exemple décrit une API de vente avec des clients, produits, commandes et lignes de commande. Elle est validée avec `nestgen-cli@0.2.1` et TypeORM, avec le profil d’architecture `advanced`.
3
+ Cette application exemple décrit une API de vente avec des clients, produits, commandes et lignes de commande. Elle est validée avec `nestgen-cli@0.2.2` et TypeORM, avec le profil d’architecture `advanced`.
4
4
 
5
5
  Les fichiers de `definitions/` sont la source de vérité. Ils restent volontairement déclaratifs : l’application est reconstruite par le CLI au lieu de conserver une copie de fichiers générés qui pourrait diverger du produit.
6
6
 
@@ -11,7 +11,7 @@ Créez un dossier de travail, installez la version exacte du CLI puis générez
11
11
  ```bash
12
12
  mkdir nestgen-store-api && cd nestgen-store-api
13
13
  npm init --yes
14
- npm install nestgen-cli@0.2.1
14
+ npm install nestgen-cli@0.2.2
15
15
 
16
16
  ./node_modules/.bin/nestgen init store-api \
17
17
  --no-interactive \
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "cliVersion": "0.2.1",
3
+ "cliVersion": "0.2.2",
4
4
  "orm": "typeorm",
5
5
  "profile": "advanced",
6
6
  "resources": ["customers", "products", "orders", "order-items"]
package/nestgen.js CHANGED
@@ -18,6 +18,16 @@ import {
18
18
  import { configFileName, defaultConfig, loadConfigIfPresent, writeConfig } from './dist/engine/project-config.js';
19
19
  import { parseArchitectureProfile } from './dist/engine/architecture-profile.js';
20
20
  import { planResourceMigration } from './dist/engine/migration-plan.js';
21
+ import { formatDoctorOutput } from './dist/cli/doctor-output.js';
22
+ import {
23
+ heading,
24
+ success,
25
+ formatError,
26
+ formatConfig,
27
+ formatPlan,
28
+ formatMigration,
29
+ formatHelp,
30
+ } from './dist/cli/output.js';
21
31
 
22
32
  // ────── Resolve __dirname compatible ES Modules
23
33
  const __filename = fileURLToPath(import.meta.url);
@@ -32,17 +42,7 @@ const MODULE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i;
32
42
 
33
43
  // ────── Logo CLI
34
44
  function printLogo() {
35
- console.log(
36
- chalk.magentaBright(`
37
- ███╗ ██╗███████╗███████╗████████╗ ██████╗ ███████╗███╗ ██╗
38
- ████╗ ██║██╔════╝██╔════╝╚══██╔══╝██╔════╝ ██╔════╝████╗ ██║
39
- ██╔██╗ ██║█████╗ ███████╗ ██║ ██║ ███╗█████╗ ██╔██╗ ██║
40
- ██║╚██╗██║██╔══╝ ╚════██║ ██║ ██║ ██║██╔══╝ ██║╚██╗██║
41
- ██║ ╚████║███████╗███████║ ██║ ╚██████╔╝███████╗██║ ╚████║
42
- ╚═╝ ╚═══╝╚══════╝╚══════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝
43
- `),
44
- );
45
- console.log(chalk.cyan.bold('✨ NestGen CLI — Générateur modulaire NestJS'));
45
+ console.log(heading('CLI', 'Générateur modulaire NestJS'));
46
46
  }
47
47
 
48
48
  // ────── Helpers
@@ -226,7 +226,7 @@ function dryRunOutput(operation, plan) {
226
226
 
227
227
  export function runBashScript(scriptPath, args = [], env = {}, quiet = false) {
228
228
  const result = spawnSync('bash', [scriptPath, ...args], {
229
- env: { ...process.env, ...env },
229
+ env: { ...process.env, ...(chalk.level === 0 ? { NO_COLOR: '1' } : {}), ...env },
230
230
  stdio: quiet ? 'pipe' : 'inherit',
231
231
  });
232
232
 
@@ -370,7 +370,7 @@ async function runInteractiveInit(options) {
370
370
  MODULES: modules.map(validateModuleName).join(' '),
371
371
  };
372
372
 
373
- if (!options.quiet) console.log('\n🚀 Lancement de la génération du projet...\n');
373
+ if (!options.quiet) console.log(chalk.cyan(' › Génération du projet…'));
374
374
  runBashScript(GENERATE_SCRIPT, [], env, options.quiet);
375
375
  return {
376
376
  projectName: env.APP_NAME,
@@ -419,11 +419,11 @@ async function runModuleGeneration(parsed) {
419
419
  if (parsed.options.dryRun) {
420
420
  const plan = await planModuleGeneration(process.cwd(), moduleName, orm, parsed.options.application);
421
421
  const output = dryRunOutput('module', plan);
422
- if (!parsed.options.json) console.log(JSON.stringify(output, null, 2));
422
+ if (!parsed.options.json) console.log(formatPlan(output, parsed.options.verbose));
423
423
  return output;
424
424
  }
425
425
 
426
- if (!parsed.options.quiet) console.log(chalk.cyan(`\n⚙️ Génération du module ${moduleName}...\n`));
426
+ if (!parsed.options.quiet) console.log(chalk.cyan(` › Génération du module ${moduleName}…`));
427
427
  if (process.env.NESTGEN_ROOT) {
428
428
  runBashScript(ADD_MODULE_SCRIPT, [moduleName, orm], {}, parsed.options.quiet);
429
429
  return { module: moduleName, orm, generated: true };
@@ -548,7 +548,7 @@ async function runResourceGeneration(parsed) {
548
548
  if (parsed.options.dryRun) {
549
549
  const plan = await planResourceGeneration(process.cwd(), resourceOptions);
550
550
  const output = { ...dryRunOutput('resource', plan), ...(migration ? { migration } : {}) };
551
- if (!parsed.options.json) console.log(JSON.stringify(output, null, 2));
551
+ if (!parsed.options.json) console.log(formatPlan(output, parsed.options.verbose));
552
552
  return output;
553
553
  }
554
554
  await generateResource(process.cwd(), resourceOptions);
@@ -564,14 +564,14 @@ async function runResourceGeneration(parsed) {
564
564
 
565
565
  function printUsage() {
566
566
  console.log(
567
- `Usage: nestgen <commande> [options]\n\nCommandes:\n init [nom] Génère un projet NestJS\n module <nom> [--orm <orm>] Génère un module\n resource <nom> --fields ... Génère un CRUD TypeORM ou Prisma
567
+ formatHelp(`Usage: nestgen <commande> [options]\n\nCommandes:\n init [nom] Génère un projet NestJS\n module <nom> [--orm <orm>] Génère un module\n resource <nom> --fields ... Génère un CRUD TypeORM ou Prisma
568
568
  config init|show Gère nestgen.config.json
569
569
  doctor Vérifie l'installation\n\nOptions:\n -h, --help Affiche cette aide\n -V, --version Affiche la version\n --no-interactive Refuse les prompts\n --quiet Supprime les sorties non essentielles\n --verbose Active les diagnostics\n --no-color Désactive les couleurs\n --profile <simple|advanced> Choisit le profil d'architecture
570
570
  --package-manager <pm> Définit npm, pnpm ou yarn
571
571
  --project-path <chemin> Définit le dossier parent du projet init
572
572
  --docker, --no-docker Active ou désactive Docker pour init
573
573
  --swagger, --no-swagger Active ou désactive Swagger pour init
574
- --operations, --no-operations Active ou désactive le socle d’exploitation pour init
574
+ --operations, --no-operations Active ou désactive le socle d’exploitation pour init
575
575
  --git, --no-git Active ou désactive Git pour init
576
576
  --modules <a,b> Définit les modules init, séparés par des virgules
577
577
  --application <nom> Cible une application d’un workspace Nest
@@ -579,18 +579,19 @@ function printUsage() {
579
579
  --migration-name <nom> Prépare une commande de migration officielle avec --update, sans l’appliquer
580
580
  --dry-run Affiche le plan sans écrire
581
581
  --json Retourne un résultat JSON versionné
582
- --indexes <a+b,c> Ajoute des index composites ou simples\n\nChamps resource : string, number, integer, decimal(precision;scale), enum(VALEUR|VALEUR), boolean, date, uuid.`,
582
+ --indexes <a+b,c> Ajoute des index composites ou simples\n\nChamps resource : string, number, integer, decimal(precision;scale), enum(VALEUR|VALEUR), boolean, date, uuid.`),
583
583
  );
584
584
  }
585
585
 
586
- function runDoctor(machine = false) {
586
+ function runDoctor(options = {}) {
587
587
  const diagnostics = [];
588
588
  const add = (id, severity, cause, action, detail) => diagnostics.push({ id, severity, cause, action, detail });
589
589
  const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number);
590
+ const supportedNode = (nodeMajor === 24 && nodeMinor >= 15) || nodeMajor === 25 || nodeMajor === 26;
590
591
  add(
591
592
  'NODE_VERSION',
592
- nodeMajor === 24 && nodeMinor >= 15 ? 'INFO' : 'ERROR',
593
- nodeMajor === 24 && nodeMinor >= 15 ? 'Version Node.js compatible.' : 'Node.js doit être >=24.15.0 et <27.',
593
+ supportedNode ? 'INFO' : 'ERROR',
594
+ supportedNode ? 'Version Node.js compatible.' : 'Node.js doit être >=24.15.0 et <27.',
594
595
  'Installe une version Node.js supportée puis relance doctor.',
595
596
  process.versions.node,
596
597
  );
@@ -722,9 +723,10 @@ function runDoctor(machine = false) {
722
723
  error.message,
723
724
  );
724
725
  }
725
- if (!machine)
726
- for (const diagnostic of diagnostics)
727
- console.log(`${diagnostic.severity} ${diagnostic.id}: ${diagnostic.cause} — ${diagnostic.action}`);
726
+ if (!options.json) {
727
+ const output = formatDoctorOutput(diagnostics, options);
728
+ if (output) console.log(output);
729
+ }
728
730
  return { diagnostics, project };
729
731
  }
730
732
 
@@ -785,7 +787,15 @@ export async function main(args = process.argv.slice(2)) {
785
787
  const action = parsed.positionals[0];
786
788
  if (action === 'show') {
787
789
  result = loadConfigIfPresent(process.cwd()) ?? defaultConfig;
788
- if (!machine) console.log(JSON.stringify(result, null, 2));
790
+ if (!machine)
791
+ console.log(
792
+ formatConfig(
793
+ result,
794
+ fs.existsSync(path.join(process.cwd(), configFileName))
795
+ ? configFileName
796
+ : 'Valeurs par défaut',
797
+ ),
798
+ );
789
799
  break;
790
800
  }
791
801
  if (action === 'init') {
@@ -799,25 +809,37 @@ export async function main(args = process.argv.slice(2)) {
799
809
  throw new Error('Package manager invalide.');
800
810
  if (parsed.options.dryRun) {
801
811
  result = { operation: 'config-init', file: configFileName, config };
802
- if (!machine) console.log(JSON.stringify(result, null, 2));
812
+ if (!parsed.options.quiet) console.log(formatConfig(config, 'Aperçu · aucun fichier écrit'));
803
813
  break;
804
814
  }
805
815
  writeConfig(process.cwd(), config);
806
816
  result = { file: configFileName, created: true, config };
807
- if (!machine) console.log(configFileName);
817
+ if (!parsed.options.quiet) console.log(success(`Configuration créée : ${configFileName}`));
808
818
  break;
809
819
  }
810
820
  throw new Error('config requiert init ou show.');
811
821
  }
812
822
 
813
823
  case 'doctor':
814
- result = runDoctor(machine);
824
+ result = runDoctor(parsed.options);
815
825
  break;
816
826
 
817
827
  default:
818
828
  throw new Error(`Commande inconnue : ${parsed.command}. Utilise --help.`);
819
829
  }
820
830
  if (machine) console.log(JSON.stringify(machineResult(parsed.command, result)));
831
+ else if (!parsed.options.quiet && result) {
832
+ if (parsed.command === 'module' && result.generated)
833
+ console.log(success(`Module ${result.module} généré (${result.orm}).`));
834
+ if (parsed.command === 'resource' && result.generated) {
835
+ console.log(
836
+ success(
837
+ `Ressource ${result.resource} ${result.updated ? 'mise à jour' : 'générée'} (${result.orm} · ${result.profile}).`,
838
+ ),
839
+ );
840
+ if (result.migration) console.log(formatMigration(result.migration));
841
+ }
842
+ }
821
843
  return result;
822
844
  }
823
845
 
@@ -826,7 +848,10 @@ if (process.argv[1] && fs.realpathSync(process.argv[1]) === __filename) {
826
848
  if (process.argv.includes('--json')) {
827
849
  const message = error instanceof Error ? error.message : String(error);
828
850
  console.error(JSON.stringify({ version: 1, ok: false, error: { code: errorCode(error), message } }));
829
- } else console.error(chalk.red(`❌ ${error.message}`));
851
+ } else {
852
+ if (process.argv.includes('--no-color')) chalk.level = 0;
853
+ console.error(formatError(error.message));
854
+ }
830
855
  process.exitCode = 1;
831
856
  });
832
857
  }
@@ -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
  }
@@ -1,16 +1,18 @@
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
  ORM=${1:?ORM requis}
6
8
  FEATURES_PATH="$(dirname "$0")"
7
9
 
8
10
  if [[ "$ORM" != "typeorm" && "$ORM" != "prisma" ]]; then
9
- echo "❌ ORM non supporté : $ORM" >&2
11
+ log_error "ORM non supporté : $ORM" >&2
10
12
  exit 1
11
13
  fi
12
14
 
13
15
  node "$FEATURES_PATH/operational_foundation.mjs" src "$ORM"
14
16
  node "$FEATURES_PATH/update_app_module.mjs" src/app.module.ts OperationsModule './operations/operations.module' "$ORM"
15
17
 
16
- echo "✅ Socle d’exploitation configuré : /health/live et /health/ready."
18
+ log_success "Socle d’exploitation configuré : /health/live et /health/ready."
@@ -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
  }
@@ -108,12 +108,12 @@ done
108
108
 
109
109
  # ────── Résumé final ──────
110
110
  echo ""
111
- log_success "✅ Projet NestJS \"$APP_NAME\" généré avec succès 🎉"
112
- echo "📁 Localisation : $FULL_PATH"
113
- echo "📦 Package manager : $PM"
114
- echo "🧠 ORM : $ORM"
115
- if [ "$WITH_DOCKER" = "y" ]; then echo "🐳 Docker activé"; fi
116
- if [ "$WITH_SWAGGER" = "y" ]; then echo "📚 Swagger activé"; fi
117
- if [ "$WITH_OPERATIONS" = "y" ]; then echo "🩺 Socle d’exploitation activé"; fi
118
- if [ "$WITH_GIT" = "y" ]; then echo "🔃 Git initialisé"; fi
119
- 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,6 +1,6 @@
1
1
  {
2
2
  "name": "nestgen-cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "NestGen CLI — Générateur modulaire NestJS (DDD, CQRS, Hexagonal)",
5
5
  "files": [
6
6
  "dist",
package/readme.md CHANGED
@@ -6,18 +6,18 @@
6
6
 
7
7
  NestGen CLI generates NestJS project scaffolding and TypeORM or Prisma resources. The canonical package is [`nestgen-cli`](https://www.npmjs.com/package/nestgen-cli); the canonical repository is [`el-hack/nestgen-cli`](https://github.com/el-hack/nestgen-cli).
8
8
 
9
- ## Published in 0.2.1 / Disponible dans 0.2.1
9
+ ## Published in 0.2.2 / Disponible dans 0.2.2
10
10
 
11
11
  | Available now / Disponible maintenant | Evidence / Preuve |
12
12
  | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
13
- | Nest project, module and TypeORM or Prisma resource generation | The commands are shipped by `nestgen-cli@0.2.1` and covered by the CLI contract tests. |
13
+ | Nest project, module and TypeORM or Prisma resource generation | The commands are shipped by `nestgen-cli@0.2.2` and covered by the CLI contract tests. |
14
14
  | Declarative resource relations, safe updates and migration plans | The generated manifest protects manual edits; migrations remain explicitly reviewed and applied by the developer. |
15
15
  | Operational foundation and authorization metadata | Both are optional extension points; NestGen does not configure an identity provider, a global guard or an external monitoring service. |
16
16
  | Store API example | The package ships the [version-pinned example](examples/store-api/README.md), which CI reconstructs from its resource definitions. |
17
17
 
18
18
  ## Planned (not released) / Prévu (non publié)
19
19
 
20
- The [roadmap](roadmap.md) contains topics under consideration after 0.2.1. They have no release date and are not part of the npm contract until they appear in a versioned guide and `CHANGELOG.md`. La roadmap recense des pistes après 0.2.1 ; elles n’ont ni date de sortie ni engagement de compatibilité avant leur publication dans un guide versionné et le changelog.
20
+ The [roadmap](roadmap.md) contains topics under consideration after 0.2.2. They have no release date and are not part of the npm contract until they appear in a versioned guide and `CHANGELOG.md`. La roadmap recense des pistes après 0.2.2 ; elles n’ont ni date de sortie ni engagement de compatibilité avant leur publication dans un guide versionné et le changelog.
21
21
 
22
22
  ## Supported contract
23
23
 
@@ -52,11 +52,11 @@ For Prisma, run `npx prisma generate` and apply the schema with `npx prisma db p
52
52
  Install the announced release and verify the exact binary before generating anything. Installez la version annoncée et vérifiez le binaire exact avant toute génération.
53
53
 
54
54
  ```bash
55
- npm install --global nestgen-cli@0.2.1
56
- nestgen --version # 0.2.1
55
+ npm install --global nestgen-cli@0.2.2
56
+ nestgen --version # 0.2.2
57
57
  ```
58
58
 
59
- Then follow the [Store API example](examples/store-api/README.md): it generates customers, products, orders and order items; starts PostgreSQL; creates an explicit migration; and runs generated tests. The versioned [guide](docs/versions/0.2.1.md) documents the commands and constraints used by the demo. Ensuite, suivez l’exemple Store API : il couvre la génération, PostgreSQL, une migration explicite, les tests générés et la documentation de la version.
59
+ Then follow the [Store API example](examples/store-api/README.md): it generates customers, products, orders and order items; starts PostgreSQL; creates an explicit migration; and runs generated tests. The versioned [guide](docs/versions/0.2.2.md) documents the commands and constraints used by the demo. Ensuite, suivez l’exemple Store API : il couvre la génération, PostgreSQL, une migration explicite, les tests générés et la documentation de la version.
60
60
 
61
61
  The CI verifies the public commands against the announced package version, reconstructs the example and installs the npm tarball in a clean consumer. It validates reproducibility of these flows, not a production certification / pas une certification de production.
62
62
 
@@ -65,7 +65,7 @@ The CI verifies the public commands against the announced package version, recon
65
65
  The following flow is the supported end-to-end TypeORM path. Prerequisites: Node.js `>=24.15.0 <27`, npm 11 and Docker with Compose. It uses Docker for PostgreSQL and creates an explicit migration.
66
66
 
67
67
  ```bash
68
- npm install --global nestgen-cli@0.2.1
68
+ npm install --global nestgen-cli@0.2.2
69
69
  nestgen --version
70
70
  nestgen init
71
71
  ```