novatec-cli 3.0.5 → 3.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/bin/index.js CHANGED
@@ -26,8 +26,9 @@ async function checkForUpdates() {
26
26
  async function showBanner() {
27
27
  console.clear();
28
28
  const logo = figlet.textSync('NOVATEC', { font: 'Slant' });
29
+ const grad = (await import('gradient-string')).default;
29
30
  console.log();
30
- logo.split('\n').filter(l => l.trim()).forEach(l => console.log(' ' + chalk.bold.white(l)));
31
+ logo.split('\n').filter(l => l.trim()).forEach(l => console.log(' ' + grad.cristal.multiline(l)));
31
32
  console.log();
32
33
  const now = new Date().toLocaleDateString('es-MX', { day: '2-digit', month: 'short', year: 'numeric' });
33
34
  console.log(' ' + chalk.gray(`v${pkg.version} · Enterprise Fullstack CLI · ${now}`));
@@ -36,10 +37,40 @@ async function showBanner() {
36
37
  console.log();
37
38
  console.log(boxen(
38
39
  chalk.bold.white(' STACK DISPONIBLE\n\n') +
39
- chalk.white.bold(' Frontend ') + chalk.gray(' │ ') + ['React','Next.js','Vue','Nuxt','Astro','Svelte','SolidJS','Angular','Remix','Qwik'].join(chalk.gray(' · ')) + '\n\n' +
40
- chalk.white.bold(' Backend ') + chalk.gray(' │ ') + ['Express','NestJS','Fastify','Hono','FastAPI','Django','Flask','Spring','Deno','Go/Gin'].join(chalk.gray(' · ')) + '\n\n' +
41
- chalk.white.bold(' Database ') + chalk.gray(' ') + ['PostgreSQL','MySQL','MongoDB','SQLite','Supabase'].join(chalk.gray(' · ')) + '\n\n' +
42
- chalk.white.bold(' Modos ') + chalk.gray('') + chalk.white('--mode business') + chalk.gray(' (landing + auth + dashboard + CRUD)') + '\n\n' +
40
+ chalk.white.bold(' Frontend ') + chalk.gray(' │ ') +
41
+ [
42
+ grad('#61DAFB','#21D4FD')(' React'),
43
+ grad('#fff','#888')(' Next.js'),
44
+ grad('#42b883','#35495e')(' Vue'),
45
+ grad('#00DC82','#003543')(' Nuxt'),
46
+ grad('#FF5D01','#BC52EE')(' Astro'),
47
+ grad('#FF3E00','#FF8C00')(' Svelte'),
48
+ grad('#2C4F7C','#446B9E')(' Solid'),
49
+ grad('#DD0031','#C3002F')(' Angular'),
50
+ grad('#121212','#818CF8')(' Remix'),
51
+ grad('#18B6F6','#AC7EF4')(' Qwik'),
52
+ ].join(chalk.gray(' · ')) + '\n\n' +
53
+ chalk.white.bold(' Backend ') + chalk.gray(' │ ') +
54
+ [
55
+ grad('#fff','#aaa')(' Express'),
56
+ grad('#E0234E','#FF6B6B')(' NestJS'),
57
+ grad('#000','#888')(' Fastify'),
58
+ grad('#FF5733','#FF8C00')(' Hono'),
59
+ grad('#009688','#26A69A')(' FastAPI'),
60
+ grad('#092E20','#44B78B')(' Django'),
61
+ grad('#fff','#aaa')(' Flask'),
62
+ grad('#6DB33F','#77BC1F')(' Spring'),
63
+ grad('#fff','#aaa')(' Deno'),
64
+ grad('#00ACD7','#5DC9E2')(' Go/Gin'),
65
+ ].join(chalk.gray(' · ')) + '\n\n' +
66
+ chalk.white.bold(' Database ') + chalk.gray(' │ ') +
67
+ [
68
+ grad('#336791','#4A90D9')(' PostgreSQL'),
69
+ grad('#4479A1','#00758F')(' MySQL'),
70
+ grad('#47A248','#00ED64')(' MongoDB'),
71
+ grad('#003B57','#0F80CC')(' SQLite'),
72
+ grad('#3ECF8E','#1C1C1C')(' Supabase'),
73
+ ].join(chalk.gray(' · ')) + '\n\n' +
43
74
  chalk.white.bold(' Comandos ') + chalk.gray(' │ ') +
44
75
  [chalk.white('create'), chalk.white('add'), chalk.white('doctor'), chalk.white('build'), chalk.white('deploy'), chalk.white('list'), chalk.white('update')]
45
76
  .join(chalk.gray(' · ')),
@@ -48,7 +79,7 @@ async function showBanner() {
48
79
  console.log();
49
80
  console.log(boxen(
50
81
  chalk.gray(' $ novatec create my-app\n') +
51
- chalk.gray(' $ novatec create my-app --mode business --frontend next --backend express\n') +
82
+ chalk.gray(' $ novatec create my-app --frontend next --backend express --db postgres\n') +
52
83
  chalk.gray(' $ novatec add crud Product\n') +
53
84
  chalk.gray(' $ novatec doctor'),
54
85
  { padding: { top: 0, bottom: 0, left: 1, right: 4 }, margin: { left: 2 }, borderStyle: 'single', borderColor: 'gray', dimBorder: true }
package/lib/create.js CHANGED
@@ -118,18 +118,65 @@ export async function runCreate(nameArg, options) {
118
118
 
119
119
  // ── ARQUITECTURA ──────────────────────────────────────────────────────────────
120
120
  async function applyArchitecture(config, root) {
121
- const beDir = path.join(root, 'backend', 'src');
122
121
  const arch = config.architecture || 'mvc';
123
122
 
124
- const dirs = {
123
+ // Backend
124
+ const beDirs = {
125
125
  mvc: ['controllers', 'models', 'routes', 'middlewares', 'services'],
126
126
  hexagonal: ['domain/entities', 'domain/ports', 'application/usecases', 'infrastructure/adapters', 'infrastructure/repositories', 'interfaces/http'],
127
127
  clean: ['domain/entities', 'domain/usecases', 'data/repositories', 'data/datasources', 'presentation/controllers', 'presentation/routes'],
128
128
  };
129
+ const beBase = path.join(root, 'backend', 'src');
130
+ for (const dir of (beDirs[arch] || beDirs.mvc)) {
131
+ await fse.ensureDir(path.join(beBase, dir));
132
+ await fse.writeFile(path.join(beBase, dir, '.gitkeep'), '');
133
+ }
134
+
135
+ // Frontend — estructura de src según arquitectura
136
+ const feDirs = {
137
+ mvc: [
138
+ 'components',
139
+ 'pages',
140
+ 'services',
141
+ 'hooks',
142
+ 'utils',
143
+ 'assets',
144
+ ],
145
+ hexagonal: [
146
+ 'domain/entities',
147
+ 'domain/ports',
148
+ 'application/usecases',
149
+ 'infrastructure/api',
150
+ 'infrastructure/storage',
151
+ 'presentation/components',
152
+ 'presentation/pages',
153
+ 'presentation/hooks',
154
+ ],
155
+ clean: [
156
+ 'domain/entities',
157
+ 'domain/usecases',
158
+ 'data/repositories',
159
+ 'data/datasources',
160
+ 'presentation/components',
161
+ 'presentation/pages',
162
+ 'presentation/hooks',
163
+ 'core/utils',
164
+ ],
165
+ };
129
166
 
130
- for (const dir of (dirs[arch] || dirs.mvc)) {
131
- await fse.ensureDir(path.join(beDir, dir));
132
- await fse.writeFile(path.join(beDir, dir, '.gitkeep'), '');
167
+ const feBase = path.join(root, 'frontend', 'src');
168
+ // Solo crear si existe el directorio src (algunos frameworks lo generan, otros no)
169
+ if (await fse.pathExists(feBase)) {
170
+ for (const dir of (feDirs[arch] || feDirs.mvc)) {
171
+ await fse.ensureDir(path.join(feBase, dir));
172
+ await fse.writeFile(path.join(feBase, dir, '.gitkeep'), '');
173
+ }
174
+ } else {
175
+ // Si no existe src aún (ej: nuxt, astro), crearlo igual
176
+ for (const dir of (feDirs[arch] || feDirs.mvc)) {
177
+ await fse.ensureDir(path.join(feBase, dir));
178
+ await fse.writeFile(path.join(feBase, dir, '.gitkeep'), '');
179
+ }
133
180
  }
134
181
  }
135
182
 
@@ -1,10 +1,10 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
3
  import fse from 'fs-extra';
4
+ import gradient from 'gradient-string';
4
5
  import { execSync } from 'child_process';
5
6
  import { isInstalled } from '../utils.js';
6
7
 
7
- // DB packages por backend type
8
8
  const NODE_DB_DEPS = {
9
9
  postgres: ['pg', 'pg-hstore'],
10
10
  mysql: ['mysql2'],
@@ -14,13 +14,17 @@ const NODE_DB_DEPS = {
14
14
  none: [],
15
15
  };
16
16
 
17
+ const COLORS = {
18
+ express: ['#ffffff', '#aaaaaa'], nestjs: ['#E0234E', '#FF6B6B'],
19
+ fastify: ['#000000', '#888888'], hono: ['#FF5733', '#FF8C00'],
20
+ fastapi: ['#009688', '#26A69A'], django: ['#092E20', '#44B78B'],
21
+ flask: ['#ffffff', '#aaaaaa'], spring: ['#6DB33F', '#77BC1F'],
22
+ deno: ['#ffffff', '#aaaaaa'], gin: ['#00ACD7', '#5DC9E2'],
23
+ };
24
+
17
25
  function installNodeDeps(prod, dev, cwd) {
18
- if (prod.length) {
19
- execSync(`npm install ${prod.join(' ')}`, { cwd, stdio: 'pipe' });
20
- }
21
- if (dev && dev.length) {
22
- execSync(`npm install -D ${dev.join(' ')}`, { cwd, stdio: 'pipe' });
23
- }
26
+ if (prod.length) execSync(`npm install ${prod.join(' ')}`, { cwd, stdio: 'pipe' });
27
+ if (dev && dev.length) execSync(`npm install -D ${dev.join(' ')}`, { cwd, stdio: 'pipe' });
24
28
  }
25
29
 
26
30
  function backendPath(projectPath) {
@@ -28,7 +32,9 @@ function backendPath(projectPath) {
28
32
  }
29
33
 
30
34
  export async function generateBackend(config, projectPath) {
31
- console.log(chalk.blue(`\n ⚙ Generando backend (${chalk.bold(config.backend)})...`));
35
+ const [c1, c2] = COLORS[config.backend] || ['#ffffff', '#aaaaaa'];
36
+ const label = gradient(c1, c2)(` ◆ ${config.backend.toUpperCase()} `);
37
+ console.log(`\n ${label} ${chalk.gray('Generando backend...')}`);
32
38
  const handlers = { express, nestjs, fastify, hono, fastapi, django, flask, spring, deno, gin };
33
39
  const fn = handlers[config.backend];
34
40
  if (!fn) throw new Error(`Backend no soportado: ${config.backend}`);
@@ -1,9 +1,9 @@
1
1
  import path from 'path';
2
2
  import chalk from 'chalk';
3
3
  import fse from 'fs-extra';
4
+ import gradient from 'gradient-string';
4
5
  import { execSync } from 'child_process';
5
6
 
6
- // Comandos de scaffolding por framework
7
7
  const SCAFFOLD = {
8
8
  react: 'npm create vite@latest frontend -- --template react',
9
9
  vue: 'npm create vite@latest frontend -- --template vue',
@@ -17,11 +17,26 @@ const SCAFFOLD = {
17
17
  qwik: 'npm create qwik@latest frontend -- --no-install',
18
18
  };
19
19
 
20
+ const COLORS = {
21
+ react: ['#61DAFB', '#21D4FD'],
22
+ next: ['#ffffff', '#888888'],
23
+ vue: ['#42b883', '#35495e'],
24
+ nuxt: ['#00DC82', '#003543'],
25
+ astro: ['#FF5D01', '#BC52EE'],
26
+ svelte: ['#FF3E00', '#FF8C00'],
27
+ solid: ['#2C4F7C', '#446B9E'],
28
+ angular: ['#DD0031', '#C3002F'],
29
+ remix: ['#818CF8', '#6366F1'],
30
+ qwik: ['#18B6F6', '#AC7EF4'],
31
+ };
32
+
20
33
  export async function generateFrontend(config, projectPath) {
21
34
  const cmd = SCAFFOLD[config.frontend];
22
35
  if (!cmd) throw new Error(`Frontend no soportado: ${config.frontend}`);
23
36
 
24
- console.log(chalk.blue(`\n ⚛ Generando frontend (${chalk.bold(config.frontend)})...`));
37
+ const [c1, c2] = COLORS[config.frontend] || ['#ffffff', '#aaaaaa'];
38
+ const label = gradient(c1, c2)(` ◆ ${config.frontend.toUpperCase()} `);
39
+ console.log(chalk.blue(`\n ${label} ${chalk.gray('Generando frontend...')}`));
25
40
 
26
41
  try {
27
42
  execSync(cmd, { cwd: projectPath, stdio: 'pipe' });
package/lib/prompts.js CHANGED
@@ -1,54 +1,88 @@
1
1
  import inquirer from 'inquirer';
2
2
  import chalk from 'chalk';
3
+ import gradient from 'gradient-string';
3
4
  import fse from 'fs-extra';
4
5
  import path from 'path';
5
6
  import boxen from 'boxen';
6
7
 
8
+ // Colores reales de cada framework/DB
9
+ const g = {
10
+ react: gradient('#61DAFB', '#21D4FD'),
11
+ next: gradient('#ffffff', '#888888'),
12
+ vue: gradient('#42b883', '#35495e'),
13
+ nuxt: gradient('#00DC82', '#003543'),
14
+ astro: gradient('#FF5D01', '#BC52EE'),
15
+ svelte: gradient('#FF3E00', '#FF8C00'),
16
+ solid: gradient('#2C4F7C', '#446B9E'),
17
+ angular: gradient('#DD0031', '#C3002F'),
18
+ remix: gradient('#121212', '#818CF8'),
19
+ qwik: gradient('#18B6F6', '#AC7EF4'),
20
+ express: gradient('#ffffff', '#aaaaaa'),
21
+ nestjs: gradient('#E0234E', '#FF6B6B'),
22
+ fastify: gradient('#000000', '#888888'),
23
+ hono: gradient('#FF5733', '#FF8C00'),
24
+ fastapi: gradient('#009688', '#26A69A'),
25
+ django: gradient('#092E20', '#44B78B'),
26
+ flask: gradient('#ffffff', '#aaaaaa'),
27
+ spring: gradient('#6DB33F', '#77BC1F'),
28
+ deno: gradient('#ffffff', '#aaaaaa'),
29
+ gin: gradient('#00ACD7', '#5DC9E2'),
30
+ postgres: gradient('#336791', '#4A90D9'),
31
+ mysql: gradient('#4479A1', '#00758F'),
32
+ mongo: gradient('#47A248', '#00ED64'),
33
+ sqlite: gradient('#003B57', '#0F80CC'),
34
+ supabase: gradient('#3ECF8E', '#1C1C1C'),
35
+ };
36
+
37
+ function logo(key, text) {
38
+ return g[key] ? g[key](text) : chalk.white(text);
39
+ }
40
+
7
41
  export const FRONTEND_CHOICES = [
8
- { name: 'React — Vite + React 18', value: 'react' },
9
- { name: 'Next.js — SSR/SSG App Router', value: 'next' },
10
- { name: 'Vue 3 — Vite + Composition API', value: 'vue' },
11
- { name: 'Nuxt 3 — Vue SSR/SSG', value: 'nuxt' },
12
- { name: 'Astro — MPA ultra-rápido', value: 'astro' },
13
- { name: 'SvelteKit — Svelte SSR/SSG', value: 'svelte' },
14
- { name: 'SolidJS — Sin Virtual DOM', value: 'solid' },
15
- { name: 'Angular — Framework empresarial', value: 'angular' },
16
- { name: 'Remix — Full-stack React', value: 'remix' },
17
- { name: 'Qwik — Resumability', value: 'qwik' },
42
+ { name: logo('react', ' ◆ React ') + chalk.gray(' — Vite + React 18'), value: 'react' },
43
+ { name: logo('next', ' ▲ Next.js ') + chalk.gray(' — SSR/SSG App Router'), value: 'next' },
44
+ { name: logo('vue', ' ◈ Vue 3 ') + chalk.gray(' — Vite + Composition API'), value: 'vue' },
45
+ { name: logo('nuxt', ' ◈ Nuxt 3 ') + chalk.gray(' — Vue SSR/SSG'), value: 'nuxt' },
46
+ { name: logo('astro', ' ✦ Astro ') + chalk.gray(' — MPA ultra-rápido'), value: 'astro' },
47
+ { name: logo('svelte', ' ◆ SvelteKit ') + chalk.gray(' — Svelte SSR/SSG'), value: 'svelte' },
48
+ { name: logo('solid', ' ◆ SolidJS ') + chalk.gray(' — Sin Virtual DOM'), value: 'solid' },
49
+ { name: logo('angular', ' ◆ Angular ') + chalk.gray(' — Framework empresarial'), value: 'angular' },
50
+ { name: logo('remix', ' ◆ Remix ') + chalk.gray(' — Full-stack React'), value: 'remix' },
51
+ { name: logo('qwik', ' ⚡ Qwik ') + chalk.gray(' — Resumability'), value: 'qwik' },
18
52
  ];
19
53
 
20
54
  export const BACKEND_CHOICES = [
21
- { name: 'Express — Minimalista Node.js', value: 'express' },
22
- { name: 'NestJS — Modular TypeScript', value: 'nestjs' },
23
- { name: 'Fastify — Ultra-rápido Node', value: 'fastify' },
24
- { name: 'Hono — Edge-ready ligero', value: 'hono' },
25
- { name: 'FastAPI — Async Python', value: 'fastapi' },
26
- { name: 'Django — Baterías incluidas', value: 'django' },
27
- { name: 'Flask — Micro-framework Python', value: 'flask' },
28
- { name: 'Spring Boot — Empresarial Java', value: 'spring' },
29
- { name: 'Deno/Oak — TypeScript seguro', value: 'deno' },
30
- { name: 'Go/Gin — Rendimiento extremo', value: 'gin' },
55
+ { name: logo('express', ' ◆ Express ') + chalk.gray(' — Minimalista Node.js'), value: 'express' },
56
+ { name: logo('nestjs', ' ◆ NestJS ') + chalk.gray(' — Modular TypeScript'), value: 'nestjs' },
57
+ { name: logo('fastify', ' ⚡ Fastify ') + chalk.gray(' — Ultra-rápido Node'), value: 'fastify' },
58
+ { name: logo('hono', ' ◆ Hono ') + chalk.gray(' — Edge-ready ligero'), value: 'hono' },
59
+ { name: logo('fastapi', ' ◆ FastAPI ') + chalk.gray(' — Async Python'), value: 'fastapi' },
60
+ { name: logo('django', ' ◆ Django ') + chalk.gray(' — Baterías incluidas'), value: 'django' },
61
+ { name: logo('flask', ' ◆ Flask ') + chalk.gray(' — Micro-framework Python'), value: 'flask' },
62
+ { name: logo('spring', ' ◆ Spring Boot') + chalk.gray(' — Empresarial Java'), value: 'spring' },
63
+ { name: logo('deno', ' ◆ Deno/Oak ') + chalk.gray(' — TypeScript seguro'), value: 'deno' },
64
+ { name: logo('gin', ' ◆ Go/Gin ') + chalk.gray(' — Rendimiento extremo'), value: 'gin' },
31
65
  ];
32
66
 
33
67
  export const DB_CHOICES = [
34
- { name: 'PostgreSQL — Relacional robusto', value: 'postgres' },
35
- { name: 'MySQL — Relacional popular', value: 'mysql' },
36
- { name: 'MongoDB — NoSQL documentos', value: 'mongo' },
37
- { name: 'SQLite — Embebido ligero', value: 'sqlite' },
38
- { name: 'Supabase — BaaS con Auth incluida', value: 'supabase' },
39
- { name: 'Ninguna', value: 'none' },
68
+ { name: logo('postgres', ' ◆ PostgreSQL ') + chalk.gray(' — Relacional robusto'), value: 'postgres' },
69
+ { name: logo('mysql', ' ◆ MySQL ') + chalk.gray(' — Relacional popular'), value: 'mysql' },
70
+ { name: logo('mongo', ' ◆ MongoDB ') + chalk.gray(' — NoSQL documentos'), value: 'mongo' },
71
+ { name: logo('sqlite', ' ◆ SQLite ') + chalk.gray(' — Embebido ligero'), value: 'sqlite' },
72
+ { name: logo('supabase', ' ◆ Supabase ') + chalk.gray(' — BaaS con Auth incluida'), value: 'supabase' },
73
+ { name: chalk.gray('Ninguna'), value: 'none' },
40
74
  ];
41
75
 
42
76
  export const ARCH_CHOICES = [
43
- { name: 'MVC — Model-View-Controller (clásico)', value: 'mvc' },
44
- { name: 'Hexagonal — Ports & Adapters', value: 'hexagonal' },
45
- { name: 'Clean — Clean Architecture (capas)', value: 'clean' },
77
+ { name: chalk.white('MVC ') + chalk.gray(' — Model-View-Controller (clásico)'), value: 'mvc' },
78
+ { name: chalk.white('Hexagonal ') + chalk.gray(' — Ports & Adapters'), value: 'hexagonal' },
79
+ { name: chalk.white('Clean ') + chalk.gray(' — Clean Architecture (capas)'), value: 'clean' },
46
80
  ];
47
81
 
48
82
  export const PM_CHOICES = [
49
- { name: 'npm', value: 'npm' },
50
- { name: 'pnpm', value: 'pnpm' },
51
- { name: 'yarn', value: 'yarn' },
83
+ { name: chalk.white('npm'), value: 'npm' },
84
+ { name: chalk.white('pnpm'), value: 'pnpm' },
85
+ { name: chalk.white('yarn'), value: 'yarn' },
52
86
  ];
53
87
 
54
88
  export async function askProjectOptions(nameArg, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "novatec-cli",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "🚀 NOVATEC FULLSTACK CLI — Generador profesional de proyectos full stack | React, Next.js, Vue, Express, NestJS, FastAPI y más",
5
5
  "type": "module",
6
6
  "main": "./lib/create.js",