slicejs-cli 2.3.2 → 2.5.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.
@@ -1,9 +1,11 @@
1
- // commands/startServer/startServer.js - CON ARGUMENTOS
1
+ // commands/startServer/startServer.js - MEJORADO CON VALIDACIÓN Y FEEDBACK
2
2
 
3
3
  import fs from 'fs-extra';
4
4
  import path from 'path';
5
5
  import { fileURLToPath } from 'url';
6
6
  import { spawn } from 'child_process';
7
+ import { createServer } from 'net';
8
+ import setupWatcher, { stopWatcher } from './watchServer.js';
7
9
  import Print from '../Print.js';
8
10
 
9
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
@@ -22,6 +24,30 @@ const loadConfig = () => {
22
24
  }
23
25
  };
24
26
 
27
+ /**
28
+ * Verifica si un puerto está disponible
29
+ */
30
+ async function isPortAvailable(port) {
31
+ return new Promise((resolve) => {
32
+ const server = createServer();
33
+
34
+ server.once('error', (err) => {
35
+ if (err.code === 'EADDRINUSE') {
36
+ resolve(false);
37
+ } else {
38
+ resolve(false);
39
+ }
40
+ });
41
+
42
+ server.once('listening', () => {
43
+ server.close();
44
+ resolve(true);
45
+ });
46
+
47
+ server.listen(port);
48
+ });
49
+ }
50
+
25
51
  /**
26
52
  * Verifica si existe un build de producción
27
53
  */
@@ -36,19 +62,25 @@ async function checkProductionBuild() {
36
62
  async function checkDevelopmentStructure() {
37
63
  const srcDir = path.join(__dirname, '../../../../src');
38
64
  const apiDir = path.join(__dirname, '../../../../api');
39
-
65
+
40
66
  return (await fs.pathExists(srcDir)) && (await fs.pathExists(apiDir));
41
67
  }
42
68
 
43
69
  /**
44
- * Inicia el servidor Node.js con argumentos
70
+ * Inicia el servidor Node.js con argumentos y mejor feedback
45
71
  */
46
72
  function startNodeServer(port, mode) {
47
73
  return new Promise((resolve, reject) => {
48
74
  const apiIndexPath = path.join(__dirname, '../../../../api/index.js');
49
-
50
- Print.info(`Starting ${mode} server on port ${port}...`);
51
-
75
+
76
+ // Verificar que el archivo existe
77
+ if (!fs.existsSync(apiIndexPath)) {
78
+ reject(new Error(`Server file not found: ${apiIndexPath}`));
79
+ return;
80
+ }
81
+
82
+ Print.serverStatus('starting', 'Starting server...');
83
+
52
84
  // Construir argumentos basados en el modo
53
85
  const args = [apiIndexPath];
54
86
  if (mode === 'production') {
@@ -56,35 +88,78 @@ function startNodeServer(port, mode) {
56
88
  } else {
57
89
  args.push('--development');
58
90
  }
59
-
91
+
60
92
  const serverProcess = spawn('node', args, {
61
- stdio: 'inherit',
93
+ stdio: ['inherit', 'pipe', 'pipe'],
62
94
  env: {
63
95
  ...process.env,
64
96
  PORT: port
65
- // Ya no necesitamos NODE_ENV ni SLICE_CLI_MODE
66
97
  }
67
98
  });
68
99
 
100
+ let serverStarted = false;
101
+ let outputBuffer = '';
102
+
103
+ // Capturar la salida para detectar cuando el servidor está listo
104
+ serverProcess.stdout.on('data', (data) => {
105
+ const output = data.toString();
106
+ outputBuffer += output;
107
+
108
+ // Detectar mensajes comunes que indican que el servidor ha iniciado
109
+ if (!serverStarted && (
110
+ output.includes('Server running') ||
111
+ output.includes('listening on') ||
112
+ output.includes('Started on') ||
113
+ output.includes(`port ${port}`)
114
+ )) {
115
+ serverStarted = true;
116
+ Print.serverReady(port);
117
+ }
118
+
119
+ // Mostrar la salida del servidor
120
+ process.stdout.write(output);
121
+ });
122
+
123
+ serverProcess.stderr.on('data', (data) => {
124
+ const output = data.toString();
125
+ process.stderr.write(output);
126
+ });
127
+
69
128
  serverProcess.on('error', (error) => {
70
- Print.error(`Failed to start server: ${error.message}`);
71
- reject(error);
129
+ if (!serverStarted) {
130
+ Print.serverStatus('error', `Failed to start server: ${error.message}`);
131
+ reject(error);
132
+ }
133
+ });
134
+
135
+ serverProcess.on('exit', (code, signal) => {
136
+ if (code !== null && code !== 0 && !serverStarted) {
137
+ reject(new Error(`Server exited with code ${code}`));
138
+ }
72
139
  });
73
140
 
74
141
  // Manejar Ctrl+C
75
142
  process.on('SIGINT', () => {
143
+ Print.newLine();
76
144
  Print.info('Shutting down server...');
77
145
  serverProcess.kill('SIGINT');
78
- process.exit(0);
146
+ setTimeout(() => {
147
+ process.exit(0);
148
+ }, 100);
79
149
  });
80
150
 
81
151
  process.on('SIGTERM', () => {
82
152
  serverProcess.kill('SIGTERM');
83
153
  });
84
154
 
155
+ // Si después de 3 segundos no detectamos inicio, asumimos que está listo
85
156
  setTimeout(() => {
157
+ if (!serverStarted) {
158
+ serverStarted = true;
159
+ Print.serverReady(port);
160
+ }
86
161
  resolve(serverProcess);
87
- }, 500);
162
+ }, 3000);
88
163
  });
89
164
  }
