slicejs-cli 2.7.4 → 2.7.5

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.
@@ -9,6 +9,7 @@ import inquirer from 'inquirer';
9
9
  import { exec } from 'child_process';
10
10
  import { promisify } from 'util';
11
11
  import { getProjectRoot, getSrcPath, getApiPath, getConfigPath, getPath } from '../utils/PathHelper.js';
12
+ import updateManager from '../utils/updateManager.js';
12
13
 
13
14
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
15
 
@@ -148,7 +149,8 @@ async function checkPort() {
148
149
  * Verifica dependencias en package.json
149
150
  */
150
151
  async function checkDependencies() {
151
- const packagePath = getPath(import.meta.url, '', 'package.json');
152
+ const projectRoot = getProjectRoot(import.meta.url);
153
+ const packagePath = path.join(projectRoot, 'package.json');
152
154
 
153
155
  if (!await fs.pathExists(packagePath)) {
154
156
  return {
@@ -160,18 +162,18 @@ async function checkDependencies() {
160
162
 
161
163
  try {
162
164
  const pkg = await fs.readJson(packagePath);
163
- const hasCli = pkg.dependencies?.['slicejs-cli'] || pkg.devDependencies?.['slicejs-cli'];
164
- const hasFramework = pkg.dependencies?.['slicejs-web-framework'];
165
+ const hasFrameworkDep = pkg.dependencies?.['slicejs-web-framework'] || pkg.devDependencies?.['slicejs-web-framework'];
166
+ const frameworkNodePath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'package.json');
167
+ const hasFrameworkNode = await fs.pathExists(frameworkNodePath);
168
+ const hasFramework = !!(hasFrameworkDep || hasFrameworkNode);
165
169
 
166
- if (hasCli && hasFramework) {
170
+ if (hasFramework) {
167
171
  return {
168
172
  pass: true,
169
- message: 'All required dependencies are installed'
173
+ message: 'Required framework dependency is installed'
170
174
  };
171
175
  } else {
172
- const missing = [];
173
- if (!hasCli) missing.push('slicejs-cli');
174
- if (!hasFramework) missing.push('slicejs-web-framework');
176
+ const missing = ['slicejs-web-framework'];
175
177
 
176
178
  return {
177
179
  warn: true,
@@ -334,9 +336,7 @@ export default async function runDiagnostics() {
334
336
  if (confirmInstall) {
335
337
  for (const pkg of depsResult.missing) {
336
338
  try {
337
- const cmd = pkg === 'slicejs-cli'
338
- ? 'npm install -D slicejs-cli@latest'
339
- : 'npm install slicejs-web-framework@latest';
339
+ const cmd = 'npm install slicejs-web-framework@latest';
340
340
  Print.info(`Installing ${pkg}...`);
341
341
  await execAsync(cmd, { cwd: projectRoot });
342
342
  Print.success(`${pkg} installed`);
@@ -7,6 +7,7 @@ import inquirer from "inquirer";
7
7
  import validations from "../Validations.js";
8
8
  import Print from "../Print.js";
9
9
  import { getConfigPath, getComponentsJsPath, getPath } from "../utils/PathHelper.js";
10
+ import ora from "ora";
10
11
 
11
12
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
13
 
@@ -188,32 +189,51 @@ filterOfficialComponents(allComponents) {
188
189
 
189
190
  const downloadedFiles = [];
190
191
  const failedFiles = [];
191
- Print.info(`Downloading ${componentName} from official repository...`);
192
-
193
- for (const fileName of component.files) {
194
- const githubUrl = `${DOCS_REPO_BASE_URL}/${category}/${componentName}/${fileName}`;
192
+ const total = component.files.length;
193
+ let done = 0;
194
+ const spinner = ora(`Downloading ${componentName} 0/${total}`).start();
195
+ const fetchWithRetry = async (url, retries = 3, baseDelay = 500) => {
196
+ for (let attempt = 0; attempt <= retries; attempt++) {
197
+ try {
198
+ const response = await fetch(url);
199
+ if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
200
+ return await response.text();
201
+ } catch (e) {
202
+ if (attempt === retries) throw e;
203
+ const delay = baseDelay * Math.pow(2, attempt);
204
+ await new Promise(r => setTimeout(r, delay));
205
+ }
206
+ }
207
+ };
208
+ const worker = async (fileName) => {
209
+ const url = `${DOCS_REPO_BASE_URL}/${category}/${componentName}/${fileName}`;
195
210
  const localPath = path.join(targetPath, fileName);
196
-
197
211
  try {
198
- const response = await fetch(githubUrl);
199
-
200
- if (!response.ok) {
201
- Print.downloadError(fileName, `HTTP ${response.status}: ${response.statusText}`);
202
- failedFiles.push(fileName);
203
- continue; // ✅ CONTINUAR en lugar de lanzar error
204
- }
205
-
206
- const content = await response.text();
212
+ const content = await fetchWithRetry(url);
207
213
  await fs.writeFile(localPath, content, 'utf8');
208
214
  downloadedFiles.push(fileName);
209
-
210
215
  Print.downloadSuccess(fileName);
211
216
  } catch (error) {
212
217
  Print.downloadError(fileName, error.message);
213
218
  failedFiles.push(fileName);
214
- continue; // ✅ CONTINUAR en lugar de lanzar error
219
+ } finally {
220
+ done += 1;
221
+ spinner.text = `Downloading ${componentName} ${done}/${total}`;
215
222
  }
216
- }
223
+ };
224
+ const runConcurrent = async (items, concurrency = 3) => {
225
+ let index = 0;
226
+ const runners = Array(Math.min(concurrency, items.length)).fill(0).map(async () => {
227
+ while (true) {
228
+ const i = index++;
229
+ if (i >= items.length) break;
230
+ await worker(items[i]);
231
+ }
232
+ });
233
+ await Promise.all(runners);
234
+ };
235
+ await runConcurrent(component.files, 3);
236
+ spinner.stop();
217
237
 
218
238
  // ✅ NUEVO: Solo lanzar error si NO se descargó el archivo principal (.js)
219
239
  const mainFileDownloaded = downloadedFiles.some(file => file.endsWith('.js'));
@@ -311,7 +331,7 @@ filterOfficialComponents(allComponents) {
311
331
  const availableComponents = this.getAvailableComponents(category);
312
332
 
313
333
  if (!availableComponents[componentName]) {
314
- throw new Error(`Componente '${componentName}' no encontrado en la categoría '${category}' del repositorio oficial`);
334
+ throw new Error(`Component '${componentName}' not found in category '${category}' in the official repository`);
315
335
  }
316
336
 
317
337
  // ✅ MEJORADO: Detectar si validations tiene acceso a la configuración
@@ -347,7 +367,7 @@ filterOfficialComponents(allComponents) {
347
367
  {
348
368
  type: 'confirm',
349
369
  name: 'overwrite',
350
- message: `El componente '${componentName}' ya existe localmente. ¿Deseas sobrescribirlo con la versión del repositorio?`,
370
+ message: `The component '${componentName}' already exists locally. Overwrite with the repository version?`,
351
371
  default: false
352
372
  }
353
373
  ]);
@@ -395,16 +415,34 @@ filterOfficialComponents(allComponents) {
395
415
  async installMultipleComponents(componentNames, category = 'Visual', force = false) {
396
416
  const results = [];
397
417
  Print.info(`Getting ${componentNames.length} ${category} components from official repository...`);
398
-
399
- for (const componentName of componentNames) {
418
+ const total = componentNames.length;
419
+ let done = 0;
420
+ const spinner = ora(`Installing 0/${total}`).start();
421
+ const worker = async (componentName) => {
400
422
  try {
401
423
  const result = await this.installComponent(componentName, category, force);
402
424
  results.push({ name: componentName, success: result });
403
425
  } catch (error) {
404
426
  Print.componentError(componentName, 'getting', error.message);
405
427
  results.push({ name: componentName, success: false, error: error.message });
428
+ } finally {
429
+ done += 1;
430
+ spinner.text = `Installing ${done}/${total}`;
406
431
  }
407
- }
432
+ };
433
+ const runConcurrent = async (items, concurrency = 3) => {
434
+ let index = 0;
435
+ const runners = Array(Math.min(concurrency, items.length)).fill(0).map(async () => {
436
+ while (true) {
437
+ const i = index++;
438
+ if (i >= items.length) break;
439
+ await worker(items[i]);
440
+ }
441
+ });
442
+ await Promise.all(runners);
443
+ };
444
+ await runConcurrent(componentNames, 3);
445
+ spinner.stop();
408
446
 
409
447
  // Summary
410
448
  const successful = results.filter(r => r.success).length;
@@ -492,11 +530,11 @@ filterOfficialComponents(allComponents) {
492
530
 
493
531
  displayAvailableComponents() {
494
532
  if (!this.componentsRegistry) {
495
- Print.error('❌ No se pudo cargar el registro de componentes');
533
+ Print.error('❌ Could not load component registry');
496
534
  return;
497
535
  }
498
536
 
499
- console.log('\n📚 Componentes disponibles en el repositorio oficial de Slice.js:\n');
537
+ console.log('\n📚 Available components in the official Slice.js repository:\n');
500
538
 
501
539
  const visualComponents = this.getAvailableComponents('Visual');
502
540
  const serviceComponents = this.getAvailableComponents('Service');
@@ -522,10 +560,10 @@ filterOfficialComponents(allComponents) {
522
560
  Print.newLine();
523
561
  Print.info(`Total: ${Object.keys(visualComponents).length} Visual + ${Object.keys(serviceComponents).length} Service components`);
524
562
 
525
- console.log(`\n💡 Ejemplos de uso:`);
526
- console.log(`slice get Button Card Input # Obtener componentes Visual`);
527
- console.log(`slice get FetchManager --service # Obtener componente Service`);
528
- console.log(`slice sync # Sincronizar componentes Visual`);
563
+ console.log(`\n💡 Usage examples:`);
564
+ console.log(`slice get Button Card Input # Install Visual components`);
565
+ console.log(`slice get FetchManager --service # Install Service component`);
566
+ console.log(`slice sync # Sync Visual components`);
529
567
  }
530
568
 
531
569
  async interactiveInstall() {
@@ -533,7 +571,7 @@ filterOfficialComponents(allComponents) {
533
571
  {
534
572
  type: 'list',
535
573
  name: 'componentType',
536
- message: 'Selecciona el tipo de componente a obtener del repositorio:',
574
+ message: 'Select the type of component to install from the repository:',
537
575
  choices: [
538
576
  { name: '🎨 Visual Components (UI)', value: 'Visual' },
539
577
  { name: '⚙️ Service Components (Logic)', value: 'Service' }
@@ -552,10 +590,10 @@ filterOfficialComponents(allComponents) {
552
590
  {
553
591
  type: 'list',
554
592
  name: 'installMode',
555
- message: '¿Cómo deseas obtener los componentes Visual?',
593
+ message: 'How do you want to install Visual components?',
556
594
  choices: [
557
- { name: 'Obtener uno solo', value: 'single' },
558
- { name: 'Obtener múltiples', value: 'multiple' }
595
+ { name: 'Get one', value: 'single' },
596
+ { name: 'Get multiple', value: 'multiple' }
559
597
  ]
560
598
  }
561
599
  ]);
@@ -565,11 +603,11 @@ filterOfficialComponents(allComponents) {
565
603
  {
566
604
  type: 'checkbox',
567
605
  name: 'selectedComponents',
568
- message: 'Selecciona los componentes Visual a obtener del repositorio:',
606
+ message: 'Select Visual components to install from the repository:',
569
607
  choices: componentChoices,
570
608
  validate: (input) => {
571
609
  if (input.length === 0) {
572
- return 'Debes seleccionar al menos un componente';
610
+ return 'You must select at least one component';
573
611
  }
574
612
  return true;
575
613
  }
@@ -582,7 +620,7 @@ filterOfficialComponents(allComponents) {
582
620
  {
583
621
  type: 'list',
584
622
  name: 'selectedComponent',
585
- message: 'Selecciona un componente Visual:',
623
+ message: 'Select a Visual component:',
586
624
  choices: componentChoices
587
625
  }
588
626
  ]);
@@ -594,7 +632,7 @@ filterOfficialComponents(allComponents) {
594
632
  {
595
633
  type: 'list',
596
634
  name: 'selectedComponent',
597
- message: 'Selecciona un componente Service:',
635
+ message: 'Select a Service component:',
598
636
  choices: componentChoices
599
637
  }
600
638
  ]);
@@ -48,30 +48,29 @@ export default async function initializeProject(projectType) {
48
48
  const srcDir = path.join(sliceBaseDir, 'src');
49
49
 
50
50
  try {
51
- // Verificar si los directorios de destino ya existen
52
- if (fs.existsSync(destinationApi)) throw new Error(`El directorio "api" ya existe: ${destinationApi}`);
53
- if (fs.existsSync(destinationSrc)) throw new Error(`El directorio "src" ya existe: ${destinationSrc}`);
54
- } catch (error) {
55
- Print.error('Error validando directorios de destino:', error.message);
56
- return;
57
- }
51
+ if (fs.existsSync(destinationApi)) throw new Error(`The "api" directory already exists: ${destinationApi}`);
52
+ if (fs.existsSync(destinationSrc)) throw new Error(`The "src" directory already exists: ${destinationSrc}`);
53
+ } catch (error) {
54
+ Print.error('Validating destination directories:', error.message);
55
+ return;
56
+ }
58
57
 
59
58
  // 1. COPIAR LA CARPETA API (mantener lógica original)
60
- const apiSpinner = ora('Copying API structure...').start();
61
- try {
62
- if (!fs.existsSync(apiDir)) throw new Error(`No se encontró la carpeta api: ${apiDir}`);
63
- await fs.copy(apiDir, destinationApi, { recursive: true });
64
- apiSpinner.succeed('API structure created successfully');
65
- } catch (error) {
66
- apiSpinner.fail('Error copying API structure');
67
- Print.error(error.message);
68
- return;
69
- }
59
+ const apiSpinner = ora('Copying API structure...').start();
60
+ try {
61
+ if (!fs.existsSync(apiDir)) throw new Error(`API folder not found: ${apiDir}`);
62
+ await fs.copy(apiDir, destinationApi, { recursive: true });
63
+ apiSpinner.succeed('API structure created successfully');
64
+ } catch (error) {
65
+ apiSpinner.fail('Error copying API structure');
66
+ Print.error(error.message);
67
+ return;
68
+ }
70
69
 
71
70
  // 2. CREAR ESTRUCTURA SRC BÁSICA (sin copiar componentes Visual)
72
- const srcSpinner = ora('Creating src structure...').start();
73
- try {
74
- if (!fs.existsSync(srcDir)) throw new Error(`No se encontró la carpeta src: ${srcDir}`);
71
+ const srcSpinner = ora('Creating src structure...').start();
72
+ try {
73
+ if (!fs.existsSync(srcDir)) throw new Error(`src folder not found: ${srcDir}`);
75
74
 
76
75
  // Copiar solo los archivos base de src, excluyendo Components/Visual
77
76
  await fs.ensureDir(destinationSrc);
@@ -95,22 +94,22 @@ export default async function initializeProject(projectType) {
95
94
  const destComponentItemPath = path.join(destItemPath, componentItem);
96
95
 
97
96
  if (componentItem !== 'Visual') {
98
- // Copiar Service y otros tipos de components
99
- await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
100
- } else {
101
- // Solo crear el directorio Visual vacío
102
- await fs.ensureDir(destComponentItemPath);
103
- }
104
- }
105
- } else {
106
- // Copiar otras carpetas normalmente
107
- await fs.copy(srcItemPath, destItemPath, { recursive: true });
108
- }
109
- } else {
110
- // Copiar archivos normalmente
111
- await fs.copy(srcItemPath, destItemPath);
112
- }
113
- }
97
+ // Copy Service and other component types
98
+ await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
99
+ } else {
100
+ // Only create empty Visual directory
101
+ await fs.ensureDir(destComponentItemPath);
102
+ }
103
+ }
104
+ } else {
105
+ // Copy other folders normally
106
+ await fs.copy(srcItemPath, destItemPath, { recursive: true });
107
+ }
108
+ } else {
109
+ // Copy files normally
110
+ await fs.copy(srcItemPath, destItemPath);
111
+ }
112
+ }
114
113
 
115
114
  srcSpinner.succeed('Source structure created successfully');
116
115
  } catch (error) {
@@ -143,15 +142,15 @@ export default async function initializeProject(projectType) {
143
142
  if (successful > 0 && failed === 0) {
144
143
  componentsSpinner.succeed(`All ${successful} Visual components installed successfully`);
145
144
  } else if (successful > 0) {
146
- componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
147
- Print.info('You can install failed components later using "slice get <component-name>"');
148
- } else {
149
- componentsSpinner.fail('Failed to install Visual components');
150
- }
151
- } else {
152
- componentsSpinner.warn('No Visual components found in registry');
153
- Print.info('You can add components later using "slice get <component-name>"');
154
- }
145
+ componentsSpinner.warn(`${successful} components installed, ${failed} failed`);
146
+ Print.info('You can install failed components later using "slice get <component-name>"');
147
+ } else {
148
+ componentsSpinner.fail('Failed to install Visual components');
149
+ }
150
+ } else {
151
+ componentsSpinner.warn('No Visual components found in registry');
152
+ Print.info('You can add components later using "slice get <component-name>"');
153
+ }
155
154
 
156
155
  } catch (error) {
157
156
  componentsSpinner.fail('Could not download Visual components from official repository');
@@ -223,21 +222,21 @@ export default async function initializeProject(projectType) {
223
222
  console.log(' npm run get - Install components');
224
223
  console.log(' npm run browse - Browse components');
225
224
  } catch (error) {
226
- pkgSpinner.fail('Failed to configure npm scripts');
227
- Print.error(error.message);
228
- }
225
+ pkgSpinner.fail('Failed to configure npm scripts');
226
+ Print.error(error.message);
227
+ }
229
228
 
230
- Print.success('Proyecto inicializado correctamente.');
231
- Print.newLine();
232
- Print.info('Next steps:');
233
- console.log(' slice browse - View available components');
234
- console.log(' slice get Button - Install specific components');
235
- console.log(' slice sync - Update all components to latest versions');
229
+ Print.success('Project initialized successfully.');
230
+ Print.newLine();
231
+ Print.info('Next steps:');
232
+ console.log(' slice browse - View available components');
233
+ console.log(' slice get Button - Install specific components');
234
+ console.log(' slice sync - Update all components to latest versions');
236
235
 
237
236
  } catch (error) {
238
- Print.error('Error inesperado al inicializar el proyecto:', error.message);
239
- }
240
- }
237
+ Print.error('Unexpected error initializing project:', error.message);
238
+ }
239
+ }
241
240
 
242
241
  /**
243
242
  * Obtiene TODOS los componentes Visual disponibles en el registry
@@ -14,6 +14,16 @@ function candidates(moduleUrl) {
14
14
  }
15
15
 
16
16
  function resolveProjectRoot(moduleUrl) {
17
+ const initCwd = process.env.INIT_CWD
18
+ if (initCwd && fs.pathExistsSync(initCwd)) {
19
+ return initCwd
20
+ }
21
+
22
+ const cwd = process.cwd()
23
+ if (cwd && fs.pathExistsSync(cwd)) {
24
+ return cwd
25
+ }
26
+
17
27
  const dirs = candidates(moduleUrl)
18
28
  for (const root of dirs) {
19
29
  const hasSrc = fs.pathExistsSync(path.join(root, 'src'))
@@ -111,7 +111,7 @@ class VersionChecker {
111
111
 
112
112
  if (!silent && (cliStatus === 'outdated' || frameworkStatus === 'outdated')) {
113
113
  console.log(''); // Line break
114
- Print.warning('📦 Actualizaciones Disponibles:');
114
+ Print.warning('📦 Available Updates:');
115
115
 
116
116
  if (cliStatus === 'outdated') {
117
117
  console.log(` 🔧 CLI: ${current.cli} → ${latest.cli}`);
@@ -142,7 +142,7 @@ class VersionChecker {
142
142
  const current = await this.getCurrentVersions();
143
143
  const latest = await this.getLatestVersions();
144
144
 
145
- console.log('\n📋 Información de Versiones:');
145
+ console.log('\n📋 Version Information:');
146
146
  console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
147
147
 
148
148
  if (current?.cli) {
@@ -95,7 +95,7 @@ class UpdateManager {
95
95
  }
96
96
 
97
97
  console.log('');
98
- Print.warning('📦 Actualizaciones Disponibles:');
98
+ Print.warning('📦 Available Updates:');
99
99
  console.log('');
100
100
 
101
101
  updateInfo.updates.forEach(pkg => {
@@ -145,17 +145,17 @@ class UpdateManager {
145
145
  const answers = await inquirer.prompt([
146
146
  {
147
147
  type: 'checkbox',
148
- name: 'packages',
149
- message: '¿Qué paquetes deseas actualizar?',
150
- choices,
151
- validate: (answer) => {
152
- if (answer.length === 0) {
153
- return 'Debes seleccionar al menos un paquete';
154
- }
155
- return true;
156
- }
157
- }
158
- ]);
148
+ name: 'packages',
149
+ message: 'Which packages do you want to update?',
150
+ choices,
151
+ validate: (answer) => {
152
+ if (answer.length === 0) {
153
+ return 'You must select at least one package';
154
+ }
155
+ return true;
156
+ }
157
+ }
158
+ ]);
159
159
 
160
160
  return answers.packages;
161
161
  }
@@ -229,25 +229,25 @@ class UpdateManager {
229
229
  const results = [];
230
230
 
231
231
  for (const packageName of packages) {
232
- const spinner = ora(`Actualizando ${packageName}...`).start();
232
+ const spinner = ora(`Updating ${packageName}...`).start();
233
233
 
234
234
  try {
235
235
  const result = await this.updatePackage(packageName);
236
236
 
237
- if (result.success) {
238
- spinner.succeed(`${packageName} actualizado exitosamente`);
239
- results.push({ packageName, success: true });
240
- } else {
241
- spinner.fail(`Error actualizando ${packageName}`);
242
- Print.error(`Detalles: ${result.error}`);
243
- results.push({ packageName, success: false, error: result.error });
244
- }
245
- } catch (error) {
246
- spinner.fail(`Error actualizando ${packageName}`);
247
- Print.error(`Detalles: ${error.message}`);
248
- results.push({ packageName, success: false, error: error.message });
249
- }
250
- }
237
+ if (result.success) {
238
+ spinner.succeed(`${packageName} updated successfully`);
239
+ results.push({ packageName, success: true });
240
+ } else {
241
+ spinner.fail(`Error updating ${packageName}`);
242
+ Print.error(`Details: ${result.error}`);
243
+ results.push({ packageName, success: false, error: result.error });
244
+ }
245
+ } catch (error) {
246
+ spinner.fail(`Error updating ${packageName}`);
247
+ Print.error(`Details: ${error.message}`);
248
+ results.push({ packageName, success: false, error: error.message });
249
+ }
250
+ }
251
251
 
252
252
  return results;
253
253
  }
@@ -256,26 +256,26 @@ class UpdateManager {
256
256
  * Main method to check and prompt for updates
257
257
  */
258
258
  async checkAndPromptUpdates(options = {}) {
259
- const spinner = ora('Verificando actualizaciones...').start();
259
+ const spinner = ora('Checking for updates...').start();
260
260
 
261
261
  try {
262
262
  const updateInfo = await this.checkForUpdates();
263
263
  spinner.stop();
264
264
 
265
- if (!updateInfo) {
266
- Print.error('No se pudo verificar actualizaciones. Verifica tu conexión a internet.');
267
- return false;
268
- }
265
+ if (!updateInfo) {
266
+ Print.error('Could not check for updates. Verify your internet connection.');
267
+ return false;
268
+ }
269
269
 
270
- if (updateInfo.allCurrent) {
271
- Print.success('✅ Todos los componentes están actualizados!');
272
- return true;
273
- }
270
+ if (updateInfo.allCurrent) {
271
+ Print.success('✅ All components are up to date!');
272
+ return true;
273
+ }
274
274
 
275
- if (!updateInfo.hasUpdates) {
276
- Print.success('✅ Todos los componentes están actualizados!');
277
- return true;
278
- }
275
+ if (!updateInfo.hasUpdates) {
276
+ Print.success('✅ All components are up to date!');
277
+ return true;
278
+ }
279
279
 
280
280
  // Display available updates
281
281
  this.displayUpdates(updateInfo);
@@ -283,20 +283,20 @@ class UpdateManager {
283
283
  // Get packages to update
284
284
  const packagesToUpdate = await this.promptForUpdates(updateInfo, options);
285
285
 
286
- if (!packagesToUpdate || packagesToUpdate.length === 0) {
287
- Print.info('No se seleccionaron paquetes para actualizar.');
288
- return false;
289
- }
286
+ if (!packagesToUpdate || packagesToUpdate.length === 0) {
287
+ Print.info('No packages selected for update.');
288
+ return false;
289
+ }
290
290
 
291
291
  // Show plan and confirm installation if not auto-confirmed
292
292
  let plan = await this.buildUpdatePlan(packagesToUpdate);
293
293
  console.log('');
294
- Print.info('🧭 Plan de actualización:');
295
- plan.forEach(item => {
296
- const where = item.target === 'global' ? 'GLOBAL' : 'PROYECTO';
297
- console.log(` • ${item.package} → ${where}`);
298
- console.log(` ${item.command}`);
299
- });
294
+ Print.info('🧭 Update plan:');
295
+ plan.forEach(item => {
296
+ const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
297
+ console.log(` • ${item.package} → ${where}`);
298
+ console.log(` ${item.command}`);
299
+ });
300
300
  console.log('');
301
301
 
302
302
  const cliInfo = await this.detectCliInstall();
@@ -306,47 +306,47 @@ class UpdateManager {
306
306
  {
307
307
  type: 'confirm',
308
308
  name: 'addCli',
309
- message: 'Se detectó CLI global. ¿Agregar la actualización global del CLI al plan?',
310
- default: true
311
- }
312
- ]);
309
+ message: 'Global CLI detected. Add the global CLI update to the plan?',
310
+ default: true
311
+ }
312
+ ]);
313
313
  if (addCli) {
314
314
  packagesToUpdate.push('slicejs-cli');
315
315
  plan = await this.buildUpdatePlan(packagesToUpdate);
316
316
  console.log('');
317
- Print.info('🧭 Plan actualizado:');
318
- plan.forEach(item => {
319
- const where = item.target === 'global' ? 'GLOBAL' : 'PROYECTO';
320
- console.log(` • ${item.package} → ${where}`);
321
- console.log(` ${item.command}`);
322
- });
317
+ Print.info('🧭 Updated plan:');
318
+ plan.forEach(item => {
319
+ const where = item.target === 'global' ? 'GLOBAL' : 'PROJECT';
320
+ console.log(` • ${item.package} → ${where}`);
321
+ console.log(` ${item.command}`);
322
+ });
323
323
  console.log('');
324
324
  }
325
325
  } else {
326
- Print.warning('CLI global detectado. Se recomienda actualizar slicejs-cli global para mantener alineado con el framework.');
327
- console.log(' Sugerencia: npm install -g slicejs-cli@latest');
328
- console.log('');
329
- }
330
- }
326
+ Print.warning('Global CLI detected. It is recommended to update slicejs-cli globally to keep aligned with the framework.');
327
+ console.log(' Suggestion: npm install -g slicejs-cli@latest');
328
+ console.log('');
329
+ }
330
+ }
331
331
 
332
332
  if (!options.yes && !options.cli && !options.framework) {
333
333
  const { confirm } = await inquirer.prompt([
334
- {
335
- type: 'confirm',
336
- name: 'confirm',
337
- message: '¿Deseas continuar con la actualización según el plan mostrado?',
338
- default: true
339
- }
340
- ]);
334
+ {
335
+ type: 'confirm',
336
+ name: 'confirm',
337
+ message: 'Do you want to proceed with the update according to the plan shown?',
338
+ default: true
339
+ }
340
+ ]);
341
341
 
342
342
  if (!confirm) {
343
- Print.info('Actualización cancelada.');
344
- return false;
345
- }
346
- }
343
+ Print.info('Update cancelled.');
344
+ return false;
345
+ }
346
+ }
347
347
 
