slicejs-cli 3.4.0 → 3.4.1
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/AGENTS.md +247 -0
- package/client.js +63 -64
- package/commands/Print.js +11 -15
- package/commands/Validations.js +12 -23
- package/commands/buildProduction/buildProduction.js +23 -26
- package/commands/bundle/bundle.js +10 -11
- package/commands/createComponent/createComponent.js +14 -16
- package/commands/deleteComponent/deleteComponent.js +6 -6
- package/commands/doctor/doctor.js +11 -14
- package/commands/getComponent/getComponent.js +99 -162
- package/commands/init/init.js +25 -21
- package/commands/listComponents/listComponents.js +18 -21
- package/commands/startServer/startServer.js +21 -24
- package/commands/startServer/watchServer.js +7 -7
- package/commands/types/types.js +53 -18
- package/commands/utils/PathHelper.js +9 -2
- package/commands/utils/VersionChecker.js +3 -3
- package/commands/utils/bundling/DependencyAnalyzer.js +8 -16
- package/commands/utils/loadConfig.js +31 -0
- package/commands/utils/updateManager.js +3 -4
- package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +105 -105
- package/package.json +15 -3
- package/post.js +2 -2
- package/tests/bundle-generator.test.js +3 -20
- package/tests/component-registry-parse.test.js +34 -0
- package/tests/fixtures/components.js +8 -0
- package/tests/fixtures/sliceConfig.json +74 -0
- package/tests/getcomponent.test.js +407 -0
- package/tests/helpers/setup.js +97 -0
- package/tests/init-command-contract.test.js +46 -0
- package/tests/local-cli-delegation.test.js +7 -5
- package/tests/path-helper.test.js +206 -0
- package/tests/types-breakage.test.js +491 -0
- package/tests/types-generator-errors.test.js +361 -0
- package/tests/types-generator.test.js +172 -184
|
@@ -2,87 +2,108 @@
|
|
|
2
2
|
|
|
3
3
|
import fs from "fs-extra";
|
|
4
4
|
import path from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
5
|
import inquirer from "inquirer";
|
|
7
6
|
import validations from "../Validations.js";
|
|
8
7
|
import Print from "../Print.js";
|
|
9
|
-
import {
|
|
8
|
+
import { getComponentsJsPath, getPath } from "../utils/PathHelper.js";
|
|
9
|
+
import { loadConfig as sharedLoadConfig } from "../utils/loadConfig.js";
|
|
10
10
|
import ora from "ora";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// Base URL del repositorio de documentación de Slice.js
|
|
12
|
+
// Base URL of the Slice.js documentation repository
|
|
15
13
|
const DOCS_REPO_BASE_URL = 'https://raw.githubusercontent.com/VKneider/slice.js_visual_library/master/src/Components';
|
|
16
14
|
const COMPONENTS_REGISTRY_URL = 'https://raw.githubusercontent.com/VKneider/slice.js_visual_library/master/src/Components/components.js';
|
|
17
15
|
|
|
18
16
|
/**
|
|
19
|
-
*
|
|
20
|
-
* @returns {object} -
|
|
17
|
+
* Loads configuration from sliceConfig.json
|
|
18
|
+
* @returns {object} - Configuration object
|
|
21
19
|
*/
|
|
22
|
-
const loadConfig = () =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
const loadConfig = () => sharedLoadConfig(import.meta.url);
|
|
21
|
+
|
|
22
|
+
const fetchWithRetry = async (url, retries = 3, baseDelay = 500) => {
|
|
23
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
const response = await fetch(url);
|
|
26
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
27
|
+
return await response.text();
|
|
28
|
+
} catch (e) {
|
|
29
|
+
if (attempt === retries) throw e;
|
|
30
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
31
|
+
await new Promise(r => setTimeout(r, delay));
|
|
27
32
|
}
|
|
28
|
-
const rawData = fs.readFileSync(configPath, 'utf-8');
|
|
29
|
-
return JSON.parse(rawData);
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error(`Error loading configuration: ${error.message}`);
|
|
32
|
-
return null;
|
|
33
33
|
}
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const runConcurrent = async (items, worker, concurrency = 3) => {
|
|
37
|
+
let index = 0;
|
|
38
|
+
const runners = Array(Math.min(concurrency, items.length)).fill(0).map(async () => {
|
|
39
|
+
while (true) {
|
|
40
|
+
const i = index++;
|
|
41
|
+
if (i >= items.length) break;
|
|
42
|
+
await worker(items[i]);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
await Promise.all(runners);
|
|
46
|
+
};
|
|
47
|
+
|
|
36
48
|
class ComponentRegistry {
|
|
37
49
|
constructor() {
|
|
38
50
|
this.componentsRegistry = null;
|
|
39
|
-
this.config =
|
|
51
|
+
this.config = null;
|
|
52
|
+
this._configPromise = null;
|
|
40
53
|
}
|
|
41
54
|
|
|
42
|
-
async
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
try {
|
|
46
|
-
const response = await fetch(COMPONENTS_REGISTRY_URL);
|
|
47
|
-
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
55
|
+
async _ensureConfig() {
|
|
56
|
+
if (!this.config && !this._configPromise) {
|
|
57
|
+
this._configPromise = loadConfig();
|
|
50
58
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Parse the components.js file content
|
|
55
|
-
const match = content.match(/const components = ({[\s\S]*?});/);
|
|
56
|
-
if (!match) {
|
|
57
|
-
throw new Error('Invalid components.js format from repository');
|
|
59
|
+
if (this._configPromise) {
|
|
60
|
+
this.config = await this._configPromise;
|
|
58
61
|
}
|
|
62
|
+
}
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
async loadRegistry() {
|
|
65
|
+
await this._ensureConfig();
|
|
66
|
+
Print.info('Loading component registry from official repository...');
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(COMPONENTS_REGISTRY_URL);
|
|
70
|
+
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const content = await response.text();
|
|
76
|
+
|
|
77
|
+
// Parse the components.js file content
|
|
78
|
+
const match = content.match(/const components = ({[\s\S]*?});/);
|
|
79
|
+
if (!match) {
|
|
80
|
+
throw new Error('Invalid components.js format from repository');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const allComponents = JSON.parse(match[1]);
|
|
84
|
+
|
|
85
|
+
// Filter only Visual and Service components
|
|
86
|
+
this.componentsRegistry = this.filterOfficialComponents(allComponents);
|
|
87
|
+
|
|
88
|
+
Print.success('Component registry loaded successfully');
|
|
89
|
+
|
|
90
|
+
} catch (error) {
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
71
93
|
}
|
|
72
|
-
}
|
|
73
94
|
|
|
74
95
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
* @param {object} allComponents -
|
|
78
|
-
* @returns {object} -
|
|
96
|
+
* Filters the registry to include ONLY Visual and Service category components
|
|
97
|
+
* Excludes AppComponents and any other category
|
|
98
|
+
* @param {object} allComponents - Object with all registry components
|
|
99
|
+
* @returns {object} - Filtered object with only Visual and Service
|
|
79
100
|
*/
|
|
80
101
|
filterOfficialComponents(allComponents) {
|
|
81
102
|
const filtered = {};
|
|
82
103
|
let excludedCount = 0;
|
|
83
104
|
|
|
84
105
|
Object.entries(allComponents).forEach(([name, category]) => {
|
|
85
|
-
//
|
|
106
|
+
// Only include Visual or Service category components
|
|
86
107
|
if (category === 'Visual' || category === 'Service') {
|
|
87
108
|
filtered[name] = category;
|
|
88
109
|
} else {
|
|
@@ -112,14 +133,15 @@ filterOfficialComponents(allComponents) {
|
|
|
112
133
|
return {};
|
|
113
134
|
}
|
|
114
135
|
|
|
115
|
-
return
|
|
136
|
+
return JSON.parse(match[1]);
|
|
116
137
|
} catch (error) {
|
|
117
|
-
Print.warning(
|
|
138
|
+
Print.warning(`⚠️ Could not read the local component registry at: ${componentsPath}`);
|
|
118
139
|
return {};
|
|
119
140
|
}
|
|
120
141
|
}
|
|
121
142
|
|
|
122
143
|
async findUpdatableComponents() {
|
|
144
|
+
await this._ensureConfig();
|
|
123
145
|
const localComponents = await this.getLocalComponents();
|
|
124
146
|
const updatableComponents = [];
|
|
125
147
|
|
|
@@ -129,7 +151,7 @@ filterOfficialComponents(allComponents) {
|
|
|
129
151
|
// Check if local component directory exists using dynamic paths
|
|
130
152
|
const categoryPath = validations.getCategoryPath(category);
|
|
131
153
|
|
|
132
|
-
//
|
|
154
|
+
// Use 4 levels for node_modules compatibility
|
|
133
155
|
const isProduction = this.config?.production?.enabled === true;
|
|
134
156
|
const folderSuffix = isProduction ? 'dist' : 'src';
|
|
135
157
|
const componentPath = getPath(import.meta.url, folderSuffix, categoryPath, name);
|
|
@@ -153,18 +175,18 @@ filterOfficialComponents(allComponents) {
|
|
|
153
175
|
const components = {};
|
|
154
176
|
Object.entries(this.componentsRegistry).forEach(([name, componentCategory]) => {
|
|
155
177
|
if (!category || componentCategory === category) {
|
|
156
|
-
//
|
|
178
|
+
// Special components that don't need all files
|
|
157
179
|
let files;
|
|
158
180
|
if (componentCategory === 'Visual') {
|
|
159
|
-
//
|
|
160
|
-
if (['Route', 'MultiRoute', '
|
|
181
|
+
// Logical routing components only need JS
|
|
182
|
+
if (['Route', 'MultiRoute', 'Link'].includes(name)) {
|
|
161
183
|
files = [`${name}.js`];
|
|
162
184
|
} else {
|
|
163
|
-
//
|
|
185
|
+
// Normal visual components need JS, HTML, CSS
|
|
164
186
|
files = [`${name}.js`, `${name}.html`, `${name}.css`];
|
|
165
187
|
}
|
|
166
188
|
} else {
|
|
167
|
-
// Service components
|
|
189
|
+
// Service components only need JS
|
|
168
190
|
files = [`${name}.js`];
|
|
169
191
|
}
|
|
170
192
|
|
|
@@ -192,19 +214,6 @@ filterOfficialComponents(allComponents) {
|
|
|
192
214
|
const total = component.files.length;
|
|
193
215
|
let done = 0;
|
|
194
216
|
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
217
|
const worker = async (fileName) => {
|
|
209
218
|
const url = `${DOCS_REPO_BASE_URL}/${category}/${componentName}/${fileName}`;
|
|
210
219
|
const localPath = path.join(targetPath, fileName);
|
|
@@ -212,37 +221,25 @@ filterOfficialComponents(allComponents) {
|
|
|
212
221
|
const content = await fetchWithRetry(url);
|
|
213
222
|
await fs.writeFile(localPath, content, 'utf8');
|
|
214
223
|
downloadedFiles.push(fileName);
|
|
215
|
-
Print.downloadSuccess(fileName);
|
|
216
224
|
} catch (error) {
|
|
217
|
-
Print.downloadError(fileName
|
|
225
|
+
Print.downloadError(fileName);
|
|
218
226
|
failedFiles.push(fileName);
|
|
219
227
|
} finally {
|
|
220
228
|
done += 1;
|
|
221
229
|
spinner.text = `Downloading ${componentName} ${done}/${total}`;
|
|
222
230
|
}
|
|
223
231
|
};
|
|
224
|
-
|
|
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);
|
|
232
|
+
await runConcurrent(component.files, worker, 3);
|
|
236
233
|
spinner.stop();
|
|
237
234
|
|
|
238
|
-
//
|
|
235
|
+
// Only throw error if main file (.js) was not downloaded
|
|
239
236
|
const mainFileDownloaded = downloadedFiles.some(file => file.endsWith('.js'));
|
|
240
237
|
|
|
241
238
|
if (!mainFileDownloaded) {
|
|
242
239
|
throw new Error(`Failed to download main component file (${componentName}.js)`);
|
|
243
240
|
}
|
|
244
241
|
|
|
245
|
-
//
|
|
242
|
+
// Report files that failed (but don't stop the process)
|
|
246
243
|
if (failedFiles.length > 0) {
|
|
247
244
|
Print.warning(`Some files couldn't be downloaded: ${failedFiles.join(', ')}`);
|
|
248
245
|
Print.info('Component installed with available files');
|
|
@@ -252,7 +249,7 @@ filterOfficialComponents(allComponents) {
|
|
|
252
249
|
}
|
|
253
250
|
|
|
254
251
|
async updateLocalRegistrySafe(componentName, category) {
|
|
255
|
-
const componentsPath =
|
|
252
|
+
const componentsPath = getComponentsJsPath(import.meta.url);
|
|
256
253
|
try {
|
|
257
254
|
if (!await fs.pathExists(componentsPath)) {
|
|
258
255
|
const dir = path.dirname(componentsPath);
|
|
@@ -263,7 +260,7 @@ filterOfficialComponents(allComponents) {
|
|
|
263
260
|
const content = await fs.readFile(componentsPath, 'utf8');
|
|
264
261
|
const match = content.match(/const components = ({[\s\S]*?});/);
|
|
265
262
|
if (!match) throw new Error('Invalid components.js format in local project');
|
|
266
|
-
const componentsObj =
|
|
263
|
+
const componentsObj = JSON.parse(match[1]);
|
|
267
264
|
if (!componentsObj[componentName]) {
|
|
268
265
|
componentsObj[componentName] = category;
|
|
269
266
|
const sorted = Object.keys(componentsObj)
|
|
@@ -276,65 +273,19 @@ filterOfficialComponents(allComponents) {
|
|
|
276
273
|
Print.info(`${componentName} already exists in local registry`);
|
|
277
274
|
}
|
|
278
275
|
} catch (error) {
|
|
279
|
-
Print.error(`Updating local components.js: ${error.message}`);
|
|
280
|
-
throw error;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async updateLocalRegistry(componentName, category) {
|
|
285
|
-
// ✅ CORREGIDO: Usar 4 niveles para compatibilidad con node_modules
|
|
286
|
-
const componentsPath = path.join(__dirname, '../../../../src/Components/components.js');
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
let content = await fs.readFile(componentsPath, 'utf8');
|
|
290
|
-
|
|
291
|
-
// Parse existing components
|
|
292
|
-
const componentsMatch = content.match(/const components = ({[\s\S]*?});/);
|
|
293
|
-
if (!componentsMatch) {
|
|
294
|
-
throw new Error('Invalid components.js format in local project');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const componentsObj = eval('(' + componentsMatch[1] + ')');
|
|
298
|
-
|
|
299
|
-
// Add new component if it doesn't exist
|
|
300
|
-
if (!componentsObj[componentName]) {
|
|
301
|
-
componentsObj[componentName] = category;
|
|
302
|
-
|
|
303
|
-
// Generate new content
|
|
304
|
-
const sortedComponents = Object.keys(componentsObj)
|
|
305
|
-
.sort()
|
|
306
|
-
.reduce((obj, key) => {
|
|
307
|
-
obj[key] = componentsObj[key];
|
|
308
|
-
return obj;
|
|
309
|
-
}, {});
|
|
310
|
-
|
|
311
|
-
const newComponentsString = JSON.stringify(sortedComponents, null, 2)
|
|
312
|
-
.replace(/"/g, '"')
|
|
313
|
-
.replace(/: "/g, ': "')
|
|
314
|
-
.replace(/",\n/g, '",\n');
|
|
315
|
-
|
|
316
|
-
const newContent = `const components = ${newComponentsString}; export default components;`;
|
|
317
|
-
|
|
318
|
-
await fs.writeFile(componentsPath, newContent, 'utf8');
|
|
319
|
-
Print.registryUpdate(`Registered ${componentName} in local components.js`);
|
|
320
|
-
} else {
|
|
321
|
-
Print.info(`${componentName} already exists in local registry`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
} catch (error) {
|
|
325
|
-
Print.error(`Updating local components.js: ${error.message}`);
|
|
326
276
|
throw error;
|
|
327
277
|
}
|
|
328
278
|
}
|
|
329
279
|
|
|
330
280
|
async installComponent(componentName, category, force = false) {
|
|
281
|
+
await this._ensureConfig();
|
|
331
282
|
const availableComponents = this.getAvailableComponents(category);
|
|
332
283
|
|
|
333
284
|
if (!availableComponents[componentName]) {
|
|
334
285
|
throw new Error(`Component '${componentName}' not found in category '${category}' in the official repository`);
|
|
335
286
|
}
|
|
336
287
|
|
|
337
|
-
|
|
288
|
+
// Detect if validations has access to configuration
|
|
338
289
|
let categoryPath;
|
|
339
290
|
const hasValidConfig = validations.config &&
|
|
340
291
|
validations.config.paths &&
|
|
@@ -394,16 +345,13 @@ filterOfficialComponents(allComponents) {
|
|
|
394
345
|
return true;
|
|
395
346
|
|
|
396
347
|
} catch (error) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
// ✅ MEJORADO: Solo borrar si el archivo principal (.js) no existe
|
|
348
|
+
// Only clean up if main file (.js) does not exist
|
|
400
349
|
const mainFilePath = path.join(targetPath, `${componentName}.js`);
|
|
401
350
|
const mainFileExists = await fs.pathExists(mainFilePath);
|
|
402
351
|
|
|
403
352
|
if (!mainFileExists && await fs.pathExists(targetPath)) {
|
|
404
|
-
//
|
|
353
|
+
// Only clean up if main file was not installed
|
|
405
354
|
await fs.remove(targetPath);
|
|
406
|
-
Print.info('Cleaned up failed installation');
|
|
407
355
|
} else if (mainFileExists) {
|
|
408
356
|
Print.warning('Component partially installed - main file exists');
|
|
409
357
|
}
|
|
@@ -430,18 +378,7 @@ filterOfficialComponents(allComponents) {
|
|
|
430
378
|
spinner.text = `Installing ${done}/${total}`;
|
|
431
379
|
}
|
|
432
380
|
};
|
|
433
|
-
|
|
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);
|
|
381
|
+
await runConcurrent(componentNames, worker, 3);
|
|
445
382
|
spinner.stop();
|
|
446
383
|
|
|
447
384
|
// Summary
|
|
@@ -459,7 +396,7 @@ filterOfficialComponents(allComponents) {
|
|
|
459
396
|
|
|
460
397
|
const allUpdatableComponents = await this.findUpdatableComponents();
|
|
461
398
|
|
|
462
|
-
//
|
|
399
|
+
// Filter only Visual components
|
|
463
400
|
const updatableComponents = allUpdatableComponents.filter(comp => comp.category === 'Visual');
|
|
464
401
|
|
|
465
402
|
if (updatableComponents.length === 0) {
|
|
@@ -468,7 +405,7 @@ filterOfficialComponents(allComponents) {
|
|
|
468
405
|
return true;
|
|
469
406
|
}
|
|
470
407
|
|
|
471
|
-
//
|
|
408
|
+
// Show statistics if there are Service components that won't be synced
|
|
472
409
|
const serviceComponents = allUpdatableComponents.filter(comp => comp.category === 'Service');
|
|
473
410
|
if (serviceComponents.length > 0) {
|
|
474
411
|
Print.info(`Found ${serviceComponents.length} Service components (skipped - sync only affects Visual components)`);
|
|
@@ -497,7 +434,7 @@ filterOfficialComponents(allComponents) {
|
|
|
497
434
|
}
|
|
498
435
|
}
|
|
499
436
|
|
|
500
|
-
//
|
|
437
|
+
// Only update Visual components
|
|
501
438
|
const visualComponentNames = updatableComponents.map(c => c.name);
|
|
502
439
|
|
|
503
440
|
Print.info(`Updating ${visualComponentNames.length} Visual components...`);
|
|
@@ -517,7 +454,7 @@ filterOfficialComponents(allComponents) {
|
|
|
517
454
|
Print.success('All your Visual components are now updated to the latest official versions!');
|
|
518
455
|
}
|
|
519
456
|
|
|
520
|
-
//
|
|
457
|
+
// Additional information about Service components
|
|
521
458
|
if (serviceComponents.length > 0) {
|
|
522
459
|
Print.newLine();
|
|
523
460
|
Print.info(`Note: ${serviceComponents.length} Service components were found but not updated`);
|
|
@@ -539,7 +476,7 @@ filterOfficialComponents(allComponents) {
|
|
|
539
476
|
const visualComponents = this.getAvailableComponents('Visual');
|
|
540
477
|
const serviceComponents = this.getAvailableComponents('Service');
|
|
541
478
|
|
|
542
|
-
//
|
|
479
|
+
// Only show names without descriptions
|
|
543
480
|
Print.info('🎨 Visual Components (UI):');
|
|
544
481
|
Object.keys(visualComponents).forEach(name => {
|
|
545
482
|
const files = visualComponents[name].files;
|
|
@@ -695,7 +632,7 @@ async function getComponents(componentNames = [], options = {}) {
|
|
|
695
632
|
await registry.installComponent(componentInfo.name, actualCategory, options.force);
|
|
696
633
|
return true;
|
|
697
634
|
} catch (error) {
|
|
698
|
-
Print.error(
|
|
635
|
+
Print.error(`Error installing component: ${error.message}`);
|
|
699
636
|
return false;
|
|
700
637
|
}
|
|
701
638
|
} else {
|
|
@@ -708,7 +645,7 @@ async function getComponents(componentNames = [], options = {}) {
|
|
|
708
645
|
await registry.installMultipleComponents(normalizedComponents, category, options.force);
|
|
709
646
|
return true;
|
|
710
647
|
} catch (error) {
|
|
711
|
-
Print.error(
|
|
648
|
+
Print.error(`Error installing components: ${error.message}`);
|
|
712
649
|
return false;
|
|
713
650
|
}
|
|
714
651
|
}
|
|
@@ -744,4 +681,4 @@ async function syncComponents(options = {}) {
|
|
|
744
681
|
}
|
|
745
682
|
|
|
746
683
|
export default getComponents;
|
|
747
|
-
export { listComponents, syncComponents, ComponentRegistry };
|
|
684
|
+
export { listComponents, syncComponents, ComponentRegistry, loadConfig, runConcurrent, fetchWithRetry };
|
package/commands/init/init.js
CHANGED
|
@@ -3,12 +3,10 @@ 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';
|
|
6
|
+
import { getProjectRoot, getApiPath, getSrcPath, getPath } from '../utils/PathHelper.js';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// Importar la clase ComponentRegistry del getComponent
|
|
9
|
+
// Import ComponentRegistry class from getComponent
|
|
12
10
|
import { ComponentRegistry } from '../getComponent/getComponent.js';
|
|
13
11
|
|
|
14
12
|
// Visual components used by the App Shell + MultiRoute starter project.
|
|
@@ -35,7 +33,7 @@ export default async function initializeProject(projectType) {
|
|
|
35
33
|
try {
|
|
36
34
|
const latest = execSync('npm view slicejs-web-framework version', { cwd: projectRoot }).toString().trim();
|
|
37
35
|
latestVersion = latest;
|
|
38
|
-
const installedPkgPath =
|
|
36
|
+
const installedPkgPath = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework', 'package.json');
|
|
39
37
|
let installed = null;
|
|
40
38
|
if (await fs.pathExists(installedPkgPath)) {
|
|
41
39
|
const pkg = await fs.readJson(installedPkgPath);
|
|
@@ -44,9 +42,11 @@ export default async function initializeProject(projectType) {
|
|
|
44
42
|
if (installed !== latest) {
|
|
45
43
|
execSync(`npm install slicejs-web-framework@${latest} --save`, { cwd: projectRoot, stdio: 'inherit' });
|
|
46
44
|
}
|
|
47
|
-
sliceBaseDir =
|
|
45
|
+
sliceBaseDir = getPath(import.meta.url, 'node_modules', 'slicejs-web-framework');
|
|
48
46
|
fwSpinner.succeed(`slicejs-web-framework@${latest} ready`);
|
|
49
47
|
} catch (err) {
|
|
48
|
+
// Fallback uses __dirname-style path because it looks for a local development copy,
|
|
49
|
+
// not a project-relative path — npm install failed, so we fall back to monorepo sibling.
|
|
50
50
|
const fallback = path.join(path.dirname(fileURLToPath(import.meta.url)), '../../../slicejs-web-framework');
|
|
51
51
|
if (await fs.pathExists(fallback)) {
|
|
52
52
|
sliceBaseDir = fallback;
|
|
@@ -58,6 +58,8 @@ export default async function initializeProject(projectType) {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
// These derive from sliceBaseDir (which comes from npm install or fallback),
|
|
62
|
+
// so they're already dynamic — no PathHelper needed.
|
|
61
63
|
const apiDir = path.join(sliceBaseDir, 'api');
|
|
62
64
|
const srcDir = path.join(sliceBaseDir, 'src');
|
|
63
65
|
|
|
@@ -69,7 +71,7 @@ export default async function initializeProject(projectType) {
|
|
|
69
71
|
return;
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
// 1.
|
|
74
|
+
// 1. COPY API FOLDER (keep original logic)
|
|
73
75
|
const apiSpinner = ora('Copying API structure...').start();
|
|
74
76
|
try {
|
|
75
77
|
if (!fs.existsSync(apiDir)) throw new Error(`API folder not found: ${apiDir}`);
|
|
@@ -81,15 +83,15 @@ export default async function initializeProject(projectType) {
|
|
|
81
83
|
return;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
// 2.
|
|
86
|
+
// 2. CREATE BASIC SRC STRUCTURE (without copying Visual components)
|
|
85
87
|
const srcSpinner = ora('Creating src structure...').start();
|
|
86
88
|
try {
|
|
87
89
|
if (!fs.existsSync(srcDir)) throw new Error(`src folder not found: ${srcDir}`);
|
|
88
90
|
|
|
89
|
-
//
|
|
91
|
+
// Copy only base src files, excluding Components/Visual
|
|
90
92
|
await fs.ensureDir(destinationSrc);
|
|
91
93
|
|
|
92
|
-
//
|
|
94
|
+
// Copy src files and folders except Components/Visual
|
|
93
95
|
const srcItems = await fs.readdir(srcDir);
|
|
94
96
|
|
|
95
97
|
for (const item of srcItems) {
|
|
@@ -99,7 +101,7 @@ export default async function initializeProject(projectType) {
|
|
|
99
101
|
|
|
100
102
|
if (stat.isDirectory()) {
|
|
101
103
|
if (item === 'Components') {
|
|
102
|
-
//
|
|
104
|
+
// Create Components structure but without copying Visual
|
|
103
105
|
await fs.ensureDir(destItemPath);
|
|
104
106
|
|
|
105
107
|
const componentItems = await fs.readdir(srcItemPath);
|
|
@@ -132,7 +134,7 @@ export default async function initializeProject(projectType) {
|
|
|
132
134
|
return;
|
|
133
135
|
}
|
|
134
136
|
|
|
135
|
-
// 3.
|
|
137
|
+
// 3. DOWNLOAD ALL VISUAL COMPONENTS FROM OFFICIAL REPOSITORY
|
|
136
138
|
const componentsSpinner = ora('Loading component registry...').start();
|
|
137
139
|
try {
|
|
138
140
|
const registry = new ComponentRegistry();
|
|
@@ -148,7 +150,7 @@ export default async function initializeProject(projectType) {
|
|
|
148
150
|
const results = await registry.installMultipleComponents(
|
|
149
151
|
allVisualComponents,
|
|
150
152
|
'Visual',
|
|
151
|
-
true // force = true
|
|
153
|
+
true // force = true for initial installation
|
|
152
154
|
);
|
|
153
155
|
|
|
154
156
|
const successful = results.filter(r => r.success).length;
|
|
@@ -174,11 +176,11 @@ export default async function initializeProject(projectType) {
|
|
|
174
176
|
Print.info('You can add them later using "slice get <component-name>"');
|
|
175
177
|
}
|
|
176
178
|
|
|
177
|
-
// 4.
|
|
179
|
+
// 4. CONFIGURE SCRIPTS IN PROJECT package.json
|
|
178
180
|
const pkgSpinner = ora('Configuring npm scripts...').start();
|
|
179
181
|
try {
|
|
180
182
|
const projectRoot = getProjectRoot(import.meta.url);
|
|
181
|
-
const pkgPath =
|
|
183
|
+
const pkgPath = getPath(import.meta.url, 'package.json');
|
|
182
184
|
|
|
183
185
|
let pkg;
|
|
184
186
|
if (await fs.pathExists(pkgPath)) {
|
|
@@ -200,7 +202,7 @@ export default async function initializeProject(projectType) {
|
|
|
200
202
|
pkg.scripts['dev'] = 'slice dev';
|
|
201
203
|
pkg.scripts['start'] = 'slice start';
|
|
202
204
|
|
|
203
|
-
//
|
|
205
|
+
// Component management
|
|
204
206
|
pkg.scripts['component:create'] = 'slice component create';
|
|
205
207
|
pkg.scripts['component:list'] = 'slice component list';
|
|
206
208
|
pkg.scripts['component:delete'] = 'slice component delete';
|
|
@@ -215,7 +217,7 @@ export default async function initializeProject(projectType) {
|
|
|
215
217
|
pkg.scripts['slice:update'] = 'slice update';
|
|
216
218
|
pkg.scripts['slice:types'] = 'slice types generate';
|
|
217
219
|
|
|
218
|
-
// Legacy (
|
|
220
|
+
// Legacy (compatibility)
|
|
219
221
|
pkg.scripts['slice:init'] = 'slice init';
|
|
220
222
|
pkg.scripts['slice:start'] = 'slice start';
|
|
221
223
|
pkg.scripts['slice:dev'] = 'slice dev';
|
|
@@ -227,7 +229,7 @@ export default async function initializeProject(projectType) {
|
|
|
227
229
|
pkg.scripts['slice:sync'] = 'slice sync';
|
|
228
230
|
pkg.scripts['run'] = 'slice dev';
|
|
229
231
|
|
|
230
|
-
//
|
|
232
|
+
// Module configuration
|
|
231
233
|
pkg.type = 'module';
|
|
232
234
|
pkg.engines = pkg.engines || { node: '>=20.0.0' };
|
|
233
235
|
|
|
@@ -239,7 +241,7 @@ export default async function initializeProject(projectType) {
|
|
|
239
241
|
await fs.writeFile(pkgPath, JSON.stringify(pkg, null, 2), 'utf8');
|
|
240
242
|
pkgSpinner.succeed('npm scripts configured successfully');
|
|
241
243
|
|
|
242
|
-
|
|
244
|
+
Print.title('New recommended commands:');
|
|
243
245
|
console.log(' npm run dev - Start development server');
|
|
244
246
|
console.log(' npm run get - Install components');
|
|
245
247
|
console.log(' npm run browse - Browse components');
|
|
@@ -248,9 +250,11 @@ export default async function initializeProject(projectType) {
|
|
|
248
250
|
Print.error(error.message);
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
|
|
253
|
+
const projectName = path.basename(process.cwd());
|
|
254
|
+
Print.success(`Project initialized successfully in "${projectName}/"`);
|
|
252
255
|
Print.newLine();
|
|
253
|
-
Print.
|
|
256
|
+
Print.title('Next steps:');
|
|
257
|
+
console.log(` cd ${projectName}`);
|
|
254
258
|
console.log(' slice browse - View available components');
|
|
255
259
|
console.log(' slice get Button - Install specific components');
|
|
256
260
|
console.log(' slice sync - Update all components to latest versions');
|