slicejs-cli 2.3.2 → 2.5.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.
@@ -0,0 +1,333 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { createServer } from 'net';
5
+ import chalk from 'chalk';
6
+ import Table from 'cli-table3';
7
+ import Print from '../Print.js';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+
11
+ /**
12
+ * Verifica la versión de Node.js
13
+ */
14
+ async function checkNodeVersion() {
15
+ const currentVersion = process.version;
16
+ const majorVersion = parseInt(currentVersion.slice(1).split('.')[0]);
17
+ const required = 20;
18
+
19
+ if (majorVersion >= required) {
20
+ return {
21
+ pass: true,
22
+ message: `Node.js version: ${currentVersion} (required: >= v${required})`
23
+ };
24
+ } else {
25
+ return {
26
+ pass: false,
27
+ message: `Node.js version: ${currentVersion} (required: >= v${required})`,
28
+ suggestion: `Update Node.js to v${required} or higher`
29
+ };
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Verifica la estructura de directorios
35
+ */
36
+ async function checkDirectoryStructure() {
37
+ const srcPath = path.join(process.cwd(), 'src');
38
+ const apiPath = path.join(process.cwd(), 'api');
39
+
40
+ const srcExists = await fs.pathExists(srcPath);
41
+ const apiExists = await fs.pathExists(apiPath);
42
+
43
+ if (srcExists && apiExists) {
44
+ return {
45
+ pass: true,
46
+ message: 'Project structure (src/ and api/) exists'
47
+ };
48
+ } else {
49
+ const missing = [];
50
+ if (!srcExists) missing.push('src/');
51
+ if (!apiExists) missing.push('api/');
52
+
53
+ return {
54
+ pass: false,
55
+ message: `Missing directories: ${missing.join(', ')}`,
56
+ suggestion: 'Run "slice init" to initialize your project'
57
+ };
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Verifica sliceConfig.json
63
+ */
64
+ async function checkConfig() {
65
+ const configPath = path.join(process.cwd(), 'src', 'sliceConfig.json');
66
+
67
+ if (!await fs.pathExists(configPath)) {
68
+ return {
69
+ pass: false,
70
+ message: 'sliceConfig.json not found',
71
+ suggestion: 'Run "slice init" to create configuration'
72
+ };
73
+ }
74
+
75
+ try {
76
+ const config = await fs.readJson(configPath);
77
+
78
+ if (!config.paths || !config.paths.components) {
79
+ return {
80
+ pass: false,
81
+ message: 'sliceConfig.json is invalid (missing paths.components)',
82
+ suggestion: 'Check your configuration file'
83
+ };
84
+ }
85
+
86
+ return {
87
+ pass: true,
88
+ message: 'sliceConfig.json is valid'
89
+ };
90
+ } catch (error) {
91
+ return {
92
+ pass: false,
93
+ message: `sliceConfig.json is invalid JSON: ${error.message}`,
94
+ suggestion: 'Fix JSON syntax errors in sliceConfig.json'
95
+ };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Verifica disponibilidad del puerto
101
+ */
102
+ async function checkPort() {
103
+ const configPath = path.join(process.cwd(), 'src', 'sliceConfig.json');
104
+ let port = 3000;
105
+
106
+ try {
107
+ if (await fs.pathExists(configPath)) {
108
+ const config = await fs.readJson(configPath);
109
+ port = config.server?.port || 3000;
110
+ }
111
+ } catch { }
112
+
113
+ return new Promise((resolve) => {
114
+ const server = createServer();
115
+
116
+ server.once('error', (err) => {
117
+ if (err.code === 'EADDRINUSE') {
118
+ resolve({
119
+ warn: true,
120
+ message: `Port ${port} is already in use`,
121
+ suggestion: `Stop the process using port ${port} or use: slice dev -p <other-port>`
122
+ });
123
+ } else {
124
+ resolve({
125
+ pass: true,
126
+ message: `Port ${port} is available`
127
+ });
128
+ }
129
+ });
130
+
131
+ server.once('listening', () => {
132
+ server.close();
133
+ resolve({
134
+ pass: true,
135
+ message: `Port ${port} is available`
136
+ });
137
+ });
138
+
139
+ server.listen(port);
140
+ });
141
+ }
142
+
143
+ /**
144
+ * Verifica dependencias en package.json
145
+ */
146
+ async function checkDependencies() {
147
+ const packagePath = path.join(process.cwd(), 'package.json');
148
+
149
+ if (!await fs.pathExists(packagePath)) {
150
+ return {
151
+ warn: true,
152
+ message: 'package.json not found',
153
+ suggestion: 'Run "npm init" to create package.json'
154
+ };
155
+ }
156
+
157
+ try {
158
+ const pkg = await fs.readJson(packagePath);
159
+ const hasCli = pkg.dependencies?.['slicejs-cli'] || pkg.devDependencies?.['slicejs-cli'];
160
+ const hasFramework = pkg.dependencies?.['slicejs-web-framework'];
161
+
162
+ if (hasCli && hasFramework) {
163
+ return {
164
+ pass: true,
165
+ message: 'All required dependencies are installed'
166
+ };
167
+ } else {
168
+ const missing = [];
169
+ if (!hasCli) missing.push('slicejs-cli');
170
+ if (!hasFramework) missing.push('slicejs-web-framework');
171
+
172
+ return {
173
+ warn: true,
174
+ message: `Missing dependencies: ${missing.join(', ')}`,
175
+ suggestion: 'Run "npm install"'
176
+ };
177
+ }
178
+ } catch (error) {
179
+ return {
180
+ pass: false,
181
+ message: `package.json is invalid: ${error.message}`,
182
+ suggestion: 'Fix JSON syntax errors in package.json'
183
+ };
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Verifica integridad de componentes
189
+ */
190
+ async function checkComponents() {
191
+ const configPath = path.join(process.cwd(), 'src', 'sliceConfig.json');
192
+
193
+ if (!await fs.pathExists(configPath)) {
194
+ return {
195
+ warn: true,
196
+ message: 'Cannot check components (no config)',
197
+ suggestion: 'Run "slice init" first'
198
+ };
199
+ }
200
+
201
+ try {
202
+ const config = await fs.readJson(configPath);
203
+ const componentPaths = config.paths?.components || {};
204
+
205
+ let totalComponents = 0;
206
+ let componentIssues = 0;
207
+
208
+ for (const [category, { path: compPath }] of Object.entries(componentPaths)) {
209
+ const fullPath = path.join(process.cwd(), 'src', compPath);
210
+
211
+ if (await fs.pathExists(fullPath)) {
212
+ const items = await fs.readdir(fullPath);
213
+
214
+ for (const item of items) {
215
+ const itemPath = path.join(fullPath, item);
216
+ const stat = await fs.stat(itemPath);
217
+
218
+ if (stat.isDirectory()) {
219
+ totalComponents++;
220
+
221
+ // Verificar archivos JS
222
+ const jsFile = path.join(itemPath, `${item}.js`);
223
+ if (!await fs.pathExists(jsFile)) {
224
+ componentIssues++;
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ if (componentIssues === 0) {
232
+ return {
233
+ pass: true,
234
+ message: `${totalComponents} components checked, all OK`
235
+ };
236
+ } else {
237
+ return {
238
+ warn: true,
239
+ message: `${componentIssues} component(s) have missing files`,
240
+ suggestion: 'Check your component directories'
241
+ };
242
+ }
243
+ } catch (error) {
244
+ return {
245
+ warn: true,
246
+ message: `Cannot check components: ${error.message}`,
247
+ suggestion: 'Verify your project structure'
248
+ };
249
+ }
250
+ }
251
+
252
+ /**
253
+ * Comando principal de diagnóstico
254
+ */
255
+ export default async function runDiagnostics() {
256
+ Print.newLine();
257
+ Print.title('🔍 Running Slice.js Diagnostics...');
258
+ Print.newLine();
259
+
260
+ const checks = [
261
+ { name: 'Node.js Version', fn: checkNodeVersion },
262
+ { name: 'Project Structure', fn: checkDirectoryStructure },
263
+ { name: 'Configuration', fn: checkConfig },
264
+ { name: 'Port Availability', fn: checkPort },
265
+ { name: 'Dependencies', fn: checkDependencies },
266
+ { name: 'Components', fn: checkComponents }
267
+ ];
268
+
269
+ const results = [];
270
+
271
+ for (const check of checks) {
272
+ const result = await check.fn();
273
+ results.push({ ...result, name: check.name });
274
+ }
275
+
276
+ // Crear tabla de resultados
277
+ const table = new Table({
278
+ head: [chalk.cyan.bold('Check'), chalk.cyan.bold('Status'), chalk.cyan.bold('Details')],
279
+ colWidths: [25, 10, 55],
280
+ style: {
281
+ head: [],
282
+ border: ['gray']
283
+ },
284
+ wordWrap: true
285
+ });
286
+
287
+ results.forEach(result => {
288
+ let status;
289
+ if (result.pass) {
290
+ status = chalk.green('✅ PASS');
291
+ } else if (result.warn) {
292
+ status = chalk.yellow('⚠️ WARN');
293
+ } else {
294
+ status = chalk.red('❌ FAIL');
295
+ }
296
+
297
+ const details = result.suggestion
298
+ ? `${result.message}\n${chalk.gray('→ ' + result.suggestion)}`
299
+ : result.message;
300
+
301
+ table.push([result.name, status, details]);
302
+ });
303
+
304
+ console.log(table.toString());
305
+
306
+ // Resumen
307
+ const issues = results.filter(r => !r.pass && !r.warn).length;
308
+ const warnings = results.filter(r => r.warn).length;
309
+ const passed = results.filter(r => r.pass).length;
310
+
311
+ Print.newLine();
312
+ Print.separator();
313
+
314
+ if (issues === 0 && warnings === 0) {
315
+ Print.success('All checks passed! 🎉');
316
+ Print.info('Your Slice.js project is correctly configured');
317
+ } else {
318
+ console.log(chalk.bold('📊 Summary:'));
319
+ console.log(chalk.green(` ✅ Passed: ${passed}`));
320
+ if (warnings > 0) console.log(chalk.yellow(` ⚠️ Warnings: ${warnings}`));
321
+ if (issues > 0) console.log(chalk.red(` ❌ Issues: ${issues}`));
322
+
323
+ Print.newLine();
324
+
325
+ if (issues > 0) {
326
+ Print.warning('Fix the issues above to ensure proper functionality');
327
+ } else {
328
+ Print.info('Warnings are non-critical but should be addressed');
329
+ }
330
+ }
331
+
332
+ Print.separator();
333
+ }
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import ora from 'ora';
4
5
  import Print from '../Print.js';
5
6
 
6
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -27,40 +28,43 @@ export default async function initializeProject(projectType) {
27
28
  }
28
29
 
29
30
  // 1. COPIAR LA CARPETA API (mantener lógica original)
31
+ const apiSpinner = ora('Copying API structure...').start();
30
32
  try {
31
33
  if (!fs.existsSync(apiDir)) throw new Error(`No se encontró la carpeta api: ${apiDir}`);
32
34
  await fs.copy(apiDir, destinationApi, { recursive: true });
33
- Print.success('Carpeta "api" copiada correctamente.');
35
+ apiSpinner.succeed('API structure created successfully');
34
36
  } catch (error) {
35
- Print.error('Error copiando la carpeta "api":', error.message);
37
+ apiSpinner.fail('Error copying API structure');
38
+ Print.error(error.message);
36
39
  return;
37
40
  }
38
41
 
39
42
  // 2. CREAR ESTRUCTURA SRC BÁSICA (sin copiar componentes Visual)
43
+ const srcSpinner = ora('Creating src structure...').start();
40
44
  try {
41
45
  if (!fs.existsSync(srcDir)) throw new Error(`No se encontró la carpeta src: ${srcDir}`);
42
-
46
+
43
47
  // Copiar solo los archivos base de src, excluyendo Components/Visual
44
48
  await fs.ensureDir(destinationSrc);
45
-
49
+
46
50
  // Copiar archivos y carpetas de src excepto Components/Visual
47
51
  const srcItems = await fs.readdir(srcDir);
48
-
52
+
49
53
  for (const item of srcItems) {
50
54
  const srcItemPath = path.join(srcDir, item);
51
55
  const destItemPath = path.join(destinationSrc, item);
52
56
  const stat = await fs.stat(srcItemPath);
53
-
57
+
54
58
  if (stat.isDirectory()) {
55
59
  if (item === 'Components') {
56
60
  // Crear estructura de Components pero sin copiar Visual
57
61
  await fs.ensureDir(destItemPath);
58
-
62
+
59
63
  const componentItems = await fs.readdir(srcItemPath);
60
64
  for (const componentItem of componentItems) {
61
65
  const componentItemPath = path.join(srcItemPath, componentItem);
62
66
  const destComponentItemPath = path.join(destItemPath, componentItem);
63
-
67
+
64
68
  if (componentItem !== 'Visual') {
65
69
  // Copiar Service y otros tipos de components
66
70
  await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
@@ -78,50 +82,50 @@ export default async function initializeProject(projectType) {
78
82
  await fs.copy(srcItemPath, destItemPath);
79
83
  }
80
84
  }
81
-
82
- Print.success('Estructura "src" creada correctamente.');
85
+
86
+ srcSpinner.succeed('Source structure created successfully');
83
87
  } catch (error) {
84
- Print.error('Error creando la estructura "src":', error.message);
88
+ srcSpinner.fail('Error creating source structure');
89
+ Print.error(error.message);
85
90
  return;
86
91
  }
87
92
 
88
93
  // 3. DESCARGAR TODOS LOS COMPONENTES VISUAL DESDE EL REPOSITORIO OFICIAL
94
+ const componentsSpinner = ora('Loading component registry...').start();
89
95
  try {
90
- Print.info('Downloading all Visual components from official repository...');
91
-
92
96
  const registry = new ComponentRegistry();
93
97
  await registry.loadRegistry();
94
-
98
+
95
99
  // Obtener TODOS los componentes Visual disponibles
96
100
  const allVisualComponents = await getAllVisualComponents(registry);
97
-
101
+
98
102
  if (allVisualComponents.length > 0) {
99
- Print.info(`Installing ${allVisualComponents.length} Visual components...`);
100
-
103
+ componentsSpinner.text = `Installing ${allVisualComponents.length} Visual components...`;
104
+
101
105
  const results = await registry.installMultipleComponents(
102
- allVisualComponents,
103
- 'Visual',
106
+ allVisualComponents,
107
+ 'Visual',
104
108
  true // force = true para instalación inicial
105
109
  );
106
-
110
+
107
111
  const successful = results.filter(r => r.success).length;
108
112
  const failed = results.filter(r => !r.success).length;
109
-
110
- if (successful > 0) {
111
- Print.success(`${successful} Visual components installed from official repository`);
112
- }
113
-
114
- if (failed > 0) {
115
- Print.warning(`${failed} Visual components could not be installed`);
116
- Print.info('You can install them later using "slice get <component-name>"');
113
+
114
+ if (successful > 0 && failed === 0) {
115
+ componentsSpinner.succeed(`All ${successful} Visual components installed successfully`);
116
+ } else if (successful > 0) {
117
+ componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
118
+ Print.info('You can install failed components later using "slice get <component-name>"');
119
+ } else {
120
+ componentsSpinner.fail('Failed to install Visual components');
117
121
  }
118
122
  } else {
119
- Print.warning('No Visual components found in registry');
123
+ componentsSpinner.warn('No Visual components found in registry');
120
124
  Print.info('You can add components later using "slice get <component-name>"');
121
125
  }
122
-
126
+
123
127
  } catch (error) {
124
- Print.warning('Could not download Visual components from official repository');
128
+ componentsSpinner.fail('Could not download Visual components from official repository');
125
129
  Print.error(`Repository error: ${error.message}`);
126
130
  Print.info('Project initialized without Visual components');
127
131
  Print.info('You can add them later using "slice get <component-name>"');
@@ -133,7 +137,7 @@ export default async function initializeProject(projectType) {
133
137
  console.log(' slice browse - View available components');
134
138
  console.log(' slice get Button - Install specific components');
135
139
  console.log(' slice sync - Update all components to latest versions');
136
-
140
+
137
141
  } catch (error) {
138
142
  Print.error('Error inesperado al inicializar el proyecto:', error.message);
139
143
  }
@@ -147,8 +151,8 @@ export default async function initializeProject(projectType) {
147
151
  async function getAllVisualComponents(registry) {
148
152
  const availableComponents = registry.getAvailableComponents('Visual');
149
153
  const allVisualComponents = Object.keys(availableComponents);
150
-
154
+
151
155
  Print.info(`Found ${allVisualComponents.length} Visual components in official repository`);
152
-
156
+
153
157
  return allVisualComponents;
154
158
  }
@@ -1,6 +1,8 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
+ import Table from 'cli-table3';
5
+ import chalk from 'chalk';
4
6
  import Print from '../Print.js';
5
7
 
6
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -34,7 +36,6 @@ const loadConfig = () => {
34
36
  const listComponents = (folderPath) => {
35
37
  try {
36
38
  if (!fs.existsSync(folderPath)) {
37
- Print.warning(`Component directory not found: ${folderPath}`);
38
39
  return [];
39
40
  }
40
41
  const result = fs.readdirSync(folderPath);
@@ -45,6 +46,19 @@ const listComponents = (folderPath) => {
45
46
  }
46
47
  };
47
48
 
49
+ /**
50
+ * Cuenta archivos en un directorio de componente
51
+ */
52
+ const countComponentFiles = (componentPath) => {
53
+ try {
54
+ if (!fs.existsSync(componentPath)) return 0;
55
+ const files = fs.readdirSync(componentPath);
56
+ return files.filter(f => fs.statSync(path.join(componentPath, f)).isFile()).length;
57
+ } catch {
58
+ return 0;
59
+ }
60
+ };
61
+
48
62
  /**
49
63
  * Obtiene los componentes dinámicamente desde sliceConfig.json
50
64
  * @returns {object} - Mapeo de componentes con su categoría
@@ -53,29 +67,27 @@ const getComponents = () => {
53
67
  const config = loadConfig();
54
68
  if (!config) return {};
55
69
 
56
- //const isProduction = config.production.enabled===true;
57
70
  const folderSuffix = 'src'; // Siempre usar 'src' para desarrollo
58
-
59
- const componentPaths = config.paths?.components || {}; // Obtiene dinámicamente las rutas de los componentes
71
+ const componentPaths = config.paths?.components || {};
60
72
  let allComponents = new Map();
61
73
 
62
74
  Object.entries(componentPaths).forEach(([category, { path: folderPath }]) => {
63
75
  const fullPath = path.join(__dirname, `../../../../${folderSuffix}`, folderPath);
64
76
  const files = listComponents(fullPath);
65
77
 
66
-
67
78
  files.forEach(file => {
68
- const componentName = path.basename(file, '.js');
69
- allComponents.set(componentName, category);
79
+ const componentPath = path.join(fullPath, file);
80
+ if (fs.statSync(componentPath).isDirectory()) {
81
+ const fileCount = countComponentFiles(componentPath);
82
+ allComponents.set(file, { category, files: fileCount });
83
+ }
70
84
  });
71
85
  });
72
86
 
73
-
74
-
75
87
  return Object.fromEntries(allComponents);
76
88
  };
77
89
 
78
- function listComponentsReal(){
90
+ function listComponentsReal() {
79
91
  try {
80
92
  // Obtener componentes dinámicamente
81
93
  const components = getComponents();
@@ -86,26 +98,76 @@ function listComponentsReal(){
86
98
  return;
87
99
  }
88
100
 
101
+ // Crear tabla con cli-table3
102
+ const table = new Table({
103
+ head: [
104
+ chalk.cyan.bold('Component'),
105
+ chalk.cyan.bold('Category'),
106
+ chalk.cyan.bold('Files')
107
+ ],
108
+ colWidths: [30, 20, 10],
109
+ style: {
110
+ head: [],
111
+ border: ['gray']
112
+ }
113
+ });
114
+
115
+ // Agrupar por categoría para mejor visualización
116
+ const byCategory = {};
117
+ Object.entries(components).forEach(([name, data]) => {
118
+ if (!byCategory[data.category]) {
119
+ byCategory[data.category] = [];
120
+ }
121
+ byCategory[data.category].push({ name, files: data.files });
122
+ });
123
+
124
+ // Agregar filas a la tabla
125
+ Object.entries(byCategory).forEach(([category, comps]) => {
126
+ comps.forEach((comp, index) => {
127
+ if (index === 0) {
128
+ // Primera fila de la categoría
129
+ table.push([
130
+ chalk.bold(comp.name),
131
+ chalk.yellow(category),
132
+ comp.files.toString()
133
+ ]);
134
+ } else {
135
+ // Resto de componentes en la categoría
136
+ table.push([
137
+ chalk.bold(comp.name),
138
+ chalk.gray('″'), // Ditto mark
139
+ comp.files.toString()
140
+ ]);
141
+ }
142
+ });
143
+ });
144
+
145
+ Print.newLine();
146
+ Print.title('📦 Local Components');
147
+ Print.newLine();
148
+ console.log(table.toString());
149
+ Print.newLine();
150
+ Print.info(`Total: ${Object.keys(components).length} component${Object.keys(components).length !== 1 ? 's' : ''} found`);
151
+
89
152
  // Ruta donde se generará components.js
90
153
  const outputPath = path.join(__dirname, '../../../../src/Components/components.js');
91
-
154
+
92
155
  // Asegurar que el directorio existe
93
156
  const outputDir = path.dirname(outputPath);
94
157
  if (!fs.existsSync(outputDir)) {
95
158
  fs.mkdirSync(outputDir, { recursive: true });
96
- Print.info('Created Components directory');
97
159
  }
98
160
 
99
161
  // Generar archivo components.js con los componentes detectados
100
- fs.writeFileSync(outputPath, `const components = ${JSON.stringify(components, null, 2)};\n\nexport default components;\n`);
162
+ const componentsForExport = Object.fromEntries(
163
+ Object.entries(components).map(([name, data]) => [name, data.category])
164
+ );
165
+ fs.writeFileSync(outputPath, `const components = ${JSON.stringify(componentsForExport, null, 2)};\n\nexport default components;\n`);
101
166
 
102
- Print.success(`Component list updated successfully (${Object.keys(components).length} component${Object.keys(components).length !== 1 ? 's' : ''} found)`);
103
167
  } catch (error) {
104
- Print.error(`Failed to update component list: ${error.message}`);
168
+ Print.error(`Failed to list components: ${error.message}`);
105
169
  Print.info('Make sure your project structure is correct');
106
170
  }
107
171
  }
108
172
 
109
173
  export default listComponentsReal;
110
-
111
-