slicejs-cli 2.1.10 → 2.2.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 +87 -2
- package/commands/Print.js +24 -0
- package/commands/buildProduction/buildProduction.js +493 -0
- package/commands/getComponent/getComponent.js +107 -54
- package/commands/init/init.js +113 -16
- package/commands/startServer/startServer.js +198 -0
- package/package.json +1 -1
- package/post.js +53 -16
|
@@ -109,8 +109,7 @@ class ComponentRegistry {
|
|
|
109
109
|
updatableComponents.push({
|
|
110
110
|
name,
|
|
111
111
|
category,
|
|
112
|
-
path: componentPath
|
|
113
|
-
description: this.getComponentDescription(name, category)
|
|
112
|
+
path: componentPath
|
|
114
113
|
});
|
|
115
114
|
}
|
|
116
115
|
}
|
|
@@ -125,13 +124,25 @@ class ComponentRegistry {
|
|
|
125
124
|
const components = {};
|
|
126
125
|
Object.entries(this.componentsRegistry).forEach(([name, componentCategory]) => {
|
|
127
126
|
if (!category || componentCategory === category) {
|
|
127
|
+
// ✅ CORREGIDO: Componentes especiales que no necesitan todos los archivos
|
|
128
|
+
let files;
|
|
129
|
+
if (componentCategory === 'Visual') {
|
|
130
|
+
// Componentes de routing lógico solo necesitan JS
|
|
131
|
+
if (['Route', 'MultiRoute', 'NotFound'].includes(name)) {
|
|
132
|
+
files = [`${name}.js`];
|
|
133
|
+
} else {
|
|
134
|
+
// Componentes visuales normales necesitan JS, HTML, CSS
|
|
135
|
+
files = [`${name}.js`, `${name}.html`, `${name}.css`];
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
// Service components solo necesitan JS
|
|
139
|
+
files = [`${name}.js`];
|
|
140
|
+
}
|
|
141
|
+
|
|
128
142
|
components[name] = {
|
|
129
143
|
name,
|
|
130
144
|
category: componentCategory,
|
|
131
|
-
files:
|
|
132
|
-
[`${name}.js`, `${name}.html`, `${name}.css`] :
|
|
133
|
-
[`${name}.js`],
|
|
134
|
-
description: this.getComponentDescription(name, componentCategory)
|
|
145
|
+
files: files
|
|
135
146
|
};
|
|
136
147
|
}
|
|
137
148
|
});
|
|
@@ -139,37 +150,42 @@ class ComponentRegistry {
|
|
|
139
150
|
return components;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
'
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
153
|
+
displayAvailableComponents() {
|
|
154
|
+
if (!this.componentsRegistry) {
|
|
155
|
+
Print.error('❌ No se pudo cargar el registro de componentes');
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
console.log('\n📚 Componentes disponibles en el repositorio oficial de Slice.js:\n');
|
|
160
|
+
|
|
161
|
+
const visualComponents = this.getAvailableComponents('Visual');
|
|
162
|
+
const serviceComponents = this.getAvailableComponents('Service');
|
|
163
|
+
|
|
164
|
+
// ✅ SIMPLIFICADO: Solo mostrar nombres sin descripciones
|
|
165
|
+
Print.info('🎨 Visual Components (UI):');
|
|
166
|
+
Object.keys(visualComponents).forEach(name => {
|
|
167
|
+
const files = visualComponents[name].files;
|
|
168
|
+
const fileIcons = files.map(file => {
|
|
169
|
+
if (file.endsWith('.js')) return '📜';
|
|
170
|
+
if (file.endsWith('.html')) return '🌐';
|
|
171
|
+
if (file.endsWith('.css')) return '🎨';
|
|
172
|
+
return '📄';
|
|
173
|
+
}).join(' ');
|
|
174
|
+
console.log(` • ${name} ${fileIcons}`);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
Print.info('\n⚙️ Service Components (Logic):');
|
|
178
|
+
Object.keys(serviceComponents).forEach(name => {
|
|
179
|
+
console.log(` • ${name} 📜`);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
Print.newLine();
|
|
183
|
+
Print.info(`Total: ${Object.keys(visualComponents).length} Visual + ${Object.keys(serviceComponents).length} Service components`);
|
|
184
|
+
|
|
185
|
+
console.log(`\n💡 Ejemplos de uso:`);
|
|
186
|
+
console.log(`slice get Button Card Input # Obtener componentes Visual`);
|
|
187
|
+
console.log(`slice get FetchManager --service # Obtener componente Service`);
|
|
188
|
+
console.log(`slice sync # Sincronizar componentes Visual`);
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
async downloadComponentFiles(componentName, category, targetPath) {
|
|
@@ -180,6 +196,7 @@ class ComponentRegistry {
|
|
|
180
196
|
}
|
|
181
197
|
|
|
182
198
|
const downloadedFiles = [];
|
|
199
|
+
const failedFiles = [];
|
|
183
200
|
Print.info(`Downloading ${componentName} from official repository...`);
|
|
184
201
|
|
|
185
202
|
for (const fileName of component.files) {
|
|
@@ -190,7 +207,9 @@ class ComponentRegistry {
|
|
|
190
207
|
const response = await fetch(githubUrl);
|
|
191
208
|
|
|
192
209
|
if (!response.ok) {
|
|
193
|
-
|
|
210
|
+
Print.downloadError(fileName, `HTTP ${response.status}: ${response.statusText}`);
|
|
211
|
+
failedFiles.push(fileName);
|
|
212
|
+
continue; // ✅ CONTINUAR en lugar de lanzar error
|
|
194
213
|
}
|
|
195
214
|
|
|
196
215
|
const content = await response.text();
|
|
@@ -200,10 +219,24 @@ class ComponentRegistry {
|
|
|
200
219
|
Print.downloadSuccess(fileName);
|
|
201
220
|
} catch (error) {
|
|
202
221
|
Print.downloadError(fileName, error.message);
|
|
203
|
-
|
|
222
|
+
failedFiles.push(fileName);
|
|
223
|
+
continue; // ✅ CONTINUAR en lugar de lanzar error
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
226
|
|
|
227
|
+
// ✅ NUEVO: Solo lanzar error si NO se descargó el archivo principal (.js)
|
|
228
|
+
const mainFileDownloaded = downloadedFiles.some(file => file.endsWith('.js'));
|
|
229
|
+
|
|
230
|
+
if (!mainFileDownloaded) {
|
|
231
|
+
throw new Error(`Failed to download main component file (${componentName}.js)`);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ✅ ADVERTENCIA: Informar sobre archivos que fallaron (pero no detener el proceso)
|
|
235
|
+
if (failedFiles.length > 0) {
|
|
236
|
+
Print.warning(`Some files couldn't be downloaded: ${failedFiles.join(', ')}`);
|
|
237
|
+
Print.info('Component installed with available files');
|
|
238
|
+
}
|
|
239
|
+
|
|
207
240
|
return downloadedFiles;
|
|
208
241
|
}
|
|
209
242
|
|
|
@@ -295,18 +328,27 @@ class ComponentRegistry {
|
|
|
295
328
|
// Update components registry
|
|
296
329
|
await this.updateLocalRegistry(componentName, category);
|
|
297
330
|
|
|
298
|
-
Print.success(`${componentName}
|
|
331
|
+
Print.success(`${componentName} installed successfully from official repository!`);
|
|
299
332
|
console.log(`📁 Location: ${folderSuffix}/${categoryPath}/${componentName}/`);
|
|
300
333
|
console.log(`📄 Files: ${downloadedFiles.join(', ')}`);
|
|
301
334
|
|
|
302
335
|
return true;
|
|
303
336
|
|
|
304
337
|
} catch (error) {
|
|
305
|
-
Print.error(`Error
|
|
306
|
-
|
|
307
|
-
|
|
338
|
+
Print.error(`Error installing ${componentName}: ${error.message}`);
|
|
339
|
+
|
|
340
|
+
// ✅ MEJORADO: Solo borrar si el archivo principal (.js) no existe
|
|
341
|
+
const mainFilePath = path.join(targetPath, `${componentName}.js`);
|
|
342
|
+
const mainFileExists = await fs.pathExists(mainFilePath);
|
|
343
|
+
|
|
344
|
+
if (!mainFileExists && await fs.pathExists(targetPath)) {
|
|
345
|
+
// Solo limpiar si no se instaló el archivo principal
|
|
308
346
|
await fs.remove(targetPath);
|
|
347
|
+
Print.info('Cleaned up failed installation');
|
|
348
|
+
} else if (mainFileExists) {
|
|
349
|
+
Print.warning('Component partially installed - main file exists');
|
|
309
350
|
}
|
|
351
|
+
|
|
310
352
|
throw error;
|
|
311
353
|
}
|
|
312
354
|
}
|
|
@@ -420,20 +462,31 @@ class ComponentRegistry {
|
|
|
420
462
|
const visualComponents = this.getAvailableComponents('Visual');
|
|
421
463
|
const serviceComponents = this.getAvailableComponents('Service');
|
|
422
464
|
|
|
465
|
+
// ✅ SIMPLIFICADO: Solo mostrar nombres sin descripciones
|
|
423
466
|
Print.info('🎨 Visual Components (UI):');
|
|
424
|
-
Object.
|
|
425
|
-
|
|
467
|
+
Object.keys(visualComponents).forEach(name => {
|
|
468
|
+
const files = visualComponents[name].files;
|
|
469
|
+
const fileIcons = files.map(file => {
|
|
470
|
+
if (file.endsWith('.js')) return '📜';
|
|
471
|
+
if (file.endsWith('.html')) return '🌐';
|
|
472
|
+
if (file.endsWith('.css')) return '🎨';
|
|
473
|
+
return '📄';
|
|
474
|
+
}).join(' ');
|
|
475
|
+
console.log(` • ${name} ${fileIcons}`);
|
|
426
476
|
});
|
|
427
477
|
|
|
428
478
|
Print.info('\n⚙️ Service Components (Logic):');
|
|
429
|
-
Object.
|
|
430
|
-
console.log(` • ${name}
|
|
479
|
+
Object.keys(serviceComponents).forEach(name => {
|
|
480
|
+
console.log(` • ${name} 📜`);
|
|
431
481
|
});
|
|
432
482
|
|
|
483
|
+
Print.newLine();
|
|
484
|
+
Print.info(`Total: ${Object.keys(visualComponents).length} Visual + ${Object.keys(serviceComponents).length} Service components`);
|
|
485
|
+
|
|
433
486
|
console.log(`\n💡 Ejemplos de uso:`);
|
|
434
|
-
console.log(`
|
|
435
|
-
console.log(`
|
|
436
|
-
console.log(`
|
|
487
|
+
console.log(`slice get Button Card Input # Obtener componentes Visual`);
|
|
488
|
+
console.log(`slice get FetchManager --service # Obtener componente Service`);
|
|
489
|
+
console.log(`slice sync # Sincronizar componentes Visual`);
|
|
437
490
|
}
|
|
438
491
|
|
|
439
492
|
async interactiveInstall() {
|
|
@@ -450,8 +503,8 @@ class ComponentRegistry {
|
|
|
450
503
|
]);
|
|
451
504
|
|
|
452
505
|
const availableComponents = this.getAvailableComponents(componentType);
|
|
453
|
-
const componentChoices = Object.
|
|
454
|
-
name:
|
|
506
|
+
const componentChoices = Object.keys(availableComponents).map(name => ({
|
|
507
|
+
name: name,
|
|
455
508
|
value: name
|
|
456
509
|
}));
|
|
457
510
|
|
|
@@ -554,7 +607,7 @@ async function getComponents(componentNames = [], options = {}) {
|
|
|
554
607
|
|
|
555
608
|
if (!componentInfo) {
|
|
556
609
|
Print.error(`Component '${componentNames[0]}' not found in official repository`);
|
|
557
|
-
Print.commandExample('View available components', '
|
|
610
|
+
Print.commandExample('View available components', 'slice browse');
|
|
558
611
|
return false;
|
|
559
612
|
}
|
|
560
613
|
|
|
@@ -614,4 +667,4 @@ async function syncComponents(options = {}) {
|
|
|
614
667
|
}
|
|
615
668
|
|
|
616
669
|
export default getComponents;
|
|
617
|
-
export { listComponents, syncComponents };
|
|
670
|
+
export { listComponents, syncComponents, ComponentRegistry };
|
package/commands/init/init.js
CHANGED
|
@@ -2,28 +2,21 @@ import fs from 'fs-extra';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import Print from '../Print.js';
|
|
5
|
+
|
|
5
6
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
6
7
|
|
|
8
|
+
// Importar la clase ComponentRegistry del getComponent
|
|
9
|
+
import { ComponentRegistry } from '../getComponent/getComponent.js';
|
|
10
|
+
|
|
7
11
|
export default async function initializeProject(projectType) {
|
|
8
12
|
try {
|
|
9
|
-
// Directorio de origen
|
|
13
|
+
// Directorio de origen para API (mantener copia local)
|
|
10
14
|
let sliceBaseDir = path.join(__dirname, '../../../slicejs-web-framework');
|
|
11
15
|
let apiDir = path.join(sliceBaseDir, 'api');
|
|
12
16
|
let srcDir = path.join(sliceBaseDir, 'src');
|
|
13
|
-
|
|
14
17
|
let destinationApi = path.join(__dirname, '../../../../api');
|
|
15
18
|
let destinationSrc = path.join(__dirname, '../../../../src');
|
|
16
19
|
|
|
17
|
-
try {
|
|
18
|
-
// Verificar si los directorios de origen existen
|
|
19
|
-
if (!fs.existsSync(sliceBaseDir)) throw new Error(`No se encontró el directorio base: ${sliceBaseDir}`);
|
|
20
|
-
if (!fs.existsSync(apiDir)) throw new Error(`No se encontró la carpeta api: ${apiDir}`);
|
|
21
|
-
if (!fs.existsSync(srcDir)) throw new Error(`No se encontró la carpeta src: ${srcDir}`);
|
|
22
|
-
} catch (error) {
|
|
23
|
-
Print.error('Error validando directorios de origen:', error.message);
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
20
|
try {
|
|
28
21
|
// Verificar si los directorios de destino ya existen
|
|
29
22
|
if (fs.existsSync(destinationApi)) throw new Error(`El directorio "api" ya existe: ${destinationApi}`);
|
|
@@ -33,8 +26,9 @@ export default async function initializeProject(projectType) {
|
|
|
33
26
|
return;
|
|
34
27
|
}
|
|
35
28
|
|
|
29
|
+
// 1. COPIAR LA CARPETA API (mantener lógica original)
|
|
36
30
|
try {
|
|
37
|
-
|
|
31
|
+
if (!fs.existsSync(apiDir)) throw new Error(`No se encontró la carpeta api: ${apiDir}`);
|
|
38
32
|
await fs.copy(apiDir, destinationApi, { recursive: true });
|
|
39
33
|
Print.success('Carpeta "api" copiada correctamente.');
|
|
40
34
|
} catch (error) {
|
|
@@ -42,16 +36,119 @@ export default async function initializeProject(projectType) {
|
|
|
42
36
|
return;
|
|
43
37
|
}
|
|
44
38
|
|
|
39
|
+
// 2. CREAR ESTRUCTURA SRC BÁSICA (sin copiar componentes Visual)
|
|
45
40
|
try {
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
if (!fs.existsSync(srcDir)) throw new Error(`No se encontró la carpeta src: ${srcDir}`);
|
|
42
|
+
|
|
43
|
+
// Copiar solo los archivos base de src, excluyendo Components/Visual
|
|
44
|
+
await fs.ensureDir(destinationSrc);
|
|
45
|
+
|
|
46
|
+
// Copiar archivos y carpetas de src excepto Components/Visual
|
|
47
|
+
const srcItems = await fs.readdir(srcDir);
|
|
48
|
+
|
|
49
|
+
for (const item of srcItems) {
|
|
50
|
+
const srcItemPath = path.join(srcDir, item);
|
|
51
|
+
const destItemPath = path.join(destinationSrc, item);
|
|
52
|
+
const stat = await fs.stat(srcItemPath);
|
|
53
|
+
|
|
54
|
+
if (stat.isDirectory()) {
|
|
55
|
+
if (item === 'Components') {
|
|
56
|
+
// Crear estructura de Components pero sin copiar Visual
|
|
57
|
+
await fs.ensureDir(destItemPath);
|
|
58
|
+
|
|
59
|
+
const componentItems = await fs.readdir(srcItemPath);
|
|
60
|
+
for (const componentItem of componentItems) {
|
|
61
|
+
const componentItemPath = path.join(srcItemPath, componentItem);
|
|
62
|
+
const destComponentItemPath = path.join(destItemPath, componentItem);
|
|
63
|
+
|
|
64
|
+
if (componentItem !== 'Visual') {
|
|
65
|
+
// Copiar Service y otros tipos de components
|
|
66
|
+
await fs.copy(componentItemPath, destComponentItemPath, { recursive: true });
|
|
67
|
+
} else {
|
|
68
|
+
// Solo crear el directorio Visual vacío
|
|
69
|
+
await fs.ensureDir(destComponentItemPath);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// Copiar otras carpetas normalmente
|
|
74
|
+
await fs.copy(srcItemPath, destItemPath, { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
// Copiar archivos normalmente
|
|
78
|
+
await fs.copy(srcItemPath, destItemPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Print.success('Estructura "src" creada correctamente.');
|
|
48
83
|
} catch (error) {
|
|
49
|
-
Print.error('Error
|
|
84
|
+
Print.error('Error creando la estructura "src":', error.message);
|
|
50
85
|
return;
|
|
51
86
|
}
|
|
52
87
|
|
|
88
|
+
// 3. DESCARGAR TODOS LOS COMPONENTES VISUAL DESDE EL REPOSITORIO OFICIAL
|
|
89
|
+
try {
|
|
90
|
+
Print.info('Downloading all Visual components from official repository...');
|
|
91
|
+
|
|
92
|
+
const registry = new ComponentRegistry();
|
|
93
|
+
await registry.loadRegistry();
|
|
94
|
+
|
|
95
|
+
// Obtener TODOS los componentes Visual disponibles
|
|
96
|
+
const allVisualComponents = await getAllVisualComponents(registry);
|
|
97
|
+
|
|
98
|
+
if (allVisualComponents.length > 0) {
|
|
99
|
+
Print.info(`Installing ${allVisualComponents.length} Visual components...`);
|
|
100
|
+
|
|
101
|
+
const results = await registry.installMultipleComponents(
|
|
102
|
+
allVisualComponents,
|
|
103
|
+
'Visual',
|
|
104
|
+
true // force = true para instalación inicial
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const successful = results.filter(r => r.success).length;
|
|
108
|
+
const failed = results.filter(r => !r.success).length;
|
|
109
|
+
|
|
110
|
+
if (successful > 0) {
|
|
111
|
+
Print.success(`${successful} Visual components installed from official repository`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (failed > 0) {
|
|
115
|
+
Print.warning(`${failed} Visual components could not be installed`);
|
|
116
|
+
Print.info('You can install them later using "slice get <component-name>"');
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
Print.warning('No Visual components found in registry');
|
|
120
|
+
Print.info('You can add components later using "slice get <component-name>"');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
} catch (error) {
|
|
124
|
+
Print.warning('Could not download Visual components from official repository');
|
|
125
|
+
Print.error(`Repository error: ${error.message}`);
|
|
126
|
+
Print.info('Project initialized without Visual components');
|
|
127
|
+
Print.info('You can add them later using "slice get <component-name>"');
|
|
128
|
+
}
|
|
129
|
+
|
|
53
130
|
Print.success('Proyecto inicializado correctamente.');
|
|
131
|
+
Print.newLine();
|
|
132
|
+
Print.info('Next steps:');
|
|
133
|
+
console.log(' slice browse - View available components');
|
|
134
|
+
console.log(' slice get Button - Install specific components');
|
|
135
|
+
console.log(' slice sync - Update all components to latest versions');
|
|
136
|
+
|
|
54
137
|
} catch (error) {
|
|
55
138
|
Print.error('Error inesperado al inicializar el proyecto:', error.message);
|
|
56
139
|
}
|
|
57
140
|
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Obtiene TODOS los componentes Visual disponibles en el registry
|
|
144
|
+
* @param {ComponentRegistry} registry - Instancia del registry cargado
|
|
145
|
+
* @returns {Array} - Array con todos los nombres de componentes Visual
|
|
146
|
+
*/
|
|
147
|
+
async function getAllVisualComponents(registry) {
|
|
148
|
+
const availableComponents = registry.getAvailableComponents('Visual');
|
|
149
|
+
const allVisualComponents = Object.keys(availableComponents);
|
|
150
|
+
|
|
151
|
+
Print.info(`Found ${allVisualComponents.length} Visual components in official repository`);
|
|
152
|
+
|
|
153
|
+
return allVisualComponents;
|
|
154
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// commands/startServer/startServer.js
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
|
+
import Print from '../Print.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Verifica si existe un build de producción
|
|
13
|
+
*/
|
|
14
|
+
async function checkProductionBuild() {
|
|
15
|
+
const distDir = path.join(__dirname, '../../../../dist');
|
|
16
|
+
return await fs.pathExists(distDir);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Verifica si existe la estructura de desarrollo
|
|
21
|
+
*/
|
|
22
|
+
async function checkDevelopmentStructure() {
|
|
23
|
+
const srcDir = path.join(__dirname, '../../../../src');
|
|
24
|
+
const apiDir = path.join(__dirname, '../../../../api');
|
|
25
|
+
|
|
26
|
+
return (await fs.pathExists(srcDir)) && (await fs.pathExists(apiDir));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Modifica temporalmente el servidor Express para modo producción
|
|
31
|
+
*/
|
|
32
|
+
async function createProductionIndexFile() {
|
|
33
|
+
try {
|
|
34
|
+
const apiDir = path.join(__dirname, '../../../../api');
|
|
35
|
+
const originalIndexPath = path.join(apiDir, 'index.js');
|
|
36
|
+
const backupIndexPath = path.join(apiDir, 'index.dev.js');
|
|
37
|
+
const prodIndexPath = path.join(apiDir, 'index.prod.js');
|
|
38
|
+
|
|
39
|
+
// Crear backup del index original si no existe
|
|
40
|
+
if (!await fs.pathExists(backupIndexPath)) {
|
|
41
|
+
await fs.copy(originalIndexPath, backupIndexPath);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Leer el contenido original
|
|
45
|
+
const originalContent = await fs.readFile(originalIndexPath, 'utf8');
|
|
46
|
+
|
|
47
|
+
// Modificar para servir desde /dist en lugar de /src
|
|
48
|
+
const productionContent = originalContent.replace(
|
|
49
|
+
/express\.static\(['"`]src['"`]\)/g,
|
|
50
|
+
"express.static('dist')"
|
|
51
|
+
).replace(
|
|
52
|
+
/express\.static\(path\.join\(__dirname,\s*['"`]\.\.\/src['"`]\)\)/g,
|
|
53
|
+
"express.static(path.join(__dirname, '../dist'))"
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Escribir archivo temporal de producción
|
|
57
|
+
await fs.writeFile(prodIndexPath, productionContent, 'utf8');
|
|
58
|
+
|
|
59
|
+
// Reemplazar index.js con versión de producción
|
|
60
|
+
await fs.copy(prodIndexPath, originalIndexPath);
|
|
61
|
+
|
|
62
|
+
Print.success('Express server configured for production mode');
|
|
63
|
+
|
|
64
|
+
return true;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
Print.error(`Error configuring production server: ${error.message}`);
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Restaura el servidor Express al modo desarrollo
|
|
73
|
+
*/
|
|
74
|
+
async function restoreDevelopmentIndexFile() {
|
|
75
|
+
try {
|
|
76
|
+
const apiDir = path.join(__dirname, '../../../../api');
|
|
77
|
+
const originalIndexPath = path.join(apiDir, 'index.js');
|
|
78
|
+
const backupIndexPath = path.join(apiDir, 'index.dev.js');
|
|
79
|
+
|
|
80
|
+
if (await fs.pathExists(backupIndexPath)) {
|
|
81
|
+
await fs.copy(backupIndexPath, originalIndexPath);
|
|
82
|
+
Print.success('Express server restored to development mode');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return true;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
Print.error(`Error restoring development server: ${error.message}`);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Inicia el servidor Node.js
|
|
94
|
+
*/
|
|
95
|
+
function startNodeServer(port, mode) {
|
|
96
|
+
return new Promise((resolve, reject) => {
|
|
97
|
+
const apiIndexPath = path.join(__dirname, '../../../../api/index.js');
|
|
98
|
+
|
|
99
|
+
Print.info(`Starting ${mode} server on port ${port}...`);
|
|
100
|
+
|
|
101
|
+
const serverProcess = spawn('node', [apiIndexPath], {
|
|
102
|
+
stdio: 'inherit',
|
|
103
|
+
env: {
|
|
104
|
+
...process.env,
|
|
105
|
+
PORT: port,
|
|
106
|
+
NODE_ENV: mode === 'production' ? 'production' : 'development'
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
serverProcess.on('error', (error) => {
|
|
111
|
+
Print.error(`Failed to start server: ${error.message}`);
|
|
112
|
+
reject(error);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Manejar Ctrl+C para limpiar archivos temporales
|
|
116
|
+
process.on('SIGINT', async () => {
|
|
117
|
+
Print.info('Shutting down server...');
|
|
118
|
+
|
|
119
|
+
if (mode === 'production') {
|
|
120
|
+
await restoreDevelopmentIndexFile();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
serverProcess.kill('SIGINT');
|
|
124
|
+
process.exit(0);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Manejar cierre del proceso
|
|
128
|
+
process.on('SIGTERM', async () => {
|
|
129
|
+
if (mode === 'production') {
|
|
130
|
+
await restoreDevelopmentIndexFile();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
serverProcess.kill('SIGTERM');
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// El servidor se considera iniciado exitosamente después de un breve delay
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
Print.success(`${mode === 'production' ? 'Production' : 'Development'} server running at http://localhost:${port}`);
|
|
139
|
+
Print.info(`Serving files from /${mode === 'production' ? 'dist' : 'src'} directory`);
|
|
140
|
+
Print.info('Press Ctrl+C to stop the server');
|
|
141
|
+
resolve(serverProcess);
|
|
142
|
+
}, 1000);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Función principal para iniciar servidor
|
|
148
|
+
*/
|
|
149
|
+
export default async function startServer(options = {}) {
|
|
150
|
+
const { mode = 'development', port = 3000 } = options;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
Print.title(`🚀 Starting Slice.js ${mode} server...`);
|
|
154
|
+
Print.newLine();
|
|
155
|
+
|
|
156
|
+
// Verificar estructura del proyecto
|
|
157
|
+
if (!await checkDevelopmentStructure()) {
|
|
158
|
+
throw new Error('Project structure not found. Run "slice init" first.');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (mode === 'production') {
|
|
162
|
+
// Modo producción: verificar build y configurar servidor
|
|
163
|
+
if (!await checkProductionBuild()) {
|
|
164
|
+
throw new Error('No production build found. Run "slice build" first.');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Configurar Express para modo producción
|
|
168
|
+
const configSuccess = await createProductionIndexFile();
|
|
169
|
+
if (!configSuccess) {
|
|
170
|
+
throw new Error('Failed to configure production server');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
Print.info('Production mode: serving optimized files from /dist');
|
|
174
|
+
} else {
|
|
175
|
+
// Modo desarrollo: asegurar que está en modo desarrollo
|
|
176
|
+
await restoreDevelopmentIndexFile();
|
|
177
|
+
Print.info('Development mode: serving files from /src with hot reload');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Iniciar el servidor
|
|
181
|
+
await startNodeServer(port, mode);
|
|
182
|
+
|
|
183
|
+
} catch (error) {
|
|
184
|
+
Print.error(`Failed to start server: ${error.message}`);
|
|
185
|
+
|
|
186
|
+
// Limpiar en caso de error
|
|
187
|
+
if (mode === 'production') {
|
|
188
|
+
await restoreDevelopmentIndexFile();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
throw error;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Funciones de utilidad exportadas
|
|
197
|
+
*/
|
|
198
|
+
export { checkProductionBuild, checkDevelopmentStructure };
|