slicejs-cli 2.5.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/client.js +31 -27
- package/commands/Validations.js +36 -18
- package/commands/buildProduction/buildProduction.js +12 -11
- package/commands/createComponent/createComponent.js +4 -5
- package/commands/deleteComponent/deleteComponent.js +5 -5
- package/commands/doctor/doctor.js +9 -9
- package/commands/getComponent/getComponent.js +40 -51
- package/commands/init/init.js +103 -7
- package/commands/listComponents/listComponents.js +5 -3
- package/commands/startServer/startServer.js +7 -6
- package/commands/utils/PathHelper.js +58 -0
- package/commands/utils/VersionChecker.js +9 -7
- package/commands/utils/updateManager.js +377 -0
- package/package.json +1 -1
- package/post.js +17 -138
package/commands/init/init.js
CHANGED
|
@@ -3,6 +3,8 @@ import path from 'path';
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import Print from '../Print.js';
|
|
6
|
+
import { getProjectRoot, getApiPath, getSrcPath } from '../utils/PathHelper.js';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
6
8
|
|
|
7
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
10
|
|
|
@@ -11,12 +13,39 @@ import { ComponentRegistry } from '../getComponent/getComponent.js';
|
|
|
11
13
|
|
|
12
14
|
export default async function initializeProject(projectType) {
|
|
13
15
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
let
|
|
16
|
+
const projectRoot = getProjectRoot(import.meta.url);
|
|
17
|
+
const destinationApi = getApiPath(import.meta.url);
|
|
18
|
+
const destinationSrc = getSrcPath(import.meta.url);
|
|
19
|
+
|
|
20
|
+
const fwSpinner = ora('Ensuring latest Slice framework...').start();
|
|
21
|
+
let sliceBaseDir;
|
|
22
|
+
try {
|
|
23
|
+
const latest = execSync('npm view slicejs-web-framework version', { cwd: projectRoot }).toString().trim();
|
|
24
|
+
const installedPkgPath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'package.json');
|
|
25
|
+
let installed = null;
|
|
26
|
+
if (await fs.pathExists(installedPkgPath)) {
|
|
27
|
+
const pkg = await fs.readJson(installedPkgPath);
|
|
28
|
+
installed = pkg.version;
|
|
29
|
+
}
|
|
30
|
+
if (installed !== latest) {
|
|
31
|
+
execSync(`npm install slicejs-web-framework@${latest} --save`, { cwd: projectRoot, stdio: 'inherit' });
|
|
32
|
+
}
|
|
33
|
+
sliceBaseDir = path.join(projectRoot, 'node_modules', 'slicejs-web-framework');
|
|
34
|
+
fwSpinner.succeed(`slicejs-web-framework@${latest} ready`);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const fallback = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../slicejs-web-framework');
|
|
37
|
+
if (await fs.pathExists(fallback)) {
|
|
38
|
+
sliceBaseDir = fallback;
|
|
39
|
+
fwSpinner.warn('Using local slicejs-web-framework fallback');
|
|
40
|
+
} else {
|
|
41
|
+
fwSpinner.fail('Failed to ensure latest slicejs-web-framework');
|
|
42
|
+
Print.error(err.message);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const apiDir = path.join(sliceBaseDir, 'api');
|
|
48
|
+
const srcDir = path.join(sliceBaseDir, 'src');
|
|
20
49
|
|
|
21
50
|
try {
|
|
22
51
|
// Verificar si los directorios de destino ya existen
|
|
@@ -131,6 +160,73 @@ export default async function initializeProject(projectType) {
|
|
|
131
160
|
Print.info('You can add them later using "slice get <component-name>"');
|
|
132
161
|
}
|
|
133
162
|
|
|
163
|
+
// 4. CONFIGURAR SCRIPTS EN package.json DEL PROYECTO
|
|
164
|
+
const pkgSpinner = ora('Configuring npm scripts...').start();
|
|
165
|
+
try {
|
|
166
|
+
const projectRoot = getProjectRoot(import.meta.url);
|
|
167
|
+
const pkgPath = path.join(projectRoot, 'package.json');
|
|
168
|
+
|
|
169
|
+
let pkg;
|
|
170
|
+
if (await fs.pathExists(pkgPath)) {
|
|
171
|
+
pkg = await fs.readJson(pkgPath);
|
|
172
|
+
} else {
|
|
173
|
+
pkg = {
|
|
174
|
+
name: path.basename(projectRoot),
|
|
175
|
+
version: '1.0.0',
|
|
176
|
+
description: 'Slice.js project',
|
|
177
|
+
main: 'api/index.js',
|
|
178
|
+
scripts: {}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
pkg.scripts = pkg.scripts || {};
|
|
183
|
+
|
|
184
|
+
// Comandos principales
|
|
185
|
+
pkg.scripts['dev'] = 'slice dev';
|
|
186
|
+
pkg.scripts['start'] = 'slice start';
|
|
187
|
+
|
|
188
|
+
// Gestión de componentes
|
|
189
|
+
pkg.scripts['component:create'] = 'slice component create';
|
|
190
|
+
pkg.scripts['component:list'] = 'slice component list';
|
|
191
|
+
pkg.scripts['component:delete'] = 'slice component delete';
|
|
192
|
+
|
|
193
|
+
// Atajos de repositorio
|
|
194
|
+
pkg.scripts['get'] = 'slice get';
|
|
195
|
+
pkg.scripts['browse'] = 'slice browse';
|
|
196
|
+
pkg.scripts['sync'] = 'slice sync';
|
|
197
|
+
|
|
198
|
+
// Utilidades
|
|
199
|
+
pkg.scripts['slice:version'] = 'slice version';
|
|
200
|
+
pkg.scripts['slice:update'] = 'slice update';
|
|
201
|
+
|
|
202
|
+
// Legacy (compatibilidad)
|
|
203
|
+
pkg.scripts['slice:init'] = 'slice init';
|
|
204
|
+
pkg.scripts['slice:start'] = 'slice start';
|
|
205
|
+
pkg.scripts['slice:dev'] = 'slice dev';
|
|
206
|
+
pkg.scripts['slice:create'] = 'slice component create';
|
|
207
|
+
pkg.scripts['slice:list'] = 'slice component list';
|
|
208
|
+
pkg.scripts['slice:delete'] = 'slice component delete';
|
|
209
|
+
pkg.scripts['slice:get'] = 'slice get';
|
|
210
|
+
pkg.scripts['slice:browse'] = 'slice browse';
|
|
211
|
+
pkg.scripts['slice:sync'] = 'slice sync';
|
|
212
|
+
pkg.scripts['run'] = 'slice dev';
|
|
213
|
+
|
|
214
|
+
// Configuración de módulo
|
|
215
|
+
pkg.type = pkg.type || 'module';
|
|
216
|
+
pkg.engines = pkg.engines || { node: '>=20.0.0' };
|
|
217
|
+
|
|
218
|
+
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
219
|
+
pkgSpinner.succeed('npm scripts configured successfully');
|
|
220
|
+
|
|
221
|
+
console.log('\n🎯 New recommended commands:');
|
|
222
|
+
console.log(' npm run dev - Start development server');
|
|
223
|
+
console.log(' npm run get - Install components');
|
|
224
|
+
console.log(' npm run browse - Browse components');
|
|
225
|
+
} catch (error) {
|
|
226
|
+
pkgSpinner.fail('Failed to configure npm scripts');
|
|
227
|
+
Print.error(error.message);
|
|
228
|
+
}
|
|
229
|
+
|
|
134
230
|
Print.success('Proyecto inicializado correctamente.');
|
|
135
231
|
Print.newLine();
|
|
136
232
|
Print.info('Next steps:');
|
|
@@ -155,4 +251,4 @@ async function getAllVisualComponents(registry) {
|
|
|
155
251
|
Print.info(`Found ${allVisualComponents.length} Visual components in official repository`);
|
|
156
252
|
|
|
157
253
|
return allVisualComponents;
|
|
158
|
-
}
|
|
254
|
+
}
|
|
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'url';
|
|
|
4
4
|
import Table from 'cli-table3';
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import Print from '../Print.js';
|
|
7
|
+
import { getSrcPath, getComponentsJsPath, getConfigPath } from '../utils/PathHelper.js';
|
|
7
8
|
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
13
14
|
*/
|
|
14
15
|
const loadConfig = () => {
|
|
15
16
|
try {
|
|
16
|
-
const configPath =
|
|
17
|
+
const configPath = getConfigPath(import.meta.url);
|
|
17
18
|
if (!fs.existsSync(configPath)) {
|
|
18
19
|
Print.error('sliceConfig.json not found');
|
|
19
20
|
Print.info('Run "slice init" to initialize your project');
|
|
@@ -72,7 +73,8 @@ const getComponents = () => {
|
|
|
72
73
|
let allComponents = new Map();
|
|
73
74
|
|
|
74
75
|
Object.entries(componentPaths).forEach(([category, { path: folderPath }]) => {
|
|
75
|
-
const
|
|
76
|
+
const cleanFolderPath = folderPath ? folderPath.replace(/^[/\\]+/, '') : '';
|
|
77
|
+
const fullPath = getSrcPath(import.meta.url, cleanFolderPath);
|
|
76
78
|
const files = listComponents(fullPath);
|
|
77
79
|
|
|
78
80
|
files.forEach(file => {
|
|
@@ -150,7 +152,7 @@ function listComponentsReal() {
|
|
|
150
152
|
Print.info(`Total: ${Object.keys(components).length} component${Object.keys(components).length !== 1 ? 's' : ''} found`);
|
|
151
153
|
|
|
152
154
|
// Ruta donde se generará components.js
|
|
153
|
-
const outputPath =
|
|
155
|
+
const outputPath = getComponentsJsPath(import.meta.url);
|
|
154
156
|
|
|
155
157
|
// Asegurar que el directorio existe
|
|
156
158
|
const outputDir = path.dirname(outputPath);
|
|
@@ -7,6 +7,7 @@ import { spawn } from 'child_process';
|
|
|
7
7
|
import { createServer } from 'net';
|
|
8
8
|
import setupWatcher, { stopWatcher } from './watchServer.js';
|
|
9
9
|
import Print from '../Print.js';
|
|
10
|
+
import { getConfigPath, getApiPath, getSrcPath, getDistPath } from '../utils/PathHelper.js';
|
|
10
11
|
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
|
|
@@ -15,7 +16,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
15
16
|
*/
|
|
16
17
|
const loadConfig = () => {
|
|
17
18
|
try {
|
|
18
|
-
const configPath =
|
|
19
|
+
const configPath = getConfigPath(import.meta.url);
|
|
19
20
|
const rawData = fs.readFileSync(configPath, 'utf-8');
|
|
20
21
|
return JSON.parse(rawData);
|
|
21
22
|
} catch (error) {
|
|
@@ -52,7 +53,7 @@ async function isPortAvailable(port) {
|
|
|
52
53
|
* Verifica si existe un build de producción
|
|
53
54
|
*/
|
|
54
55
|
async function checkProductionBuild() {
|
|
55
|
-
const distDir =
|
|
56
|
+
const distDir = getDistPath(import.meta.url);
|
|
56
57
|
return await fs.pathExists(distDir);
|
|
57
58
|
}
|
|
58
59
|
|
|
@@ -60,8 +61,8 @@ async function checkProductionBuild() {
|
|
|
60
61
|
* Verifica si existe la estructura de desarrollo
|
|
61
62
|
*/
|
|
62
63
|
async function checkDevelopmentStructure() {
|
|
63
|
-
const srcDir =
|
|
64
|
-
const apiDir =
|
|
64
|
+
const srcDir = getSrcPath(import.meta.url);
|
|
65
|
+
const apiDir = getApiPath(import.meta.url);
|
|
65
66
|
|
|
66
67
|
return (await fs.pathExists(srcDir)) && (await fs.pathExists(apiDir));
|
|
67
68
|
}
|
|
@@ -71,7 +72,7 @@ async function checkDevelopmentStructure() {
|
|
|
71
72
|
*/
|
|
72
73
|
function startNodeServer(port, mode) {
|
|
73
74
|
return new Promise((resolve, reject) => {
|
|
74
|
-
const apiIndexPath =
|
|
75
|
+
const apiIndexPath = getApiPath(import.meta.url, 'index.js');
|
|
75
76
|
|
|
76
77
|
// Verificar que el archivo existe
|
|
77
78
|
if (!fs.existsSync(apiIndexPath)) {
|
|
@@ -235,4 +236,4 @@ export default async function startServer(options = {}) {
|
|
|
235
236
|
/**
|
|
236
237
|
* Funciones de utilidad exportadas
|
|
237
238
|
*/
|
|
238
|
-
export { checkProductionBuild, checkDevelopmentStructure, isPortAvailable };
|
|
239
|
+
export { checkProductionBuild, checkDevelopmentStructure, isPortAvailable };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs-extra'
|
|
3
|
+
import { fileURLToPath } from 'url'
|
|
4
|
+
|
|
5
|
+
const sanitize = (s) => (s || '').replace(/^[/\\]+/, '')
|
|
6
|
+
const dirOf = (url) => path.dirname(fileURLToPath(url))
|
|
7
|
+
|
|
8
|
+
function candidates(moduleUrl) {
|
|
9
|
+
const dir = dirOf(moduleUrl)
|
|
10
|
+
return [
|
|
11
|
+
path.join(dir, '../../'),
|
|
12
|
+
path.join(dir, '../../../../')
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveProjectRoot(moduleUrl) {
|
|
17
|
+
const dirs = candidates(moduleUrl)
|
|
18
|
+
for (const root of dirs) {
|
|
19
|
+
const hasSrc = fs.pathExistsSync(path.join(root, 'src'))
|
|
20
|
+
const hasApi = fs.pathExistsSync(path.join(root, 'api'))
|
|
21
|
+
if (hasSrc || hasApi) return root
|
|
22
|
+
}
|
|
23
|
+
return dirs[1]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function joinProject(moduleUrl, ...segments) {
|
|
27
|
+
const root = resolveProjectRoot(moduleUrl)
|
|
28
|
+
const clean = segments.map(sanitize)
|
|
29
|
+
return path.join(root, ...clean)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getProjectRoot(moduleUrl) {
|
|
33
|
+
return resolveProjectRoot(moduleUrl)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getPath(moduleUrl, folder, ...segments) {
|
|
37
|
+
return joinProject(moduleUrl, folder, ...segments)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getSrcPath(moduleUrl, ...segments) {
|
|
41
|
+
return joinProject(moduleUrl, 'src', ...segments)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getApiPath(moduleUrl, ...segments) {
|
|
45
|
+
return joinProject(moduleUrl, 'api', ...segments)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function getDistPath(moduleUrl, ...segments) {
|
|
49
|
+
return joinProject(moduleUrl, 'dist', ...segments)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function getConfigPath(moduleUrl) {
|
|
53
|
+
return joinProject(moduleUrl, 'src', 'sliceConfig.json')
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getComponentsJsPath(moduleUrl) {
|
|
57
|
+
return joinProject(moduleUrl, 'src', 'Components', 'components.js')
|
|
58
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// commands/utils/VersionChecker.js
|
|
2
2
|
|
|
3
|
-
import fs from "fs-extra";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import Print from "../Print.js";
|
|
3
|
+
import fs from "fs-extra";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import Print from "../Print.js";
|
|
7
|
+
import { getProjectRoot } from "../utils/PathHelper.js";
|
|
7
8
|
|
|
8
9
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
10
|
|
|
@@ -22,8 +23,9 @@ class VersionChecker {
|
|
|
22
23
|
const cliPackage = await fs.readJson(cliPackagePath);
|
|
23
24
|
this.currentCliVersion = cliPackage.version;
|
|
24
25
|
|
|
25
|
-
// Get Framework version from node_modules
|
|
26
|
-
const
|
|
26
|
+
// Get Framework version from project node_modules
|
|
27
|
+
const projectRoot = getProjectRoot(import.meta.url);
|
|
28
|
+
const frameworkPackagePath = path.join(projectRoot, 'node_modules', 'slicejs-web-framework', 'package.json');
|
|
27
29
|
if (await fs.pathExists(frameworkPackagePath)) {
|
|
28
30
|
const frameworkPackage = await fs.readJson(frameworkPackagePath);
|
|
29
31
|
this.currentFrameworkVersion = frameworkPackage.version;
|
|
@@ -162,4 +164,4 @@ class VersionChecker {
|
|
|
162
164
|
// Singleton instance
|
|
163
165
|
const versionChecker = new VersionChecker();
|
|
164
166
|
|
|
165
|
-
export default versionChecker;
|
|
167
|
+
export default versionChecker;
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
// commands/utils/updateManager.js
|
|
2
|
+
|
|
3
|
+
import { exec } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
5
|
+
import inquirer from "inquirer";
|
|
6
|
+
import ora from "ora";
|
|
7
|
+
import Print from "../Print.js";
|
|
8
|
+
import versionChecker from "./VersionChecker.js";
|
|
9
|
+
import { getProjectRoot } from "../utils/PathHelper.js";
|
|
10
|
+
import path from "path";
|
|
11
|
+
import { fileURLToPath } from "url";
|
|
12
|
+
|
|
13
|
+
const execAsync = promisify(exec);
|
|
14
|
+
|
|
15
|
+
class UpdateManager {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.packagesToUpdate = [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async detectCliInstall() {
|
|
21
|
+
try {
|
|
22
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const cliRoot = path.join(moduleDir, '../../');
|
|
24
|
+
const projectRoot = getProjectRoot(import.meta.url);
|
|
25
|
+
let globalPrefix = '';
|
|
26
|
+
try {
|
|
27
|
+
const { stdout } = await execAsync('npm config get prefix');
|
|
28
|
+
globalPrefix = stdout.toString().trim();
|
|
29
|
+
} catch {}
|
|
30
|
+
const localNodeModules = path.join(projectRoot, 'node_modules');
|
|
31
|
+
const globalNodeModules = globalPrefix ? path.join(globalPrefix, 'node_modules') : '';
|
|
32
|
+
|
|
33
|
+
if (cliRoot.startsWith(localNodeModules)) {
|
|
34
|
+
return { type: 'local', cliRoot, projectRoot, globalPrefix };
|
|
35
|
+
}
|
|
36
|
+
if (globalNodeModules && cliRoot.startsWith(globalNodeModules)) {
|
|
37
|
+
return { type: 'global', cliRoot, projectRoot, globalPrefix };
|
|
38
|
+
}
|
|
39
|
+
return { type: 'unknown', cliRoot, projectRoot, globalPrefix };
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return { type: 'unknown' };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check for available updates and return structured info
|
|
47
|
+
*/
|
|
48
|
+
async checkForUpdates() {
|
|
49
|
+
try {
|
|
50
|
+
const updateInfo = await versionChecker.checkForUpdates(true); // Silent mode
|
|
51
|
+
|
|
52
|
+
if (!updateInfo) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const updates = [];
|
|
57
|
+
|
|
58
|
+
if (updateInfo.cli.status === 'outdated') {
|
|
59
|
+
updates.push({
|
|
60
|
+
name: 'slicejs-cli',
|
|
61
|
+
displayName: 'Slice.js CLI',
|
|
62
|
+
current: updateInfo.cli.current,
|
|
63
|
+
latest: updateInfo.cli.latest,
|
|
64
|
+
type: 'cli'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (updateInfo.framework.status === 'outdated') {
|
|
69
|
+
updates.push({
|
|
70
|
+
name: 'slicejs-web-framework',
|
|
71
|
+
displayName: 'Slice.js Framework',
|
|
72
|
+
current: updateInfo.framework.current,
|
|
73
|
+
latest: updateInfo.framework.latest,
|
|
74
|
+
type: 'framework'
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
hasUpdates: updates.length > 0,
|
|
80
|
+
updates,
|
|
81
|
+
allCurrent: updateInfo.cli.status === 'current' && updateInfo.framework.status === 'current'
|
|
82
|
+
};
|
|
83
|
+
} catch (error) {
|
|
84
|
+
Print.error(`Checking for updates: ${error.message}`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Display available updates in a formatted way
|
|
91
|
+
*/
|
|
92
|
+
displayUpdates(updateInfo) {
|
|
93
|
+
if (!updateInfo || !updateInfo.hasUpdates) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('');
|
|
98
|
+
Print.warning('📦 Actualizaciones Disponibles:');
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
updateInfo.updates.forEach(pkg => {
|
|
102
|
+
console.log(` ${pkg.type === 'cli' ? '🔧' : '⚡'} ${pkg.displayName}`);
|
|
103
|
+
console.log(` ${pkg.current} → ${pkg.latest}`);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log('');
|
|
107
|
+
console.log(' 📚 Changelog: https://github.com/VKneider/slice.js/releases');
|
|
108
|
+
console.log('');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Prompt user to select which packages to update
|
|
113
|
+
*/
|
|
114
|
+
async promptForUpdates(updateInfo, options = {}) {
|
|
115
|
+
if (!updateInfo || !updateInfo.hasUpdates) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// If --yes flag is set, return all updates
|
|
120
|
+
if (options.yes) {
|
|
121
|
+
return updateInfo.updates.map(pkg => pkg.name);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If specific package flags are set
|
|
125
|
+
if (options.cli || options.framework) {
|
|
126
|
+
const selected = [];
|
|
127
|
+
if (options.cli) {
|
|
128
|
+
const cliUpdate = updateInfo.updates.find(pkg => pkg.type === 'cli');
|
|
129
|
+
if (cliUpdate) selected.push(cliUpdate.name);
|
|
130
|
+
}
|
|
131
|
+
if (options.framework) {
|
|
132
|
+
const frameworkUpdate = updateInfo.updates.find(pkg => pkg.type === 'framework');
|
|
133
|
+
if (frameworkUpdate) selected.push(frameworkUpdate.name);
|
|
134
|
+
}
|
|
135
|
+
return selected;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Interactive selection
|
|
139
|
+
const choices = updateInfo.updates.map(pkg => ({
|
|
140
|
+
name: `${pkg.displayName} (${pkg.current} → ${pkg.latest})`,
|
|
141
|
+
value: pkg.name,
|
|
142
|
+
checked: true
|
|
143
|
+
}));
|
|
144
|
+
|
|
145
|
+
const answers = await inquirer.prompt([
|
|
146
|
+
{
|
|
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
|
+
]);
|
|
159
|
+
|
|
160
|
+
return answers.packages;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async buildUpdatePlan(packages) {
|
|
164
|
+
const plan = [];
|
|
165
|
+
const info = await this.detectCliInstall();
|
|
166
|
+
for (const pkg of packages) {
|
|
167
|
+
if (pkg === 'slicejs-cli') {
|
|
168
|
+
if (info.type === 'global') {
|
|
169
|
+
plan.push({ package: pkg, target: 'global', command: 'npm install -g slicejs-cli@latest' });
|
|
170
|
+
} else {
|
|
171
|
+
plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-cli@latest' });
|
|
172
|
+
}
|
|
173
|
+
} else if (pkg === 'slicejs-web-framework') {
|
|
174
|
+
plan.push({ package: pkg, target: 'project', command: 'npm install slicejs-web-framework@latest' });
|
|
175
|
+
} else {
|
|
176
|
+
plan.push({ package: pkg, target: 'project', command: `npm install ${pkg}@latest` });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return plan;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Execute npm update command for a specific package
|
|
184
|
+
*/
|
|
185
|
+
async updatePackage(packageName) {
|
|
186
|
+
try {
|
|
187
|
+
let command = `npm install ${packageName}@latest`;
|
|
188
|
+
let options = {};
|
|
189
|
+
|
|
190
|
+
if (packageName === 'slicejs-cli') {
|
|
191
|
+
const info = await this.detectCliInstall();
|
|
192
|
+
if (info.type === 'global') {
|
|
193
|
+
command = `npm install -g slicejs-cli@latest`;
|
|
194
|
+
} else {
|
|
195
|
+
options.cwd = info.projectRoot || getProjectRoot(import.meta.url);
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
options.cwd = getProjectRoot(import.meta.url);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const { stdout, stderr } = await execAsync(command, options);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
success: true,
|
|
205
|
+
packageName,
|
|
206
|
+
stdout,
|
|
207
|
+
stderr
|
|
208
|
+
};
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return {
|
|
211
|
+
success: false,
|
|
212
|
+
packageName,
|
|
213
|
+
error: error.message
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Update multiple packages with progress indication
|
|
220
|
+
*/
|
|
221
|
+
async installUpdates(packages) {
|
|
222
|
+
const results = [];
|
|
223
|
+
|
|
224
|
+
for (const packageName of packages) {
|
|
225
|
+
const spinner = ora(`Actualizando ${packageName}...`).start();
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const result = await this.updatePackage(packageName);
|
|
229
|
+
|
|
230
|
+
if (result.success) {
|
|
231
|
+
spinner.succeed(`${packageName} actualizado exitosamente`);
|
|
232
|
+
results.push({ packageName, success: true });
|
|
233
|
+
} else {
|
|
234
|
+
spinner.fail(`Error actualizando ${packageName}`);
|
|
235
|
+
Print.error(`Detalles: ${result.error}`);
|
|
236
|
+
results.push({ packageName, success: false, error: result.error });
|
|
237
|
+
}
|
|
238
|
+
} catch (error) {
|
|
239
|
+
spinner.fail(`Error actualizando ${packageName}`);
|
|
240
|
+
Print.error(`Detalles: ${error.message}`);
|
|
241
|
+
results.push({ packageName, success: false, error: error.message });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return results;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Main method to check and prompt for updates
|
|
250
|
+
*/
|
|
251
|
+
async checkAndPromptUpdates(options = {}) {
|
|
252
|
+
const spinner = ora('Verificando actualizaciones...').start();
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const updateInfo = await this.checkForUpdates();
|
|
256
|
+
spinner.stop();
|
|
257
|
+
|
|
258
|
+
if (!updateInfo) {
|
|
259
|
+
Print.error('No se pudo verificar actualizaciones. Verifica tu conexión a internet.');
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (updateInfo.allCurrent) {
|
|
264
|
+
Print.success('✅ Todos los componentes están actualizados!');
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (!updateInfo.hasUpdates) {
|
|
269
|
+
Print.success('✅ Todos los componentes están actualizados!');
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Display available updates
|
|
274
|
+
this.displayUpdates(updateInfo);
|
|
275
|
+
|
|
276
|
+
// Get packages to update
|
|
277
|
+
const packagesToUpdate = await this.promptForUpdates(updateInfo, options);
|
|
278
|
+
|
|
279
|
+
if (!packagesToUpdate || packagesToUpdate.length === 0) {
|
|
280
|
+
Print.info('No se seleccionaron paquetes para actualizar.');
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Show plan and confirm installation if not auto-confirmed
|
|
285
|
+
let plan = await this.buildUpdatePlan(packagesToUpdate);
|
|
286
|
+
console.log('');
|
|
287
|
+
Print.info('🧭 Plan de actualización:');
|
|
288
|
+
plan.forEach(item => {
|
|
289
|
+
const where = item.target === 'global' ? 'GLOBAL' : 'PROYECTO';
|
|
290
|
+
console.log(` • ${item.package} → ${where}`);
|
|
291
|
+
console.log(` ${item.command}`);
|
|
292
|
+
});
|
|
293
|
+
console.log('');
|
|
294
|
+
|
|
295
|
+
const cliInfo = await this.detectCliInstall();
|
|
296
|
+
if (cliInfo.type === 'global' && !packagesToUpdate.includes('slicejs-cli')) {
|
|
297
|
+
if (!options.yes && !options.cli) {
|
|
298
|
+
const { addCli } = await inquirer.prompt([
|
|
299
|
+
{
|
|
300
|
+
type: 'confirm',
|
|
301
|
+
name: 'addCli',
|
|
302
|
+
message: 'Se detectó CLI global. ¿Agregar la actualización global del CLI al plan?',
|
|
303
|
+
default: true
|
|
304
|
+
}
|
|
305
|
+
]);
|
|
306
|
+
if (addCli) {
|
|
307
|
+
packagesToUpdate.push('slicejs-cli');
|
|
308
|
+
plan = await this.buildUpdatePlan(packagesToUpdate);
|
|
309
|
+
console.log('');
|
|
310
|
+
Print.info('🧭 Plan actualizado:');
|
|
311
|
+
plan.forEach(item => {
|
|
312
|
+
const where = item.target === 'global' ? 'GLOBAL' : 'PROYECTO';
|
|
313
|
+
console.log(` • ${item.package} → ${where}`);
|
|
314
|
+
console.log(` ${item.command}`);
|
|
315
|
+
});
|
|
316
|
+
console.log('');
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
Print.warning('CLI global detectado. Se recomienda actualizar slicejs-cli global para mantener alineado con el framework.');
|
|
320
|
+
console.log(' Sugerencia: npm install -g slicejs-cli@latest');
|
|
321
|
+
console.log('');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (!options.yes && !options.cli && !options.framework) {
|
|
326
|
+
const { confirm } = await inquirer.prompt([
|
|
327
|
+
{
|
|
328
|
+
type: 'confirm',
|
|
329
|
+
name: 'confirm',
|
|
330
|
+
message: '¿Deseas continuar con la actualización según el plan mostrado?',
|
|
331
|
+
default: true
|
|
332
|
+
}
|
|
333
|
+
]);
|
|
334
|
+
|
|
335
|
+
if (!confirm) {
|
|
336
|
+
Print.info('Actualización cancelada.');
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
console.log(''); // Line break
|
|
342
|
+
Print.info('📥 Instalando actualizaciones...');
|
|
343
|
+
console.log('');
|
|
344
|
+
|
|
345
|
+
// Install updates
|
|
346
|
+
const results = await this.installUpdates(packagesToUpdate);
|
|
347
|
+
|
|
348
|
+
// Summary
|
|
349
|
+
console.log('');
|
|
350
|
+
const successCount = results.filter(r => r.success).length;
|
|
351
|
+
const failCount = results.filter(r => !r.success).length;
|
|
352
|
+
|
|
353
|
+
if (failCount === 0) {
|
|
354
|
+
Print.success(`✅ ${successCount} paquete(s) actualizado(s) exitosamente!`);
|
|
355
|
+
} else {
|
|
356
|
+
Print.warning(`⚠️ ${successCount} exitoso(s), ${failCount} fallido(s)`);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (successCount > 0) {
|
|
360
|
+
console.log('');
|
|
361
|
+
Print.info('💡 Se recomienda reiniciar el servidor de desarrollo si está ejecutándose.');
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return failCount === 0;
|
|
365
|
+
|
|
366
|
+
} catch (error) {
|
|
367
|
+
spinner.stop();
|
|
368
|
+
Print.error(`Error durante la actualización: ${error.message}`);
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Singleton instance
|
|
375
|
+
const updateManager = new UpdateManager();
|
|
376
|
+
|
|
377
|
+
export default updateManager;
|