vako 1.3.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/CHANGELOG.md +63 -0
- package/README.md +1944 -0
- package/bin/commands/quick-setup.js +111 -0
- package/bin/commands/setup-executor.js +203 -0
- package/bin/commands/setup.js +737 -0
- package/bin/create-veko-app.js +75 -0
- package/bin/veko-update.js +205 -0
- package/bin/veko.js +188 -0
- package/error/error.ejs +382 -0
- package/index.js +36 -0
- package/lib/adapters/nextjs-adapter.js +241 -0
- package/lib/app.js +749 -0
- package/lib/core/auth-manager.js +1353 -0
- package/lib/core/auto-updater.js +1118 -0
- package/lib/core/logger.js +97 -0
- package/lib/core/module-installer.js +86 -0
- package/lib/dev/dev-server.js +292 -0
- package/lib/layout/layout-manager.js +834 -0
- package/lib/plugin-manager.js +1795 -0
- package/lib/routing/route-manager.js +1000 -0
- package/package.json +231 -0
- package/templates/public/css/style.css +2 -0
- package/templates/public/js/main.js +1 -0
- package/tsconfig.json +50 -0
- package/types/index.d.ts +238 -0
|
@@ -0,0 +1,737 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
const inquirer = require('inquirer');
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const boxen = require('boxen');
|
|
5
|
+
const figlet = require('figlet');
|
|
6
|
+
const gradient = require('gradient-string');
|
|
7
|
+
const { createSpinner } = require('nanospinner');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs').promises;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Assistant de configuration interactif pour les projets Veko.js
|
|
13
|
+
* Fournit une interface utilisateur complète pour la création de projets
|
|
14
|
+
*/
|
|
15
|
+
class SetupWizard {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.config = {
|
|
18
|
+
projectName: '',
|
|
19
|
+
template: 'default',
|
|
20
|
+
features: [],
|
|
21
|
+
database: 'sqlite',
|
|
22
|
+
auth: { enabled: false },
|
|
23
|
+
plugins: [],
|
|
24
|
+
styling: 'bootstrap',
|
|
25
|
+
theme: 'light',
|
|
26
|
+
git: true,
|
|
27
|
+
install: true,
|
|
28
|
+
description: '',
|
|
29
|
+
author: '',
|
|
30
|
+
license: 'MIT',
|
|
31
|
+
scripts: true,
|
|
32
|
+
docker: false,
|
|
33
|
+
env: true
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Configuration de sécurité
|
|
37
|
+
this.securityConfig = {
|
|
38
|
+
maxProjectNameLength: 50,
|
|
39
|
+
maxDescriptionLength: 200,
|
|
40
|
+
maxAuthorLength: 100,
|
|
41
|
+
allowedFileNameChars: /^[a-zA-Z0-9\-_]+$/,
|
|
42
|
+
maxFeatures: 20,
|
|
43
|
+
maxPlugins: 15
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Templates prédéfinis
|
|
47
|
+
this.templates = new Map([
|
|
48
|
+
['default', {
|
|
49
|
+
name: '🌟 Default - Full-featured web application',
|
|
50
|
+
description: 'Complete web application with all features',
|
|
51
|
+
files: ['views/', 'routes/', 'public/', 'plugins/', 'middleware/', 'app.js']
|
|
52
|
+
}],
|
|
53
|
+
['api', {
|
|
54
|
+
name: '🔌 API Only - REST API server',
|
|
55
|
+
description: 'RESTful API server with authentication',
|
|
56
|
+
files: ['routes/api/', 'middleware/', 'models/', 'tests/', 'server.js']
|
|
57
|
+
}],
|
|
58
|
+
['blog', {
|
|
59
|
+
name: '📝 Blog - Content management system',
|
|
60
|
+
description: 'Blog engine with admin interface',
|
|
61
|
+
files: ['views/blog/', 'content/posts/', 'admin/', 'uploads/', 'blog.js']
|
|
62
|
+
}],
|
|
63
|
+
['admin', {
|
|
64
|
+
name: '👑 Admin Dashboard - Management interface',
|
|
65
|
+
description: 'Administrative dashboard and management tools',
|
|
66
|
+
files: ['admin/views/', 'dashboard/', 'auth/', 'api/admin/', 'admin.js']
|
|
67
|
+
}],
|
|
68
|
+
['ecommerce', {
|
|
69
|
+
name: '🛍️ E-commerce - Online store',
|
|
70
|
+
description: 'Complete e-commerce solution',
|
|
71
|
+
files: ['shop/views/', 'products/', 'orders/', 'payments/', 'store.js']
|
|
72
|
+
}],
|
|
73
|
+
['portfolio', {
|
|
74
|
+
name: '🎭 Portfolio - Personal showcase',
|
|
75
|
+
description: 'Personal portfolio and project showcase',
|
|
76
|
+
files: ['portfolio/views/', 'projects/', 'gallery/', 'blog/', 'portfolio.js']
|
|
77
|
+
}],
|
|
78
|
+
['pwa', {
|
|
79
|
+
name: '📱 PWA - Progressive Web App',
|
|
80
|
+
description: 'Progressive Web Application with offline support',
|
|
81
|
+
files: ['pwa/', 'sw/', 'manifest/', 'offline/', 'pwa.js']
|
|
82
|
+
}]
|
|
83
|
+
]);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Point d'entrée principal du wizard
|
|
88
|
+
*/
|
|
89
|
+
async start() {
|
|
90
|
+
try {
|
|
91
|
+
console.clear();
|
|
92
|
+
await this.showWelcome();
|
|
93
|
+
await this.gatherProjectInfo();
|
|
94
|
+
await this.selectTemplate();
|
|
95
|
+
await this.selectFeatures();
|
|
96
|
+
await this.configureDatabase();
|
|
97
|
+
await this.configureAuth();
|
|
98
|
+
await this.selectPlugins();
|
|
99
|
+
await this.selectStyling();
|
|
100
|
+
await this.finalOptions();
|
|
101
|
+
await this.confirmOptions();
|
|
102
|
+
await this.executeSetup();
|
|
103
|
+
await this.showCompletion();
|
|
104
|
+
} catch (error) {
|
|
105
|
+
await this.handleError(error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Affichage de l'écran d'accueil avec titre stylisé
|
|
111
|
+
*/
|
|
112
|
+
async showWelcome() {
|
|
113
|
+
try {
|
|
114
|
+
const title = figlet.textSync('VEKO.JS', {
|
|
115
|
+
font: 'ANSI Shadow',
|
|
116
|
+
horizontalLayout: 'fitted'
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
console.log(gradient.rainbow(title));
|
|
120
|
+
} catch (error) {
|
|
121
|
+
// Fallback si figlet échoue
|
|
122
|
+
console.log(chalk.cyan.bold('╔══════════════════════════════════╗'));
|
|
123
|
+
console.log(chalk.cyan.bold('║ VEKO.JS ║'));
|
|
124
|
+
console.log(chalk.cyan.bold('╚══════════════════════════════════╝'));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log(chalk.cyan.bold('\n✨ Interactive Project Setup Wizard ✨\n'));
|
|
128
|
+
|
|
129
|
+
const welcomeBox = boxen(
|
|
130
|
+
chalk.white('🎉 Welcome to Veko.js Setup Wizard!\n\n') +
|
|
131
|
+
chalk.gray('This wizard will guide you through creating a new\n') +
|
|
132
|
+
chalk.gray('Veko.js project with all the features you need.\n\n') +
|
|
133
|
+
chalk.blue('✓ Templates & Examples\n') +
|
|
134
|
+
chalk.blue('✓ Authentication System\n') +
|
|
135
|
+
chalk.blue('✓ Database Integration\n') +
|
|
136
|
+
chalk.blue('✓ Plugin Ecosystem\n') +
|
|
137
|
+
chalk.blue('✓ Beautiful UI Frameworks'),
|
|
138
|
+
{
|
|
139
|
+
padding: 1,
|
|
140
|
+
margin: 1,
|
|
141
|
+
borderStyle: 'double',
|
|
142
|
+
borderColor: 'cyan',
|
|
143
|
+
textAlignment: 'center'
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
console.log(welcomeBox);
|
|
148
|
+
|
|
149
|
+
const { ready } = await inquirer.prompt([{
|
|
150
|
+
type: 'confirm',
|
|
151
|
+
name: 'ready',
|
|
152
|
+
message: '🚀 Ready to create something amazing?',
|
|
153
|
+
default: true
|
|
154
|
+
}]);
|
|
155
|
+
|
|
156
|
+
if (!ready) {
|
|
157
|
+
console.log(chalk.yellow('\n👋 See you later! Happy coding!'));
|
|
158
|
+
process.exit(0);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Collecte des informations de base du projet
|
|
164
|
+
*/
|
|
165
|
+
async gatherProjectInfo() {
|
|
166
|
+
console.log(chalk.blue.bold('\n📝 Project Information\n'));
|
|
167
|
+
|
|
168
|
+
const questions = [
|
|
169
|
+
{
|
|
170
|
+
type: 'input',
|
|
171
|
+
name: 'projectName',
|
|
172
|
+
message: '📁 What\'s your project name?',
|
|
173
|
+
default: 'my-veko-app',
|
|
174
|
+
validate: (input) => this.validateProjectName(input)
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
type: 'input',
|
|
178
|
+
name: 'description',
|
|
179
|
+
message: '📄 Project description:',
|
|
180
|
+
default: 'A modern web application built with Veko.js',
|
|
181
|
+
validate: (input) => this.validateDescription(input)
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'input',
|
|
185
|
+
name: 'author',
|
|
186
|
+
message: '👤 Author name:',
|
|
187
|
+
default: process.env.USER || process.env.USERNAME || '',
|
|
188
|
+
validate: (input) => this.validateAuthor(input)
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'list',
|
|
192
|
+
name: 'license',
|
|
193
|
+
message: '📜 Choose a license:',
|
|
194
|
+
choices: [
|
|
195
|
+
{ name: '📋 MIT - Most permissive', value: 'MIT' },
|
|
196
|
+
{ name: '🔒 ISC - Simple and permissive', value: 'ISC' },
|
|
197
|
+
{ name: '⚖️ Apache-2.0 - Patent protection', value: 'Apache-2.0' },
|
|
198
|
+
{ name: '🆓 GPL-3.0 - Copyleft', value: 'GPL-3.0' },
|
|
199
|
+
{ name: '🚫 Unlicense - Public domain', value: 'Unlicense' }
|
|
200
|
+
],
|
|
201
|
+
default: 'MIT'
|
|
202
|
+
}
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
const answers = await inquirer.prompt(questions);
|
|
206
|
+
Object.assign(this.config, this.sanitizeProjectInfo(answers));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Sélection du template de projet
|
|
211
|
+
*/
|
|
212
|
+
async selectTemplate() {
|
|
213
|
+
console.log(chalk.blue.bold('\n🎨 Choose Your Template\n'));
|
|
214
|
+
|
|
215
|
+
const templateChoices = Array.from(this.templates.entries()).map(([value, template]) => ({
|
|
216
|
+
name: template.name,
|
|
217
|
+
value
|
|
218
|
+
}));
|
|
219
|
+
|
|
220
|
+
const { template } = await inquirer.prompt([{
|
|
221
|
+
type: 'list',
|
|
222
|
+
name: 'template',
|
|
223
|
+
message: '🎯 Select a template:',
|
|
224
|
+
choices: templateChoices,
|
|
225
|
+
pageSize: 10
|
|
226
|
+
}]);
|
|
227
|
+
|
|
228
|
+
this.config.template = template;
|
|
229
|
+
this.showTemplatePreview(template);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Affichage de l'aperçu du template sélectionné
|
|
234
|
+
*/
|
|
235
|
+
showTemplatePreview(templateName) {
|
|
236
|
+
const template = this.templates.get(templateName);
|
|
237
|
+
if (!template) return;
|
|
238
|
+
|
|
239
|
+
const preview = template.files.map(file => `📁 ${file}`).join('\n');
|
|
240
|
+
|
|
241
|
+
const previewBox = boxen(
|
|
242
|
+
chalk.cyan('📋 Template Structure:\n\n') +
|
|
243
|
+
chalk.gray(preview) + '\n\n' +
|
|
244
|
+
chalk.blue('Description: ') + chalk.white(template.description),
|
|
245
|
+
{
|
|
246
|
+
padding: 1,
|
|
247
|
+
margin: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
248
|
+
borderStyle: 'round',
|
|
249
|
+
borderColor: 'blue',
|
|
250
|
+
title: '📦 Project Structure',
|
|
251
|
+
titleAlignment: 'center'
|
|
252
|
+
}
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
console.log(previewBox);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Sélection des fonctionnalités
|
|
260
|
+
*/
|
|
261
|
+
async selectFeatures() {
|
|
262
|
+
console.log(chalk.blue.bold('\n⚡ Select Features & Add-ons\n'));
|
|
263
|
+
|
|
264
|
+
const featureChoices = [
|
|
265
|
+
{ name: '🔥 Hot Reload Development Server', value: 'hotreload', checked: true },
|
|
266
|
+
{ name: '📱 Progressive Web App (PWA)', value: 'pwa' },
|
|
267
|
+
{ name: '🎨 Advanced Layout System', value: 'layouts', checked: true },
|
|
268
|
+
{ name: '🔍 SEO Optimization', value: 'seo' },
|
|
269
|
+
{ name: '📊 Analytics Integration', value: 'analytics' },
|
|
270
|
+
{ name: '💬 Real-time WebSocket Support', value: 'websocket' },
|
|
271
|
+
{ name: '📧 Email System (Nodemailer)', value: 'email' },
|
|
272
|
+
{ name: '🔒 Rate Limiting & Security', value: 'security' },
|
|
273
|
+
{ name: '📁 File Upload System', value: 'upload' },
|
|
274
|
+
{ name: '🌐 Multi-language (i18n)', value: 'i18n' },
|
|
275
|
+
{ name: '📋 Form Validation', value: 'validation' },
|
|
276
|
+
{ name: '🎭 Component System', value: 'components' },
|
|
277
|
+
{ name: '🗜️ Image Processing', value: 'imageprocessing' },
|
|
278
|
+
{ name: '🔄 Backup System', value: 'backup' }
|
|
279
|
+
];
|
|
280
|
+
|
|
281
|
+
const { features } = await inquirer.prompt([{
|
|
282
|
+
type: 'checkbox',
|
|
283
|
+
name: 'features',
|
|
284
|
+
message: '🎁 Which features would you like to include?',
|
|
285
|
+
choices: featureChoices,
|
|
286
|
+
pageSize: 15,
|
|
287
|
+
validate: (input) => this.validateFeatures(input)
|
|
288
|
+
}]);
|
|
289
|
+
|
|
290
|
+
this.config.features = features;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Configuration de la base de données
|
|
295
|
+
*/
|
|
296
|
+
async configureDatabase() {
|
|
297
|
+
const dbRequiredTemplates = ['api', 'blog', 'admin', 'ecommerce'];
|
|
298
|
+
|
|
299
|
+
if (dbRequiredTemplates.includes(this.config.template)) {
|
|
300
|
+
console.log(chalk.blue.bold('\n🗄️ Database Configuration\n'));
|
|
301
|
+
|
|
302
|
+
const databaseChoices = [
|
|
303
|
+
{ name: '📄 SQLite - File-based (recommended for dev)', value: 'sqlite' },
|
|
304
|
+
{ name: '🐘 PostgreSQL - Advanced relational database', value: 'postgresql' },
|
|
305
|
+
{ name: '🐬 MySQL - Popular relational database', value: 'mysql' },
|
|
306
|
+
{ name: '🍃 MongoDB - Document database', value: 'mongodb' },
|
|
307
|
+
{ name: '⚡ Redis - In-memory cache/database', value: 'redis' },
|
|
308
|
+
{ name: '🚫 None - No database', value: 'none' }
|
|
309
|
+
];
|
|
310
|
+
|
|
311
|
+
const { database } = await inquirer.prompt([{
|
|
312
|
+
type: 'list',
|
|
313
|
+
name: 'database',
|
|
314
|
+
message: '💾 Choose your database:',
|
|
315
|
+
choices: databaseChoices
|
|
316
|
+
}]);
|
|
317
|
+
|
|
318
|
+
this.config.database = database;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Configuration du système d'authentification
|
|
324
|
+
*/
|
|
325
|
+
async configureAuth() {
|
|
326
|
+
const authRequiredTemplates = ['default', 'blog', 'admin', 'ecommerce'];
|
|
327
|
+
|
|
328
|
+
if (authRequiredTemplates.includes(this.config.template)) {
|
|
329
|
+
console.log(chalk.blue.bold('\n🔐 Authentication System\n'));
|
|
330
|
+
|
|
331
|
+
const { enableAuth } = await inquirer.prompt([{
|
|
332
|
+
type: 'confirm',
|
|
333
|
+
name: 'enableAuth',
|
|
334
|
+
message: '🔑 Enable authentication system?',
|
|
335
|
+
default: ['admin', 'ecommerce'].includes(this.config.template)
|
|
336
|
+
}]);
|
|
337
|
+
|
|
338
|
+
if (enableAuth) {
|
|
339
|
+
const authConfig = await this.configureAuthDetails();
|
|
340
|
+
this.config.auth = { enabled: true, ...authConfig };
|
|
341
|
+
} else {
|
|
342
|
+
this.config.auth = { enabled: false };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Configuration détaillée de l'authentification
|
|
349
|
+
*/
|
|
350
|
+
async configureAuthDetails() {
|
|
351
|
+
return await inquirer.prompt([
|
|
352
|
+
{
|
|
353
|
+
type: 'checkbox',
|
|
354
|
+
name: 'methods',
|
|
355
|
+
message: '🚪 Authentication methods:',
|
|
356
|
+
choices: [
|
|
357
|
+
{ name: '📧 Email/Password (Local)', value: 'local', checked: true },
|
|
358
|
+
{ name: '🌐 Google OAuth', value: 'google' },
|
|
359
|
+
{ name: '📘 Facebook OAuth', value: 'facebook' },
|
|
360
|
+
{ name: '🐙 GitHub OAuth', value: 'github' },
|
|
361
|
+
{ name: '💼 LinkedIn OAuth', value: 'linkedin' },
|
|
362
|
+
{ name: '🔗 JWT Tokens', value: 'jwt' }
|
|
363
|
+
],
|
|
364
|
+
validate: (input) => input.length > 0 || 'At least one method is required'
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
type: 'checkbox',
|
|
368
|
+
name: 'features',
|
|
369
|
+
message: '🛡️ Authentication features:',
|
|
370
|
+
choices: [
|
|
371
|
+
{ name: '👤 User profiles', value: 'profiles', checked: true },
|
|
372
|
+
{ name: '👑 Role-based access control', value: 'roles' },
|
|
373
|
+
{ name: '📧 Email verification', value: 'emailVerification' },
|
|
374
|
+
{ name: '🔄 Password reset', value: 'passwordReset' },
|
|
375
|
+
{ name: '🔒 Two-factor authentication', value: '2fa' },
|
|
376
|
+
{ name: '📊 Login analytics', value: 'analytics' },
|
|
377
|
+
{ name: '🚫 Account lockout', value: 'lockout' }
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
]);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Sélection des plugins
|
|
385
|
+
*/
|
|
386
|
+
async selectPlugins() {
|
|
387
|
+
console.log(chalk.blue.bold('\n🔌 Plugins & Extensions\n'));
|
|
388
|
+
|
|
389
|
+
const pluginChoices = [
|
|
390
|
+
{ name: '📊 Logger - Advanced request/error logging', value: 'logger', checked: true },
|
|
391
|
+
{ name: '🛡️ Security - Helmet, CORS, rate limiting', value: 'security', checked: true },
|
|
392
|
+
{ name: '⚡ Cache - Redis/Memory caching system', value: 'cache' },
|
|
393
|
+
{ name: '📈 Monitoring - Health checks & metrics', value: 'monitoring' },
|
|
394
|
+
{ name: '📦 Compression - Gzip response compression', value: 'compression' },
|
|
395
|
+
{ name: '🔄 Backup - Automated data backups', value: 'backup' },
|
|
396
|
+
{ name: '🎨 Image Processing - Sharp/Jimp integration', value: 'images' },
|
|
397
|
+
{ name: '📧 Mailer - Email templates & sending', value: 'mailer' },
|
|
398
|
+
{ name: '📅 Scheduler - Cron jobs & tasks', value: 'scheduler' },
|
|
399
|
+
{ name: '🔍 Search - Full-text search engine', value: 'search' },
|
|
400
|
+
{ name: '📱 Push Notifications', value: 'notifications' },
|
|
401
|
+
{ name: '🏪 Session Store - Persistent sessions', value: 'sessionstore' }
|
|
402
|
+
];
|
|
403
|
+
|
|
404
|
+
const { plugins } = await inquirer.prompt([{
|
|
405
|
+
type: 'checkbox',
|
|
406
|
+
name: 'plugins',
|
|
407
|
+
message: '🎯 Select plugins to install:',
|
|
408
|
+
choices: pluginChoices,
|
|
409
|
+
pageSize: 12,
|
|
410
|
+
validate: (input) => this.validatePlugins(input)
|
|
411
|
+
}]);
|
|
412
|
+
|
|
413
|
+
this.config.plugins = plugins;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Sélection du framework de style
|
|
418
|
+
*/
|
|
419
|
+
async selectStyling() {
|
|
420
|
+
console.log(chalk.blue.bold('\n🎨 Styling & UI Framework\n'));
|
|
421
|
+
|
|
422
|
+
const stylingQuestions = [
|
|
423
|
+
{
|
|
424
|
+
type: 'list',
|
|
425
|
+
name: 'framework',
|
|
426
|
+
message: '🎭 Choose a CSS framework:',
|
|
427
|
+
choices: [
|
|
428
|
+
{ name: '🅱️ Bootstrap 5 - Popular component library', value: 'bootstrap' },
|
|
429
|
+
{ name: '🎯 Tailwind CSS - Utility-first framework', value: 'tailwind' },
|
|
430
|
+
{ name: '🎪 Bulma - Modern CSS framework', value: 'bulma' },
|
|
431
|
+
{ name: '⚡ Material Design - Google Material UI', value: 'material' },
|
|
432
|
+
{ name: '🎨 Foundation - Responsive front-end framework', value: 'foundation' },
|
|
433
|
+
{ name: '🎭 Semantic UI - Human-friendly HTML', value: 'semantic' },
|
|
434
|
+
{ name: '🖼️ Custom CSS - Write your own styles', value: 'custom' },
|
|
435
|
+
{ name: '🚫 None - No CSS framework', value: 'none' }
|
|
436
|
+
]
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
type: 'list',
|
|
440
|
+
name: 'theme',
|
|
441
|
+
message: '🌈 Color theme preference:',
|
|
442
|
+
choices: [
|
|
443
|
+
{ name: '🌅 Light - Clean and bright', value: 'light' },
|
|
444
|
+
{ name: '🌙 Dark - Easy on the eyes', value: 'dark' },
|
|
445
|
+
{ name: '🎨 Auto - Follow system preference', value: 'auto' },
|
|
446
|
+
{ name: '🌈 Custom - Define your own colors', value: 'custom' }
|
|
447
|
+
],
|
|
448
|
+
when: (answers) => answers.framework !== 'none'
|
|
449
|
+
}
|
|
450
|
+
];
|
|
451
|
+
|
|
452
|
+
const stylingAnswers = await inquirer.prompt(stylingQuestions);
|
|
453
|
+
this.config.styling = stylingAnswers.framework;
|
|
454
|
+
this.config.theme = stylingAnswers.theme || 'light';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Options finales de configuration
|
|
459
|
+
*/
|
|
460
|
+
async finalOptions() {
|
|
461
|
+
console.log(chalk.blue.bold('\n⚙️ Final Configuration\n'));
|
|
462
|
+
|
|
463
|
+
const finalQuestions = [
|
|
464
|
+
{
|
|
465
|
+
type: 'confirm',
|
|
466
|
+
name: 'git',
|
|
467
|
+
message: '📦 Initialize Git repository?',
|
|
468
|
+
default: true
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
type: 'confirm',
|
|
472
|
+
name: 'install',
|
|
473
|
+
message: '📥 Install dependencies automatically?',
|
|
474
|
+
default: true
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
type: 'confirm',
|
|
478
|
+
name: 'scripts',
|
|
479
|
+
message: '📜 Add useful npm scripts?',
|
|
480
|
+
default: true
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
type: 'confirm',
|
|
484
|
+
name: 'docker',
|
|
485
|
+
message: '🐳 Generate Docker configuration?',
|
|
486
|
+
default: false
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
type: 'confirm',
|
|
490
|
+
name: 'env',
|
|
491
|
+
message: '🔐 Create environment configuration?',
|
|
492
|
+
default: true
|
|
493
|
+
}
|
|
494
|
+
];
|
|
495
|
+
|
|
496
|
+
const finalAnswers = await inquirer.prompt(finalQuestions);
|
|
497
|
+
Object.assign(this.config, finalAnswers);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Confirmation de la configuration
|
|
502
|
+
*/
|
|
503
|
+
async confirmOptions() {
|
|
504
|
+
console.log(chalk.blue.bold('\n📋 Configuration Summary\n'));
|
|
505
|
+
|
|
506
|
+
const summary = this.generateSummary();
|
|
507
|
+
const summaryBox = boxen(summary, {
|
|
508
|
+
padding: 1,
|
|
509
|
+
margin: 1,
|
|
510
|
+
borderStyle: 'double',
|
|
511
|
+
borderColor: 'green',
|
|
512
|
+
title: '📦 Project Configuration',
|
|
513
|
+
titleAlignment: 'center'
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
console.log(summaryBox);
|
|
517
|
+
|
|
518
|
+
const { confirm } = await inquirer.prompt([{
|
|
519
|
+
type: 'confirm',
|
|
520
|
+
name: 'confirm',
|
|
521
|
+
message: '✅ Proceed with this configuration?',
|
|
522
|
+
default: true
|
|
523
|
+
}]);
|
|
524
|
+
|
|
525
|
+
if (!confirm) {
|
|
526
|
+
console.log(chalk.yellow('\n👋 Setup cancelled. See you later!'));
|
|
527
|
+
process.exit(0);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Génération du résumé de configuration
|
|
533
|
+
*/
|
|
534
|
+
generateSummary() {
|
|
535
|
+
const { projectName, template, features, database, auth, plugins, styling, theme } = this.config;
|
|
536
|
+
|
|
537
|
+
return chalk.white(`
|
|
538
|
+
🏷️ Project: ${chalk.cyan.bold(projectName)}
|
|
539
|
+
📝 Description: ${chalk.gray(this.config.description)}
|
|
540
|
+
👤 Author: ${chalk.green(this.config.author)}
|
|
541
|
+
🎨 Template: ${chalk.yellow(template)}
|
|
542
|
+
🗄️ Database: ${chalk.blue(database)}
|
|
543
|
+
🔐 Auth: ${chalk.magenta(auth.enabled ? '✅ Enabled' : '❌ Disabled')}
|
|
544
|
+
🎭 Styling: ${chalk.yellow(styling)} ${theme ? `(${theme})` : ''}
|
|
545
|
+
|
|
546
|
+
📦 Features (${features.length}):
|
|
547
|
+
${features.map(f => ` ✓ ${f}`).join('\n') || ' No additional features'}
|
|
548
|
+
|
|
549
|
+
🔌 Plugins (${plugins.length}):
|
|
550
|
+
${plugins.map(p => ` ⚡ ${p}`).join('\n') || ' No plugins selected'}
|
|
551
|
+
|
|
552
|
+
⚙️ Options:
|
|
553
|
+
📦 Git: ${this.config.git ? '✅' : '❌'}
|
|
554
|
+
📥 Auto-install: ${this.config.install ? '✅' : '❌'}
|
|
555
|
+
🐳 Docker: ${this.config.docker ? '✅' : '❌'}
|
|
556
|
+
🔐 Environment: ${this.config.env ? '✅' : '❌'}
|
|
557
|
+
`);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* Exécution de la configuration
|
|
562
|
+
*/
|
|
563
|
+
async executeSetup() {
|
|
564
|
+
try {
|
|
565
|
+
const SetupExecutor = require('./setup-executor');
|
|
566
|
+
const executor = new SetupExecutor(this.config);
|
|
567
|
+
await executor.execute();
|
|
568
|
+
} catch (error) {
|
|
569
|
+
throw new Error(`Setup execution failed: ${error.message}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Écran de finalisation
|
|
575
|
+
*/
|
|
576
|
+
async showCompletion() {
|
|
577
|
+
console.log(chalk.green.bold('\n🎉 Setup Complete!\n'));
|
|
578
|
+
|
|
579
|
+
const completionMessage = this.generateCompletionMessage();
|
|
580
|
+
const completionBox = boxen(completionMessage, {
|
|
581
|
+
padding: 1,
|
|
582
|
+
margin: 1,
|
|
583
|
+
borderStyle: 'round',
|
|
584
|
+
borderColor: 'green',
|
|
585
|
+
title: '🎊 Success!',
|
|
586
|
+
titleAlignment: 'center'
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
console.log(completionBox);
|
|
590
|
+
console.log(chalk.rainbow('\n✨ Happy coding with Veko.js! ✨\n'));
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Génération du message de finalisation
|
|
595
|
+
*/
|
|
596
|
+
generateCompletionMessage() {
|
|
597
|
+
const { projectName } = this.config;
|
|
598
|
+
|
|
599
|
+
return chalk.white(`Your project "${chalk.cyan.bold(projectName)}" has been created successfully!\n\n`) +
|
|
600
|
+
chalk.gray('Next steps:\n') +
|
|
601
|
+
chalk.white(` 📁 cd ${projectName}\n`) +
|
|
602
|
+
chalk.white(' 🚀 npm run dev\n') +
|
|
603
|
+
chalk.white(' 🌐 veko dev\n\n`') +
|
|
604
|
+
chalk.gray('Your app will be available at: ') +
|
|
605
|
+
chalk.blue.underline('http://localhost:3000\n\n') +
|
|
606
|
+
chalk.yellow('📚 Documentation: ') + chalk.blue.underline('https://veko.js.org');
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// === Méthodes de validation sécurisées ===
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Validation du nom de projet
|
|
613
|
+
*/
|
|
614
|
+
validateProjectName(input) {
|
|
615
|
+
if (!input || input.length < 1) {
|
|
616
|
+
return 'Project name is required';
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (input.length > this.securityConfig.maxProjectNameLength) {
|
|
620
|
+
return `Project name must be less than ${this.securityConfig.maxProjectNameLength} characters`;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (!this.securityConfig.allowedFileNameChars.test(input)) {
|
|
624
|
+
return 'Use only letters, numbers, hyphens and underscores';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
try {
|
|
628
|
+
// Vérification synchrone de l'existence du répertoire
|
|
629
|
+
const fs = require('fs');
|
|
630
|
+
if (fs.existsSync(input)) {
|
|
631
|
+
return 'Directory already exists';
|
|
632
|
+
}
|
|
633
|
+
} catch (error) {
|
|
634
|
+
// Ignorer les erreurs de vérification
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Validation de la description
|
|
642
|
+
*/
|
|
643
|
+
validateDescription(input) {
|
|
644
|
+
if (input && input.length > this.securityConfig.maxDescriptionLength) {
|
|
645
|
+
return `Description must be less than ${this.securityConfig.maxDescriptionLength} characters`;
|
|
646
|
+
}
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Validation de l'auteur
|
|
652
|
+
*/
|
|
653
|
+
validateAuthor(input) {
|
|
654
|
+
if (input && input.length > this.securityConfig.maxAuthorLength) {
|
|
655
|
+
return `Author name must be less than ${this.securityConfig.maxAuthorLength} characters`;
|
|
656
|
+
}
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Validation des fonctionnalités
|
|
662
|
+
*/
|
|
663
|
+
validateFeatures(input) {
|
|
664
|
+
if (input.length > this.securityConfig.maxFeatures) {
|
|
665
|
+
return `Maximum ${this.securityConfig.maxFeatures} features allowed`;
|
|
666
|
+
}
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
/**
|
|
671
|
+
* Validation des plugins
|
|
672
|
+
*/
|
|
673
|
+
validatePlugins(input) {
|
|
674
|
+
if (input.length > this.securityConfig.maxPlugins) {
|
|
675
|
+
return `Maximum ${this.securityConfig.maxPlugins} plugins allowed`;
|
|
676
|
+
}
|
|
677
|
+
return true;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Nettoyage sécurisé des informations de projet
|
|
682
|
+
*/
|
|
683
|
+
sanitizeProjectInfo(info) {
|
|
684
|
+
return {
|
|
685
|
+
projectName: this.sanitizeString(info.projectName, this.securityConfig.maxProjectNameLength),
|
|
686
|
+
description: this.sanitizeString(info.description, this.securityConfig.maxDescriptionLength),
|
|
687
|
+
author: this.sanitizeString(info.author, this.securityConfig.maxAuthorLength),
|
|
688
|
+
license: info.license || 'MIT'
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Nettoyage générique de chaîne
|
|
694
|
+
*/
|
|
695
|
+
sanitizeString(str, maxLength) {
|
|
696
|
+
if (typeof str !== 'string') return '';
|
|
697
|
+
return str.trim().substring(0, maxLength);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Gestion centralisée des erreurs
|
|
702
|
+
*/
|
|
703
|
+
async handleError(error) {
|
|
704
|
+
console.log(chalk.red.bold('\n❌ Setup Error\n'));
|
|
705
|
+
|
|
706
|
+
const errorBox = boxen(
|
|
707
|
+
chalk.red('An error occurred during setup:\n\n') +
|
|
708
|
+
chalk.white(error.message) + '\n\n' +
|
|
709
|
+
chalk.gray('Please try again or report this issue if it persists.'),
|
|
710
|
+
{
|
|
711
|
+
padding: 1,
|
|
712
|
+
margin: 1,
|
|
713
|
+
borderStyle: 'round',
|
|
714
|
+
borderColor: 'red',
|
|
715
|
+
title: '🚨 Error',
|
|
716
|
+
titleAlignment: 'center'
|
|
717
|
+
}
|
|
718
|
+
);
|
|
719
|
+
|
|
720
|
+
console.log(errorBox);
|
|
721
|
+
|
|
722
|
+
const { retry } = await inquirer.prompt([{
|
|
723
|
+
type: 'confirm',
|
|
724
|
+
name: 'retry',
|
|
725
|
+
message: '🔄 Would you like to try again?',
|
|
726
|
+
default: true
|
|
727
|
+
}]);
|
|
728
|
+
|
|
729
|
+
if (retry) {
|
|
730
|
+
await this.start();
|
|
731
|
+
} else {
|
|
732
|
+
process.exit(1);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
module.exports = SetupWizard;
|