slicejs-cli 2.8.6 → 2.9.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/client.js CHANGED
@@ -1,539 +1,526 @@
1
- #!/usr/bin/env node
2
- import { program } from "commander";
3
- import inquirer from "inquirer";
4
- import initializeProject from "./commands/init/init.js";
5
- import createComponent from "./commands/createComponent/createComponent.js";
6
- import listComponents from "./commands/listComponents/listComponents.js";
7
- import deleteComponent from "./commands/deleteComponent/deleteComponent.js";
8
- import getComponent, { listComponents as listRemoteComponents, syncComponents } from "./commands/getComponent/getComponent.js";
9
- import startServer from "./commands/startServer/startServer.js";
10
- import runDiagnostics from "./commands/doctor/doctor.js";
11
- import versionChecker from "./commands/utils/VersionChecker.js";
12
- import updateManager from "./commands/utils/updateManager.js";
13
- import fs from "fs";
14
- import path from "path";
15
- import { fileURLToPath } from "url";
16
- import { getConfigPath, getProjectRoot } from "./commands/utils/PathHelper.js";
17
- import { exec } from "child_process";
18
- import { promisify } from "util";
19
- import validations from "./commands/Validations.js";
20
- import Print from "./commands/Print.js";
21
- import bundle, { cleanBundles, bundleInfo } from './commands/bundle/bundle.js';
22
-
23
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
24
-
25
- const loadConfig = () => {
26
- try {
27
- const configPath = getConfigPath(import.meta.url);
28
- const rawData = fs.readFileSync(configPath, "utf-8");
29
- return JSON.parse(rawData);
30
- } catch {
31
- return null;
32
- }
33
- };
34
-
35
- const getCategories = () => {
36
- const config = loadConfig();
37
- return config && config.paths?.components ? Object.keys(config.paths.components) : [];
38
- };
39
-
40
- // Function to run version check for all commands
41
- async function runWithVersionCheck(commandFunction, ...args) {
42
- try {
43
- const execAsync = promisify(exec);
44
- await (async () => {
45
- try {
46
- const info = await updateManager.detectCliInstall();
47
- if (info && info.type === 'global') {
48
- const projectRoot = getProjectRoot(import.meta.url);
49
- const pkgPath = path.join(projectRoot, 'package.json');
50
- let hasPkg = fs.existsSync(pkgPath);
51
- if (!hasPkg) {
52
- const { confirmInit } = await inquirer.prompt([
53
- {
54
- type: 'confirm',
55
- name: 'confirmInit',
56
- message: 'No package.json found. Initialize npm in this project now?',
57
- default: true
58
- }
59
- ]);
60
- if (confirmInit) {
61
- await execAsync('npm init -y', { cwd: projectRoot });
62
- hasPkg = true;
63
- }
64
- }
65
- if (hasPkg) {
66
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
67
- const hasFramework = pkg.dependencies?.['slicejs-web-framework'];
68
- if (!hasFramework) {
69
- const { confirm } = await inquirer.prompt([
70
- {
71
- type: 'confirm',
72
- name: 'confirm',
73
- message: 'slicejs-web-framework is not installed in this project. Install it now?',
74
- default: true
75
- }
76
- ]);
77
- if (confirm) {
78
- await updateManager.updatePackage('slicejs-web-framework');
79
- }
80
- }
81
- }
82
- }
83
- } catch {}
84
- })();
85
-
86
- const updateInfo = await updateManager.checkForUpdates();
87
- if (updateInfo && updateInfo.hasUpdates) {
88
- await updateManager.checkAndPromptUpdates({});
89
- }
90
-
91
- const result = await commandFunction(...args);
92
-
93
- setTimeout(() => {
94
- versionChecker.checkForUpdates(false);
95
- }, 100);
96
-
97
- return result;
98
- } catch (error) {
99
- Print.error(`Command execution: ${error.message}`);
100
- return false;
101
- }
102
- }
103
-
104
- const sliceClient = program;
105
-
106
- try {
107
- const pkgPath = path.join(__dirname, "./package.json");
108
- const pkgRaw = fs.readFileSync(pkgPath, "utf-8");
109
- const pkg = JSON.parse(pkgRaw);
110
- sliceClient.version(pkg.version).description("CLI for managing Slice.js framework components");
111
- } catch {
112
- sliceClient.version("0.0.0").description("CLI for managing Slice.js framework components");
113
- }
114
-
115
- // INIT COMMAND
116
- sliceClient
117
- .command("init")
118
- .description("Initialize a new Slice.js project")
119
- .action(async () => {
120
- await runWithVersionCheck(() => {
121
- initializeProject();
122
- return Promise.resolve();
123
- });
124
- });
125
-
126
- // VERSION COMMAND
127
- sliceClient
128
- .command("version")
129
- .alias("v")
130
- .description("Show version information and check for updates")
131
- .action(async () => {
132
- await versionChecker.showVersionInfo();
133
- });
134
-
135
- // BUNDLE COMMAND
136
- const bundleCommand = sliceClient.command("bundle")
137
- .description("Build component bundles for production")
138
- .action(async (options) => {
139
- await runWithVersionCheck(async () => {
140
- await bundle(options);
141
- });
142
- });
143
-
144
- bundleCommand
145
- .command("clean")
146
- .description("Remove all generated bundles")
147
- .action(async () => {
148
- await cleanBundles();
149
- });
150
-
151
- bundleCommand
152
- .command("info")
153
- .description("Show information about generated bundles")
154
- .action(async () => {
155
- await bundleInfo();
156
- });
157
-
158
- bundleCommand
159
- .option("-a, --analyze", "Analyze project dependencies without bundling")
160
- .option("-v, --verbose", "Show detailed output");
161
-
162
- // DEV COMMAND (DEVELOPMENT) - COMANDO PRINCIPAL
163
- sliceClient
164
- .command("dev")
165
- .description("Start development server")
166
- .option("-p, --port <port>", "Port for development server", 3000)
167
- .option("-w, --watch", "Enable watch mode for file changes")
168
- .option("-b, --bundled", "Generate bundles before starting server")
169
- .action(async (options) => {
170
- await runWithVersionCheck(async () => {
171
- // Si se solicita bundles, generarlos primero
172
- if (options.bundled) {
173
- Print.info("Generating bundles before starting server...");
174
- await bundle({ verbose: false });
175
- Print.newLine();
176
- }
177
-
178
- await startServer({
179
- mode: options.bundled ? 'bundled' : 'development',
180
- port: parseInt(options.port),
181
- watch: options.watch,
182
- bundled: options.bundled
183
- });
184
- });
185
- });
186
-
187
- // START COMMAND - ALIAS PARA DEV
188
- sliceClient
189
- .command("start")
190
- .description("Start development server (alias for dev)")
191
- .option("-p, --port <port>", "Port for server", 3000)
192
- .option("-w, --watch", "Enable watch mode for file changes")
193
- .option("-b, --bundled", "Generate bundles before starting server")
194
- .action(async (options) => {
195
- await runWithVersionCheck(async () => {
196
- // Si se solicita bundles, generarlos primero
197
- if (options.bundled) {
198
- Print.info("Generating bundles before starting server...");
199
- await bundle({ verbose: false });
200
- Print.newLine();
201
- }
202
-
203
- await startServer({
204
- mode: options.bundled ? 'bundled' : 'development',
205
- port: parseInt(options.port),
206
- watch: options.watch,
207
- bundled: options.bundled
208
- });
209
- });
210
- });
211
-
212
- // COMPONENT COMMAND GROUP - For local component management
213
- const componentCommand = sliceClient.command("component").alias("comp").description("Manage local project components");
214
-
215
- // CREATE LOCAL COMPONENT
216
- componentCommand
217
- .command("create")
218
- .alias("new")
219
- .description("Create a new component in your local project")
220
- .action(async () => {
221
- await runWithVersionCheck(async () => {
222
- const categories = getCategories();
223
- if (categories.length === 0) {
224
- Print.error("No categories found in your project configuration");
225
- Print.info("Run 'slice init' to initialize your project first");
226
- Print.commandExample("Initialize project", "slice init");
227
- return;
228
- }
229
-
230
- const answers = await inquirer.prompt([
231
- {
232
- type: "input",
233
- name: "componentName",
234
- message: "Enter the component name:",
235
- validate: (input) => {
236
- if (!input) return "Component name cannot be empty";
237
- if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(input)) {
238
- return "Component name must start with a letter and contain only alphanumeric characters";
239
- }
240
- return true;
241
- },
242
- },
243
- {
244
- type: "list",
245
- name: "category",
246
- message: "Select the component category:",
247
- choices: categories,
248
- }
249
- ]);
250
-
251
- const result = createComponent(answers.componentName, answers.category);
252
- if (result) {
253
- Print.success(`Component '${answers.componentName}' created successfully in category '${answers.category}'`);
254
- Print.info("Listing updated components:");
255
- listComponents();
256
- }
257
- });
258
- });
259
-
260
- // LIST LOCAL COMPONENTS
261
- componentCommand
262
- .command("list")
263
- .alias("ls")
264
- .description("List all components in your local project")
265
- .action(async () => {
266
- await runWithVersionCheck(() => {
267
- listComponents();
268
- return Promise.resolve();
269
- });
270
- });
271
-
272
- // DELETE LOCAL COMPONENT
273
- componentCommand
274
- .command("delete")
275
- .alias("remove")
276
- .description("Delete a component from your local project")
277
- .action(async () => {
278
- await runWithVersionCheck(async () => {
279
- const categories = getCategories();
280
- if (categories.length === 0) {
281
- Print.error("No categories available. Check your configuration");
282
- Print.info("Run 'slice init' to initialize your project");
283
- return;
284
- }
285
-
286
- try {
287
- // Paso 1: Seleccionar categoría
288
- const categoryAnswer = await inquirer.prompt([
289
- {
290
- type: "list",
291
- name: "category",
292
- message: "Select the component category:",
293
- choices: categories,
294
- }
295
- ]);
296
-
297
- // Paso 2: Listar componentes de esa categoría
298
- const config = loadConfig();
299
- if (!config) {
300
- Print.error("Could not load configuration");
301
- return;
302
- }
303
-
304
- const categoryPath = config.paths.components[categoryAnswer.category].path;
305
- const fullPath = path.join(__dirname, "../../src", categoryPath);
306
-
307
- if (!fs.existsSync(fullPath)) {
308
- Print.error(`Category path does not exist: ${categoryPath}`);
309
- return;
310
- }
311
-
312
- const components = fs.readdirSync(fullPath).filter(item => {
313
- const itemPath = path.join(fullPath, item);
314
- return fs.statSync(itemPath).isDirectory();
315
- });
316
-
317
- if (components.length === 0) {
318
- Print.info(`No components found in category '${categoryAnswer.category}'`);
319
- return;
320
- }
321
-
322
- // Paso 3: Seleccionar componente a eliminar
323
- const componentAnswer = await inquirer.prompt([
324
- {
325
- type: "list",
326
- name: "componentName",
327
- message: "Select the component to delete:",
328
- choices: components,
329
- },
330
- {
331
- type: "confirm",
332
- name: "confirm",
333
- message: (answers) => `Are you sure you want to delete '${answers.componentName}'?`,
334
- default: false,
335
- }
336
- ]);
337
-
338
- if (!componentAnswer.confirm) {
339
- Print.info("Delete operation cancelled");
340
- return;
341
- }
342
-
343
- // Paso 4: Eliminar el componente
344
- if (deleteComponent(componentAnswer.componentName, categoryAnswer.category)) {
345
- Print.success(`Component ${componentAnswer.componentName} deleted successfully`);
346
- Print.info("Listing updated components:");
347
- listComponents();
348
- }
349
- } catch (error) {
350
- Print.error(`Deleting component: ${error.message}`);
351
- }
352
- });
353
- });
354
-
355
- // REGISTRY COMMAND GROUP - For component registry operations
356
- const registryCommand = sliceClient.command("registry").alias("reg").description("Manage components from official Slice.js repository");
357
-
358
- // GET COMPONENTS FROM REGISTRY
359
- registryCommand
360
- .command("get [components...]")
361
- .description("Download and install components from official repository")
362
- .option("-f, --force", "Force overwrite existing components")
363
- .option("-s, --service", "Install Service components instead of Visual")
364
- .action(async (components, options) => {
365
- await runWithVersionCheck(async () => {
366
- await getComponent(components, {
367
- force: options.force,
368
- service: options.service
369
- });
370
- });
371
- });
372
-
373
- // LIST REGISTRY COMPONENTS
374
- registryCommand
375
- .command("list")
376
- .alias("ls")
377
- .description("List all available components in the official repository")
378
- .action(async () => {
379
- await runWithVersionCheck(async () => {
380
- await listRemoteComponents();
381
- });
382
- });
383
-
384
- // SYNC COMPONENTS FROM REGISTRY
385
- registryCommand
386
- .command("sync")
387
- .description("Update all local components to latest versions from repository")
388
- .option("-f, --force", "Force update without confirmation")
389
- .action(async (options) => {
390
- await runWithVersionCheck(async () => {
391
- await syncComponents({
392
- force: options.force
393
- });
394
- });
395
- });
396
-
397
- // SHORTCUTS - Top-level convenient commands
398
- sliceClient
399
- .command("get [components...]")
400
- .description("Quick install components from registry")
401
- .option("-f, --force", "Force overwrite existing components")
402
- .option("-s, --service", "Install Service components instead of Visual")
403
- .action(async (components, options) => {
404
- await runWithVersionCheck(async () => {
405
- if (!components || components.length === 0) {
406
- Print.info("Use 'slice registry list' to see available components");
407
- Print.commandExample("Get multiple components", "slice get Button Card Input");
408
- Print.commandExample("Get service component", "slice get FetchManager --service");
409
- Print.commandExample("Browse components", "slice browse");
410
- return;
411
- }
412
-
413
- await getComponent(components, {
414
- force: options.force,
415
- service: options.service
416
- });
417
- });
418
- });
419
-
420
- sliceClient
421
- .command("browse")
422
- .description("Quick browse available components")
423
- .action(async () => {
424
- await runWithVersionCheck(async () => {
425
- await listRemoteComponents();
426
- });
427
- });
428
-
429
- sliceClient
430
- .command("sync")
431
- .description("Quick sync local components to latest versions")
432
- .option("-f, --force", "Force update without confirmation")
433
- .action(async (options) => {
434
- await runWithVersionCheck(async () => {
435
- await syncComponents({
436
- force: options.force
437
- });
438
- });
439
- });
440
-
441
- // LIST COMMAND - Quick shortcut for listing local components
442
- sliceClient
443
- .command("list")
444
- .description("Quick list all local components (alias for component list)")
445
- .action(async () => {
446
- await runWithVersionCheck(() => {
447
- listComponents();
448
- return Promise.resolve();
449
- });
450
- });
451
-
452
- // UPDATE COMMAND
453
- sliceClient
454
- .command("update")
455
- .alias("upgrade")
456
- .description("Update CLI and framework to latest versions")
457
- .option("-y, --yes", "Skip confirmation and update all packages automatically")
458
- .option("--cli", "Update only the Slice.js CLI")
459
- .option("-f, --framework", "Update only the Slice.js Framework")
460
- .action(async (options) => {
461
- await updateManager.checkAndPromptUpdates(options);
462
- });
463
-
464
- // DOCTOR COMMAND - Diagnose project issues
465
- sliceClient
466
- .command("doctor")
467
- .alias("diagnose")
468
- .description("Run diagnostics to check project health")
469
- .action(async () => {
470
- await runWithVersionCheck(async () => {
471
- await runDiagnostics();
472
- });
473
- });
474
-
475
- // Enhanced help
476
- sliceClient
477
- .option("--no-version-check", "Skip version check for this command")
478
- .configureHelp({
479
- sortSubcommands: true,
480
- subcommandTerm: (cmd) => cmd.name() + ' ' + cmd.usage()
481
- });
482
-
483
-
484
- // Custom help - SIMPLIFICADO para development only
485
- sliceClient.addHelpText('after', `
486
- Common Usage Examples:
487
- slice init - Initialize new Slice.js project
488
- slice dev - Start development server
489
- slice start - Start development server (same as dev)
490
- slice dev --bundled - Generate bundles then start server
491
- slice start --bundled - Same as above (bundle -> start)
492
- slice get Button Card Input - Install Visual components from registry
493
- slice get FetchManager -s - Install Service component from registry
494
- slice browse - Browse all available components
495
- slice sync - Update local components to latest versions
496
- slice component create - Create new local component
497
- slice list - List all local components
498
- slice doctor - Run project diagnostics
499
-
500
- Command Categories:
501
- • init, dev, start - Project lifecycle (development only)
502
- get, browse, sync, list - Quick shortcuts
503
- • component <cmd> - Local component management
504
- • registry <cmd> - Official repository operations
505
- version, update, doctor - Maintenance commands
506
-
507
- Development Workflow:
508
- slice init - Initialize project
509
- slice dev - Start development server (serves from /src)
510
- slice start - Alternative to dev command
511
- slice dev --bundled - Start with bundles (bundle -> start)
512
-
513
- Note: Production builds are disabled. Use development mode for all workflows.
514
-
515
- More info: https://slice-js-docs.vercel.app/
516
- `);
517
-
518
- // Default action with better messaging
519
- if (!process.argv.slice(2).length) {
520
- Print.newLine();
521
- Print.info("Start with: slice init");
522
- Print.commandExample("Development", "slice dev");
523
- Print.commandExample("View available components", "slice browse");
524
- Print.info("Use 'slice --help' for full help");
525
- }
526
-
527
- // Error handling for unknown commands
528
- program.on('command:*', () => {
529
- Print.error('Invalid command. See available commands above');
530
- Print.info("Use 'slice --help' for help");
531
- process.exit(1);
532
- });
533
-
534
- // HELP Command
535
- const helpCommand = sliceClient.command("help").description("Display help information for Slice.js CLI").action(() => {
536
- sliceClient.outputHelp();
537
- });
538
-
539
- program.parse();
1
+ #!/usr/bin/env node
2
+ import { program } from "commander";
3
+ import inquirer from "inquirer";
4
+ import initializeProject from "./commands/init/init.js";
5
+ import createComponent from "./commands/createComponent/createComponent.js";
6
+ import listComponents from "./commands/listComponents/listComponents.js";
7
+ import deleteComponent from "./commands/deleteComponent/deleteComponent.js";
8
+ import getComponent, { listComponents as listRemoteComponents, syncComponents } from "./commands/getComponent/getComponent.js";
9
+ import startServer from "./commands/startServer/startServer.js";
10
+ import runDiagnostics from "./commands/doctor/doctor.js";
11
+ import versionChecker from "./commands/utils/VersionChecker.js";
12
+ import updateManager from "./commands/utils/updateManager.js";
13
+ import fs from "fs";
14
+ import path from "path";
15
+ import { fileURLToPath } from "url";
16
+ import { getConfigPath, getProjectRoot } from "./commands/utils/PathHelper.js";
17
+ import { exec } from "child_process";
18
+ import { promisify } from "util";
19
+ import validations from "./commands/Validations.js";
20
+ import Print from "./commands/Print.js";
21
+ import build from './commands/build/build.js';
22
+ import { cleanBundles, bundleInfo } from './commands/bundle/bundle.js';
23
+
24
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
25
+
26
+ const loadConfig = () => {
27
+ try {
28
+ const configPath = getConfigPath(import.meta.url);
29
+ const rawData = fs.readFileSync(configPath, "utf-8");
30
+ return JSON.parse(rawData);
31
+ } catch {
32
+ return null;
33
+ }
34
+ };
35
+
36
+ const getCategories = () => {
37
+ const config = loadConfig();
38
+ return config && config.paths?.components ? Object.keys(config.paths.components) : [];
39
+ };
40
+
41
+ // Function to run version check for all commands
42
+ async function runWithVersionCheck(commandFunction, ...args) {
43
+ try {
44
+ const execAsync = promisify(exec);
45
+ await (async () => {
46
+ try {
47
+ const info = await updateManager.detectCliInstall();
48
+ if (info && info.type === 'global') {
49
+ const projectRoot = getProjectRoot(import.meta.url);
50
+ const pkgPath = path.join(projectRoot, 'package.json');
51
+ let hasPkg = fs.existsSync(pkgPath);
52
+ if (!hasPkg) {
53
+ const { confirmInit } = await inquirer.prompt([
54
+ {
55
+ type: 'confirm',
56
+ name: 'confirmInit',
57
+ message: 'No package.json found. Initialize npm in this project now?',
58
+ default: true
59
+ }
60
+ ]);
61
+ if (confirmInit) {
62
+ await execAsync('npm init -y', { cwd: projectRoot });
63
+ hasPkg = true;
64
+ }
65
+ }
66
+ if (hasPkg) {
67
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
68
+ const hasFramework = pkg.dependencies?.['slicejs-web-framework'];
69
+ if (!hasFramework) {
70
+ const { confirm } = await inquirer.prompt([
71
+ {
72
+ type: 'confirm',
73
+ name: 'confirm',
74
+ message: 'slicejs-web-framework is not installed in this project. Install it now?',
75
+ default: true
76
+ }
77
+ ]);
78
+ if (confirm) {
79
+ await updateManager.updatePackage('slicejs-web-framework');
80
+ }
81
+ }
82
+ }
83
+ }
84
+ } catch {}
85
+ })();
86
+
87
+ const updateInfo = await updateManager.checkForUpdates();
88
+ if (updateInfo && updateInfo.hasUpdates) {
89
+ await updateManager.checkAndPromptUpdates({});
90
+ }
91
+
92
+ const result = await commandFunction(...args);
93
+
94
+ setTimeout(() => {
95
+ versionChecker.checkForUpdates(false);
96
+ }, 100);
97
+
98
+ return result;
99
+ } catch (error) {
100
+ Print.error(`Command execution: ${error.message}`);
101
+ return false;
102
+ }
103
+ }
104
+
105
+ const sliceClient = program;
106
+
107
+ try {
108
+ const pkgPath = path.join(__dirname, "./package.json");
109
+ const pkgRaw = fs.readFileSync(pkgPath, "utf-8");
110
+ const pkg = JSON.parse(pkgRaw);
111
+ sliceClient.version(pkg.version).description("CLI for managing Slice.js framework components");
112
+ } catch {
113
+ sliceClient.version("0.0.0").description("CLI for managing Slice.js framework components");
114
+ }
115
+
116
+ // INIT COMMAND
117
+ sliceClient
118
+ .command("init")
119
+ .description("Initialize a new Slice.js project")
120
+ .action(async () => {
121
+ await runWithVersionCheck(() => {
122
+ initializeProject();
123
+ return Promise.resolve();
124
+ });
125
+ });
126
+
127
+ // VERSION COMMAND
128
+ sliceClient
129
+ .command("version")
130
+ .alias("v")
131
+ .description("Show version information and check for updates")
132
+ .action(async () => {
133
+ await versionChecker.showVersionInfo();
134
+ });
135
+
136
+ // BUILD COMMAND
137
+ const buildCommand = sliceClient.command("build")
138
+ .description("Build Slice.js project for production")
139
+ .action(async (options) => {
140
+ await runWithVersionCheck(async () => {
141
+ await build(options);
142
+ });
143
+ });
144
+
145
+ buildCommand
146
+ .command("clean")
147
+ .description("Remove all generated bundles")
148
+ .action(async () => {
149
+ await cleanBundles();
150
+ });
151
+
152
+ buildCommand
153
+ .command("info")
154
+ .description("Show information about generated bundles")
155
+ .action(async () => {
156
+ await bundleInfo();
157
+ });
158
+
159
+ buildCommand
160
+ .option("-a, --analyze", "Analyze project dependencies without bundling")
161
+ .option("-v, --verbose", "Show detailed output")
162
+ .option("--no-minify", "Disable minification (enabled by default)")
163
+ .option("--no-obfuscate", "Disable obfuscation (enabled by default, no prop mangling)")
164
+ .option("--preview", "Start preview server after build")
165
+ .option("--serve", "Start preview server without building")
166
+ .option("--skip-clean", "Skip cleaning dist before build");
167
+
168
+ // DEV COMMAND (DEVELOPMENT)
169
+ sliceClient
170
+ .command("dev")
171
+ .description("Start development server")
172
+ .option("-p, --port <port>", "Port for development server", 3000)
173
+ .option("-w, --watch", "Enable watch mode for file changes")
174
+ .action(async (options) => {
175
+ await runWithVersionCheck(async () => {
176
+ await startServer({
177
+ mode: 'development',
178
+ port: parseInt(options.port),
179
+ watch: options.watch,
180
+ bundled: false
181
+ });
182
+ });
183
+ });
184
+
185
+ // START COMMAND - PRODUCTION MODE
186
+ sliceClient
187
+ .command("start")
188
+ .description("Start production server")
189
+ .option("-p, --port <port>", "Port for server", 3000)
190
+ .option("-w, --watch", "Enable watch mode for file changes")
191
+ .action(async (options) => {
192
+ await runWithVersionCheck(async () => {
193
+ await startServer({
194
+ mode: 'production',
195
+ port: parseInt(options.port),
196
+ watch: options.watch,
197
+ bundled: false
198
+ });
199
+ });
200
+ });
201
+
202
+ // COMPONENT COMMAND GROUP - For local component management
203
+ const componentCommand = sliceClient.command("component").alias("comp").description("Manage local project components");
204
+
205
+ // CREATE LOCAL COMPONENT
206
+ componentCommand
207
+ .command("create")
208
+ .alias("new")
209
+ .description("Create a new component in your local project")
210
+ .action(async () => {
211
+ await runWithVersionCheck(async () => {
212
+ const categories = getCategories();
213
+ if (categories.length === 0) {
214
+ Print.error("No categories found in your project configuration");
215
+ Print.info("Run 'slice init' to initialize your project first");
216
+ Print.commandExample("Initialize project", "slice init");
217
+ return;
218
+ }
219
+
220
+ const answers = await inquirer.prompt([
221
+ {
222
+ type: "input",
223
+ name: "componentName",
224
+ message: "Enter the component name:",
225
+ validate: (input) => {
226
+ if (!input) return "Component name cannot be empty";
227
+ if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(input)) {
228
+ return "Component name must start with a letter and contain only alphanumeric characters";
229
+ }
230
+ return true;
231
+ },
232
+ },
233
+ {
234
+ type: "list",
235
+ name: "category",
236
+ message: "Select the component category:",
237
+ choices: categories,
238
+ }
239
+ ]);
240
+
241
+ const result = createComponent(answers.componentName, answers.category);
242
+ if (result) {
243
+ Print.success(`Component '${answers.componentName}' created successfully in category '${answers.category}'`);
244
+ Print.info("Listing updated components:");
245
+ listComponents();
246
+ }
247
+ });
248
+ });
249
+
250
+ // LIST LOCAL COMPONENTS
251
+ componentCommand
252
+ .command("list")
253
+ .alias("ls")
254
+ .description("List all components in your local project")
255
+ .action(async () => {
256
+ await runWithVersionCheck(() => {
257
+ listComponents();
258
+ return Promise.resolve();
259
+ });
260
+ });
261
+
262
+ // DELETE LOCAL COMPONENT
263
+ componentCommand
264
+ .command("delete")
265
+ .alias("remove")
266
+ .description("Delete a component from your local project")
267
+ .action(async () => {
268
+ await runWithVersionCheck(async () => {
269
+ const categories = getCategories();
270
+ if (categories.length === 0) {
271
+ Print.error("No categories available. Check your configuration");
272
+ Print.info("Run 'slice init' to initialize your project");
273
+ return;
274
+ }
275
+
276
+ try {
277
+ // Paso 1: Seleccionar categoría
278
+ const categoryAnswer = await inquirer.prompt([
279
+ {
280
+ type: "list",
281
+ name: "category",
282
+ message: "Select the component category:",
283
+ choices: categories,
284
+ }
285
+ ]);
286
+
287
+ // Paso 2: Listar componentes de esa categoría
288
+ const config = loadConfig();
289
+ if (!config) {
290
+ Print.error("Could not load configuration");
291
+ return;
292
+ }
293
+
294
+ const categoryPath = config.paths.components[categoryAnswer.category].path;
295
+ const fullPath = path.join(__dirname, "../../src", categoryPath);
296
+
297
+ if (!fs.existsSync(fullPath)) {
298
+ Print.error(`Category path does not exist: ${categoryPath}`);
299
+ return;
300
+ }
301
+
302
+ const components = fs.readdirSync(fullPath).filter(item => {
303
+ const itemPath = path.join(fullPath, item);
304
+ return fs.statSync(itemPath).isDirectory();
305
+ });
306
+
307
+ if (components.length === 0) {
308
+ Print.info(`No components found in category '${categoryAnswer.category}'`);
309
+ return;
310
+ }
311
+
312
+ // Paso 3: Seleccionar componente a eliminar
313
+ const componentAnswer = await inquirer.prompt([
314
+ {
315
+ type: "list",
316
+ name: "componentName",
317
+ message: "Select the component to delete:",
318
+ choices: components,
319
+ },
320
+ {
321
+ type: "confirm",
322
+ name: "confirm",
323
+ message: (answers) => `Are you sure you want to delete '${answers.componentName}'?`,
324
+ default: false,
325
+ }
326
+ ]);
327
+
328
+ if (!componentAnswer.confirm) {
329
+ Print.info("Delete operation cancelled");
330
+ return;
331
+ }
332
+
333
+ // Paso 4: Eliminar el componente
334
+ if (deleteComponent(componentAnswer.componentName, categoryAnswer.category)) {
335
+ Print.success(`Component ${componentAnswer.componentName} deleted successfully`);
336
+ Print.info("Listing updated components:");
337
+ listComponents();
338
+ }
339
+ } catch (error) {
340
+ Print.error(`Deleting component: ${error.message}`);
341
+ }
342
+ });
343
+ });
344
+
345
+ // REGISTRY COMMAND GROUP - For component registry operations
346
+ const registryCommand = sliceClient.command("registry").alias("reg").description("Manage components from official Slice.js repository");
347
+
348
+ // GET COMPONENTS FROM REGISTRY
349
+ registryCommand
350
+ .command("get [components...]")
351
+ .description("Download and install components from official repository")
352
+ .option("-f, --force", "Force overwrite existing components")
353
+ .option("-s, --service", "Install Service components instead of Visual")
354
+ .action(async (components, options) => {
355
+ await runWithVersionCheck(async () => {
356
+ await getComponent(components, {
357
+ force: options.force,
358
+ service: options.service
359
+ });
360
+ });
361
+ });
362
+
363
+ // LIST REGISTRY COMPONENTS
364
+ registryCommand
365
+ .command("list")
366
+ .alias("ls")
367
+ .description("List all available components in the official repository")
368
+ .action(async () => {
369
+ await runWithVersionCheck(async () => {
370
+ await listRemoteComponents();
371
+ });
372
+ });
373
+
374
+ // SYNC COMPONENTS FROM REGISTRY
375
+ registryCommand
376
+ .command("sync")
377
+ .description("Update all local components to latest versions from repository")
378
+ .option("-f, --force", "Force update without confirmation")
379
+ .action(async (options) => {
380
+ await runWithVersionCheck(async () => {
381
+ await syncComponents({
382
+ force: options.force
383
+ });
384
+ });
385
+ });
386
+
387
+ // SHORTCUTS - Top-level convenient commands
388
+ sliceClient
389
+ .command("get [components...]")
390
+ .description("Quick install components from registry")
391
+ .option("-f, --force", "Force overwrite existing components")
392
+ .option("-s, --service", "Install Service components instead of Visual")
393
+ .action(async (components, options) => {
394
+ await runWithVersionCheck(async () => {
395
+ if (!components || components.length === 0) {
396
+ Print.info("Use 'slice registry list' to see available components");
397
+ Print.commandExample("Get multiple components", "slice get Button Card Input");
398
+ Print.commandExample("Get service component", "slice get FetchManager --service");
399
+ Print.commandExample("Browse components", "slice browse");
400
+ return;
401
+ }
402
+
403
+ await getComponent(components, {
404
+ force: options.force,
405
+ service: options.service
406
+ });
407
+ });
408
+ });
409
+
410
+ sliceClient
411
+ .command("browse")
412
+ .description("Quick browse available components")
413
+ .action(async () => {
414
+ await runWithVersionCheck(async () => {
415
+ await listRemoteComponents();
416
+ });
417
+ });
418
+
419
+ sliceClient
420
+ .command("sync")
421
+ .description("Quick sync local components to latest versions")
422
+ .option("-f, --force", "Force update without confirmation")
423
+ .action(async (options) => {
424
+ await runWithVersionCheck(async () => {
425
+ await syncComponents({
426
+ force: options.force
427
+ });
428
+ });
429
+ });
430
+
431
+ // LIST COMMAND - Quick shortcut for listing local components
432
+ sliceClient
433
+ .command("list")
434
+ .description("Quick list all local components (alias for component list)")
435
+ .action(async () => {
436
+ await runWithVersionCheck(() => {
437
+ listComponents();
438
+ return Promise.resolve();
439
+ });
440
+ });
441
+
442
+ // UPDATE COMMAND
443
+ sliceClient
444
+ .command("update")
445
+ .alias("upgrade")
446
+ .description("Update CLI and framework to latest versions")
447
+ .option("-y, --yes", "Skip confirmation and update all packages automatically")
448
+ .option("--cli", "Update only the Slice.js CLI")
449
+ .option("-f, --framework", "Update only the Slice.js Framework")
450
+ .action(async (options) => {
451
+ await updateManager.checkAndPromptUpdates(options);
452
+ });
453
+
454
+ // DOCTOR COMMAND - Diagnose project issues
455
+ sliceClient
456
+ .command("doctor")
457
+ .alias("diagnose")
458
+ .description("Run diagnostics to check project health")
459
+ .action(async () => {
460
+ await runWithVersionCheck(async () => {
461
+ await runDiagnostics();
462
+ });
463
+ });
464
+
465
+ // Enhanced help
466
+ sliceClient
467
+ .option("--no-version-check", "Skip version check for this command")
468
+ .configureHelp({
469
+ sortSubcommands: true,
470
+ subcommandTerm: (cmd) => cmd.name() + ' ' + cmd.usage()
471
+ });
472
+
473
+
474
+ // Custom help - SIMPLIFICADO para development only
475
+ sliceClient.addHelpText('after', `
476
+ Common Usage Examples:
477
+ slice init - Initialize new Slice.js project
478
+ slice dev - Start development server
479
+ slice build - Build production output (bundles + dist)
480
+ slice start - Start production server
481
+ slice get Button Card Input - Install Visual components from registry
482
+ slice get FetchManager -s - Install Service component from registry
483
+ slice browse - Browse all available components
484
+ slice sync - Update local components to latest versions
485
+ slice component create - Create new local component
486
+ slice list - List all local components
487
+ slice doctor - Run project diagnostics
488
+
489
+ Command Categories:
490
+ init, dev, start - Project lifecycle (development only)
491
+ get, browse, sync, list - Quick shortcuts
492
+ component <cmd> - Local component management
493
+ registry <cmd> - Official repository operations
494
+ version, update, doctor - Maintenance commands
495
+
496
+ Development Workflow:
497
+ slice init - Initialize project
498
+ slice dev - Start development server (serves from /src)
499
+ • slice build - Build production output to /dist (includes bundles)
500
+ slice start - Serve production build from /dist
501
+
502
+ More info: https://slice-js-docs.vercel.app/
503
+ `);
504
+
505
+ // Default action with better messaging
506
+ if (!process.argv.slice(2).length) {
507
+ Print.newLine();
508
+ Print.info("Start with: slice init");
509
+ Print.commandExample("Development", "slice dev");
510
+ Print.commandExample("View available components", "slice browse");
511
+ Print.info("Use 'slice --help' for full help");
512
+ }
513
+
514
+ // Error handling for unknown commands
515
+ program.on('command:*', () => {
516
+ Print.error('Invalid command. See available commands above');
517
+ Print.info("Use 'slice --help' for help");
518
+ process.exit(1);
519
+ });
520
+
521
+ // HELP Command
522
+ const helpCommand = sliceClient.command("help").description("Display help information for Slice.js CLI").action(() => {
523
+ sliceClient.outputHelp();
524
+ });
525
+
526
+ program.parse();