348
348
  console.log(''); // Line break
349
- Print.info('📥 Instalando actualizaciones...');
349
+ Print.info('📥 Installing updates...');
350
350
  console.log('');
351
351
 
352
352
  // Install updates
@@ -357,24 +357,24 @@ class UpdateManager {
357
357
  const successCount = results.filter(r => r.success).length;
358
358
  const failCount = results.filter(r => !r.success).length;
359
359
 
360
- if (failCount === 0) {
361
- Print.success(`✅ ${successCount} paquete(s) actualizado(s) exitosamente!`);
362
- } else {
363
- Print.warning(`⚠️ ${successCount} exitoso(s), ${failCount} fallido(s)`);
364
- }
360
+ if (failCount === 0) {
361
+ Print.success(`✅ ${successCount} package(s) updated successfully!`);
362
+ } else {
363
+ Print.warning(`⚠️ ${successCount} successful, ${failCount} failed`);
364
+ }
365
365
 
366
366
  if (successCount > 0) {
367
367
  console.log('');
368
- Print.info('💡 Se recomienda reiniciar el servidor de desarrollo si está ejecutándose.');
369
- }
368
+ Print.info('💡 It is recommended to restart the development server if it is running.');
369
+ }
370
370
 
371
371
  return failCount === 0;
372
372
 
373
373
  } catch (error) {
374
374
  spinner.stop();
375
- Print.error(`Error durante la actualización: ${error.message}`);
376
- return false;
377
- }
375
+ Print.error(`Error during update: ${error.message}`);
376
+ return false;
377
+ }
378
378
  }
379
379
  }
380
380
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slicejs-cli",
3
- "version": "2.7.4",
3
+ "version": "2.7.5",
4
4
  "description": "Command client for developing web applications with Slice.js framework",
5
5
  "main": "client.js",
6
6
  "bin": {