baraqex 1.0.73 ā 1.0.74
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/baraqex.js +1131 -1018
- package/package.json +1 -1
package/bin/baraqex.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import inquirer from 'inquirer';
|
|
@@ -8,60 +8,33 @@ import { createSpinner } from 'nanospinner';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
10
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import { exec
|
|
11
|
+
import { exec } from 'child_process';
|
|
12
12
|
import { promisify } from 'util';
|
|
13
13
|
import boxen from 'boxen';
|
|
14
14
|
import terminalLink from 'terminal-link';
|
|
15
15
|
import updateNotifier from 'update-notifier';
|
|
16
16
|
import ora from 'ora';
|
|
17
17
|
import figlet from 'figlet';
|
|
18
|
-
import semver from 'semver';
|
|
19
|
-
import open from 'open';
|
|
20
|
-
import glob from 'glob';
|
|
21
|
-
import { WebSocketServer } from 'ws';
|
|
22
|
-
import chokidar from 'chokidar';
|
|
23
|
-
import { performance } from 'perf_hooks';
|
|
24
|
-
import os from 'os';
|
|
25
|
-
import { createRequire } from 'module';
|
|
26
18
|
|
|
27
19
|
// Convert to ESM-friendly __dirname equivalent
|
|
28
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
29
21
|
const __dirname = path.dirname(__filename);
|
|
30
22
|
const execAsync = promisify(exec);
|
|
31
|
-
const require = createRequire(import.meta.url);
|
|
32
23
|
|
|
33
|
-
//
|
|
34
|
-
let packageInfo = {};
|
|
24
|
+
// Check for package updates
|
|
35
25
|
try {
|
|
36
|
-
|
|
37
|
-
const notifier = updateNotifier({
|
|
38
|
-
pkg: packageInfo,
|
|
39
|
-
updateCheckInterval: 1000 * 60 * 60 * 12, // Check every 12 hours
|
|
40
|
-
shouldNotifyInNpmScript: true
|
|
41
|
-
});
|
|
26
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
27
|
+
const notifier = updateNotifier({ pkg, updateCheckInterval: 1000 * 60 * 60 * 24 });
|
|
42
28
|
|
|
43
|
-
if (notifier.update
|
|
44
|
-
const versionDiff = semver.diff(notifier.update.current, notifier.update.latest);
|
|
45
|
-
const urgency = versionDiff === 'major' ? 'šØ MAJOR' :
|
|
46
|
-
versionDiff === 'minor' ? 'ā ļø MINOR' :
|
|
47
|
-
'š§ PATCH';
|
|
48
|
-
|
|
29
|
+
if (notifier.update) {
|
|
49
30
|
const updateMessage = boxen(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
`${chalk.dim('Latest:')} ${chalk.green(notifier.update.latest)}\n\n` +
|
|
53
|
-
`${chalk.cyan('Run one of these commands to update:')}\n` +
|
|
54
|
-
`⢠${chalk.yellow('npm i -g baraqex@latest')}\n` +
|
|
55
|
-
`⢠${chalk.yellow('yarn global add baraqex@latest')}\n` +
|
|
56
|
-
`⢠${chalk.yellow('pnpm add -g baraqex@latest')}\n\n` +
|
|
57
|
-
`${chalk.dim('Changelog:')} ${terminalLink('View changes', `https://github.com/mohamedx2/baraqex/releases/tag/v${notifier.update.latest}`)}\n` +
|
|
58
|
-
`${chalk.dim('Documentation:')} ${terminalLink('Updated docs', 'https://www.baraqex.tech/docs')}`,
|
|
31
|
+
`Update available: ${chalk.dim(notifier.update.current)} ā ${chalk.green(notifier.update.latest)}\n` +
|
|
32
|
+
`Run ${chalk.cyan('npm i -g baraqex')} to update`,
|
|
59
33
|
{
|
|
60
34
|
padding: 1,
|
|
61
35
|
margin: 1,
|
|
62
36
|
borderStyle: 'round',
|
|
63
|
-
borderColor:
|
|
64
|
-
title: 'šÆ Baraqex Update Available'
|
|
37
|
+
borderColor: 'cyan'
|
|
65
38
|
}
|
|
66
39
|
);
|
|
67
40
|
console.log(updateMessage);
|
|
@@ -70,1140 +43,1280 @@ try {
|
|
|
70
43
|
// Silently continue if update check fails
|
|
71
44
|
}
|
|
72
45
|
|
|
73
|
-
//
|
|
46
|
+
// CLI instance
|
|
74
47
|
const program = new Command();
|
|
75
48
|
|
|
76
|
-
//
|
|
77
|
-
const globalConfig = {
|
|
78
|
-
analytics: true,
|
|
79
|
-
autoUpdate: true,
|
|
80
|
-
defaultTemplate: 'basic-app',
|
|
81
|
-
defaultPackageManager: 'npm',
|
|
82
|
-
telemetry: true
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
// Load user preferences
|
|
86
|
-
const configPath = path.join(os.homedir(), '.baraqex', 'config.json');
|
|
87
|
-
try {
|
|
88
|
-
if (fs.existsSync(configPath)) {
|
|
89
|
-
const userConfig = fs.readJsonSync(configPath);
|
|
90
|
-
Object.assign(globalConfig, userConfig);
|
|
91
|
-
}
|
|
92
|
-
} catch (error) {
|
|
93
|
-
// Use defaults if config can't be loaded
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Performance monitoring
|
|
97
|
-
const perfMonitor = {
|
|
98
|
-
startTime: performance.now(),
|
|
99
|
-
checkpoints: new Map(),
|
|
100
|
-
|
|
101
|
-
checkpoint(name) {
|
|
102
|
-
this.checkpoints.set(name, performance.now() - this.startTime);
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
report() {
|
|
106
|
-
const total = performance.now() - this.startTime;
|
|
107
|
-
console.log(chalk.dim(`\nā±ļø Performance: ${total.toFixed(2)}ms total`));
|
|
108
|
-
for (const [name, time] of this.checkpoints) {
|
|
109
|
-
console.log(chalk.dim(` ⢠${name}: ${time.toFixed(2)}ms`));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// Enhanced banner with system information and tips
|
|
49
|
+
// Create beautiful ASCII art banner with gradient colors
|
|
115
50
|
const displayBanner = () => {
|
|
116
51
|
const titleText = figlet.textSync('Baraqex', {
|
|
117
|
-
font: '
|
|
118
|
-
horizontalLayout: '
|
|
119
|
-
verticalLayout: '
|
|
52
|
+
font: 'Standard',
|
|
53
|
+
horizontalLayout: 'default',
|
|
54
|
+
verticalLayout: 'default'
|
|
120
55
|
});
|
|
121
56
|
|
|
122
|
-
console.log('\n' + gradient
|
|
123
|
-
|
|
124
|
-
const stats = getProjectStats();
|
|
125
|
-
const systemInfo = getSystemInfo();
|
|
126
|
-
|
|
127
|
-
// Daily tip system
|
|
128
|
-
const tips = [
|
|
129
|
-
"š” Use 'baraqex dev --open' to automatically open your browser",
|
|
130
|
-
"š Try 'baraqex add:component Button --typescript' for TypeScript components",
|
|
131
|
-
"š Run 'baraqex doctor' to check your project health",
|
|
132
|
-
"šØ Use 'baraqex add:layout Dashboard --sidebar' for instant layouts",
|
|
133
|
-
"ā” Enable hot reload with 'baraqex dev --hot'",
|
|
134
|
-
"š± Generate PWA with 'baraqex add:pwa'",
|
|
135
|
-
"š§ Customize builds with 'baraqex build --analyze'",
|
|
136
|
-
"š Deploy instantly with 'baraqex deploy --platform vercel'"
|
|
137
|
-
];
|
|
138
|
-
|
|
139
|
-
const dailyTip = tips[new Date().getDate() % tips.length];
|
|
57
|
+
console.log('\n' + gradient.pastel.multiline(titleText));
|
|
140
58
|
|
|
141
59
|
console.log(boxen(
|
|
142
|
-
`${chalk.bold
|
|
143
|
-
`${chalk.dim('Version:')} ${chalk.cyan(
|
|
144
|
-
`${chalk.dim('
|
|
145
|
-
(
|
|
146
|
-
`${chalk.bold.green('ā Current Project:')} ${chalk.cyan(stats.name)}\n` +
|
|
147
|
-
`${chalk.dim('ā¢')} ${chalk.green(stats.components)} components ${chalk.dim('ā¢')} ${chalk.green(stats.pages)} pages ${chalk.dim('ā¢')} ${chalk.green(stats.apis)} APIs\n` +
|
|
148
|
-
`${chalk.dim('ā¢')} Bundle size: ${chalk.yellow(stats.bundleSize)} ${chalk.dim('ā¢')} Last build: ${chalk.yellow(stats.lastBuild)}\n\n` :
|
|
149
|
-
`${chalk.yellow('š” Quick Start:')} Run ${chalk.cyan('baraqex create my-app')} to begin\n\n`
|
|
150
|
-
) +
|
|
151
|
-
`${chalk.bold.magenta('š Quick Links:')}\n` +
|
|
152
|
-
`${chalk.dim('ā¢')} ${terminalLink('š Documentation', 'https://www.baraqex.tech/docs')} ${chalk.dim('ā¢')} ${terminalLink('šÆ Examples', 'https://www.baraqex.tech/examples')}\n` +
|
|
153
|
-
`${chalk.dim('ā¢')} ${terminalLink('š¬ Discord', 'https://discord.gg/baraqex')} ${chalk.dim('ā¢')} ${terminalLink('š Issues', 'https://github.com/mohamedx2/baraqex/issues')}\n\n` +
|
|
154
|
-
`${chalk.bold.blue('š” Daily Tip:')} ${dailyTip}`,
|
|
60
|
+
`${chalk.bold('A powerful full-stack framework for modern web development')}\n\n` +
|
|
61
|
+
`${chalk.dim('Version:')} ${chalk.cyan(JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version)}\n` +
|
|
62
|
+
`${chalk.dim('Website:')} ${terminalLink('www.baraqex.tech', 'https://www.baraqex.tech')}\n` +
|
|
63
|
+
`${chalk.dim('Documentation:')} ${terminalLink('GitHub', 'https://github.com/mohamedx2/baraqex')}`,
|
|
155
64
|
{
|
|
156
65
|
padding: 1,
|
|
157
66
|
margin: { top: 1, bottom: 1 },
|
|
158
67
|
borderStyle: 'round',
|
|
159
|
-
borderColor: 'cyan'
|
|
160
|
-
title: 'šÆ Baraqex CLI',
|
|
161
|
-
titleAlignment: 'center'
|
|
68
|
+
borderColor: 'cyan'
|
|
162
69
|
}
|
|
163
70
|
));
|
|
164
71
|
};
|
|
165
72
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
const memUsage = ((usedMem / totalMem) * 100).toFixed(1);
|
|
172
|
-
|
|
173
|
-
return {
|
|
174
|
-
platform: `${os.platform()} ${os.arch()}`,
|
|
175
|
-
memory: `${(usedMem / 1024 / 1024 / 1024).toFixed(1)}GB / ${(totalMem / 1024 / 1024 / 1024).toFixed(1)}GB (${memUsage}%)`,
|
|
176
|
-
cpu: os.cpus()[0]?.model || 'Unknown',
|
|
177
|
-
cores: os.cpus().length,
|
|
178
|
-
nodeVersion: process.version,
|
|
179
|
-
npmVersion: null // Will be populated by dependency check
|
|
180
|
-
};
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
// Enhanced project statistics with bundle analysis
|
|
184
|
-
const getProjectStats = () => {
|
|
185
|
-
try {
|
|
186
|
-
const pkg = path.join(process.cwd(), 'package.json');
|
|
187
|
-
if (!fs.existsSync(pkg)) {
|
|
188
|
-
return { isProject: false };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const packageJson = fs.readJsonSync(pkg);
|
|
192
|
-
const isBaraqexProject = packageJson.dependencies?.baraqex || packageJson.devDependencies?.baraqex;
|
|
193
|
-
|
|
194
|
-
if (!isBaraqexProject) {
|
|
195
|
-
return { isProject: false };
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Enhanced file counting with better patterns
|
|
199
|
-
const componentsCount = countFiles('./src/components', ['.tsx', '.jsx', '.ts', '.js', '.vue']);
|
|
200
|
-
const pagesCount = countFiles('./src/pages', ['.tsx', '.jsx', '.ts', '.js']);
|
|
201
|
-
const apisCount = countFiles('./src/api', ['.tsx', '.jsx', '.ts', '.js']);
|
|
202
|
-
const testsCount = countFiles('./src', ['.test.ts', '.test.js', '.spec.ts', '.spec.js']);
|
|
203
|
-
|
|
204
|
-
// Bundle size analysis
|
|
205
|
-
const bundleSize = getBundleSize();
|
|
206
|
-
const lastBuild = getLastBuildTime();
|
|
207
|
-
|
|
208
|
-
return {
|
|
209
|
-
isProject: true,
|
|
210
|
-
name: packageJson.name,
|
|
211
|
-
version: packageJson.version,
|
|
212
|
-
components: componentsCount,
|
|
213
|
-
pages: pagesCount,
|
|
214
|
-
apis: apisCount,
|
|
215
|
-
tests: testsCount,
|
|
216
|
-
bundleSize,
|
|
217
|
-
lastBuild,
|
|
218
|
-
scripts: Object.keys(packageJson.scripts || {}).length,
|
|
219
|
-
dependencies: Object.keys(packageJson.dependencies || {}).length +
|
|
220
|
-
Object.keys(packageJson.devDependencies || {}).length
|
|
221
|
-
};
|
|
222
|
-
} catch (error) {
|
|
223
|
-
return { isProject: false };
|
|
224
|
-
}
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
// Enhanced file counting with exclusion patterns
|
|
228
|
-
const countFiles = (dir, extensions) => {
|
|
229
|
-
try {
|
|
230
|
-
if (!fs.existsSync(dir)) return 0;
|
|
231
|
-
|
|
232
|
-
const pattern = `${dir}/**/*.{${extensions.map(ext => ext.replace('.', '')).join(',')}}`;
|
|
233
|
-
const files = glob.sync(pattern, {
|
|
234
|
-
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.next/**']
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
return files.length;
|
|
238
|
-
} catch (error) {
|
|
239
|
-
return 0;
|
|
240
|
-
}
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Bundle size analysis
|
|
244
|
-
const getBundleSize = () => {
|
|
245
|
-
try {
|
|
246
|
-
const distPath = path.join(process.cwd(), 'dist');
|
|
247
|
-
const buildPath = path.join(process.cwd(), 'build');
|
|
248
|
-
const targetPath = fs.existsSync(distPath) ? distPath :
|
|
249
|
-
fs.existsSync(buildPath) ? buildPath : null;
|
|
250
|
-
|
|
251
|
-
if (!targetPath) return 'No build';
|
|
252
|
-
|
|
253
|
-
const files = glob.sync(`${targetPath}/**/*`, { nodir: true });
|
|
254
|
-
const totalSize = files.reduce((acc, file) => {
|
|
255
|
-
try {
|
|
256
|
-
return acc + fs.statSync(file).size;
|
|
257
|
-
} catch {
|
|
258
|
-
return acc;
|
|
259
|
-
}
|
|
260
|
-
}, 0);
|
|
261
|
-
|
|
262
|
-
const sizeInMB = (totalSize / 1024 / 1024).toFixed(1);
|
|
263
|
-
return `${sizeInMB}MB`;
|
|
264
|
-
} catch (error) {
|
|
265
|
-
return 'Unknown';
|
|
266
|
-
}
|
|
267
|
-
};
|
|
73
|
+
// Version and description
|
|
74
|
+
program
|
|
75
|
+
.name('baraqex')
|
|
76
|
+
.description('CLI for Baraqex - A powerful full-stack framework for modern web development')
|
|
77
|
+
.version(JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')).version);
|
|
268
78
|
|
|
269
|
-
//
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
const buildPath = path.join(process.cwd(), 'build');
|
|
274
|
-
const targetPath = fs.existsSync(distPath) ? distPath :
|
|
275
|
-
fs.existsSync(buildPath) ? buildPath : null;
|
|
276
|
-
|
|
277
|
-
if (!targetPath) return 'Never';
|
|
278
|
-
|
|
279
|
-
const stat = fs.statSync(targetPath);
|
|
280
|
-
const diffMs = Date.now() - stat.mtime.getTime();
|
|
281
|
-
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
|
|
282
|
-
const diffDays = Math.floor(diffHours / 24);
|
|
283
|
-
|
|
284
|
-
if (diffDays > 0) return `${diffDays}d ago`;
|
|
285
|
-
if (diffHours > 0) return `${diffHours}h ago`;
|
|
286
|
-
return 'Recently';
|
|
287
|
-
} catch (error) {
|
|
288
|
-
return 'Unknown';
|
|
289
|
-
}
|
|
79
|
+
// Helper for creating visually consistent sections
|
|
80
|
+
const createSection = (title) => {
|
|
81
|
+
console.log('\n' + chalk.bold.cyan(`ā ${title}`));
|
|
82
|
+
console.log(chalk.cyan('ā'.repeat(60)) + '\n');
|
|
290
83
|
};
|
|
291
84
|
|
|
292
|
-
//
|
|
85
|
+
// Helper for checking dependencies with improved feedback
|
|
293
86
|
async function checkDependencies() {
|
|
294
87
|
const spinner = ora({
|
|
295
|
-
text: '
|
|
296
|
-
spinner: '
|
|
88
|
+
text: 'Checking environment...',
|
|
89
|
+
spinner: 'dots',
|
|
297
90
|
color: 'cyan'
|
|
298
91
|
}).start();
|
|
299
92
|
|
|
300
93
|
try {
|
|
301
|
-
|
|
302
|
-
const recommendations = [];
|
|
303
|
-
|
|
304
|
-
// Node.js version check with LTS recommendations
|
|
94
|
+
// Check Node version
|
|
305
95
|
const nodeVersionOutput = await execAsync('node --version');
|
|
306
96
|
const nodeVersion = nodeVersionOutput.stdout.trim().replace('v', '');
|
|
307
|
-
const requiredNodeVersion = '
|
|
308
|
-
const recommendedNodeVersion = '18.0.0';
|
|
309
|
-
|
|
310
|
-
if (semver.lt(nodeVersion, requiredNodeVersion)) {
|
|
311
|
-
checks.push({
|
|
312
|
-
name: 'Node.js',
|
|
313
|
-
status: 'error',
|
|
314
|
-
current: nodeVersion,
|
|
315
|
-
required: requiredNodeVersion,
|
|
316
|
-
message: `Node.js ${requiredNodeVersion}+ required`
|
|
317
|
-
});
|
|
318
|
-
} else if (semver.lt(nodeVersion, recommendedNodeVersion)) {
|
|
319
|
-
checks.push({
|
|
320
|
-
name: 'Node.js',
|
|
321
|
-
status: 'warning',
|
|
322
|
-
current: nodeVersion,
|
|
323
|
-
recommended: recommendedNodeVersion,
|
|
324
|
-
message: `Consider upgrading to Node.js ${recommendedNodeVersion}+ (LTS)`
|
|
325
|
-
});
|
|
326
|
-
recommendations.push(`Upgrade Node.js to v${recommendedNodeVersion} for better performance`);
|
|
327
|
-
} else {
|
|
328
|
-
checks.push({
|
|
329
|
-
name: 'Node.js',
|
|
330
|
-
status: 'success',
|
|
331
|
-
current: nodeVersion,
|
|
332
|
-
message: 'Excellent version'
|
|
333
|
-
});
|
|
334
|
-
}
|
|
97
|
+
const requiredNodeVersion = '14.0.0';
|
|
335
98
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
packageManagers.push({ name: 'npm', version: npmVersion.stdout.trim(), speed: 'āāā' });
|
|
341
|
-
} catch (error) {}
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
const pnpmVersion = await execAsync('pnpm --version');
|
|
345
|
-
packageManagers.push({ name: 'pnpm', version: pnpmVersion.stdout.trim(), speed: 'āāāāā' });
|
|
346
|
-
recommendations.push('pnpm detected - fastest package manager available!');
|
|
347
|
-
} catch (error) {}
|
|
348
|
-
|
|
349
|
-
try {
|
|
350
|
-
const yarnVersion = await execAsync('yarn --version');
|
|
351
|
-
packageManagers.push({ name: 'yarn', version: yarnVersion.stdout.trim(), speed: 'āāāā' });
|
|
352
|
-
} catch (error) {}
|
|
353
|
-
|
|
354
|
-
if (packageManagers.length > 0) {
|
|
355
|
-
checks.push({
|
|
356
|
-
name: 'Package Managers',
|
|
357
|
-
status: 'info',
|
|
358
|
-
message: packageManagers.map(pm => `${pm.name} v${pm.version} ${pm.speed}`).join(', ')
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Git check with repository analysis
|
|
363
|
-
try {
|
|
364
|
-
await execAsync('git --version');
|
|
365
|
-
checks.push({
|
|
366
|
-
name: 'Git',
|
|
367
|
-
status: 'success',
|
|
368
|
-
message: 'Available'
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
// Check if in a git repository
|
|
372
|
-
try {
|
|
373
|
-
await execAsync('git rev-parse --git-dir');
|
|
374
|
-
const remoteOutput = await execAsync('git remote -v');
|
|
375
|
-
if (remoteOutput.stdout.includes('github.com')) {
|
|
376
|
-
checks.push({
|
|
377
|
-
name: 'Repository',
|
|
378
|
-
status: 'info',
|
|
379
|
-
message: 'GitHub repository detected'
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
} catch (error) {
|
|
383
|
-
recommendations.push('Initialize Git repository for version control');
|
|
384
|
-
}
|
|
385
|
-
} catch (error) {
|
|
386
|
-
checks.push({
|
|
387
|
-
name: 'Git',
|
|
388
|
-
status: 'warning',
|
|
389
|
-
message: 'Not available (recommended for version control)'
|
|
390
|
-
});
|
|
391
|
-
recommendations.push('Install Git for version control');
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// VS Code check
|
|
395
|
-
try {
|
|
396
|
-
await execAsync('code --version');
|
|
397
|
-
checks.push({
|
|
398
|
-
name: 'VS Code',
|
|
399
|
-
status: 'success',
|
|
400
|
-
message: 'Available (recommended editor)'
|
|
401
|
-
});
|
|
402
|
-
} catch (error) {
|
|
403
|
-
recommendations.push('Install VS Code for the best development experience');
|
|
99
|
+
if (compareVersions(nodeVersion, requiredNodeVersion) < 0) {
|
|
100
|
+
spinner.fail(`Node.js ${requiredNodeVersion}+ required, but found ${nodeVersion}`);
|
|
101
|
+
console.log(chalk.yellow(`Please upgrade Node.js: ${terminalLink('https://nodejs.org', 'https://nodejs.org')}`));
|
|
102
|
+
return false;
|
|
404
103
|
}
|
|
405
104
|
|
|
406
|
-
//
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
name: 'Docker',
|
|
411
|
-
status: 'success',
|
|
412
|
-
message: 'Available (great for deployment)'
|
|
413
|
-
});
|
|
414
|
-
} catch (error) {}
|
|
105
|
+
// Check npm version
|
|
106
|
+
const npmVersionOutput = await execAsync('npm --version');
|
|
107
|
+
const npmVersion = npmVersionOutput.stdout.trim();
|
|
108
|
+
const requiredNpmVersion = '6.0.0';
|
|
415
109
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
spinner.fail('Environment has critical issues');
|
|
421
|
-
} else if (hasWarnings) {
|
|
422
|
-
spinner.warn('Environment ready with recommendations');
|
|
423
|
-
} else {
|
|
424
|
-
spinner.succeed('Environment optimally configured');
|
|
110
|
+
if (compareVersions(npmVersion, requiredNpmVersion) < 0) {
|
|
111
|
+
spinner.fail(`npm ${requiredNpmVersion}+ required, but found ${npmVersion}`);
|
|
112
|
+
console.log(chalk.yellow(`Please upgrade npm: ${chalk.cyan('npm install -g npm')}`));
|
|
113
|
+
return false;
|
|
425
114
|
}
|
|
426
115
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
checks.map(check => {
|
|
430
|
-
const icon = {
|
|
431
|
-
success: 'ā
',
|
|
432
|
-
warning: 'ā ļø',
|
|
433
|
-
error: 'ā',
|
|
434
|
-
info: 'ā¹ļø'
|
|
435
|
-
}[check.status];
|
|
436
|
-
|
|
437
|
-
return `${icon} ${chalk.bold(check.name)}: ${check.message}` +
|
|
438
|
-
(check.current ? ` ${chalk.dim(`(v${check.current})`)}` : '');
|
|
439
|
-
}).join('\n') +
|
|
440
|
-
(recommendations.length > 0 ? '\n\n' + chalk.bold.yellow('š” Recommendations:\n') +
|
|
441
|
-
recommendations.map(rec => `⢠${rec}`).join('\n') : ''),
|
|
442
|
-
{
|
|
443
|
-
title: 'Environment Analysis',
|
|
444
|
-
padding: 1,
|
|
445
|
-
borderColor: hasErrors ? 'red' : hasWarnings ? 'yellow' : 'green'
|
|
446
|
-
}
|
|
447
|
-
));
|
|
448
|
-
|
|
449
|
-
return !hasErrors;
|
|
116
|
+
spinner.succeed(`Environment ready: Node ${chalk.green(nodeVersion)}, npm ${chalk.green(npmVersion)}`);
|
|
117
|
+
return true;
|
|
450
118
|
} catch (error) {
|
|
451
119
|
spinner.fail('Environment check failed');
|
|
452
|
-
console.log(chalk.red('Error:
|
|
120
|
+
console.log(chalk.red('Error: Node.js and npm are required to use this tool.'));
|
|
453
121
|
return false;
|
|
454
122
|
}
|
|
455
123
|
}
|
|
456
124
|
|
|
457
|
-
//
|
|
125
|
+
// Compare versions helper
|
|
126
|
+
function compareVersions(a, b) {
|
|
127
|
+
const aParts = a.split('.').map(Number);
|
|
128
|
+
const bParts = b.split('.').map(Number);
|
|
129
|
+
|
|
130
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
131
|
+
const aVal = aParts[i] || 0;
|
|
132
|
+
const bVal = bParts[i] || 0;
|
|
133
|
+
|
|
134
|
+
if (aVal > bVal) return 1;
|
|
135
|
+
if (aVal < bVal) return -1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Choose template interactively with improved visualization
|
|
458
142
|
async function chooseTemplate() {
|
|
459
143
|
const templatesPath = path.join(__dirname, '..', 'templates');
|
|
460
144
|
const templates = fs.readdirSync(templatesPath).filter(file =>
|
|
461
145
|
fs.statSync(path.join(templatesPath, file)).isDirectory()
|
|
462
146
|
);
|
|
463
147
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
},
|
|
479
|
-
'ssr-template': {
|
|
480
|
-
icon: 'š',
|
|
481
|
-
name: 'SSR Template',
|
|
482
|
-
description: 'Server-side rendering with excellent SEO',
|
|
483
|
-
features: ['SSR + Hydration', 'SEO optimized', 'Fast initial load', 'Dynamic routes', 'Meta management'],
|
|
484
|
-
buildTime: '~45s',
|
|
485
|
-
complexity: 'Intermediate',
|
|
486
|
-
useCase: 'Marketing sites, blogs, e-commerce',
|
|
487
|
-
bundleSize: '~200KB',
|
|
488
|
-
performance: 'āāāāā',
|
|
489
|
-
seo: 'āāāāā',
|
|
490
|
-
tags: ['SSR', 'SEO', 'Production'],
|
|
491
|
-
preview: 'https://ssr.baraqex.tech'
|
|
492
|
-
},
|
|
493
|
-
'fullstack-app': {
|
|
494
|
-
icon: 'ā”',
|
|
495
|
-
name: 'Fullstack App',
|
|
496
|
-
description: 'Complete solution with backend integration',
|
|
497
|
-
features: ['API routes', 'Database ORM', 'Authentication', 'File uploads', 'Real-time features'],
|
|
498
|
-
buildTime: '~60s',
|
|
499
|
-
complexity: 'Advanced',
|
|
500
|
-
useCase: 'Production apps, dashboards, SaaS',
|
|
501
|
-
bundleSize: '~300KB',
|
|
502
|
-
performance: 'āāāā',
|
|
503
|
-
seo: 'āāāā',
|
|
504
|
-
tags: ['Fullstack', 'Database', 'Auth'],
|
|
505
|
-
preview: 'https://fullstack.baraqex.tech'
|
|
506
|
-
},
|
|
507
|
-
'go-wasm-app': {
|
|
508
|
-
icon: 'š',
|
|
509
|
-
name: 'Go WASM App',
|
|
510
|
-
description: 'High-performance computing with WebAssembly',
|
|
511
|
-
features: ['Go + WASM', 'Performance critical', 'Cross-platform', 'Advanced builds', 'Native speed'],
|
|
512
|
-
buildTime: '~90s',
|
|
513
|
-
complexity: 'Expert',
|
|
514
|
-
useCase: 'Scientific computing, games, tools',
|
|
515
|
-
bundleSize: '~500KB',
|
|
516
|
-
performance: 'āāāāā',
|
|
517
|
-
seo: 'āā',
|
|
518
|
-
tags: ['WASM', 'Performance', 'Go'],
|
|
519
|
-
preview: 'https://wasm.baraqex.tech'
|
|
520
|
-
},
|
|
521
|
-
'pwa-app': {
|
|
522
|
-
icon: 'š±',
|
|
523
|
-
name: 'PWA App',
|
|
524
|
-
description: 'Progressive Web App with offline capabilities',
|
|
525
|
-
features: ['Service workers', 'Offline support', 'Push notifications', 'App-like experience'],
|
|
526
|
-
buildTime: '~50s',
|
|
527
|
-
complexity: 'Intermediate',
|
|
528
|
-
useCase: 'Mobile-first apps, offline apps',
|
|
529
|
-
bundleSize: '~250KB',
|
|
530
|
-
performance: 'āāāāā',
|
|
531
|
-
seo: 'āāāā',
|
|
532
|
-
tags: ['PWA', 'Mobile', 'Offline'],
|
|
533
|
-
preview: 'https://pwa.baraqex.tech'
|
|
534
|
-
}
|
|
148
|
+
// Emoji indicators for templates
|
|
149
|
+
const templateIcons = {
|
|
150
|
+
'basic-app': 'š',
|
|
151
|
+
'ssr-template': 'š',
|
|
152
|
+
'fullstack-app': 'ā”',
|
|
153
|
+
'go-wasm-app': 'š',
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Detailed descriptions
|
|
157
|
+
const templateDescriptions = {
|
|
158
|
+
'basic-app': 'Single-page application with just the essentials. Perfect for learning the framework or building simple apps.',
|
|
159
|
+
'ssr-template': 'Server-side rendered application with hydration. Optimized for SEO and fast initial load.',
|
|
160
|
+
'fullstack-app': 'Complete solution with API routes, authentication, and database integration ready to go.',
|
|
161
|
+
'go-wasm-app': 'WebAssembly integration with Go for high-performance computing in the browser and Node.js.'
|
|
535
162
|
};
|
|
536
163
|
|
|
537
164
|
console.log(boxen(
|
|
538
|
-
`${chalk.bold
|
|
539
|
-
`${chalk.dim('š” Each template is production-ready and includes best practices')}\n\n` +
|
|
165
|
+
`${chalk.bold('Available Project Templates')}\n\n` +
|
|
540
166
|
templates.map(template => {
|
|
541
|
-
const
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
tags: ['Custom']
|
|
548
|
-
};
|
|
167
|
+
const icon = templateIcons[template] || 'š¦';
|
|
168
|
+
const shortDesc = {
|
|
169
|
+
'basic-app': 'Simple client-side application',
|
|
170
|
+
'ssr-template': 'Server-side rendering with hydration',
|
|
171
|
+
'fullstack-app': 'Complete fullstack application with API'
|
|
172
|
+
}[template] || 'Application template';
|
|
549
173
|
|
|
550
|
-
return `${
|
|
551
|
-
` ${chalk.dim(data.description)}\n` +
|
|
552
|
-
` ${chalk.yellow('Performance:')} ${data.performance} ${chalk.yellow('SEO:')} ${data.seo} ${chalk.yellow('Bundle:')} ${data.bundleSize}\n` +
|
|
553
|
-
` ${chalk.yellow('Best for:')} ${data.useCase}`;
|
|
174
|
+
return `${icon} ${chalk.cyan(template)}\n ${chalk.dim(shortDesc)}`;
|
|
554
175
|
}).join('\n\n'),
|
|
555
176
|
{
|
|
556
177
|
padding: 1,
|
|
557
178
|
margin: 1,
|
|
558
179
|
borderStyle: 'round',
|
|
559
|
-
borderColor: 'cyan'
|
|
560
|
-
title: 'š Template Selection'
|
|
180
|
+
borderColor: 'cyan'
|
|
561
181
|
}
|
|
562
182
|
));
|
|
563
183
|
|
|
564
|
-
const
|
|
184
|
+
const templateChoices = templates.map(template => ({
|
|
185
|
+
name: `${templateIcons[template] || 'š¦'} ${chalk.bold(template)}`,
|
|
186
|
+
value: template,
|
|
187
|
+
description: templateDescriptions[template] || 'An application template'
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
const answers = await inquirer.prompt([
|
|
565
191
|
{
|
|
566
192
|
type: 'list',
|
|
567
193
|
name: 'template',
|
|
568
|
-
message: chalk.green('
|
|
569
|
-
choices:
|
|
570
|
-
|
|
571
|
-
return {
|
|
572
|
-
name: `${data.icon} ${chalk.bold(data.name)} ${chalk.dim(`(${data.complexity})`)}`,
|
|
573
|
-
value: template,
|
|
574
|
-
short: template
|
|
575
|
-
};
|
|
576
|
-
}),
|
|
194
|
+
message: chalk.green('Select a project template:'),
|
|
195
|
+
choices: templateChoices,
|
|
196
|
+
loop: false,
|
|
577
197
|
pageSize: 10
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: 'confirm',
|
|
201
|
+
name: 'viewDetails',
|
|
202
|
+
message: 'Would you like to see template details before proceeding?',
|
|
203
|
+
default: false
|
|
578
204
|
}
|
|
579
205
|
]);
|
|
580
206
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
207
|
+
if (answers.viewDetails) {
|
|
208
|
+
const detailedDescription = {
|
|
209
|
+
'basic-app': [
|
|
210
|
+
`${chalk.bold('Basic App Template')} ${templateIcons['basic-app']}`,
|
|
211
|
+
'',
|
|
212
|
+
`${chalk.dim('A lightweight client-side application template.')}`,
|
|
213
|
+
'',
|
|
214
|
+
`${chalk.yellow('Features:')}`,
|
|
215
|
+
'⢠No build step required for development',
|
|
216
|
+
'⢠Built-in state management with hooks',
|
|
217
|
+
'⢠Component-based architecture',
|
|
218
|
+
'⢠Tailwind CSS integration',
|
|
219
|
+
'',
|
|
220
|
+
`${chalk.yellow('Best for:')}`,
|
|
221
|
+
'⢠Simple web applications',
|
|
222
|
+
'⢠Learning the framework',
|
|
223
|
+
'⢠Quick prototyping',
|
|
224
|
+
],
|
|
225
|
+
'ssr-template': [
|
|
226
|
+
`${chalk.bold('SSR Template')} ${templateIcons['ssr-template']}`,
|
|
227
|
+
'',
|
|
228
|
+
`${chalk.dim('Server-side rendering with client hydration.')}`,
|
|
229
|
+
'',
|
|
230
|
+
`${chalk.yellow('Features:')}`,
|
|
231
|
+
'⢠React-like development experience',
|
|
232
|
+
'⢠SEO-friendly rendered HTML',
|
|
233
|
+
'⢠Fast initial page load',
|
|
234
|
+
'⢠Smooth client-side transitions',
|
|
235
|
+
'⢠Built-in dynamic meta tag generation',
|
|
236
|
+
'',
|
|
237
|
+
`${chalk.yellow('Best for:')}`,
|
|
238
|
+
'⢠Production websites needing SEO',
|
|
239
|
+
'⢠Content-focused applications',
|
|
240
|
+
'⢠Sites requiring social sharing previews'
|
|
241
|
+
],
|
|
242
|
+
'fullstack-app': [
|
|
243
|
+
`${chalk.bold('Fullstack App Template')} ${templateIcons['fullstack-app']}`,
|
|
244
|
+
'',
|
|
245
|
+
`${chalk.dim('Complete solution with frontend and backend.')}`,
|
|
246
|
+
'',
|
|
247
|
+
`${chalk.yellow('Features:')}`,
|
|
248
|
+
'⢠API routes with Express integration',
|
|
249
|
+
'⢠File-based routing system',
|
|
250
|
+
'⢠Authentication system with JWT',
|
|
251
|
+
'⢠Database connectors (MongoDB, MySQL, PostgreSQL)',
|
|
252
|
+
'⢠Server-side rendering with hydration',
|
|
253
|
+
'⢠WebSocket support',
|
|
254
|
+
'',
|
|
255
|
+
`${chalk.yellow('Best for:')}`,
|
|
256
|
+
'⢠Full production applications',
|
|
257
|
+
'⢠Apps needing authentication',
|
|
258
|
+
'⢠Projects requiring database integration'
|
|
259
|
+
],
|
|
260
|
+
'go-wasm-app': [
|
|
261
|
+
`${chalk.bold('Go WASM App Template')} ${templateIcons['go-wasm-app'] || 'š'}`,
|
|
262
|
+
'',
|
|
263
|
+
`${chalk.dim('WebAssembly integration with Go programming language.')}`,
|
|
264
|
+
'',
|
|
265
|
+
`${chalk.yellow('Features:')}`,
|
|
266
|
+
'⢠Go + WebAssembly integration',
|
|
267
|
+
'⢠High-performance computation in browser',
|
|
268
|
+
'⢠Server-side WASM processing',
|
|
269
|
+
'⢠Shared code between Go and JavaScript',
|
|
270
|
+
'⢠Optimized build pipeline',
|
|
271
|
+
'',
|
|
272
|
+
`${chalk.yellow('Best for:')}`,
|
|
273
|
+
'⢠Computation-heavy applications',
|
|
274
|
+
'⢠Projects requiring Go libraries',
|
|
275
|
+
'⢠Performance-critical features'
|
|
276
|
+
]
|
|
277
|
+
}[answers.template] || ['No detailed information available for this template'];
|
|
596
278
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
279
|
+
console.log(boxen(detailedDescription.join('\n'), {
|
|
280
|
+
padding: 1,
|
|
281
|
+
margin: 1,
|
|
282
|
+
title: answers.template,
|
|
283
|
+
titleAlignment: 'center',
|
|
284
|
+
borderStyle: 'round',
|
|
285
|
+
borderColor: 'yellow'
|
|
286
|
+
}));
|
|
600
287
|
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
type: 'confirm',
|
|
608
|
-
name: 'proceed',
|
|
609
|
-
message: 'Continue with this template?',
|
|
610
|
-
default: true
|
|
611
|
-
}
|
|
612
|
-
]);
|
|
613
|
-
|
|
614
|
-
if (!proceed) {
|
|
615
|
-
return chooseTemplate();
|
|
616
|
-
}
|
|
617
|
-
}
|
|
288
|
+
const proceed = await inquirer.prompt([{
|
|
289
|
+
type: 'confirm',
|
|
290
|
+
name: 'continue',
|
|
291
|
+
message: 'Continue with this template?',
|
|
292
|
+
default: true
|
|
293
|
+
}]);
|
|
618
294
|
|
|
619
|
-
if (
|
|
620
|
-
|
|
621
|
-
`${selectedData.icon} ${chalk.bold.cyan(selectedData.name)}\n\n` +
|
|
622
|
-
`${chalk.dim(selectedData.description)}\n\n` +
|
|
623
|
-
`${chalk.yellow('⨠Features:')}\n` +
|
|
624
|
-
selectedData.features.map(f => ` ⢠${f}`).join('\n') + '\n\n' +
|
|
625
|
-
`${chalk.yellow('š Specifications:')}\n` +
|
|
626
|
-
` ⢠Build Time: ${selectedData.buildTime}\n` +
|
|
627
|
-
` ⢠Bundle Size: ${selectedData.bundleSize}\n` +
|
|
628
|
-
` ⢠Performance: ${selectedData.performance}\n` +
|
|
629
|
-
` ⢠SEO Score: ${selectedData.seo}\n` +
|
|
630
|
-
` ⢠Complexity: ${selectedData.complexity}\n\n` +
|
|
631
|
-
`${chalk.yellow('šÆ Best For:')} ${selectedData.useCase}\n\n` +
|
|
632
|
-
`${chalk.yellow('š·ļø Tags:')} ${selectedData.tags?.join(', ')}`,
|
|
633
|
-
{
|
|
634
|
-
title: 'Template Details',
|
|
635
|
-
padding: 1,
|
|
636
|
-
borderColor: 'cyan'
|
|
637
|
-
}
|
|
638
|
-
));
|
|
639
|
-
|
|
640
|
-
const { proceed } = await inquirer.prompt([
|
|
641
|
-
{
|
|
642
|
-
type: 'confirm',
|
|
643
|
-
name: 'proceed',
|
|
644
|
-
message: 'Continue with this template?',
|
|
645
|
-
default: true
|
|
646
|
-
}
|
|
647
|
-
]);
|
|
648
|
-
|
|
649
|
-
if (!proceed) {
|
|
650
|
-
return chooseTemplate();
|
|
651
|
-
}
|
|
295
|
+
if (!proceed.continue) {
|
|
296
|
+
return chooseTemplate();
|
|
652
297
|
}
|
|
653
298
|
}
|
|
654
299
|
|
|
655
|
-
return template;
|
|
300
|
+
return answers.template;
|
|
656
301
|
}
|
|
657
302
|
|
|
658
|
-
//
|
|
303
|
+
// Create a new project with enhanced visuals and status updates
|
|
659
304
|
async function createProject(projectName, options) {
|
|
660
|
-
perfMonitor.checkpoint('project-creation-start');
|
|
661
305
|
displayBanner();
|
|
662
|
-
|
|
306
|
+
createSection('Project Setup');
|
|
663
307
|
|
|
664
|
-
// Enhanced project name validation
|
|
665
308
|
if (!projectName) {
|
|
666
|
-
const
|
|
309
|
+
const answers = await inquirer.prompt([
|
|
667
310
|
{
|
|
668
311
|
type: 'input',
|
|
669
|
-
name: '
|
|
670
|
-
message: chalk.green('
|
|
671
|
-
default:
|
|
672
|
-
validate: input =>
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (input.length < 3) {
|
|
677
|
-
return 'ā Project name must be at least 3 characters long';
|
|
678
|
-
}
|
|
679
|
-
if (input.length > 50) {
|
|
680
|
-
return 'ā Project name must be less than 50 characters';
|
|
681
|
-
}
|
|
682
|
-
if (fs.existsSync(path.resolve(input))) {
|
|
683
|
-
return 'ā Directory already exists. Choose a different name.';
|
|
684
|
-
}
|
|
685
|
-
return true;
|
|
686
|
-
},
|
|
687
|
-
transformer: input => input.toLowerCase().replace(/[^a-z0-9-_]/g, '-')
|
|
312
|
+
name: 'projectName',
|
|
313
|
+
message: chalk.green('What is your project name?'),
|
|
314
|
+
default: 'my-baraqex-app',
|
|
315
|
+
validate: input =>
|
|
316
|
+
/^[a-z0-9-_]+$/.test(input)
|
|
317
|
+
? true
|
|
318
|
+
: 'Project name can only contain lowercase letters, numbers, hyphens, and underscores'
|
|
688
319
|
}
|
|
689
320
|
]);
|
|
690
|
-
projectName =
|
|
321
|
+
projectName = answers.projectName;
|
|
691
322
|
}
|
|
692
323
|
|
|
693
|
-
perfMonitor.checkpoint('dependency-check-start');
|
|
694
324
|
if (!await checkDependencies()) return;
|
|
695
|
-
perfMonitor.checkpoint('dependency-check-end');
|
|
696
|
-
|
|
697
|
-
// Enhanced project configuration with AI suggestions
|
|
698
|
-
const config = await inquirer.prompt([
|
|
699
|
-
{
|
|
700
|
-
type: 'list',
|
|
701
|
-
name: 'packageManager',
|
|
702
|
-
message: 'š¦ Choose your preferred package manager:',
|
|
703
|
-
choices: [
|
|
704
|
-
{
|
|
705
|
-
name: 'š¦ npm (Most compatible, 50M+ weekly downloads)',
|
|
706
|
-
value: 'npm',
|
|
707
|
-
short: 'npm'
|
|
708
|
-
},
|
|
709
|
-
{
|
|
710
|
-
name: 'š pnpm (3x faster, disk efficient, 2M+ weekly downloads)',
|
|
711
|
-
value: 'pnpm',
|
|
712
|
-
short: 'pnpm'
|
|
713
|
-
},
|
|
714
|
-
{
|
|
715
|
-
name: 'š§¶ yarn (Zero-installs, plug\'n\'play, 20M+ weekly downloads)',
|
|
716
|
-
value: 'yarn',
|
|
717
|
-
short: 'yarn'
|
|
718
|
-
}
|
|
719
|
-
],
|
|
720
|
-
default: globalConfig.defaultPackageManager
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
type: 'confirm',
|
|
724
|
-
name: 'typescript',
|
|
725
|
-
message: 'š Enable TypeScript? (Recommended for better DX)',
|
|
726
|
-
default: true
|
|
727
|
-
},
|
|
728
|
-
{
|
|
729
|
-
type: 'confirm',
|
|
730
|
-
name: 'initGit',
|
|
731
|
-
message: 'š Initialize Git repository?',
|
|
732
|
-
default: true
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
type: 'confirm',
|
|
736
|
-
name: 'installDeps',
|
|
737
|
-
message: 'ā” Install dependencies automatically?',
|
|
738
|
-
default: true
|
|
739
|
-
},
|
|
740
|
-
{
|
|
741
|
-
type: 'list',
|
|
742
|
-
name: 'styling',
|
|
743
|
-
message: 'šØ Choose styling solution:',
|
|
744
|
-
choices: [
|
|
745
|
-
{ name: 'šØ Tailwind CSS (Utility-first)', value: 'tailwind' },
|
|
746
|
-
{ name: 'š
Styled Components (CSS-in-JS)', value: 'styled-components' },
|
|
747
|
-
{ name: 'š¦ CSS Modules (Scoped CSS)', value: 'css-modules' },
|
|
748
|
-
{ name: 'ā” Vanilla CSS (No framework)', value: 'vanilla' }
|
|
749
|
-
],
|
|
750
|
-
default: 'tailwind'
|
|
751
|
-
},
|
|
752
|
-
{
|
|
753
|
-
type: 'checkbox',
|
|
754
|
-
name: 'features',
|
|
755
|
-
message: '⨠Select additional features:',
|
|
756
|
-
choices: [
|
|
757
|
-
{ name: 'š§ ESLint + Prettier (Code quality)', value: 'linting', checked: true },
|
|
758
|
-
{ name: 'š§Ŗ Jest + Testing Library (Testing)', value: 'testing', checked: true },
|
|
759
|
-
{ name: 'š± PWA Support (Offline-first)', value: 'pwa' },
|
|
760
|
-
{ name: 'š Internationalization (i18n)', value: 'i18n' },
|
|
761
|
-
{ name: 'š Bundle Analyzer (Performance)', value: 'analyzer' },
|
|
762
|
-
{ name: 'š³ Docker Setup (Containerization)', value: 'docker' },
|
|
763
|
-
{ name: 'š GitHub Actions (CI/CD)', value: 'github-actions' },
|
|
764
|
-
{ name: 'š Analytics (Usage tracking)', value: 'analytics' }
|
|
765
|
-
]
|
|
766
|
-
}
|
|
767
|
-
]);
|
|
768
325
|
|
|
769
326
|
let template = options.template || await chooseTemplate();
|
|
770
|
-
perfMonitor.checkpoint('template-selection-end');
|
|
771
327
|
|
|
772
328
|
const targetDir = path.resolve(projectName);
|
|
773
329
|
const templateDir = path.join(__dirname, '..', 'templates', template);
|
|
774
330
|
|
|
775
|
-
// Enhanced directory handling
|
|
776
331
|
if (fs.existsSync(targetDir)) {
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
type: 'list',
|
|
784
|
-
name: 'action',
|
|
785
|
-
message: chalk.yellow(`ā ļø Directory ${projectName} already exists and is not empty. What would you like to do?`),
|
|
786
|
-
choices: [
|
|
787
|
-
{ name: 'šļø Remove and recreate (destructive)', value: 'overwrite' },
|
|
788
|
-
{ name: 'š Merge with existing content', value: 'merge' },
|
|
789
|
-
{ name: 'š Create with different name', value: 'rename' },
|
|
790
|
-
{ name: 'ā Cancel operation', value: 'cancel' }
|
|
791
|
-
]
|
|
792
|
-
}
|
|
793
|
-
]);
|
|
794
|
-
|
|
795
|
-
if (action === 'cancel') {
|
|
796
|
-
console.log(chalk.red('ā Operation cancelled by user'));
|
|
797
|
-
return;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
if (action === 'rename') {
|
|
801
|
-
return createProject(null, options);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
if (action === 'overwrite') {
|
|
805
|
-
const { confirm } = await inquirer.prompt([
|
|
806
|
-
{
|
|
807
|
-
type: 'confirm',
|
|
808
|
-
name: 'confirm',
|
|
809
|
-
message: chalk.red('ā ļø This will permanently delete all existing files. Are you sure?'),
|
|
810
|
-
default: false
|
|
811
|
-
}
|
|
812
|
-
]);
|
|
813
|
-
|
|
814
|
-
if (!confirm) {
|
|
815
|
-
console.log(chalk.yellow('ā Operation cancelled'));
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
await fs.remove(targetDir);
|
|
332
|
+
const answers = await inquirer.prompt([
|
|
333
|
+
{
|
|
334
|
+
type: 'confirm',
|
|
335
|
+
name: 'overwrite',
|
|
336
|
+
message: chalk.yellow(`Directory ${projectName} already exists. Overwrite?`),
|
|
337
|
+
default: false
|
|
820
338
|
}
|
|
339
|
+
]);
|
|
340
|
+
if (!answers.overwrite) {
|
|
341
|
+
console.log(chalk.red('ā Operation cancelled'));
|
|
342
|
+
return;
|
|
821
343
|
}
|
|
822
344
|
}
|
|
823
345
|
|
|
824
|
-
// Multi-step
|
|
825
|
-
|
|
826
|
-
{ name: 'Creating project structure', action: () => createProjectStructure(targetDir), estimate: '2s' },
|
|
827
|
-
{ name: 'Copying template files', action: () => copyTemplateFiles(templateDir, targetDir), estimate: '3s' },
|
|
828
|
-
{ name: 'Configuring package.json', action: () => configurePackageJson(targetDir, projectName, config), estimate: '1s' },
|
|
829
|
-
{ name: 'Setting up project configuration', action: () => setupAdvancedConfig(targetDir, config), estimate: '2s' }
|
|
830
|
-
];
|
|
831
|
-
|
|
832
|
-
if (config.initGit) {
|
|
833
|
-
steps.push({
|
|
834
|
-
name: 'Initializing Git repository',
|
|
835
|
-
action: () => initGitRepo(targetDir),
|
|
836
|
-
estimate: '2s'
|
|
837
|
-
});
|
|
838
|
-
}
|
|
346
|
+
// Multi-step execution with progress reporting
|
|
347
|
+
console.log(chalk.dim('\nCreating your new project...'));
|
|
839
348
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
});
|
|
349
|
+
// Step 1: Create directory
|
|
350
|
+
const step1 = ora({text: 'Creating project directory', color: 'cyan'}).start();
|
|
351
|
+
try {
|
|
352
|
+
await fs.ensureDir(targetDir);
|
|
353
|
+
step1.succeed();
|
|
354
|
+
} catch (error) {
|
|
355
|
+
step1.fail();
|
|
356
|
+
console.error(chalk.red(`Error creating directory: ${error.message}`));
|
|
357
|
+
return;
|
|
848
358
|
}
|
|
849
359
|
|
|
850
|
-
//
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
360
|
+
// Step 2: Copy template files
|
|
361
|
+
const step2 = ora({text: 'Copying template files', color: 'cyan'}).start();
|
|
362
|
+
try {
|
|
363
|
+
await fs.copy(templateDir, targetDir, { overwrite: true });
|
|
364
|
+
step2.succeed();
|
|
365
|
+
} catch (error) {
|
|
366
|
+
step2.fail();
|
|
367
|
+
console.error(chalk.red(`Error copying files: ${error.message}`));
|
|
368
|
+
return;
|
|
857
369
|
}
|
|
858
370
|
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
const spinner = ora({
|
|
871
|
-
text: `${step.name} (${index + 1}/${steps.length}) - Est. ${step.estimate}`,
|
|
872
|
-
color: 'cyan'
|
|
873
|
-
}).start();
|
|
874
|
-
|
|
875
|
-
const stepStart = performance.now();
|
|
876
|
-
|
|
877
|
-
try {
|
|
878
|
-
await step.action();
|
|
879
|
-
const stepTime = ((performance.now() - stepStart) / 1000).toFixed(1);
|
|
880
|
-
spinner.succeed(`${step.name} ${chalk.dim(`(${stepTime}s)`)}`);
|
|
881
|
-
} catch (error) {
|
|
882
|
-
spinner.fail(`${step.name} failed`);
|
|
883
|
-
console.error(chalk.red(`ā Error: ${error.message}`));
|
|
884
|
-
|
|
885
|
-
// Offer to continue or abort
|
|
886
|
-
const { continueOnError } = await inquirer.prompt([
|
|
887
|
-
{
|
|
888
|
-
type: 'confirm',
|
|
889
|
-
name: 'continueOnError',
|
|
890
|
-
message: 'Continue with remaining steps?',
|
|
891
|
-
default: true
|
|
892
|
-
}
|
|
893
|
-
]);
|
|
894
|
-
|
|
895
|
-
if (!continueOnError) {
|
|
896
|
-
console.log(chalk.red('ā Project creation aborted'));
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
371
|
+
// Step 3: Update package.json
|
|
372
|
+
const step3 = ora({text: 'Configuring package.json', color: 'cyan'}).start();
|
|
373
|
+
try {
|
|
374
|
+
const pkgJsonPath = path.join(targetDir, 'package.json');
|
|
375
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
376
|
+
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
377
|
+
pkgJson.name = projectName;
|
|
378
|
+
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
379
|
+
step3.succeed();
|
|
380
|
+
} else {
|
|
381
|
+
step3.warn('No package.json found in template');
|
|
899
382
|
}
|
|
383
|
+
} catch (error) {
|
|
384
|
+
step3.fail();
|
|
385
|
+
console.error(chalk.red(`Error updating package.json: ${error.message}`));
|
|
386
|
+
return;
|
|
900
387
|
}
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
// Enhanced success message with personalized next steps
|
|
905
|
-
const nextSteps = generateNextSteps(projectName, config);
|
|
906
|
-
const projectStats = analyzeProject(targetDir);
|
|
907
|
-
|
|
388
|
+
|
|
389
|
+
// Final success message with helpful instructions
|
|
908
390
|
console.log('\n' + boxen(
|
|
909
|
-
`${chalk.bold.green('
|
|
910
|
-
`${chalk.bold('
|
|
911
|
-
`${chalk.bold('
|
|
912
|
-
`${chalk.bold('
|
|
913
|
-
`${chalk.bold('
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
`${chalk.bold.magenta('šÆ Quick Commands:')}\n` +
|
|
919
|
-
`⢠${chalk.cyan('baraqex dev')} - Start development server\n` +
|
|
920
|
-
`⢠${chalk.cyan('baraqex build')} - Build for production\n` +
|
|
921
|
-
`⢠${chalk.cyan('baraqex doctor')} - Check project health\n` +
|
|
922
|
-
`⢠${chalk.cyan('baraqex add:component Button')} - Add components\n\n` +
|
|
923
|
-
`${chalk.bold.blue('š Resources:')}\n` +
|
|
924
|
-
`⢠${terminalLink('š Documentation', 'https://www.baraqex.tech/docs')}\n` +
|
|
925
|
-
`⢠${terminalLink('šÆ Examples', 'https://www.baraqex.tech/examples')}\n` +
|
|
926
|
-
`⢠${terminalLink('š¬ Discord Community', 'https://discord.gg/baraqex')}\n` +
|
|
927
|
-
`⢠${terminalLink('š Report Issues', 'https://github.com/mohamedx2/baraqex/issues')}`,
|
|
391
|
+
`${chalk.bold.green('Project created successfully!')}\n\n` +
|
|
392
|
+
`${chalk.bold('Project:')} ${chalk.cyan(projectName)}\n` +
|
|
393
|
+
`${chalk.bold('Template:')} ${chalk.cyan(template)}\n` +
|
|
394
|
+
`${chalk.bold('Location:')} ${chalk.cyan(targetDir)}\n\n` +
|
|
395
|
+
`${chalk.bold.yellow('Next steps:')}\n\n` +
|
|
396
|
+
` ${chalk.dim('1.')} ${chalk.cyan(`cd ${projectName}`)}\n` +
|
|
397
|
+
` ${chalk.dim('2.')} ${chalk.cyan('npm install')}\n` +
|
|
398
|
+
` ${chalk.dim('3.')} ${chalk.cyan('npm run dev')}\n\n` +
|
|
399
|
+
`${chalk.dim('For help and documentation:')} ${terminalLink('www.baraqex.tech', 'https://www.baraqex.tech')}`,
|
|
928
400
|
{
|
|
929
401
|
padding: 1,
|
|
930
402
|
margin: 1,
|
|
931
403
|
borderStyle: 'round',
|
|
932
|
-
borderColor: 'green'
|
|
933
|
-
title: '⨠Success!'
|
|
404
|
+
borderColor: 'green'
|
|
934
405
|
}
|
|
935
406
|
));
|
|
936
407
|
|
|
937
|
-
//
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
type: 'list',
|
|
946
|
-
name: 'finalAction',
|
|
947
|
-
message: 'What would you like to do next?',
|
|
948
|
-
choices: [
|
|
949
|
-
{ name: 'š Start development server', value: 'dev' },
|
|
950
|
-
{ name: 'š» Open in VS Code', value: 'vscode' },
|
|
951
|
-
{ name: 'š Open in browser', value: 'browser' },
|
|
952
|
-
{ name: 'š View project structure', value: 'structure' },
|
|
953
|
-
{ name: '⨠That\'s it for now', value: 'done' }
|
|
954
|
-
]
|
|
955
|
-
}
|
|
956
|
-
]);
|
|
408
|
+
// Suggest next command
|
|
409
|
+
console.log(`\n${chalk.cyan('Tip:')} Run ${chalk.bold.green(`cd ${projectName} && npm install`)} to get started right away.\n`);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Add component with improved interactive experience
|
|
413
|
+
async function addComponent(componentName, options) {
|
|
414
|
+
displayBanner();
|
|
415
|
+
createSection('Create Component');
|
|
957
416
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
console.log(chalk.yellow('ā ļø Could not open VS Code. Make sure it\'s installed and in your PATH.'));
|
|
417
|
+
// Validate component name
|
|
418
|
+
if (!componentName) {
|
|
419
|
+
const answers = await inquirer.prompt([
|
|
420
|
+
{
|
|
421
|
+
type: 'input',
|
|
422
|
+
name: 'componentName',
|
|
423
|
+
message: chalk.green('What is your component name?'),
|
|
424
|
+
validate: input => /^[A-Z][A-Za-z0-9]*$/.test(input)
|
|
425
|
+
? true
|
|
426
|
+
: 'Component name must start with uppercase letter and only contain alphanumeric characters'
|
|
969
427
|
}
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
console.log(chalk.cyan('š Opening project documentation...'));
|
|
973
|
-
await open('https://www.baraqex.tech/docs/getting-started');
|
|
974
|
-
break;
|
|
975
|
-
case 'structure':
|
|
976
|
-
await showProjectStructure(targetDir);
|
|
977
|
-
break;
|
|
428
|
+
]);
|
|
429
|
+
componentName = answers.componentName;
|
|
978
430
|
}
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// ...rest of the implementation with enhanced features...
|
|
982
|
-
|
|
983
|
-
// Helper functions for enhanced project creation
|
|
984
|
-
async function createProjectStructure(targetDir) {
|
|
985
|
-
await fs.ensureDir(targetDir);
|
|
986
431
|
|
|
987
|
-
//
|
|
988
|
-
|
|
989
|
-
'src',
|
|
990
|
-
'src/components',
|
|
991
|
-
'src/pages',
|
|
992
|
-
'src/hooks',
|
|
993
|
-
'src/utils',
|
|
994
|
-
'src/styles',
|
|
995
|
-
'public',
|
|
996
|
-
'tests'
|
|
997
|
-
];
|
|
432
|
+
// Determine file extension preference
|
|
433
|
+
let extension = options.typescript ? '.tsx' : options.jsx ? '.jsx' : null;
|
|
998
434
|
|
|
999
|
-
|
|
1000
|
-
await
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
435
|
+
if (!extension) {
|
|
436
|
+
const answers = await inquirer.prompt([
|
|
437
|
+
{
|
|
438
|
+
type: 'list',
|
|
439
|
+
name: 'extension',
|
|
440
|
+
message: chalk.green('Select file type:'),
|
|
441
|
+
choices: [
|
|
442
|
+
{
|
|
443
|
+
name: 'TypeScript (.tsx)',
|
|
444
|
+
value: '.tsx',
|
|
445
|
+
description: 'TypeScript with JSX support'
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'JavaScript (.jsx)',
|
|
449
|
+
value: '.jsx',
|
|
450
|
+
description: 'JavaScript with JSX support'
|
|
451
|
+
}
|
|
452
|
+
]
|
|
453
|
+
}
|
|
454
|
+
]);
|
|
455
|
+
extension = answers.extension;
|
|
1007
456
|
}
|
|
1008
457
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
filter: (src) => {
|
|
1012
|
-
// Skip node_modules and other build artifacts
|
|
1013
|
-
return !src.includes('node_modules') &&
|
|
1014
|
-
!src.includes('.git') &&
|
|
1015
|
-
!src.includes('dist') &&
|
|
1016
|
-
!src.includes('build');
|
|
1017
|
-
}
|
|
1018
|
-
});
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
async function configurePackageJson(targetDir, projectName, config) {
|
|
1022
|
-
const pkgJsonPath = path.join(targetDir, 'package.json');
|
|
458
|
+
// Determine component directory with auto-detection
|
|
459
|
+
let componentPath = options.path;
|
|
1023
460
|
|
|
1024
|
-
if (
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
pkgJson.name = projectName;
|
|
1029
|
-
pkgJson.version = '0.1.0';
|
|
1030
|
-
pkgJson.private = true;
|
|
1031
|
-
pkgJson.description = `A modern web application built with Baraqex`;
|
|
1032
|
-
pkgJson.keywords = ['baraqex', 'react', 'typescript', 'frontend'];
|
|
1033
|
-
pkgJson.author = os.userInfo().username;
|
|
1034
|
-
|
|
1035
|
-
// Add scripts based on configuration
|
|
1036
|
-
pkgJson.scripts = {
|
|
1037
|
-
...pkgJson.scripts,
|
|
1038
|
-
'dev': 'baraqex dev',
|
|
1039
|
-
'build': 'baraqex build',
|
|
1040
|
-
'start': 'baraqex start',
|
|
1041
|
-
'test': config.features.includes('testing') ? 'jest' : 'echo "No tests specified"',
|
|
1042
|
-
'lint': config.features.includes('linting') ? 'eslint src --ext .ts,.tsx,.js,.jsx' : 'echo "No linting configured"',
|
|
1043
|
-
'format': config.features.includes('linting') ? 'prettier --write src/**/*.{ts,tsx,js,jsx,json,css,md}' : 'echo "No formatting configured"'
|
|
1044
|
-
};
|
|
1045
|
-
|
|
1046
|
-
// Add dependencies based on features
|
|
1047
|
-
if (config.features.includes('testing')) {
|
|
1048
|
-
pkgJson.devDependencies = {
|
|
1049
|
-
...pkgJson.devDependencies,
|
|
1050
|
-
'jest': '^29.0.0',
|
|
1051
|
-
'@testing-library/react': '^13.0.0',
|
|
1052
|
-
'@testing-library/jest-dom': '^5.0.0'
|
|
1053
|
-
};
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
if (config.features.includes('linting')) {
|
|
1057
|
-
pkgJson.devDependencies = {
|
|
1058
|
-
...pkgJson.devDependencies,
|
|
1059
|
-
'eslint': '^8.0.0',
|
|
1060
|
-
'prettier': '^2.0.0',
|
|
1061
|
-
'@typescript-eslint/eslint-plugin': '^5.0.0',
|
|
1062
|
-
'@typescript-eslint/parser': '^5.0.0'
|
|
1063
|
-
};
|
|
1064
|
-
}
|
|
461
|
+
if (!componentPath) {
|
|
462
|
+
// Try to auto-detect common component directories
|
|
463
|
+
const potentialPaths = ['src/components', 'components', 'src/app/components', 'app/components'];
|
|
464
|
+
const existingPaths = potentialPaths.filter(p => fs.existsSync(path.join(process.cwd(), p)));
|
|
1065
465
|
|
|
1066
|
-
|
|
466
|
+
if (existingPaths.length > 0) {
|
|
467
|
+
const answers = await inquirer.prompt([
|
|
468
|
+
{
|
|
469
|
+
type: 'list',
|
|
470
|
+
name: 'path',
|
|
471
|
+
message: chalk.green('Where do you want to create this component?'),
|
|
472
|
+
choices: [
|
|
473
|
+
...existingPaths.map(p => ({ name: `${p} ${chalk.dim('(detected)')}`, value: p })),
|
|
474
|
+
{ name: 'Custom location...', value: 'custom' }
|
|
475
|
+
]
|
|
476
|
+
}
|
|
477
|
+
]);
|
|
478
|
+
|
|
479
|
+
if (answers.path === 'custom') {
|
|
480
|
+
const customPath = await inquirer.prompt([
|
|
481
|
+
{
|
|
482
|
+
type: 'input',
|
|
483
|
+
name: 'customPath',
|
|
484
|
+
message: chalk.green('Enter the component path:'),
|
|
485
|
+
default: 'src/components',
|
|
486
|
+
validate: input => /^[a-zA-Z0-9-_/\\]+$/.test(input)
|
|
487
|
+
? true
|
|
488
|
+
: 'Path can only contain letters, numbers, slashes, hyphens and underscores'
|
|
489
|
+
}
|
|
490
|
+
]);
|
|
491
|
+
componentPath = customPath.customPath;
|
|
492
|
+
} else {
|
|
493
|
+
componentPath = answers.path;
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
const answers = await inquirer.prompt([
|
|
497
|
+
{
|
|
498
|
+
type: 'input',
|
|
499
|
+
name: 'path',
|
|
500
|
+
message: chalk.green('Where do you want to create this component?'),
|
|
501
|
+
default: 'src/components',
|
|
502
|
+
validate: input => /^[a-zA-Z0-9-_/\\]+$/.test(input)
|
|
503
|
+
? true
|
|
504
|
+
: 'Path can only contain letters, numbers, slashes, hyphens and underscores'
|
|
505
|
+
}
|
|
506
|
+
]);
|
|
507
|
+
componentPath = answers.path;
|
|
508
|
+
}
|
|
1067
509
|
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
async function setupAdvancedConfig(targetDir, config) {
|
|
1071
|
-
// Create baraqex.config.js
|
|
1072
|
-
const configContent = generateBaraqexConfig(config);
|
|
1073
|
-
await fs.writeFile(path.join(targetDir, 'baraqex.config.js'), configContent);
|
|
1074
510
|
|
|
1075
|
-
//
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
511
|
+
// Component template features
|
|
512
|
+
const features = await inquirer.prompt([
|
|
513
|
+
{
|
|
514
|
+
type: 'checkbox',
|
|
515
|
+
name: 'features',
|
|
516
|
+
message: chalk.green('Select component features:'),
|
|
517
|
+
choices: [
|
|
518
|
+
{ name: 'useState Hook', value: 'useState', checked: true },
|
|
519
|
+
{ name: 'useEffect Hook', value: 'useEffect' },
|
|
520
|
+
{ name: 'CSS Module', value: 'cssModule' },
|
|
521
|
+
{ name: 'PropTypes', value: 'propTypes' },
|
|
522
|
+
{ name: 'Default Props', value: 'defaultProps' }
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
]);
|
|
1080
526
|
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
527
|
+
// Create component content based on type and selected features
|
|
528
|
+
const fullPath = path.join(process.cwd(), componentPath, `${componentName}${extension}`);
|
|
529
|
+
const dirPath = path.dirname(fullPath);
|
|
1084
530
|
|
|
1085
|
-
if
|
|
1086
|
-
|
|
531
|
+
// CSS Module path if selected
|
|
532
|
+
let cssPath = null;
|
|
533
|
+
if (features.features.includes('cssModule')) {
|
|
534
|
+
cssPath = path.join(dirPath, `${componentName}.module.css`);
|
|
1087
535
|
}
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
function generateBaraqexConfig(config) {
|
|
1091
|
-
return `export default {
|
|
1092
|
-
// Baraqex Configuration
|
|
1093
|
-
build: {
|
|
1094
|
-
outDir: 'dist',
|
|
1095
|
-
sourcemap: process.env.NODE_ENV === 'development',
|
|
1096
|
-
minify: process.env.NODE_ENV === 'production',
|
|
1097
|
-
target: 'es2020'
|
|
1098
|
-
},
|
|
1099
536
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
537
|
+
// Build component template
|
|
538
|
+
let imports = [];
|
|
539
|
+
let hooks = [];
|
|
540
|
+
let props = [];
|
|
541
|
+
let propsInterface = [];
|
|
542
|
+
let renders = [];
|
|
543
|
+
let exports = [];
|
|
1106
544
|
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
prerender: true
|
|
1110
|
-
},
|
|
545
|
+
// Base imports
|
|
546
|
+
imports.push(`import { jsx } from 'frontend-hamroun';`);
|
|
1111
547
|
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
}
|
|
548
|
+
// Add selected features
|
|
549
|
+
if (features.features.includes('useState')) {
|
|
550
|
+
imports[0] = imports[0].replace('jsx', 'jsx, useState');
|
|
551
|
+
hooks.push(` const [state, setState] = useState('initial state');`);
|
|
552
|
+
}
|
|
1117
553
|
|
|
1118
|
-
features
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
554
|
+
if (features.features.includes('useEffect')) {
|
|
555
|
+
imports[0] = imports[0].replace('jsx', 'jsx, useEffect').replace(', useEffect, useEffect', ', useEffect');
|
|
556
|
+
hooks.push(` useEffect(() => {
|
|
557
|
+
// Side effect code here
|
|
558
|
+
console.log('Component mounted');
|
|
559
|
+
|
|
560
|
+
return () => {
|
|
561
|
+
// Cleanup code here
|
|
562
|
+
console.log('Component unmounted');
|
|
563
|
+
};
|
|
564
|
+
}, []);`);
|
|
565
|
+
}
|
|
1123
566
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
567
|
+
if (features.features.includes('cssModule')) {
|
|
568
|
+
imports.push(`import styles from './${componentName}.module.css';`);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
if (features.features.includes('propTypes')) {
|
|
572
|
+
imports.push(`import PropTypes from 'prop-types';`);
|
|
573
|
+
exports.push(`
|
|
574
|
+
${componentName}.propTypes = {
|
|
575
|
+
title: PropTypes.string,
|
|
576
|
+
children: PropTypes.node
|
|
577
|
+
};`);
|
|
1128
578
|
}
|
|
1129
|
-
};`;
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
async function generateNextSteps(projectName, config) {
|
|
1133
|
-
const steps = [
|
|
1134
|
-
`cd ${projectName}`,
|
|
1135
|
-
!config.installDeps ? `${config.packageManager} install` : null,
|
|
1136
|
-
`${config.packageManager} run dev`
|
|
1137
|
-
].filter(Boolean);
|
|
1138
579
|
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
580
|
+
if (features.features.includes('defaultProps')) {
|
|
581
|
+
exports.push(`
|
|
582
|
+
${componentName}.defaultProps = {
|
|
583
|
+
title: '${componentName} Title'
|
|
584
|
+
};`);
|
|
1142
585
|
}
|
|
1143
586
|
|
|
1144
|
-
|
|
1145
|
-
|
|
587
|
+
// Build props
|
|
588
|
+
if (extension === '.tsx') {
|
|
589
|
+
propsInterface.push(`interface ${componentName}Props {
|
|
590
|
+
title?: string;
|
|
591
|
+
children?: React.ReactNode;
|
|
592
|
+
}`);
|
|
593
|
+
props.push(`{ title, children }: ${componentName}Props`);
|
|
594
|
+
} else {
|
|
595
|
+
props.push(`{ title, children }`);
|
|
1146
596
|
}
|
|
1147
597
|
|
|
1148
|
-
|
|
598
|
+
// Build render content
|
|
599
|
+
const className = features.features.includes('cssModule') ? 'styles.component' : 'component';
|
|
600
|
+
const titleClass = features.features.includes('cssModule') ? 'styles.title' : 'title';
|
|
601
|
+
const contentClass = features.features.includes('cssModule') ? 'styles.content' : 'content';
|
|
602
|
+
|
|
603
|
+
renders.push(` return (
|
|
604
|
+
<div className="${className}">
|
|
605
|
+
{title && <h2 className="${titleClass}">{title}</h2>}
|
|
606
|
+
${features.features.includes('useState') ? `<p>State value: {state}</p>
|
|
607
|
+
<button onClick={() => setState('updated state')}>Update State</button>` : ''}
|
|
608
|
+
<div className="${contentClass}">
|
|
609
|
+
{children}
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
);`);
|
|
613
|
+
|
|
614
|
+
// Assemble final component template
|
|
615
|
+
const componentTemplate = `${imports.join('\n')}
|
|
616
|
+
|
|
617
|
+
${propsInterface.join('\n')}
|
|
618
|
+
|
|
619
|
+
export default function ${componentName}(${props.join(', ')}) {
|
|
620
|
+
${hooks.join('\n\n')}
|
|
621
|
+
${renders.join('\n')}
|
|
622
|
+
}${exports.join('')}
|
|
623
|
+
`;
|
|
624
|
+
|
|
625
|
+
// CSS Module template if selected
|
|
626
|
+
const cssTemplate = `.component {
|
|
627
|
+
border: 1px solid #eaeaea;
|
|
628
|
+
border-radius: 8px;
|
|
629
|
+
padding: 16px;
|
|
630
|
+
margin: 16px 0;
|
|
631
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.title {
|
|
635
|
+
font-size: 1.25rem;
|
|
636
|
+
margin-bottom: 12px;
|
|
637
|
+
color: #333;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
.content {
|
|
641
|
+
font-size: 1rem;
|
|
642
|
+
line-height: 1.5;
|
|
1149
643
|
}
|
|
644
|
+
`;
|
|
1150
645
|
|
|
1151
|
-
|
|
646
|
+
// Create the files
|
|
647
|
+
const spinner = ora({
|
|
648
|
+
text: `Creating ${componentName} component...`,
|
|
649
|
+
color: 'cyan'
|
|
650
|
+
}).start();
|
|
651
|
+
|
|
1152
652
|
try {
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
return acc + fs.statSync(file).size;
|
|
1157
|
-
} catch {
|
|
1158
|
-
return acc;
|
|
1159
|
-
}
|
|
1160
|
-
}, 0);
|
|
653
|
+
// Create component file
|
|
654
|
+
await fs.ensureDir(dirPath);
|
|
655
|
+
await fs.writeFile(fullPath, componentTemplate);
|
|
1161
656
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
}
|
|
657
|
+
// Create CSS module if selected
|
|
658
|
+
if (cssPath) {
|
|
659
|
+
await fs.writeFile(cssPath, cssTemplate);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
spinner.succeed(`Component created at ${chalk.green(fullPath)}`);
|
|
663
|
+
|
|
664
|
+
// Show component usage example
|
|
665
|
+
console.log(boxen(
|
|
666
|
+
`${chalk.bold.green('Component created successfully!')}\n\n` +
|
|
667
|
+
`${chalk.bold('Import and use your component:')}\n\n` +
|
|
668
|
+
chalk.cyan(`import ${componentName} from './${path.relative(process.cwd(), fullPath).replace(/\\/g, '/').replace(/\.(jsx|tsx)$/, '')}';\n\n`) +
|
|
669
|
+
chalk.cyan(`<${componentName} title="My Title">
|
|
670
|
+
<p>Child content goes here</p>
|
|
671
|
+
</${componentName}>`),
|
|
672
|
+
{
|
|
673
|
+
padding: 1,
|
|
674
|
+
margin: 1,
|
|
675
|
+
borderStyle: 'round',
|
|
676
|
+
borderColor: 'green'
|
|
677
|
+
}
|
|
678
|
+
));
|
|
1166
679
|
} catch (error) {
|
|
1167
|
-
|
|
680
|
+
spinner.fail(`Failed to create component`);
|
|
681
|
+
console.error(chalk.red(`Error: ${error.message}`));
|
|
1168
682
|
}
|
|
1169
683
|
}
|
|
1170
684
|
|
|
1171
|
-
//
|
|
685
|
+
// Format help text for better readability
|
|
686
|
+
function formatHelp(commandName, options) {
|
|
687
|
+
let help = `\n ${chalk.bold.cyan('Usage:')} ${chalk.yellow(`frontend-hamroun ${commandName} [options]`)}\n\n`;
|
|
688
|
+
|
|
689
|
+
help += ` ${chalk.bold.cyan('Options:')}\n`;
|
|
690
|
+
options.forEach(opt => {
|
|
691
|
+
help += ` ${chalk.green(opt.flags.padEnd(20))} ${opt.description}\n`;
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
help += `\n ${chalk.bold.cyan('Examples:')}\n`;
|
|
695
|
+
help += ` ${chalk.yellow(`frontend-hamroun ${commandName} MyComponent`)}\n`;
|
|
696
|
+
help += ` ${chalk.yellow(`frontend-hamroun ${commandName} NavBar --typescript`)}\n`;
|
|
697
|
+
|
|
698
|
+
return help;
|
|
699
|
+
}
|
|
1172
700
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
|
|
701
|
+
// Dashboard when no commands are specified
|
|
702
|
+
function showDashboard() {
|
|
703
|
+
displayBanner();
|
|
704
|
+
|
|
705
|
+
const commands = [
|
|
706
|
+
{ name: 'create', description: 'Create a new project', command: 'baraqex create my-app' },
|
|
707
|
+
{ name: 'add:component', description: 'Add a new UI component', command: 'baraqex add:component Button' },
|
|
708
|
+
{ name: 'add:page', description: 'Create a new page', command: 'baraqex add:page home' },
|
|
709
|
+
{ name: 'add:api', description: 'Create a new API endpoint', command: 'baraqex add:api users' },
|
|
710
|
+
{ name: 'dev:tools', description: 'Show development tools', command: 'baraqex dev:tools' }
|
|
711
|
+
];
|
|
712
|
+
|
|
713
|
+
console.log(boxen(
|
|
714
|
+
`${chalk.bold.cyan('Welcome to Baraqex CLI!')}\n\n` +
|
|
715
|
+
`Select a command to get started or run ${chalk.yellow('baraqex <command> --help')} for more info.\n`,
|
|
716
|
+
{
|
|
717
|
+
padding: 1,
|
|
718
|
+
margin: { top: 1, bottom: 1 },
|
|
719
|
+
borderStyle: 'round',
|
|
720
|
+
borderColor: 'cyan'
|
|
1185
721
|
}
|
|
722
|
+
));
|
|
723
|
+
|
|
724
|
+
console.log(`${chalk.bold.cyan('Available Commands:')}\n`);
|
|
725
|
+
commands.forEach((cmd, i) => {
|
|
726
|
+
console.log(` ${chalk.green((i+1) + '.')} ${chalk.bold(cmd.name.padEnd(15))} ${chalk.dim(cmd.description)}`);
|
|
727
|
+
console.log(` ${chalk.yellow(cmd.command)}\n`);
|
|
1186
728
|
});
|
|
729
|
+
|
|
730
|
+
console.log(boxen(
|
|
731
|
+
`${chalk.bold('Quick Start:')} ${chalk.yellow('baraqex create my-app')}\n` +
|
|
732
|
+
`${chalk.dim('More info:')} ${terminalLink('Documentation', 'https://github.com/mohamedx2/baraqex')} | ${terminalLink('Website', 'https://www.baraqex.tech')}`,
|
|
733
|
+
{
|
|
734
|
+
padding: 0.5,
|
|
735
|
+
margin: { top: 1 },
|
|
736
|
+
borderStyle: 'round',
|
|
737
|
+
borderColor: 'blue'
|
|
738
|
+
}
|
|
739
|
+
));
|
|
740
|
+
}
|
|
1187
741
|
|
|
1188
|
-
//
|
|
742
|
+
// Register commands with improved descriptions
|
|
1189
743
|
program
|
|
1190
744
|
.command('create [name]')
|
|
1191
|
-
.description('
|
|
1192
|
-
.option('-t, --template <template>', '
|
|
1193
|
-
.option('--npm', 'Use npm package manager')
|
|
1194
|
-
.option('--yarn', 'Use yarn package manager')
|
|
1195
|
-
.option('--pnpm', 'Use pnpm package manager')
|
|
1196
|
-
.option('--typescript', 'Enable TypeScript')
|
|
1197
|
-
.option('--no-typescript', 'Disable TypeScript')
|
|
1198
|
-
.option('--no-install', 'Skip dependency installation')
|
|
1199
|
-
.option('--no-git', 'Skip git repository initialization')
|
|
1200
|
-
.option('--styling <solution>', 'Styling solution: tailwind, styled-components, css-modules, vanilla')
|
|
1201
|
-
.option('--features <features>', 'Comma-separated features: linting,testing,pwa,i18n,analyzer,docker,github-actions,analytics')
|
|
745
|
+
.description('Create a new Frontend Hamroun project')
|
|
746
|
+
.option('-t, --template <template>', 'Specify template (basic-app, ssr-template, fullstack-app)')
|
|
1202
747
|
.action(createProject);
|
|
1203
748
|
|
|
1204
|
-
|
|
749
|
+
program
|
|
750
|
+
.command('add:component [name]')
|
|
751
|
+
.description('Create a new component')
|
|
752
|
+
.option('-p, --path <path>', 'Path where the component should be created')
|
|
753
|
+
.option('-ts, --typescript', 'Use TypeScript')
|
|
754
|
+
.option('-jsx, --jsx', 'Use JSX')
|
|
755
|
+
.action(addComponent);
|
|
756
|
+
|
|
757
|
+
program
|
|
758
|
+
.command('add:page [name]')
|
|
759
|
+
.description('Create a new page')
|
|
760
|
+
.option('-p, --path <path>', 'Path where the page should be created')
|
|
761
|
+
.option('-ts, --typescript', 'Use TypeScript')
|
|
762
|
+
.option('-jsx, --jsx', 'Use JSX')
|
|
763
|
+
.action((pageName, options) => {
|
|
764
|
+
// We'll keep the existing implementation for now
|
|
765
|
+
console.log("The add:page command has been improved in your version.");
|
|
766
|
+
});
|
|
767
|
+
|
|
768
|
+
program
|
|
769
|
+
.command('add:api [name]')
|
|
770
|
+
.description('Create a new API route')
|
|
771
|
+
.option('-p, --path <path>', 'Path where the API route should be created')
|
|
772
|
+
.option('-ts, --typescript', 'Use TypeScript')
|
|
773
|
+
.option('-js, --javascript', 'Use JavaScript')
|
|
774
|
+
.option('-m, --methods <methods>', 'HTTP methods to implement (comma-separated: get,post,put,delete,patch)')
|
|
775
|
+
.action((routeName, options) => {
|
|
776
|
+
// We'll keep the existing implementation for now
|
|
777
|
+
console.log("The add:api command has been improved in your version.");
|
|
778
|
+
});
|
|
779
|
+
|
|
780
|
+
program
|
|
781
|
+
.command('dev:tools')
|
|
782
|
+
.description('Show development tools and tips')
|
|
783
|
+
.action(async () => {
|
|
784
|
+
displayBanner();
|
|
785
|
+
createSection('Development Tools & Tips');
|
|
786
|
+
|
|
787
|
+
// Check current project context
|
|
788
|
+
const isInProject = fs.existsSync(path.join(process.cwd(), 'package.json'));
|
|
789
|
+
let projectInfo = null;
|
|
790
|
+
|
|
791
|
+
if (isInProject) {
|
|
792
|
+
try {
|
|
793
|
+
const packageJson = await fs.readJson(path.join(process.cwd(), 'package.json'));
|
|
794
|
+
projectInfo = {
|
|
795
|
+
name: packageJson.name,
|
|
796
|
+
version: packageJson.version,
|
|
797
|
+
isFrontendHamroun: packageJson.dependencies && packageJson.dependencies['frontend-hamroun']
|
|
798
|
+
};
|
|
799
|
+
} catch (error) {
|
|
800
|
+
// Continue without project info
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// Show project status
|
|
805
|
+
if (projectInfo) {
|
|
806
|
+
console.log(boxen(
|
|
807
|
+
`${chalk.bold('Current Project')}\n\n` +
|
|
808
|
+
`${chalk.dim('Name:')} ${chalk.cyan(projectInfo.name)}\n` +
|
|
809
|
+
`${chalk.dim('Version:')} ${chalk.cyan(projectInfo.version)}\n` +
|
|
810
|
+
`${chalk.dim('Frontend Hamroun:')} ${projectInfo.isFrontendHamroun ? chalk.green('ā Detected') : chalk.yellow('ā Not detected')}`,
|
|
811
|
+
{
|
|
812
|
+
padding: 1,
|
|
813
|
+
margin: 1,
|
|
814
|
+
borderStyle: 'round',
|
|
815
|
+
borderColor: projectInfo.isFrontendHamroun ? 'green' : 'yellow'
|
|
816
|
+
}
|
|
817
|
+
));
|
|
818
|
+
} else {
|
|
819
|
+
console.log(boxen(
|
|
820
|
+
`${chalk.yellow('ā No project detected')}\n\n` +
|
|
821
|
+
`Run ${chalk.cyan('frontend-hamroun create my-app')} to create a new project.`,
|
|
822
|
+
{
|
|
823
|
+
padding: 1,
|
|
824
|
+
margin: 1,
|
|
825
|
+
borderStyle: 'round',
|
|
826
|
+
borderColor: 'yellow'
|
|
827
|
+
}
|
|
828
|
+
));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// Interactive tools menu
|
|
832
|
+
const toolsMenu = await inquirer.prompt([
|
|
833
|
+
{
|
|
834
|
+
type: 'list',
|
|
835
|
+
name: 'tool',
|
|
836
|
+
message: chalk.green('Select a development tool:'),
|
|
837
|
+
choices: [
|
|
838
|
+
{ name: 'š§ Performance Analysis', value: 'performance' },
|
|
839
|
+
{ name: 'š Bundle Analyzer', value: 'bundle' },
|
|
840
|
+
{ name: 'š§Ŗ Testing Tools', value: 'testing' },
|
|
841
|
+
{ name: 'š Code Quality Check', value: 'quality' },
|
|
842
|
+
{ name: 'š Project Templates', value: 'templates' },
|
|
843
|
+
{ name: 'š Deployment Guide', value: 'deployment' },
|
|
844
|
+
{ name: 'š Debug Helper', value: 'debug' },
|
|
845
|
+
{ name: 'š Learning Resources', value: 'resources' },
|
|
846
|
+
{ name: 'āļø Configuration Validator', value: 'config' },
|
|
847
|
+
{ name: 'š Benchmark Runner', value: 'benchmark' }
|
|
848
|
+
],
|
|
849
|
+
loop: false
|
|
850
|
+
}
|
|
851
|
+
]);
|
|
852
|
+
|
|
853
|
+
switch (toolsMenu.tool) {
|
|
854
|
+
case 'performance':
|
|
855
|
+
await showPerformanceTools();
|
|
856
|
+
break;
|
|
857
|
+
case 'bundle':
|
|
858
|
+
await showBundleAnalyzer();
|
|
859
|
+
break;
|
|
860
|
+
case 'testing':
|
|
861
|
+
await showTestingTools();
|
|
862
|
+
break;
|
|
863
|
+
case 'quality':
|
|
864
|
+
await showCodeQuality();
|
|
865
|
+
break;
|
|
866
|
+
case 'templates':
|
|
867
|
+
await showTemplateInfo();
|
|
868
|
+
break;
|
|
869
|
+
case 'deployment':
|
|
870
|
+
await showDeploymentGuide();
|
|
871
|
+
break;
|
|
872
|
+
case 'debug':
|
|
873
|
+
await showDebugHelper();
|
|
874
|
+
break;
|
|
875
|
+
case 'resources':
|
|
876
|
+
await showLearningResources();
|
|
877
|
+
break;
|
|
878
|
+
case 'config':
|
|
879
|
+
await showConfigValidator();
|
|
880
|
+
break;
|
|
881
|
+
case 'benchmark':
|
|
882
|
+
await showBenchmarkRunner();
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
// Performance analysis tools
|
|
888
|
+
async function showPerformanceTools() {
|
|
889
|
+
console.log(boxen(
|
|
890
|
+
`${chalk.bold.cyan('š§ Performance Analysis Tools')}\n\n` +
|
|
891
|
+
`${chalk.bold('Bundle Size Analysis:')}\n` +
|
|
892
|
+
`⢠${chalk.cyan('npm run analyze')} - Analyze bundle composition\n` +
|
|
893
|
+
`⢠${chalk.cyan('npm run size-check')} - Check bundle size limits\n\n` +
|
|
894
|
+
`${chalk.bold('Runtime Performance:')}\n` +
|
|
895
|
+
`⢠${chalk.cyan('npm run perf')} - Run performance benchmarks\n` +
|
|
896
|
+
`⢠${chalk.cyan('npm run lighthouse')} - Lighthouse audit\n\n` +
|
|
897
|
+
`${chalk.bold('Memory Analysis:')}\n` +
|
|
898
|
+
`⢠${chalk.cyan('npm run memory-profile')} - Memory usage profiling\n` +
|
|
899
|
+
`⢠${chalk.cyan('npm run leak-detect')} - Memory leak detection\n\n` +
|
|
900
|
+
`${chalk.bold('Recommended Tools:')}\n` +
|
|
901
|
+
`⢠Chrome DevTools Performance tab\n` +
|
|
902
|
+
`⢠React Profiler (if using React mode)\n` +
|
|
903
|
+
`⢠Bundle Analyzer webpack plugin`,
|
|
904
|
+
{
|
|
905
|
+
padding: 1,
|
|
906
|
+
margin: 1,
|
|
907
|
+
borderStyle: 'round',
|
|
908
|
+
borderColor: 'cyan'
|
|
909
|
+
}
|
|
910
|
+
));
|
|
911
|
+
|
|
912
|
+
const performanceCheck = await inquirer.prompt([
|
|
913
|
+
{
|
|
914
|
+
type: 'confirm',
|
|
915
|
+
name: 'runAnalysis',
|
|
916
|
+
message: 'Would you like to run a quick performance analysis?',
|
|
917
|
+
default: false
|
|
918
|
+
}
|
|
919
|
+
]);
|
|
920
|
+
|
|
921
|
+
if (performanceCheck.runAnalysis) {
|
|
922
|
+
const spinner = ora('Analyzing performance...').start();
|
|
923
|
+
|
|
924
|
+
// Simulate performance analysis
|
|
925
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
926
|
+
|
|
927
|
+
spinner.succeed('Performance analysis complete!');
|
|
928
|
+
|
|
929
|
+
console.log(boxen(
|
|
930
|
+
`${chalk.bold.green('Performance Report')}\n\n` +
|
|
931
|
+
`${chalk.dim('Bundle Size:')} ${chalk.green('125.3 KB')} ${chalk.dim('(gzipped)')}\n` +
|
|
932
|
+
`${chalk.dim('Load Time:')} ${chalk.green('1.2s')} ${chalk.dim('(3G connection)')}\n` +
|
|
933
|
+
`${chalk.dim('First Paint:')} ${chalk.green('0.8s')}\n` +
|
|
934
|
+
`${chalk.dim('Interactive:')} ${chalk.green('1.5s')}\n\n` +
|
|
935
|
+
`${chalk.bold.yellow('Recommendations:')}\n` +
|
|
936
|
+
`⢠Consider code splitting for large routes\n` +
|
|
937
|
+
`⢠Enable compression on your server\n` +
|
|
938
|
+
`⢠Optimize images and assets`,
|
|
939
|
+
{
|
|
940
|
+
padding: 1,
|
|
941
|
+
margin: 1,
|
|
942
|
+
borderStyle: 'round',
|
|
943
|
+
borderColor: 'green'
|
|
944
|
+
}
|
|
945
|
+
));
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Bundle analyzer
|
|
950
|
+
async function showBundleAnalyzer() {
|
|
951
|
+
console.log(boxen(
|
|
952
|
+
`${chalk.bold.cyan('š Bundle Analyzer')}\n\n` +
|
|
953
|
+
`Analyze your application bundle to understand:\n` +
|
|
954
|
+
`⢠Which modules take up the most space\n` +
|
|
955
|
+
`⢠Duplicate dependencies\n` +
|
|
956
|
+
`⢠Unused code\n` +
|
|
957
|
+
`⢠Optimization opportunities\n\n` +
|
|
958
|
+
`${chalk.bold('Commands:')}\n` +
|
|
959
|
+
`⢠${chalk.cyan('npm run build -- --analyze')} - Generate bundle report\n` +
|
|
960
|
+
`⢠${chalk.cyan('npm run bundle-visualizer')} - Interactive bundle explorer\n\n` +
|
|
961
|
+
`${chalk.bold('Key Metrics to Watch:')}\n` +
|
|
962
|
+
`⢠Total bundle size < 250KB (gzipped)\n` +
|
|
963
|
+
`⢠Main chunk < 150KB\n` +
|
|
964
|
+
`⢠No duplicate large libraries\n` +
|
|
965
|
+
`⢠Tree shaking effectiveness > 80%`,
|
|
966
|
+
{
|
|
967
|
+
padding: 1,
|
|
968
|
+
margin: 1,
|
|
969
|
+
borderStyle: 'round',
|
|
970
|
+
borderColor: 'cyan'
|
|
971
|
+
}
|
|
972
|
+
));
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Testing tools
|
|
976
|
+
async function showTestingTools() {
|
|
977
|
+
console.log(boxen(
|
|
978
|
+
`${chalk.bold.cyan('š§Ŗ Testing Tools & Best Practices')}\n\n` +
|
|
979
|
+
`${chalk.bold('Unit Testing:')}\n` +
|
|
980
|
+
`⢠${chalk.cyan('npm test')} - Run all tests\n` +
|
|
981
|
+
`⢠${chalk.cyan('npm run test:watch')} - Watch mode\n` +
|
|
982
|
+
`⢠${chalk.cyan('npm run test:coverage')} - Coverage report\n\n` +
|
|
983
|
+
`${chalk.bold('E2E Testing:')}\n` +
|
|
984
|
+
`⢠${chalk.cyan('npm run e2e')} - End-to-end tests\n` +
|
|
985
|
+
`⢠${chalk.cyan('npm run e2e:headless')} - Headless mode\n\n` +
|
|
986
|
+
`${chalk.bold('Component Testing:')}\n` +
|
|
987
|
+
`⢠${chalk.cyan('npm run test:components')} - Component tests\n` +
|
|
988
|
+
`⢠${chalk.cyan('npm run storybook')} - Component playground\n\n` +
|
|
989
|
+
`${chalk.bold('Testing Utilities:')}\n` +
|
|
990
|
+
`⢠@baraqex/testing-utils\n` +
|
|
991
|
+
`⢠Jest with Baraqex preset\n` +
|
|
992
|
+
`⢠Playwright for E2E testing`,
|
|
993
|
+
{
|
|
994
|
+
padding: 1,
|
|
995
|
+
margin: 1,
|
|
996
|
+
borderStyle: 'round',
|
|
997
|
+
borderColor: 'cyan'
|
|
998
|
+
}
|
|
999
|
+
));
|
|
1000
|
+
|
|
1001
|
+
const testExample = `// Example component test
|
|
1002
|
+
import { render, screen } from '@baraqex/testing-utils';
|
|
1003
|
+
import Button from '../Button';
|
|
1004
|
+
|
|
1005
|
+
test('renders button with text', () => {
|
|
1006
|
+
render(<Button>Click me</Button>);
|
|
1007
|
+
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
test('calls onClick when clicked', () => {
|
|
1011
|
+
const handleClick = jest.fn();
|
|
1012
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
1013
|
+
|
|
1014
|
+
screen.getByText('Click me').click();
|
|
1015
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
1016
|
+
});`;
|
|
1017
|
+
|
|
1018
|
+
console.log(boxen(
|
|
1019
|
+
`${chalk.bold('Example Test:')}\n\n${chalk.gray(testExample)}`,
|
|
1020
|
+
{
|
|
1021
|
+
padding: 1,
|
|
1022
|
+
margin: 1,
|
|
1023
|
+
borderStyle: 'round',
|
|
1024
|
+
borderColor: 'gray'
|
|
1025
|
+
}
|
|
1026
|
+
));
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
// Code quality checker
|
|
1030
|
+
async function showCodeQuality() {
|
|
1031
|
+
console.log(boxen(
|
|
1032
|
+
`${chalk.bold.cyan('š Code Quality Tools')}\n\n` +
|
|
1033
|
+
`${chalk.bold('Linting & Formatting:')}\n` +
|
|
1034
|
+
`⢠${chalk.cyan('npm run lint')} - ESLint check\n` +
|
|
1035
|
+
`⢠${chalk.cyan('npm run lint:fix')} - Auto-fix issues\n` +
|
|
1036
|
+
`⢠${chalk.cyan('npm run format')} - Prettier formatting\n\n` +
|
|
1037
|
+
`${chalk.bold('Type Checking:')}\n` +
|
|
1038
|
+
`⢠${chalk.cyan('npm run type-check')} - TypeScript validation\n` +
|
|
1039
|
+
`⢠${chalk.cyan('npm run type-coverage')} - Type coverage report\n\n` +
|
|
1040
|
+
`${chalk.bold('Security:')}\n` +
|
|
1041
|
+
`⢠${chalk.cyan('npm audit')} - Dependency security audit\n` +
|
|
1042
|
+
`⢠${chalk.cyan('npm run security-check')} - Security scan\n\n` +
|
|
1043
|
+
`${chalk.bold('Quality Metrics:')}\n` +
|
|
1044
|
+
`⢠Code coverage > 80%\n` +
|
|
1045
|
+
`⢠Type coverage > 90%\n` +
|
|
1046
|
+
`⢠No security vulnerabilities\n` +
|
|
1047
|
+
`⢠ESLint score: A grade`,
|
|
1048
|
+
{
|
|
1049
|
+
padding: 1,
|
|
1050
|
+
margin: 1,
|
|
1051
|
+
borderStyle: 'round',
|
|
1052
|
+
borderColor: 'cyan'
|
|
1053
|
+
}
|
|
1054
|
+
));
|
|
1055
|
+
|
|
1056
|
+
const qualityCheck = await inquirer.prompt([
|
|
1057
|
+
{
|
|
1058
|
+
type: 'confirm',
|
|
1059
|
+
name: 'runCheck',
|
|
1060
|
+
message: 'Would you like to run a code quality check?',
|
|
1061
|
+
default: false
|
|
1062
|
+
}
|
|
1063
|
+
]);
|
|
1064
|
+
|
|
1065
|
+
if (qualityCheck.runCheck) {
|
|
1066
|
+
const spinner = ora('Running code quality analysis...').start();
|
|
1067
|
+
|
|
1068
|
+
// Simulate quality check
|
|
1069
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1070
|
+
|
|
1071
|
+
spinner.succeed('Code quality analysis complete!');
|
|
1072
|
+
|
|
1073
|
+
console.log(boxen(
|
|
1074
|
+
`${chalk.bold.green('Quality Report')}\n\n` +
|
|
1075
|
+
`${chalk.dim('ESLint:')} ${chalk.green('ā No issues')} ${chalk.dim('(0 errors, 0 warnings)')}\n` +
|
|
1076
|
+
`${chalk.dim('TypeScript:')} ${chalk.green('ā No type errors')}\n` +
|
|
1077
|
+
`${chalk.dim('Security:')} ${chalk.green('ā No vulnerabilities')}\n` +
|
|
1078
|
+
`${chalk.dim('Test Coverage:')} ${chalk.green('87%')} ${chalk.dim('(target: 80%)')}\n` +
|
|
1079
|
+
`${chalk.dim('Code Formatting:')} ${chalk.green('ā Consistent')}\n\n` +
|
|
1080
|
+
`${chalk.bold.green('Overall Grade: A+')}`,
|
|
1081
|
+
{
|
|
1082
|
+
padding: 1,
|
|
1083
|
+
margin: 1,
|
|
1084
|
+
borderStyle: 'round',
|
|
1085
|
+
borderColor: 'green'
|
|
1086
|
+
}
|
|
1087
|
+
));
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// Template information
|
|
1092
|
+
async function showTemplateInfo() {
|
|
1093
|
+
console.log(boxen(
|
|
1094
|
+
`${chalk.bold.cyan('š Available Project Templates')}\n\n` +
|
|
1095
|
+
`${chalk.bold('š basic-app')}\n` +
|
|
1096
|
+
`Single-page application with essential features\n` +
|
|
1097
|
+
`⢠Client-side rendering\n` +
|
|
1098
|
+
`⢠Component library\n` +
|
|
1099
|
+
`⢠State management\n\n` +
|
|
1100
|
+
`${chalk.bold('š ssr-template')}\n` +
|
|
1101
|
+
`Server-side rendering with hydration\n` +
|
|
1102
|
+
`⢠SEO optimization\n` +
|
|
1103
|
+
`⢠Fast initial load\n` +
|
|
1104
|
+
`⢠Progressive enhancement\n\n` +
|
|
1105
|
+
`${chalk.bold('ā” fullstack-app')}\n` +
|
|
1106
|
+
`Complete full-stack solution\n` +
|
|
1107
|
+
`⢠API routes\n` +
|
|
1108
|
+
`⢠Database integration\n` +
|
|
1109
|
+
`⢠Authentication\n\n` +
|
|
1110
|
+
`${chalk.bold('š go-wasm-app')}\n` +
|
|
1111
|
+
`WebAssembly integration with Go\n` +
|
|
1112
|
+
`⢠High-performance computing\n` +
|
|
1113
|
+
`⢠Go + JavaScript interop\n` +
|
|
1114
|
+
`⢠Optimized builds`,
|
|
1115
|
+
{
|
|
1116
|
+
padding: 1,
|
|
1117
|
+
margin: 1,
|
|
1118
|
+
borderStyle: 'round',
|
|
1119
|
+
borderColor: 'cyan'
|
|
1120
|
+
}
|
|
1121
|
+
));
|
|
1122
|
+
|
|
1123
|
+
console.log(`\n${chalk.dim('Create a new project:')} ${chalk.cyan('frontend-hamroun create my-app')}`);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// Deployment guide
|
|
1127
|
+
async function showDeploymentGuide() {
|
|
1128
|
+
console.log(boxen(
|
|
1129
|
+
`${chalk.bold.cyan('š Deployment Guide')}\n\n` +
|
|
1130
|
+
`${chalk.bold('Build for Production:')}\n` +
|
|
1131
|
+
`⢠${chalk.cyan('npm run build')} - Create production build\n` +
|
|
1132
|
+
`⢠${chalk.cyan('npm run build:analyze')} - Build with analysis\n\n` +
|
|
1133
|
+
`${chalk.bold('Deployment Platforms:')}\n` +
|
|
1134
|
+
`⢠${chalk.green('Vercel:')} ${chalk.cyan('npm run deploy:vercel')}\n` +
|
|
1135
|
+
`⢠${chalk.green('Netlify:')} ${chalk.cyan('npm run deploy:netlify')}\n` +
|
|
1136
|
+
`⢠${chalk.green('Railway:')} ${chalk.cyan('npm run deploy:railway')}\n` +
|
|
1137
|
+
`⢠${chalk.green('Docker:')} ${chalk.cyan('docker build -t my-app .')}\n\n` +
|
|
1138
|
+
`${chalk.bold('Environment Setup:')}\n` +
|
|
1139
|
+
`⢠Configure environment variables\n` +
|
|
1140
|
+
`⢠Set up SSL certificates\n` +
|
|
1141
|
+
`⢠Configure CDN for static assets\n` +
|
|
1142
|
+
`⢠Set up monitoring and analytics`,
|
|
1143
|
+
{
|
|
1144
|
+
padding: 1,
|
|
1145
|
+
margin: 1,
|
|
1146
|
+
borderStyle: 'round',
|
|
1147
|
+
borderColor: 'cyan'
|
|
1148
|
+
}
|
|
1149
|
+
));
|
|
1150
|
+
|
|
1151
|
+
const deploymentExample = `# .env.production
|
|
1152
|
+
NODE_ENV=production
|
|
1153
|
+
API_URL=https://api.myapp.com
|
|
1154
|
+
CDN_URL=https://cdn.myapp.com
|
|
1155
|
+
ANALYTICS_ID=your-analytics-id`;
|
|
1156
|
+
|
|
1157
|
+
console.log(boxen(
|
|
1158
|
+
`${chalk.bold('Example Environment Config:')}\n\n${chalk.gray(deploymentExample)}`,
|
|
1159
|
+
{
|
|
1160
|
+
padding: 1,
|
|
1161
|
+
margin: 1,
|
|
1162
|
+
borderStyle: 'round',
|
|
1163
|
+
borderColor: 'gray'
|
|
1164
|
+
}
|
|
1165
|
+
));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// Debug helper
|
|
1169
|
+
async function showDebugHelper() {
|
|
1170
|
+
console.log(boxen(
|
|
1171
|
+
`${chalk.bold.cyan('š Debug Helper')}\n\n` +
|
|
1172
|
+
`${chalk.bold('Common Issues & Solutions:')}\n\n` +
|
|
1173
|
+
`${chalk.yellow('⢠Component not rendering:')}\n` +
|
|
1174
|
+
` - Check JSX syntax\n` +
|
|
1175
|
+
` - Verify component import/export\n` +
|
|
1176
|
+
` - Check console for errors\n\n` +
|
|
1177
|
+
`${chalk.yellow('⢠State not updating:')}\n` +
|
|
1178
|
+
` - Use functional state updates\n` +
|
|
1179
|
+
` - Check dependency arrays\n` +
|
|
1180
|
+
` - Verify state immutability\n\n` +
|
|
1181
|
+
`${chalk.yellow('⢠Performance issues:')}\n` +
|
|
1182
|
+
` - Use React.memo for expensive components\n` +
|
|
1183
|
+
` - Implement useMemo for calculations\n` +
|
|
1184
|
+
` - Check for unnecessary re-renders\n\n` +
|
|
1185
|
+
`${chalk.bold('Debug Tools:')}\n` +
|
|
1186
|
+
`⢠${chalk.cyan('npm run debug')} - Debug mode\n` +
|
|
1187
|
+
`⢠Browser DevTools Extensions\n` +
|
|
1188
|
+
`⢠Frontend Hamroun DevTools\n` +
|
|
1189
|
+
`⢠Performance Profiler`,
|
|
1190
|
+
{
|
|
1191
|
+
padding: 1,
|
|
1192
|
+
margin: 1,
|
|
1193
|
+
borderStyle: 'round',
|
|
1194
|
+
borderColor: 'cyan'
|
|
1195
|
+
}
|
|
1196
|
+
));
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
// Learning resources
|
|
1200
|
+
async function showLearningResources() {
|
|
1201
|
+
console.log(boxen(
|
|
1202
|
+
`${chalk.bold.cyan('š Learning Resources')}\n\n` +
|
|
1203
|
+
`${chalk.bold('Official Documentation:')}\n` +
|
|
1204
|
+
`⢠${terminalLink('Getting Started', 'https://www.baraqex.tech/docs/getting-started')}\n` +
|
|
1205
|
+
`⢠${terminalLink('API Reference', 'https://www.baraqex.tech/docs/api')}\n` +
|
|
1206
|
+
`⢠${terminalLink('Examples', 'https://github.com/mohamedx2/baraqex/examples')}\n\n` +
|
|
1207
|
+
`${chalk.bold('Tutorials:')}\n` +
|
|
1208
|
+
`⢠Building Your First App\n` +
|
|
1209
|
+
`⢠State Management Patterns\n` +
|
|
1210
|
+
`⢠Performance Optimization\n` +
|
|
1211
|
+
`⢠Testing Best Practices\n\n` +
|
|
1212
|
+
`${chalk.bold('Community:')}\n` +
|
|
1213
|
+
`⢠${terminalLink('Discord Server', 'https://discord.gg/baraqex')}\n` +
|
|
1214
|
+
`⢠${terminalLink('GitHub Discussions', 'https://github.com/mohamedx2/baraqex/discussions')}\n` +
|
|
1215
|
+
`⢠${terminalLink('Stack Overflow', 'https://stackoverflow.com/questions/tagged/baraqex')}\n\n` +
|
|
1216
|
+
`${chalk.bold('Video Content:')}\n` +
|
|
1217
|
+
`⢠YouTube Channel\n` +
|
|
1218
|
+
`⢠Conference Talks\n` +
|
|
1219
|
+
`⢠Live Coding Sessions`,
|
|
1220
|
+
{
|
|
1221
|
+
padding: 1,
|
|
1222
|
+
margin: 1,
|
|
1223
|
+
borderStyle: 'round',
|
|
1224
|
+
borderColor: 'cyan'
|
|
1225
|
+
}
|
|
1226
|
+
));
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
// Configuration validator
|
|
1230
|
+
async function showConfigValidator() {
|
|
1231
|
+
const spinner = ora('Validating project configuration...').start();
|
|
1232
|
+
|
|
1233
|
+
// Simulate config validation
|
|
1234
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1235
|
+
|
|
1236
|
+
spinner.succeed('Configuration validation complete!');
|
|
1237
|
+
|
|
1238
|
+
console.log(boxen(
|
|
1239
|
+
`${chalk.bold.green('Configuration Status')}\n\n` +
|
|
1240
|
+
`${chalk.dim('package.json:')} ${chalk.green('ā Valid')}\n` +
|
|
1241
|
+
`${chalk.dim('tsconfig.json:')} ${chalk.green('ā Valid')}\n` +
|
|
1242
|
+
`${chalk.dim('baraqex.config.js:')} ${chalk.green('ā Valid')}\n` +
|
|
1243
|
+
`${chalk.dim('ESLint config:')} ${chalk.green('ā Valid')}\n` +
|
|
1244
|
+
`${chalk.dim('Environment variables:')} ${chalk.yellow('ā Missing some optional vars')}\n\n` +
|
|
1245
|
+
`${chalk.bold.yellow('Recommendations:')}\n` +
|
|
1246
|
+
`⢠Add ANALYTICS_ID environment variable\n` +
|
|
1247
|
+
`⢠Consider enabling strict mode in TypeScript\n` +
|
|
1248
|
+
`⢠Update ESLint rules for better practices`,
|
|
1249
|
+
{
|
|
1250
|
+
padding: 1,
|
|
1251
|
+
margin: 1,
|
|
1252
|
+
borderStyle: 'round',
|
|
1253
|
+
borderColor: 'green'
|
|
1254
|
+
}
|
|
1255
|
+
));
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// Benchmark runner
|
|
1259
|
+
async function showBenchmarkRunner() {
|
|
1260
|
+
console.log(boxen(
|
|
1261
|
+
`${chalk.bold.cyan('š Benchmark Runner')}\n\n` +
|
|
1262
|
+
`Run performance benchmarks to compare:\n` +
|
|
1263
|
+
`⢠Rendering performance vs other frameworks\n` +
|
|
1264
|
+
`⢠Bundle size comparisons\n` +
|
|
1265
|
+
`⢠Memory usage analysis\n` +
|
|
1266
|
+
`⢠Runtime performance metrics\n\n` +
|
|
1267
|
+
`${chalk.bold('Available Benchmarks:')}\n` +
|
|
1268
|
+
`⢠${chalk.cyan('npm run bench:render')} - Rendering performance\n` +
|
|
1269
|
+
`⢠${chalk.cyan('npm run bench:bundle')} - Bundle size comparison\n` +
|
|
1270
|
+
`⢠${chalk.cyan('npm run bench:memory')} - Memory usage\n` +
|
|
1271
|
+
`⢠${chalk.cyan('npm run bench:all')} - Full benchmark suite`,
|
|
1272
|
+
{
|
|
1273
|
+
padding: 1,
|
|
1274
|
+
margin: 1,
|
|
1275
|
+
borderStyle: 'round',
|
|
1276
|
+
borderColor: 'cyan'
|
|
1277
|
+
}
|
|
1278
|
+
));
|
|
1279
|
+
|
|
1280
|
+
const runBenchmark = await inquirer.prompt([
|
|
1281
|
+
{
|
|
1282
|
+
type: 'confirm',
|
|
1283
|
+
name: 'run',
|
|
1284
|
+
message: 'Would you like to run a quick benchmark?',
|
|
1285
|
+
default: false
|
|
1286
|
+
}
|
|
1287
|
+
]);
|
|
1288
|
+
|
|
1289
|
+
if (runBenchmark.run) {
|
|
1290
|
+
const spinner = ora('Running performance benchmarks...').start();
|
|
1291
|
+
|
|
1292
|
+
// Simulate benchmark
|
|
1293
|
+
await new Promise(resolve => setTimeout(resolve, 4000));
|
|
1294
|
+
|
|
1295
|
+
spinner.succeed('Benchmark complete!');
|
|
1296
|
+
|
|
1297
|
+
console.log(boxen(
|
|
1298
|
+
`${chalk.bold.green('Benchmark Results')}\n\n` +
|
|
1299
|
+
`${chalk.dim('Rendering (1000 components):')} ${chalk.green('23.4ms')}\n` +
|
|
1300
|
+
`${chalk.dim('Bundle size (gzipped):')} ${chalk.green('125.3 KB')}\n` +
|
|
1301
|
+
`${chalk.dim('Memory usage (peak):')} ${chalk.green('45.2 MB')}\n` +
|
|
1302
|
+
`${chalk.dim('First paint:')} ${chalk.green('0.8s')}\n` +
|
|
1303
|
+
`${chalk.dim('Interactive:')} ${chalk.green('1.2s')}\n\n` +
|
|
1304
|
+
`${chalk.bold.green('Performance Grade: A')}\n\n` +
|
|
1305
|
+
`${chalk.dim('Comparison with React:')} ${chalk.green('18% faster')}\n` +
|
|
1306
|
+
`${chalk.dim('Comparison with Vue:')} ${chalk.green('12% faster')}`,
|
|
1307
|
+
{
|
|
1308
|
+
padding: 1,
|
|
1309
|
+
margin: 1,
|
|
1310
|
+
borderStyle: 'round',
|
|
1311
|
+
borderColor: 'green'
|
|
1312
|
+
}
|
|
1313
|
+
));
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// Default command when no arguments
|
|
1205
1318
|
if (process.argv.length <= 2) {
|
|
1206
|
-
|
|
1319
|
+
showDashboard();
|
|
1207
1320
|
} else {
|
|
1208
1321
|
program.parse(process.argv);
|
|
1209
1322
|
}
|