novatec-cli 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +109 -169
- package/lib/add.js +122 -0
- package/lib/create.js +220 -310
- package/lib/doctor.js +167 -0
- package/lib/generators/business.js +262 -0
- package/lib/generators/deploy.js +166 -0
- package/lib/generators/docker.js +97 -0
- package/lib/generators/security.js +132 -0
- package/lib/prompts.js +159 -188
- package/package.json +1 -1
package/lib/prompts.js
CHANGED
|
@@ -4,272 +4,243 @@ import fse from 'fs-extra';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import boxen from 'boxen';
|
|
6
6
|
|
|
7
|
-
// ── PRESETS ───────────────────────────────────────────────────────────────────
|
|
8
7
|
export const PRESETS = {
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
'
|
|
8
|
+
'ecommerce': { frontend: 'next', backend: 'express', db: 'postgres', typescript: true, mode: 'business', architecture: 'mvc' },
|
|
9
|
+
'reservas': { frontend: 'next', backend: 'express', db: 'postgres', typescript: true, mode: 'business', architecture: 'mvc' },
|
|
10
|
+
'aula-virtual': { frontend: 'react', backend: 'nestjs', db: 'postgres', typescript: true, mode: 'business', architecture: 'clean' },
|
|
11
|
+
'empresa-web': { frontend: 'next', backend: 'express', db: 'postgres', typescript: false, mode: 'business', architecture: 'mvc' },
|
|
12
|
+
'react-express': { frontend: 'react', backend: 'express', db: 'postgres', typescript: false, mode: null, architecture: 'mvc' },
|
|
13
|
+
'next-nestjs': { frontend: 'next', backend: 'nestjs', db: 'postgres', typescript: true, mode: null, architecture: 'clean' },
|
|
14
|
+
'vue-fastapi': { frontend: 'vue', backend: 'fastapi', db: 'postgres', typescript: false, mode: null, architecture: 'mvc' },
|
|
15
|
+
'astro-hono': { frontend: 'astro', backend: 'hono', db: 'sqlite', typescript: true, mode: null, architecture: 'mvc' },
|
|
14
16
|
};
|
|
15
17
|
|
|
16
|
-
// ── FRONTEND FRAMEWORKS ───────────────────────────────────────────────────────
|
|
17
18
|
export const FRONTEND_CHOICES = [
|
|
18
|
-
{ name: 'React — Vite + React 18',
|
|
19
|
-
{ name: 'Next.js — SSR/SSG App Router',
|
|
20
|
-
{ name: 'Vue 3 — Vite + Composition API',
|
|
21
|
-
{ name: 'Nuxt 3 — Vue
|
|
22
|
-
{ name: 'Astro — MPA ultra-rápido',
|
|
23
|
-
{ name: 'SvelteKit — Svelte
|
|
24
|
-
{ name: 'SolidJS — Sin Virtual DOM',
|
|
25
|
-
{ name: 'Angular — Framework empresarial',
|
|
26
|
-
{ name: 'Remix — Full-stack React',
|
|
27
|
-
{ name: 'Qwik — Resumability',
|
|
19
|
+
{ name: 'React — Vite + React 18', value: 'react' },
|
|
20
|
+
{ name: 'Next.js — SSR/SSG App Router', value: 'next' },
|
|
21
|
+
{ name: 'Vue 3 — Vite + Composition API', value: 'vue' },
|
|
22
|
+
{ name: 'Nuxt 3 — Vue SSR/SSG', value: 'nuxt' },
|
|
23
|
+
{ name: 'Astro — MPA ultra-rápido', value: 'astro' },
|
|
24
|
+
{ name: 'SvelteKit — Svelte SSR/SSG', value: 'svelte' },
|
|
25
|
+
{ name: 'SolidJS — Sin Virtual DOM', value: 'solid' },
|
|
26
|
+
{ name: 'Angular — Framework empresarial', value: 'angular' },
|
|
27
|
+
{ name: 'Remix — Full-stack React', value: 'remix' },
|
|
28
|
+
{ name: 'Qwik — Resumability', value: 'qwik' },
|
|
28
29
|
];
|
|
29
30
|
|
|
30
|
-
// ── BACKEND FRAMEWORKS ────────────────────────────────────────────────────────
|
|
31
31
|
export const BACKEND_CHOICES = [
|
|
32
|
-
{ name: 'Express — Minimalista
|
|
33
|
-
{ name: 'NestJS — Modular TypeScript',
|
|
34
|
-
{ name: 'Fastify —
|
|
35
|
-
{ name: 'Hono — Edge-ready ligero',
|
|
36
|
-
{ name: 'FastAPI — Async Python',
|
|
37
|
-
{ name: 'Django — Baterías incluidas',
|
|
38
|
-
{ name: 'Flask — Micro-framework Python',
|
|
39
|
-
{ name: 'Spring Boot — Empresarial Java',
|
|
40
|
-
{ name: 'Deno/Oak — TypeScript seguro',
|
|
41
|
-
{ name: 'Go/Gin — Rendimiento extremo',
|
|
32
|
+
{ name: 'Express — Minimalista Node.js', value: 'express' },
|
|
33
|
+
{ name: 'NestJS — Modular TypeScript', value: 'nestjs' },
|
|
34
|
+
{ name: 'Fastify — Ultra-rápido Node', value: 'fastify' },
|
|
35
|
+
{ name: 'Hono — Edge-ready ligero', value: 'hono' },
|
|
36
|
+
{ name: 'FastAPI — Async Python', value: 'fastapi' },
|
|
37
|
+
{ name: 'Django — Baterías incluidas', value: 'django' },
|
|
38
|
+
{ name: 'Flask — Micro-framework Python', value: 'flask' },
|
|
39
|
+
{ name: 'Spring Boot — Empresarial Java', value: 'spring' },
|
|
40
|
+
{ name: 'Deno/Oak — TypeScript seguro', value: 'deno' },
|
|
41
|
+
{ name: 'Go/Gin — Rendimiento extremo', value: 'gin' },
|
|
42
42
|
];
|
|
43
43
|
|
|
44
|
-
// ── BASES DE DATOS ────────────────────────────────────────────────────────────
|
|
45
44
|
export const DB_CHOICES = [
|
|
46
|
-
{ name: 'PostgreSQL — Relacional robusto',
|
|
47
|
-
{ name: 'MySQL — Relacional popular',
|
|
48
|
-
{ name: 'MongoDB — NoSQL documentos',
|
|
49
|
-
{ name: 'SQLite — Embebido ligero',
|
|
50
|
-
{ name: '
|
|
45
|
+
{ name: 'PostgreSQL — Relacional robusto', value: 'postgres' },
|
|
46
|
+
{ name: 'MySQL — Relacional popular', value: 'mysql' },
|
|
47
|
+
{ name: 'MongoDB — NoSQL documentos', value: 'mongo' },
|
|
48
|
+
{ name: 'SQLite — Embebido ligero', value: 'sqlite' },
|
|
49
|
+
{ name: 'Supabase — BaaS con Auth incluida', value: 'supabase' },
|
|
50
|
+
{ name: 'Ninguna', value: 'none' },
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
nuxt: ['ofetch','@pinia/nuxt','@nuxtjs/tailwindcss','@nuxt/image','nuxt-auth-utils','zod','@vueuse/nuxt'],
|
|
59
|
-
astro: ['@astrojs/tailwind','@astrojs/react','@astrojs/mdx','nanostores'],
|
|
60
|
-
svelte: ['axios','tailwindcss','zod','@tanstack/svelte-query','@skeletonlabs/skeleton'],
|
|
61
|
-
solid: ['axios','@solidjs/router','tailwindcss','@tanstack/solid-query','zod'],
|
|
62
|
-
angular: ['axios','@angular/material','tailwindcss','@ngrx/store','zod','date-fns'],
|
|
63
|
-
remix: ['tailwindcss','zod','prisma','react-hook-form','date-fns'],
|
|
64
|
-
qwik: ['tailwindcss','zod','axios'],
|
|
65
|
-
};
|
|
53
|
+
export const ARCH_CHOICES = [
|
|
54
|
+
{ name: 'MVC — Model-View-Controller (clásico)', value: 'mvc' },
|
|
55
|
+
{ name: 'Hexagonal — Ports & Adapters', value: 'hexagonal' },
|
|
56
|
+
{ name: 'Clean — Clean Architecture (capas)', value: 'clean' },
|
|
57
|
+
];
|
|
66
58
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
hono: ['zod','drizzle-orm','prisma','dotenv'],
|
|
73
|
-
fastapi: ['sqlalchemy','alembic','python-jose[cryptography]','passlib[bcrypt]','python-dotenv','httpx','celery','redis','pydantic-settings'],
|
|
74
|
-
django: ['djangorestframework','django-cors-headers','djangorestframework-simplejwt','celery','redis','Pillow','python-dotenv','psycopg2-binary','drf-spectacular'],
|
|
75
|
-
flask: ['flask-restful','flask-sqlalchemy','flask-jwt-extended','flask-cors','flask-migrate','marshmallow','python-dotenv','celery'],
|
|
76
|
-
spring: ['spring-data-jpa','spring-security','spring-web','lombok','postgresql','h2'],
|
|
77
|
-
deno: ['cors','djwt','zod'],
|
|
78
|
-
gin: ['gorm','jwt-go','godotenv','validator'],
|
|
79
|
-
};
|
|
59
|
+
export const PM_CHOICES = [
|
|
60
|
+
{ name: 'npm', value: 'npm' },
|
|
61
|
+
{ name: 'pnpm', value: 'pnpm' },
|
|
62
|
+
{ name: 'yarn', value: 'yarn' },
|
|
63
|
+
];
|
|
80
64
|
|
|
81
|
-
// ── EXTRAS ────────────────────────────────────────────────────────────────────
|
|
82
65
|
export const EXTRAS_CHOICES = [
|
|
83
|
-
{ name: 'Git init automático',
|
|
84
|
-
{ name: 'ESLint + Prettier',
|
|
85
|
-
{ name: 'Husky + lint-staged
|
|
86
|
-
{ name: 'Vitest
|
|
87
|
-
{ name: 'Docker
|
|
88
|
-
{ name: 'Archivos .env.example',
|
|
89
|
-
{ name: '.editorconfig',
|
|
90
|
-
{ name: 'GitHub Actions CI/CD',
|
|
91
|
-
{ name: 'Auth JWT
|
|
92
|
-
{ name: 'Tailwind CSS auto
|
|
66
|
+
{ name: 'Git init automático', value: 'git', checked: true },
|
|
67
|
+
{ name: 'ESLint + Prettier', value: 'eslint', checked: false },
|
|
68
|
+
{ name: 'Husky + lint-staged', value: 'husky', checked: false },
|
|
69
|
+
{ name: 'Vitest / Jest', value: 'testing', checked: false },
|
|
70
|
+
{ name: 'Docker profesional', value: 'docker', checked: false },
|
|
71
|
+
{ name: 'Archivos .env.example', value: 'env', checked: true },
|
|
72
|
+
{ name: '.editorconfig', value: 'editorconfig', checked: false },
|
|
73
|
+
{ name: 'GitHub Actions CI/CD', value: 'ci', checked: false },
|
|
74
|
+
{ name: 'Auth JWT completa', value: 'auth', checked: false },
|
|
75
|
+
{ name: 'Tailwind CSS auto', value: 'tailwind', checked: false },
|
|
76
|
+
{ name: 'Swagger / OpenAPI', value: 'swagger', checked: false },
|
|
77
|
+
{ name: 'Seguridad (helmet+cors+ratelimit)', value: 'security', checked: false },
|
|
78
|
+
{ name: 'Sistema de logs (winston)', value: 'logs', checked: false },
|
|
79
|
+
{ name: 'Stripe (pagos)', value: 'stripe', checked: false },
|
|
80
|
+
{ name: 'Notificaciones toast (frontend)', value: 'notifications',checked: false },
|
|
81
|
+
{ name: 'SEO base (meta+OG+sitemap)', value: 'seo', checked: false },
|
|
82
|
+
{ name: 'Dark mode toggle', value: 'darkmode', checked: false },
|
|
83
|
+
{ name: 'WhatsApp botón de contacto', value: 'whatsapp', checked: false },
|
|
93
84
|
];
|
|
94
85
|
|
|
95
|
-
// ── FLUJO PRINCIPAL ───────────────────────────────────────────────────────────
|
|
96
86
|
export async function askProjectOptions(nameArg, options) {
|
|
97
|
-
//
|
|
87
|
+
// Preset
|
|
98
88
|
if (options.preset) {
|
|
99
89
|
const preset = PRESETS[options.preset];
|
|
100
90
|
if (!preset) {
|
|
101
91
|
console.log(chalk.red(`\n Preset "${options.preset}" no existe.`));
|
|
102
|
-
console.log(chalk.gray('
|
|
92
|
+
console.log(chalk.gray(' Disponibles: ' + Object.keys(PRESETS).join(', ') + '\n'));
|
|
103
93
|
process.exit(1);
|
|
104
94
|
}
|
|
105
95
|
const name = nameArg || options.preset + '-app';
|
|
106
|
-
console.log(chalk.gray(`\n
|
|
107
|
-
return { name, ...preset, frontendDeps: [], backendDeps: [], extras: ['git', 'env'] };
|
|
96
|
+
console.log(chalk.gray(`\n Preset: ${chalk.white(options.preset)}\n`));
|
|
97
|
+
return { name, ...preset, pm: 'npm', frontendDeps: [], backendDeps: [], extras: ['git', 'env', 'security', 'swagger'] };
|
|
108
98
|
}
|
|
109
99
|
|
|
110
|
-
//
|
|
100
|
+
// --yes mode
|
|
111
101
|
if (options.yes) {
|
|
112
102
|
const name = nameArg || 'my-app';
|
|
113
|
-
await
|
|
103
|
+
await checkFolder(name);
|
|
114
104
|
return {
|
|
115
105
|
name,
|
|
116
|
-
frontend: options.frontend
|
|
117
|
-
backend: options.backend
|
|
118
|
-
db: options.db
|
|
119
|
-
|
|
106
|
+
frontend: options.frontend || 'react',
|
|
107
|
+
backend: options.backend || 'express',
|
|
108
|
+
db: options.db || 'postgres',
|
|
109
|
+
mode: options.mode || null,
|
|
110
|
+
architecture: options.architecture || 'mvc',
|
|
111
|
+
typescript: options.typescript || false,
|
|
112
|
+
pm: options.packageManager || 'npm',
|
|
113
|
+
install: options.install || false,
|
|
114
|
+
git: options.git || false,
|
|
120
115
|
frontendDeps: [],
|
|
121
116
|
backendDeps: [],
|
|
122
117
|
extras: ['git', 'env'],
|
|
123
118
|
};
|
|
124
119
|
}
|
|
125
120
|
|
|
126
|
-
const
|
|
121
|
+
const a = {};
|
|
127
122
|
|
|
128
123
|
// Nombre
|
|
129
124
|
if (!nameArg) {
|
|
130
125
|
const { name } = await inquirer.prompt([{
|
|
131
|
-
type: 'input',
|
|
132
|
-
name: 'name',
|
|
126
|
+
type: 'input', name: 'name',
|
|
133
127
|
message: chalk.white('Nombre del proyecto:'),
|
|
134
128
|
default: 'my-app',
|
|
135
|
-
validate: v =>
|
|
136
|
-
if (!/^[a-z0-9-_]+$/i.test(v.trim())) return 'Solo letras, números, guiones y guiones bajos';
|
|
137
|
-
if (v.trim().length < 2) return 'Mínimo 2 caracteres';
|
|
138
|
-
return true;
|
|
139
|
-
},
|
|
129
|
+
validate: v => /^[a-z0-9-_]+$/i.test(v.trim()) && v.trim().length >= 2 || 'Solo letras, números, guiones. Mínimo 2 chars.',
|
|
140
130
|
}]);
|
|
141
|
-
|
|
142
|
-
} else {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
//
|
|
147
|
-
await
|
|
131
|
+
a.name = name.trim();
|
|
132
|
+
} else { a.name = nameArg; }
|
|
133
|
+
|
|
134
|
+
await checkFolder(a.name);
|
|
135
|
+
|
|
136
|
+
// Modo business
|
|
137
|
+
const { mode } = await inquirer.prompt([{
|
|
138
|
+
type: 'list', name: 'mode',
|
|
139
|
+
message: chalk.white('Modo de proyecto:'),
|
|
140
|
+
choices: [
|
|
141
|
+
{ name: 'Estándar — Solo estructura base', value: null },
|
|
142
|
+
{ name: 'Business — Landing + Auth + Dashboard + CRUD (sistema completo)', value: 'business' },
|
|
143
|
+
],
|
|
144
|
+
default: options.mode || null,
|
|
145
|
+
}]);
|
|
146
|
+
a.mode = mode;
|
|
148
147
|
|
|
149
148
|
// TypeScript
|
|
150
149
|
const { typescript } = await inquirer.prompt([{
|
|
151
|
-
type: 'confirm',
|
|
152
|
-
name: 'typescript',
|
|
150
|
+
type: 'confirm', name: 'typescript',
|
|
153
151
|
message: chalk.white('¿Usar TypeScript?'),
|
|
154
152
|
default: options.typescript || false,
|
|
155
153
|
}]);
|
|
156
|
-
|
|
154
|
+
a.typescript = typescript;
|
|
157
155
|
|
|
158
156
|
// Frontend
|
|
159
157
|
if (!options.frontend) {
|
|
160
|
-
const { frontend } = await inquirer.prompt([{
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
message: chalk.white('Framework Frontend:'),
|
|
164
|
-
choices: FRONTEND_CHOICES,
|
|
165
|
-
pageSize: 12,
|
|
166
|
-
}]);
|
|
167
|
-
answers.frontend = frontend;
|
|
168
|
-
} else {
|
|
169
|
-
answers.frontend = options.frontend;
|
|
170
|
-
}
|
|
158
|
+
const { frontend } = await inquirer.prompt([{ type: 'list', name: 'frontend', message: chalk.white('Framework Frontend:'), choices: FRONTEND_CHOICES, pageSize: 12 }]);
|
|
159
|
+
a.frontend = frontend;
|
|
160
|
+
} else { a.frontend = options.frontend; }
|
|
171
161
|
|
|
172
162
|
// Backend
|
|
173
163
|
if (!options.backend) {
|
|
174
|
-
const { backend } = await inquirer.prompt([{
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
message: chalk.white('Framework Backend:'),
|
|
178
|
-
choices: BACKEND_CHOICES,
|
|
179
|
-
pageSize: 12,
|
|
180
|
-
}]);
|
|
181
|
-
answers.backend = backend;
|
|
182
|
-
} else {
|
|
183
|
-
answers.backend = options.backend;
|
|
184
|
-
}
|
|
164
|
+
const { backend } = await inquirer.prompt([{ type: 'list', name: 'backend', message: chalk.white('Framework Backend:'), choices: BACKEND_CHOICES, pageSize: 12 }]);
|
|
165
|
+
a.backend = backend;
|
|
166
|
+
} else { a.backend = options.backend; }
|
|
185
167
|
|
|
186
|
-
//
|
|
168
|
+
// DB
|
|
187
169
|
if (!options.db) {
|
|
188
|
-
const { db } = await inquirer.prompt([{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
170
|
+
const { db } = await inquirer.prompt([{ type: 'list', name: 'db', message: chalk.white('Base de datos:'), choices: DB_CHOICES, pageSize: 7 }]);
|
|
171
|
+
a.db = db;
|
|
172
|
+
} else { a.db = options.db; }
|
|
173
|
+
|
|
174
|
+
// Supabase config
|
|
175
|
+
if (a.db === 'supabase') {
|
|
176
|
+
console.log(chalk.gray('\n Configura Supabase (puedes dejarlo vacío y editar .env después)\n'));
|
|
177
|
+
const { supabaseUrl, supabaseKey } = await inquirer.prompt([
|
|
178
|
+
{ type: 'input', name: 'supabaseUrl', message: chalk.white(' Supabase URL:'), default: 'https://your-project.supabase.co' },
|
|
179
|
+
{ type: 'input', name: 'supabaseKey', message: chalk.white(' Supabase Anon Key:'), default: 'your-anon-key' },
|
|
180
|
+
]);
|
|
181
|
+
a.supabaseUrl = supabaseUrl;
|
|
182
|
+
a.supabaseKey = supabaseKey;
|
|
198
183
|
}
|
|
199
184
|
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
type: '
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
choices: (FRONTEND_DEPS[answers.frontend] || []).map(d => ({ name: d, value: d })),
|
|
206
|
-
pageSize: 12,
|
|
207
|
-
}]);
|
|
185
|
+
// Arquitectura
|
|
186
|
+
if (!options.architecture) {
|
|
187
|
+
const { architecture } = await inquirer.prompt([{ type: 'list', name: 'architecture', message: chalk.white('Arquitectura del backend:'), choices: ARCH_CHOICES }]);
|
|
188
|
+
a.architecture = architecture;
|
|
189
|
+
} else { a.architecture = options.architecture; }
|
|
208
190
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
type: '
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
choices: (BACKEND_DEPS[answers.backend] || []).map(d => ({ name: d, value: d })),
|
|
215
|
-
pageSize: 12,
|
|
216
|
-
}]);
|
|
191
|
+
// Package manager
|
|
192
|
+
if (!options.packageManager) {
|
|
193
|
+
const { pm } = await inquirer.prompt([{ type: 'list', name: 'pm', message: chalk.white('Package manager:'), choices: PM_CHOICES }]);
|
|
194
|
+
a.pm = pm;
|
|
195
|
+
} else { a.pm = options.packageManager; }
|
|
217
196
|
|
|
218
197
|
// Extras
|
|
219
198
|
const { extras } = await inquirer.prompt([{
|
|
220
|
-
type: 'checkbox',
|
|
221
|
-
name: 'extras',
|
|
199
|
+
type: 'checkbox', name: 'extras',
|
|
222
200
|
message: chalk.white('Extras del proyecto:'),
|
|
223
|
-
choices: EXTRAS_CHOICES,
|
|
224
|
-
|
|
201
|
+
choices: EXTRAS_CHOICES, pageSize: 14,
|
|
202
|
+
}]);
|
|
203
|
+
|
|
204
|
+
// Auto-install
|
|
205
|
+
const { install } = await inquirer.prompt([{
|
|
206
|
+
type: 'confirm', name: 'install',
|
|
207
|
+
message: chalk.white('¿Instalar dependencias automáticamente?'),
|
|
208
|
+
default: options.install || false,
|
|
225
209
|
}]);
|
|
210
|
+
a.install = install;
|
|
226
211
|
|
|
227
|
-
// Resumen
|
|
212
|
+
// Resumen
|
|
228
213
|
console.log();
|
|
229
|
-
console.log(
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
);
|
|
214
|
+
console.log(boxen(
|
|
215
|
+
chalk.bold.white(' RESUMEN\n\n') +
|
|
216
|
+
chalk.gray(' Nombre │ ') + chalk.white(a.name) + '\n' +
|
|
217
|
+
chalk.gray(' Modo │ ') + chalk.white(a.mode || 'estándar') + '\n' +
|
|
218
|
+
chalk.gray(' Frontend │ ') + chalk.white(a.frontend) + (a.typescript ? chalk.gray(' + TS') : '') + '\n' +
|
|
219
|
+
chalk.gray(' Backend │ ') + chalk.white(a.backend) + '\n' +
|
|
220
|
+
chalk.gray(' Base datos │ ') + chalk.white(a.db) + '\n' +
|
|
221
|
+
chalk.gray(' Arquitectura │ ') + chalk.white(a.architecture) + '\n' +
|
|
222
|
+
chalk.gray(' PM │ ') + chalk.white(a.pm) + '\n' +
|
|
223
|
+
chalk.gray(' Instalar │ ') + chalk.white(a.install ? 'sí' : 'no') + '\n' +
|
|
224
|
+
chalk.gray(' Extras │ ') + chalk.white(extras.length ? extras.join(', ') : 'ninguno'),
|
|
225
|
+
{ padding: 1, margin: { left: 2 }, borderStyle: 'round', borderColor: 'white', dimBorder: true }
|
|
226
|
+
));
|
|
242
227
|
console.log();
|
|
243
228
|
|
|
244
|
-
const { confirm } = await inquirer.prompt([{
|
|
245
|
-
type: 'confirm',
|
|
246
|
-
name: 'confirm',
|
|
247
|
-
message: chalk.white('¿Crear el proyecto?'),
|
|
248
|
-
default: true,
|
|
249
|
-
}]);
|
|
250
|
-
|
|
229
|
+
const { confirm } = await inquirer.prompt([{ type: 'confirm', name: 'confirm', message: chalk.white('¿Crear el proyecto?'), default: true }]);
|
|
251
230
|
if (!confirm) { console.log(chalk.gray('\n Cancelado.\n')); process.exit(0); }
|
|
252
231
|
|
|
253
|
-
return { ...
|
|
232
|
+
return { ...a, frontendDeps: [], backendDeps: [], extras };
|
|
254
233
|
}
|
|
255
234
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (await fse.pathExists(projectPath)) {
|
|
260
|
-
if (autoSkip) {
|
|
261
|
-
console.log(chalk.yellow(`\n La carpeta "${name}" ya existe. Se sobreescribirá.\n`));
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
235
|
+
async function checkFolder(name) {
|
|
236
|
+
const p = path.resolve(process.cwd(), name);
|
|
237
|
+
if (await fse.pathExists(p)) {
|
|
264
238
|
const { overwrite } = await inquirer.prompt([{
|
|
265
|
-
type: 'confirm',
|
|
266
|
-
name: 'overwrite',
|
|
239
|
+
type: 'confirm', name: 'overwrite',
|
|
267
240
|
message: chalk.yellow(`La carpeta "${name}" ya existe. ¿Sobreescribir?`),
|
|
268
241
|
default: false,
|
|
269
242
|
}]);
|
|
270
243
|
if (!overwrite) { console.log(chalk.gray('\n Cancelado.\n')); process.exit(0); }
|
|
271
|
-
await fse.remove(
|
|
244
|
+
await fse.remove(p);
|
|
272
245
|
}
|
|
273
246
|
}
|
|
274
|
-
|
|
275
|
-
|
package/package.json
CHANGED