slicejs-cli 2.7.4 → 2.7.6
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/client.js +50 -8
- package/commands/bundle/bundle.js +226 -0
- package/commands/doctor/doctor.js +11 -11
- package/commands/getComponent/getComponent.js +74 -36
- package/commands/init/init.js +56 -57
- package/commands/startServer/startServer.js +6 -2
- package/commands/utils/PathHelper.js +10 -0
- package/commands/utils/VersionChecker.js +2 -2
- package/commands/utils/bundling/BundleGenerator.js +389 -0
- package/commands/utils/bundling/DependencyAnalyzer.js +340 -0
- package/commands/utils/updateManager.js +87 -87
- package/package.json +1 -1
- package/refactor.md +271 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// cli/utils/bundling/DependencyAnalyzer.js
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { parse } from '@babel/parser';
|
|
5
|
+
import traverse from '@babel/traverse';
|
|
6
|
+
import { getSrcPath, getComponentsJsPath, getProjectRoot } from '../PathHelper.js';
|
|
7
|
+
|
|
8
|
+
export default class DependencyAnalyzer {
|
|
9
|
+
constructor(moduleUrl) {
|
|
10
|
+
this.moduleUrl = moduleUrl;
|
|
11
|
+
this.projectRoot = getProjectRoot(moduleUrl);
|
|
12
|
+
this.componentsPath = path.dirname(getComponentsJsPath(moduleUrl));
|
|
13
|
+
this.routesPath = getSrcPath(moduleUrl, 'routes.js');
|
|
14
|
+
|
|
15
|
+
// Analysis storage
|
|
16
|
+
this.components = new Map();
|
|
17
|
+
this.routes = new Map();
|
|
18
|
+
this.dependencyGraph = new Map();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Executes complete project analysis
|
|
23
|
+
*/
|
|
24
|
+
async analyze() {
|
|
25
|
+
console.log('🔍 Analyzing project...');
|
|
26
|
+
|
|
27
|
+
// 1. Load component configuration
|
|
28
|
+
await this.loadComponentsConfig();
|
|
29
|
+
|
|
30
|
+
// 2. Analyze component files
|
|
31
|
+
await this.analyzeComponents();
|
|
32
|
+
|
|
33
|
+
// 3. Load and analyze routes
|
|
34
|
+
await this.analyzeRoutes();
|
|
35
|
+
|
|
36
|
+
// 4. Build dependency graph
|
|
37
|
+
this.buildDependencyGraph();
|
|
38
|
+
|
|
39
|
+
// 5. Calculate metrics
|
|
40
|
+
const metrics = this.calculateMetrics();
|
|
41
|
+
|
|
42
|
+
console.log('✅ Analysis completed');
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
components: Array.from(this.components.values()),
|
|
46
|
+
routes: Array.from(this.routes.values()),
|
|
47
|
+
dependencyGraph: this.dependencyGraph,
|
|
48
|
+
metrics
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Loads component configuration from components.js
|
|
54
|
+
*/
|
|
55
|
+
async loadComponentsConfig() {
|
|
56
|
+
const componentsConfigPath = path.join(this.componentsPath, 'components.js');
|
|
57
|
+
|
|
58
|
+
if (!await fs.pathExists(componentsConfigPath)) {
|
|
59
|
+
throw new Error('components.js not found');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Read and parse components.js
|
|
63
|
+
const content = await fs.readFile(componentsConfigPath, 'utf-8');
|
|
64
|
+
|
|
65
|
+
// Extract configuration using simple regex
|
|
66
|
+
const configMatch = content.match(/export default\s*({[\s\S]*})/);
|
|
67
|
+
if (!configMatch) {
|
|
68
|
+
throw new Error('Could not parse components.js');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Evaluate safely (in production use a more robust parser)
|
|
72
|
+
const config = eval(`(${configMatch[1]})`);
|
|
73
|
+
|
|
74
|
+
// Save category metadata
|
|
75
|
+
for (const [category, info] of Object.entries(config)) {
|
|
76
|
+
const categoryPath = path.join(this.componentsPath, info.path.replace('../', ''));
|
|
77
|
+
|
|
78
|
+
if (await fs.pathExists(categoryPath)) {
|
|
79
|
+
const files = await fs.readdir(categoryPath);
|
|
80
|
+
|
|
81
|
+
for (const file of files) {
|
|
82
|
+
const componentPath = path.join(categoryPath, file);
|
|
83
|
+
const stat = await fs.stat(componentPath);
|
|
84
|
+
|
|
85
|
+
if (stat.isDirectory()) {
|
|
86
|
+
this.components.set(file, {
|
|
87
|
+
name: file,
|
|
88
|
+
category,
|
|
89
|
+
categoryType: info.type,
|
|
90
|
+
path: componentPath,
|
|
91
|
+
dependencies: new Set(),
|
|
92
|
+
usedBy: new Set(),
|
|
93
|
+
routes: new Set(),
|
|
94
|
+
size: 0
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Analyzes each component's files
|
|
104
|
+
*/
|
|
105
|
+
async analyzeComponents() {
|
|
106
|
+
for (const [name, component] of this.components) {
|
|
107
|
+
const jsFile = path.join(component.path, `${name}.js`);
|
|
108
|
+
|
|
109
|
+
if (!await fs.pathExists(jsFile)) continue;
|
|
110
|
+
|
|
111
|
+
// Read JavaScript file
|
|
112
|
+
const content = await fs.readFile(jsFile, 'utf-8');
|
|
113
|
+
|
|
114
|
+
// Calculate size
|
|
115
|
+
component.size = await this.calculateComponentSize(component.path);
|
|
116
|
+
|
|
117
|
+
// Parse and extract dependencies
|
|
118
|
+
component.dependencies = await this.extractDependencies(content);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Extracts dependencies from a component file
|
|
124
|
+
*/
|
|
125
|
+
async extractDependencies(code) {
|
|
126
|
+
const dependencies = new Set();
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const ast = parse(code, {
|
|
130
|
+
sourceType: 'module',
|
|
131
|
+
plugins: ['jsx']
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
traverse.default(ast, {
|
|
135
|
+
// Detect slice.build() calls
|
|
136
|
+
CallExpression(path) {
|
|
137
|
+
const { callee, arguments: args } = path.node;
|
|
138
|
+
|
|
139
|
+
// slice.build('ComponentName', ...)
|
|
140
|
+
if (
|
|
141
|
+
callee.type === 'MemberExpression' &&
|
|
142
|
+
callee.object.name === 'slice' &&
|
|
143
|
+
callee.property.name === 'build' &&
|
|
144
|
+
args[0]?.type === 'StringLiteral'
|
|
145
|
+
) {
|
|
146
|
+
dependencies.add(args[0].value);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
// Detect direct imports (less common but possible)
|
|
151
|
+
ImportDeclaration(path) {
|
|
152
|
+
const importPath = path.node.source.value;
|
|
153
|
+
if (importPath.includes('/Components/')) {
|
|
154
|
+
const componentName = importPath.split('/').pop();
|
|
155
|
+
dependencies.add(componentName);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.warn(`⚠️ Error parsing component: ${error.message}`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return dependencies;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Analyzes the routes file
|
|
168
|
+
*/
|
|
169
|
+
async analyzeRoutes() {
|
|
170
|
+
if (!await fs.pathExists(this.routesPath)) {
|
|
171
|
+
throw new Error('routes.js no encontrado');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const content = await fs.readFile(this.routesPath, 'utf-8');
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
const ast = parse(content, {
|
|
178
|
+
sourceType: 'module',
|
|
179
|
+
plugins: ['jsx']
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
let currentRoute = null;
|
|
183
|
+
const self = this; // Guardar referencia a la instancia
|
|
184
|
+
|
|
185
|
+
traverse.default(ast, {
|
|
186
|
+
ObjectExpression(path) {
|
|
187
|
+
// Buscar objetos de ruta: { path: '/', component: 'HomePage' }
|
|
188
|
+
const properties = path.node.properties;
|
|
189
|
+
const pathProp = properties.find(p => p.key?.name === 'path');
|
|
190
|
+
const componentProp = properties.find(p => p.key?.name === 'component');
|
|
191
|
+
|
|
192
|
+
if (pathProp && componentProp) {
|
|
193
|
+
const routePath = pathProp.value.value;
|
|
194
|
+
const componentName = componentProp.value.value;
|
|
195
|
+
|
|
196
|
+
currentRoute = {
|
|
197
|
+
path: routePath,
|
|
198
|
+
component: componentName,
|
|
199
|
+
dependencies: new Set([componentName])
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
self.routes.set(routePath, currentRoute);
|
|
203
|
+
|
|
204
|
+
// Marcar el componente como usado por esta ruta
|
|
205
|
+
if (self.components.has(componentName)) {
|
|
206
|
+
self.components.get(componentName).routes.add(routePath);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.warn(`⚠️ Error parseando rutas: ${error.message}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Builds the complete dependency graph
|
|
218
|
+
*/
|
|
219
|
+
buildDependencyGraph() {
|
|
220
|
+
// Propagate transitive dependencies
|
|
221
|
+
for (const [name, component] of this.components) {
|
|
222
|
+
this.dependencyGraph.set(name, this.getAllDependencies(name, new Set()));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Calculate usedBy (inverse dependencies)
|
|
226
|
+
for (const [name, deps] of this.dependencyGraph) {
|
|
227
|
+
for (const dep of deps) {
|
|
228
|
+
if (this.components.has(dep)) {
|
|
229
|
+
this.components.get(dep).usedBy.add(name);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Gets all dependencies of a component (recursive)
|
|
237
|
+
*/
|
|
238
|
+
getAllDependencies(componentName, visited = new Set()) {
|
|
239
|
+
if (visited.has(componentName)) return new Set();
|
|
240
|
+
visited.add(componentName);
|
|
241
|
+
|
|
242
|
+
const component = this.components.get(componentName);
|
|
243
|
+
if (!component) return new Set();
|
|
244
|
+
|
|
245
|
+
const allDeps = new Set(component.dependencies);
|
|
246
|
+
|
|
247
|
+
for (const dep of component.dependencies) {
|
|
248
|
+
const transitiveDeps = this.getAllDependencies(dep, visited);
|
|
249
|
+
for (const transDep of transitiveDeps) {
|
|
250
|
+
allDeps.add(transDep);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return allDeps;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Calculates the total size of a component (JS + HTML + CSS)
|
|
259
|
+
*/
|
|
260
|
+
async calculateComponentSize(componentPath) {
|
|
261
|
+
let totalSize = 0;
|
|
262
|
+
const files = await fs.readdir(componentPath);
|
|
263
|
+
|
|
264
|
+
for (const file of files) {
|
|
265
|
+
const filePath = path.join(componentPath, file);
|
|
266
|
+
const stat = await fs.stat(filePath);
|
|
267
|
+
|
|
268
|
+
if (stat.isFile()) {
|
|
269
|
+
totalSize += stat.size;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return totalSize;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Calculates project metrics
|
|
278
|
+
*/
|
|
279
|
+
calculateMetrics() {
|
|
280
|
+
const totalComponents = this.components.size;
|
|
281
|
+
const totalRoutes = this.routes.size;
|
|
282
|
+
|
|
283
|
+
// Shared components (used in multiple routes)
|
|
284
|
+
const sharedComponents = Array.from(this.components.values())
|
|
285
|
+
.filter(c => c.routes.size >= 2);
|
|
286
|
+
|
|
287
|
+
// Total size
|
|
288
|
+
const totalSize = Array.from(this.components.values())
|
|
289
|
+
.reduce((sum, c) => sum + c.size, 0);
|
|
290
|
+
|
|
291
|
+
// Components by category
|
|
292
|
+
const byCategory = {};
|
|
293
|
+
for (const comp of this.components.values()) {
|
|
294
|
+
byCategory[comp.category] = (byCategory[comp.category] || 0) + 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Top components by usage
|
|
298
|
+
const topByUsage = Array.from(this.components.values())
|
|
299
|
+
.sort((a, b) => b.routes.size - a.routes.size)
|
|
300
|
+
.slice(0, 10)
|
|
301
|
+
.map(c => ({
|
|
302
|
+
name: c.name,
|
|
303
|
+
routes: c.routes.size,
|
|
304
|
+
size: c.size
|
|
305
|
+
}));
|
|
306
|
+
|
|
307
|
+
return {
|
|
308
|
+
totalComponents,
|
|
309
|
+
totalRoutes,
|
|
310
|
+
sharedComponentsCount: sharedComponents.length,
|
|
311
|
+
sharedPercentage: (sharedComponents.length / totalComponents * 100).toFixed(1),
|
|
312
|
+
totalSize,
|
|
313
|
+
averageSize: Math.round(totalSize / totalComponents),
|
|
314
|
+
byCategory,
|
|
315
|
+
topByUsage
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generates a visual report of the analysis
|
|
321
|
+
*/
|
|
322
|
+
generateReport(metrics) {
|
|
323
|
+
console.log('\n📊 PROJECT ANALYSIS\n');
|
|
324
|
+
console.log(`Total components: ${metrics.totalComponents}`);
|
|
325
|
+
console.log(`Total routes: ${metrics.totalRoutes}`);
|
|
326
|
+
console.log(`Shared components: ${metrics.sharedComponentsCount} (${metrics.sharedPercentage}%)`);
|
|
327
|
+
console.log(`Total size: ${(metrics.totalSize / 1024).toFixed(1)} KB`);
|
|
328
|
+
console.log(`Average size: ${(metrics.averageSize / 1024).toFixed(1)} KB per component`);
|
|
329
|
+
|
|
330
|
+
console.log('\n📦 By category:');
|
|
331
|
+
for (const [category, count] of Object.entries(metrics.byCategory)) {
|
|
332
|
+
console.log(` ${category}: ${count} components`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
console.log('\n🔥 Top 10 most used components:');
|
|
336
|
+
metrics.topByUsage.forEach((comp, i) => {
|
|
337
|
+
console.log(` ${i + 1}. ${comp.name} - ${comp.routes} routes - ${(comp.size / 1024).toFixed(1)} KB`);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}
|
|
@@ -95,7 +95,7 @@ class UpdateManager {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
console.log('');
|
|
98
|
-
Print.warning('📦
|
|
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: '
|
|
150
|
-
choices,
|
|
151
|
-
validate: (answer) => {
|
|
152
|
-
if (answer.length === 0) {
|
|
153
|
-
return '
|
|
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(`
|
|
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}
|
|
239
|
-
results.push({ packageName, success: true });
|
|
240
|
-
} else {
|
|
241
|
-
spinner.fail(`Error
|
|
242
|
-
Print.error(`
|
|
243
|
-
results.push({ packageName, success: false, error: result.error });
|
|
244
|
-
}
|
|
245
|
-
} catch (error) {
|
|
246
|
-
spinner.fail(`Error
|
|
247
|
-
Print.error(`
|
|
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('
|
|
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('
|
|
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('✅
|
|
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('✅
|
|
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
|
|
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('🧭
|
|
295
|
-
plan.forEach(item => {
|
|
296
|
-
const where = item.target === 'global' ? 'GLOBAL' : '
|
|
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
|
-
|
|
310
|
-
|
|
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('🧭
|
|
318
|
-
plan.forEach(item => {
|
|
319
|
-
const where = item.target === 'global' ? 'GLOBAL' : '
|
|
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
|
|
327
|
-
console.log('
|
|
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: '
|
|
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('
|
|
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('📥
|
|
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}
|
|
362
|
-
} else {
|
|
363
|
-
Print.warning(`⚠️ ${successCount}
|
|
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('💡
|
|
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
|
|
376
|
-
return false;
|
|
377
|
-
}
|
|
375
|
+
Print.error(`Error during update: ${error.message}`);
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
378
|
}
|
|
379
379
|
}
|
|
380
380
|
|