90
165
 
@@ -94,18 +169,33 @@ function startNodeServer(port, mode) {
94
169
  export default async function startServer(options = {}) {
95
170
  const config = loadConfig();
96
171
  const defaultPort = config?.server?.port || 3000;
97
-
98
- const { mode = 'development', port = defaultPort } = options;
99
-
172
+
173
+ const { mode = 'development', port = defaultPort, watch = false } = options;
174
+
100
175
  try {
101
176
  Print.title(`🚀 Starting Slice.js ${mode} server...`);
102
177
  Print.newLine();
103
-
178
+
104
179
  // Verificar estructura del proyecto
105
180
  if (!await checkDevelopmentStructure()) {
106
181
  throw new Error('Project structure not found. Run "slice init" first.');
107
182
  }
108
-
183
+
184
+ // Verificar disponibilidad del puerto
185
+ Print.checkingPort(port);
186
+ const portAvailable = await isPortAvailable(port);
187
+
188
+ if (!portAvailable) {
189
+ throw new Error(
190
+ `Port ${port} is already in use. Please:\n` +
191
+ ` 1. Stop the process using port ${port}, or\n` +
192
+ ` 2. Use a different port: slice ${mode === 'development' ? 'dev' : 'start'} -p <port>`
193
+ );
194
+ }
195
+
196
+ Print.serverStatus('checking', 'Port available ✓');
197
+ Print.newLine();
198
+
109
199
  if (mode === 'production') {
110
200
  // Verificar que existe build de producción
111
201
  if (!await checkProductionBuild()) {
@@ -115,12 +205,29 @@ export default async function startServer(options = {}) {
115
205
  } else {
116
206
  Print.info('Development mode: serving files from /src with hot reload');
117
207
  }
118
-
208
+
209
+ Print.newLine();
210
+
119
211
  // Iniciar el servidor con argumentos
120
- await startNodeServer(port, mode);
121
-
212
+ const serverProcess = await startNodeServer(port, mode);
213
+
214
+ // Configurar watch mode si está habilitado
215
+ if (watch) {
216
+ Print.newLine();
217
+ const watcher = setupWatcher(serverProcess);
218
+
219
+ // Cleanup en exit
220
+ const cleanup = () => {
221
+ stopWatcher(watcher);
222
+ };
223
+
224
+ process.on('SIGINT', cleanup);
225
+ process.on('SIGTERM', cleanup);
226
+ }
227
+
122
228
  } catch (error) {
123
- Print.error(`Failed to start server: ${error.message}`);
229
+ Print.newLine();
230
+ Print.error(error.message);
124
231
  throw error;
125
232
  }
126
233
  }
@@ -128,4 +235,4 @@ export default async function startServer(options = {}) {
128
235
  /**
129
236
  * Funciones de utilidad exportadas
130
237
  */
131
- export { checkProductionBuild, checkDevelopmentStructure };
238
+ export { checkProductionBuild, checkDevelopmentStructure, isPortAvailable };
@@ -0,0 +1,67 @@
1
+ import chokidar from 'chokidar';
2
+ import chalk from 'chalk';
3
+ import Print from '../Print.js';
4
+
5
+ /**
6
+ * Configura el watcher para archivos del proyecto
7
+ * @param {ChildProcess} serverProcess - Proceso del servidor
8
+ * @returns {FSWatcher} - Watcher de chokidar
9
+ */
10
+ export default function setupWatcher(serverProcess) {
11
+ Print.info('Watch mode enabled - monitoring file changes...');
12
+ Print.newLine();
13
+
14
+ const watcher = chokidar.watch(['src/**/*', 'api/**/*'], {
15
+ ignored: [
16
+ /(^|[\/\\])\../, // archivos ocultos
17
+ '**/node_modules/**',
18
+ '**/dist/**',
19
+ '**/*.log'
20
+ ],
21
+ persistent: true,
22
+ ignoreInitial: true,
23
+ awaitWriteFinish: {
24
+ stabilityThreshold: 100,
25
+ pollInterval: 50
26
+ }
27
+ });
28
+
29
+ let reloadTimeout;
30
+
31
+ watcher
32
+ .on('change', (path) => {
33
+ console.log(chalk.cyan(`📝 File changed: ${path}`));
34
+
35
+ // Debounce para evitar múltiples reloads
36
+ clearTimeout(reloadTimeout);
37
+ reloadTimeout = setTimeout(() => {
38
+ console.log(chalk.yellow('🔄 Changes detected, server will reload automatically...'));
39
+ }, 200);
40
+ })
41
+ .on('add', (path) => {
42
+ console.log(chalk.green(`➕ New file added: ${path}`));
43
+ })
44
+ .on('unlink', (path) => {
45
+ console.log(chalk.red(`➖ File removed: ${path}`));
46
+ })
47
+ .on('error', (error) => {
48
+ Print.error(`Watcher error: ${error.message}`);
49
+ })
50
+ .on('ready', () => {
51
+ console.log(chalk.gray('👀 Watching for file changes...'));
52
+ Print.newLine();
53
+ });
54
+
55
+ return watcher;
56
+ }
57
+
58
+ /**
59
+ * Detiene el watcher de forma segura
60
+ * @param {FSWatcher} watcher - Watcher a detener
61
+ */
62
+ export function stopWatcher(watcher) {
63
+ if (watcher) {
64
+ watcher.close();
65
+ console.log(chalk.gray('Watch mode stopped'));
66
+ }
67
+ }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "slicejs-cli",
3
- "version": "2.3.2",
3
+ "version": "2.5.0",
4
4
  "description": "Command client for developing web applications with Slice.js framework",
5
5
  "main": "client.js",
6
+ "bin": {
7
+ "slice": "./client.js"
8
+ },
6
9
  "scripts": {
7
10
  "test": "echo \"Error: no test specified\" && exit 1",
8
11
  "postinstall": "node post.js"
@@ -11,17 +14,25 @@
11
14
  "framework",
12
15
  "web",
13
16
  "client",
14
- "cli"
17
+ "cli",
18
+ "slice",
19
+ "slicejs",
20
+ "component",
21
+ "development server"
15
22
  ],
16
23
  "author": "vkneider",
17
24
  "type": "module",
18
25
  "license": "ISC",
19
26
  "dependencies": {
27
+ "chalk": "^5.6.2",
28
+ "chokidar": "^3.6.0",
20
29
  "clean-css": "^5.3.3",
30
+ "cli-table3": "^0.6.5",
21
31
  "commander": "^12.0.0",
22
32
  "fs-extra": "^11.2.0",
23
33
  "html-minifier-terser": "^7.2.0",
24
34
  "inquirer": "^12.4.2",
35
+ "ora": "^8.2.0",
25
36
  "slicejs-web-framework": "latest",
26
37
  "terser": "^5.43.1"
27
38
  }
package/post.js CHANGED
@@ -21,64 +21,63 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
21
21
 
22
22
  // Add custom commands to the project scripts
23
23
  projectPackageJson.scripts = projectPackageJson.scripts || {};
24
-
25
- // Main project commands - SOLO DEVELOPMENT
26
- projectPackageJson.scripts['slice:init'] = 'node node_modules/slicejs-cli/client.js init';
27
- projectPackageJson.scripts['slice:dev'] = 'node api/index.js --development';
28
- projectPackageJson.scripts['slice:start'] = 'node api/index.js --development';
29
- projectPackageJson.scripts['slice:version'] = 'node node_modules/slicejs-cli/client.js version';
30
- projectPackageJson.scripts['slice:update'] = 'node node_modules/slicejs-cli/client.js update';
31
-
32
- // Local component commands
33
- projectPackageJson.scripts['slice:create'] = 'node node_modules/slicejs-cli/client.js component create';
34
- projectPackageJson.scripts['slice:list'] = 'node node_modules/slicejs-cli/client.js component list';
35
- projectPackageJson.scripts['slice:delete'] = 'node node_modules/slicejs-cli/client.js component delete';
36
-
24
+
25
+ // Main project commands - using 'slice' directly
26
+ projectPackageJson.scripts['dev'] = 'slice dev';
27
+ projectPackageJson.scripts['start'] = 'slice start';
28
+
29
+ // Component management
30
+ projectPackageJson.scripts['component:create'] = 'slice component create';
31
+ projectPackageJson.scripts['component:list'] = 'slice component list';
32
+ projectPackageJson.scripts['component:delete'] = 'slice component delete';
33
+
37
34
  // Main repository commands (most used shortcuts)
38
- projectPackageJson.scripts['slice:get'] = 'node node_modules/slicejs-cli/client.js get';
39
- projectPackageJson.scripts['slice:browse'] = 'node node_modules/slicejs-cli/client.js browse';
40
- projectPackageJson.scripts['slice:sync'] = 'node node_modules/slicejs-cli/client.js sync';
41
-
42
- // Detailed registry commands (for advanced users)
43
- projectPackageJson.scripts['slice:registry-get'] = 'node node_modules/slicejs-cli/client.js registry get';
44
- projectPackageJson.scripts['slice:registry-list'] = 'node node_modules/slicejs-cli/client.js registry list';
45
- projectPackageJson.scripts['slice:registry-sync'] = 'node node_modules/slicejs-cli/client.js registry sync';
46
-
47
- // Legacy commands - SOLO DEVELOPMENT
48
- projectPackageJson.scripts['run'] = 'node api/index.js --development';
49
- projectPackageJson.scripts['development'] = 'node api/index.js --development';
35
+ projectPackageJson.scripts['get'] = 'slice get';
36
+ projectPackageJson.scripts['browse'] = 'slice browse';
37
+ projectPackageJson.scripts['sync'] = 'slice sync';
38
+
39
+ // Utility commands
40
+ projectPackageJson.scripts['slice:version'] = 'slice version';
41
+ projectPackageJson.scripts['slice:update'] = 'slice update';
42
+
43
+ // Legacy commands - mantener por compatibilidad temporal
44
+ projectPackageJson.scripts['slice:init'] = 'slice init';
45
+ projectPackageJson.scripts['slice:start'] = 'slice start';
46
+ projectPackageJson.scripts['slice:dev'] = 'slice dev';
47
+ projectPackageJson.scripts['slice:create'] = 'slice component create';
48
+ projectPackageJson.scripts['slice:list'] = 'slice component list';
49
+ projectPackageJson.scripts['slice:delete'] = 'slice component delete';
50
+ projectPackageJson.scripts['slice:get'] = 'slice get';
51
+ projectPackageJson.scripts['slice:browse'] = 'slice browse';
52
+ projectPackageJson.scripts['slice:sync'] = 'slice sync';
53
+ projectPackageJson.scripts['run'] = 'slice dev';
50
54
 
51
55
  // Module configuration
52
56
  projectPackageJson.type = 'module';
53
57
  projectPackageJson.engines = {
54
58
  "node": ">=20.0.0"
55
59
  };
56
-
60
+
57
61
  // Write the new content to package.json
58
62
  return fs.promises.writeFile(projectPackageJsonPath, JSON.stringify(projectPackageJson, null, 2), 'utf8');
59
63
  })
60
64
  .then(() => {
61
- console.log('✅ SliceJS CLI commands added to package.json');
62
- console.log('\n🚀 Main workflow commands:');
63
- console.log(' npm run slice:init - Initialize Slice.js project');
64
- console.log(' npm run slice:dev - Start development server (serves from /src)');
65
- console.log(' npm run slice:start - Start development server (same as dev)');
66
- console.log('\n📦 Component management:');
67
- console.log(' npm run slice:get Button - Get components from official repository');
68
- console.log(' npm run slice:browse - View all available components');
69
- console.log(' npm run slice:sync - Update local components to latest versions');
70
- console.log('\n⚙️ Local component management:');
71
- console.log(' npm run slice:create - Create local component');
72
- console.log(' npm run slice:list - List local components');
73
- console.log(' npm run slice:delete - Delete local component');
74
- console.log('\n🔧 Other utilities:');
75
- console.log(' npm run slice:version - View version information');
76
- console.log(' npm run slice:update - Check for available updates');
77
- console.log('\n🎯 Simplified workflow:');
78
- console.log(' 1. npm run slice:init - Initialize project');
79
- console.log(' 2. npm run slice:dev - Start development server');
80
- console.log(' 3. Develop and iterate - No build step needed!');
81
- console.log('\n💡 Development-focused: All commands serve from /src for instant changes');
65
+ console.log('✅ SliceJS CLI configured successfully');
66
+ console.log('\n🎯 New recommended commands:');
67
+ console.log(' slice dev - Start development server');
68
+ console.log(' slice get Button - Get components from repository');
69
+ console.log(' slice browse - View available components');
70
+ console.log(' slice component create - Create local component');
71
+ console.log('\n📦 Available npm scripts:');
72
+ console.log(' npm run dev - Start development server');
73
+ console.log(' npm run get - Install components');
74
+ console.log(' npm run browse - Browse components');
75
+ console.log('\n⚠️ Legacy commands (deprecated but still work):');
76
+ console.log(' npm run slice:dev - Use "slice dev" instead');
77
+ console.log(' npm run slice:get - Use "slice get" instead');
78
+ console.log('\n💡 You can also use npx:');
79
+ console.log(' npx slicejs-cli dev');
80
+ console.log('\n📘 Documentation: https://slice-js-docs.vercel.app/');
82
81
  })
83
82
  .catch(err => {
84
83
  if (err.code === 'ENOENT') {
@@ -89,33 +88,42 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
89
88
  description: 'Slice.js project',
90
89
  main: 'api/index.js',
91
90
  scripts: {
92
- // Main workflow commands - SOLO DEVELOPMENT
93
- 'slice:init': 'node node_modules/slicejs-cli/client.js init',
94
- 'slice:dev': 'node api/index.js --development',
95
- 'slice:start': 'node api/index.js --development',
96
- 'slice:version': 'node node_modules/slicejs-cli/client.js version',
97
- 'slice:update': 'node node_modules/slicejs-cli/client.js update',
98
-
99
- // Local component commands
100
- 'slice:create': 'node node_modules/slicejs-cli/client.js component create',
101
- 'slice:list': 'node node_modules/slicejs-cli/client.js component list',
102
- 'slice:delete': 'node node_modules/slicejs-cli/client.js component delete',
103
-
91
+ // Main workflow commands
92
+ 'dev': 'slice dev',
93
+ 'start': 'slice start',
94
+
95
+ // Component management
96
+ 'component:create': 'slice component create',
97
+ 'component:list': 'slice component list',
98
+ 'component:delete': 'slice component delete',
99
+
104
100
  // Repository commands
105
- 'slice:get': 'node node_modules/slicejs-cli/client.js get',
106
- 'slice:browse': 'node node_modules/slicejs-cli/client.js browse',
107
- 'slice:sync': 'node node_modules/slicejs-cli/client.js sync',
108
-
109
- // Legacy commands - SOLO DEVELOPMENT
110
- 'run': 'node api/index.js --development',
111
- 'development': 'node api/index.js --development'
101
+ 'get': 'slice get',
102
+ 'browse': 'slice browse',
103
+ 'sync': 'slice sync',
104
+
105
+ // Utility
106
+ 'slice:version': 'slice version',
107
+ 'slice:update': 'slice update',
108
+
109
+ // Legacy commands (for compatibility)
110
+ 'slice:init': 'slice init',
111
+ 'slice:start': 'slice start',
112
+ 'slice:dev': 'slice dev',
113
+ 'slice:create': 'slice component create',
114
+ 'slice:list': 'slice component list',
115
+ 'slice:delete': 'slice component delete',
116
+ 'slice:get': 'slice get',
117
+ 'slice:browse': 'slice browse',
118
+ 'slice:sync': 'slice sync',
119
+ 'run': 'slice dev'
112
120
  },
113
121
  type: 'module',
114
122
  engines: {
115
123
  "node": ">=20.0.0"
116
124
  }
117
125
  };
118
-
126
+
119
127
  return fs.promises.writeFile(projectPackageJsonPath, JSON.stringify(defaultPackageJson, null, 2), 'utf8');
120
128
  } else {
121
129
  throw err;
@@ -124,13 +132,13 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
124
132
  .then(() => {
125
133
  console.log('✅ SliceJS CLI commands configured successfully');
126
134
  console.log('\n🎯 Simplified development workflow:');
127
- console.log(' npm run slice:dev node api/index.js --development (serves /src)');
128
- console.log(' npm run slice:start → node api/index.js --development (same as dev)');
135
+ console.log(' slice dev Start development server');
136
+ console.log(' slice get Button Install components');
137
+ console.log(' slice browse → View available components');
129
138
  console.log('\n🔧 Benefits:');
130
- console.log(' • Simple development-only workflow');
131
- console.log(' • Instant changes without build steps');
132
- console.log(' • Always serves from /src directory');
133
- console.log(' • Interactive menu always available');
139
+ console.log(' • Simple and direct commands');
140
+ console.log(' • Can be used globally or with npx');
141
+ console.log(' • Legacy npm scripts still work for compatibility');
134
142
  })
135
143
  .catch(err => {
136
144
  console.error('❌ Error setting up package.json:', err.message);