slicejs-cli 2.1.11 → 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 +1 -1
- package/commands/init/init.js +113 -16
- package/commands/startServer/startServer.js +198 -0
- package/package.json +1 -1
- package/post.js +53 -16
package/client.js
CHANGED
|
@@ -6,6 +6,8 @@ import createComponent from "./commands/createComponent/createComponent.js";
|
|
|
6
6
|
import listComponents from "./commands/listComponents/listComponents.js";
|
|
7
7
|
import deleteComponent from "./commands/deleteComponent/deleteComponent.js";
|
|
8
8
|
import getComponent, { listComponents as listRemoteComponents, syncComponents } from "./commands/getComponent/getComponent.js";
|
|
9
|
+
import buildProduction, { buildCommand, serveProductionBuild } from "./commands/buildProduction/buildProduction.js";
|
|
10
|
+
import startServer from "./commands/startServer/startServer.js";
|
|
9
11
|
import versionChecker from "./commands/utils/versionChecker.js";
|
|
10
12
|
import fs from "fs";
|
|
11
13
|
import path from "path";
|
|
@@ -73,6 +75,80 @@ sliceClient
|
|
|
73
75
|
await versionChecker.showVersionInfo();
|
|
74
76
|
});
|
|
75
77
|
|
|
78
|
+
// BUILD COMMAND
|
|
79
|
+
sliceClient
|
|
80
|
+
.command("build")
|
|
81
|
+
.description("Build project for production")
|
|
82
|
+
.option("--serve", "Start production server after build")
|
|
83
|
+
.option("--preview", "Start preview server after build")
|
|
84
|
+
.option("--analyze", "Analyze build without building")
|
|
85
|
+
.option("--skip-clean", "Skip cleaning previous build")
|
|
86
|
+
.option("-p, --port <port>", "Port for preview/serve server", 3001)
|
|
87
|
+
.action(async (options) => {
|
|
88
|
+
await runWithVersionCheck(async () => {
|
|
89
|
+
const success = await buildCommand({
|
|
90
|
+
serve: options.serve,
|
|
91
|
+
preview: options.preview,
|
|
92
|
+
analyze: options.analyze,
|
|
93
|
+
skipClean: options.skipClean,
|
|
94
|
+
port: parseInt(options.port)
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (!success) {
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// START COMMAND (PRODUCTION)
|
|
104
|
+
sliceClient
|
|
105
|
+
.command("start")
|
|
106
|
+
.description("Start production server (requires build first)")
|
|
107
|
+
.option("-p, --port <port>", "Port for production server", 3000)
|
|
108
|
+
.option("--build", "Build for production before starting")
|
|
109
|
+
.action(async (options) => {
|
|
110
|
+
await runWithVersionCheck(async () => {
|
|
111
|
+
const distDir = path.join(__dirname, "../../dist");
|
|
112
|
+
|
|
113
|
+
// Verificar si existe build de producción
|
|
114
|
+
if (!fs.existsSync(distDir) && !options.build) {
|
|
115
|
+
Print.error("No production build found");
|
|
116
|
+
Print.info("Run 'slice build' first or use 'slice start --build'");
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Si se solicita build, construir primero
|
|
121
|
+
if (options.build) {
|
|
122
|
+
Print.info("Building for production...");
|
|
123
|
+
const buildSuccess = await buildProduction();
|
|
124
|
+
if (!buildSuccess) {
|
|
125
|
+
Print.error("Build failed, cannot start production server");
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Iniciar servidor de producción
|
|
131
|
+
await startServer({
|
|
132
|
+
mode: 'production',
|
|
133
|
+
port: parseInt(options.port)
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// DEV COMMAND (DEVELOPMENT)
|
|
139
|
+
sliceClient
|
|
140
|
+
.command("dev")
|
|
141
|
+
.description("Start development server")
|
|
142
|
+
.option("-p, --port <port>", "Port for development server", 3000)
|
|
143
|
+
.action(async (options) => {
|
|
144
|
+
await runWithVersionCheck(async () => {
|
|
145
|
+
await startServer({
|
|
146
|
+
mode: 'development',
|
|
147
|
+
port: parseInt(options.port)
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
76
152
|
// COMPONENT COMMAND GROUP - For local component management
|
|
77
153
|
const componentCommand = sliceClient.command("component").alias("comp").description("Manage local project components");
|
|
78
154
|
|
|
@@ -286,18 +362,26 @@ sliceClient
|
|
|
286
362
|
sliceClient.addHelpText('after', `
|
|
287
363
|
Common Usage Examples:
|
|
288
364
|
slice init - Initialize new Slice.js project
|
|
365
|
+
slice dev - Start development server
|
|
366
|
+
slice build - Build for production
|
|
367
|
+
slice start - Start production server
|
|
289
368
|
slice get Button Card Input - Install Visual components from registry
|
|
290
369
|
slice get FetchManager -s - Install Service component from registry
|
|
291
370
|
slice browse - Browse all available components
|
|
292
371
|
slice sync - Update local components to latest versions
|
|
293
372
|
slice component create - Create new local component
|
|
294
|
-
slice update - Check for CLI/framework updates
|
|
295
373
|
|
|
296
374
|
Command Categories:
|
|
297
|
-
• init,
|
|
375
|
+
• init, dev, build, start - Project lifecycle
|
|
298
376
|
• get, browse, sync - Quick registry shortcuts
|
|
299
377
|
• component <cmd> - Local component management
|
|
300
378
|
• registry <cmd> - Official repository operations
|
|
379
|
+
• version, update - Maintenance commands
|
|
380
|
+
|
|
381
|
+
Development vs Production:
|
|
382
|
+
• slice dev - Development server (serves from /src)
|
|
383
|
+
• slice build - Create optimized /dist build
|
|
384
|
+
• slice start - Production server (serves from /dist)
|
|
301
385
|
|
|
302
386
|
More info: https://slice-js-docs.vercel.app/
|
|
303
387
|
`);
|
|
@@ -307,6 +391,7 @@ if (!process.argv.slice(2).length) {
|
|
|
307
391
|
program.outputHelp();
|
|
308
392
|
Print.newLine();
|
|
309
393
|
Print.info("Start with: slice init");
|
|
394
|
+
Print.commandExample("Development", "slice dev");
|
|
310
395
|
Print.commandExample("View available components", "slice browse");
|
|
311
396
|
}
|
|
312
397
|
|
package/commands/Print.js
CHANGED
|
@@ -90,4 +90,28 @@ export default class Print {
|
|
|
90
90
|
}
|
|
91
91
|
Print.separator();
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
// Método para mostrar resultados de minificación
|
|
95
|
+
static minificationResult(filename, originalSize, minifiedSize, savingsPercent) {
|
|
96
|
+
const originalKB = (originalSize / 1024).toFixed(1);
|
|
97
|
+
const minifiedKB = (minifiedSize / 1024).toFixed(1);
|
|
98
|
+
|
|
99
|
+
console.log('\x1b[32m', ` ✅ ${filename}`, '\x1b[0m');
|
|
100
|
+
console.log('\x1b[90m', ` ${originalKB}KB → ${minifiedKB}KB (${savingsPercent}% saved)`, '\x1b[0m');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Método para mostrar progreso de build
|
|
104
|
+
static buildProgress(message) {
|
|
105
|
+
console.log('\x1b[36m', `🔄 ${message}`, '\x1b[0m');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Método para mostrar estadísticas de servidor
|
|
109
|
+
static serverStats(mode, port, directory) {
|
|
110
|
+
Print.newLine();
|
|
111
|
+
console.log('\x1b[35m', `🌐 Server Configuration:`, '\x1b[0m');
|
|
112
|
+
console.log('\x1b[90m', ` Mode: ${mode}`, '\x1b[0m');
|
|
113
|
+
console.log('\x1b[90m', ` Port: ${port}`, '\x1b[0m');
|
|
114
|
+
console.log('\x1b[90m', ` Serving: /${directory}`, '\x1b[0m');
|
|
115
|
+
Print.newLine();
|
|
116
|
+
}
|
|
93
117
|
}
|
|
@@ -0,0 +1,493 @@
|
|
|
1
|
+
// commands/buildProduction/buildProduction.js
|
|
2
|
+
|
|
3
|
+
import fs from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import { minify as terserMinify } from 'terser';
|
|
7
|
+
import CleanCSS from 'clean-css';
|
|
8
|
+
import htmlMinifier from 'html-minifier-terser';
|
|
9
|
+
import Print from '../Print.js';
|
|
10
|
+
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Opciones de minificación para diferentes tipos de archivos
|
|
15
|
+
*/
|
|
16
|
+
const getMinificationOptions = () => ({
|
|
17
|
+
js: {
|
|
18
|
+
compress: {
|
|
19
|
+
dead_code: true,
|
|
20
|
+
drop_console: true, // Remover console.log en producción
|
|
21
|
+
drop_debugger: true,
|
|
22
|
+
pure_funcs: ['console.log', 'console.info', 'console.warn'],
|
|
23
|
+
passes: 2
|
|
24
|
+
},
|
|
25
|
+
mangle: {
|
|
26
|
+
toplevel: true,
|
|
27
|
+
reserved: ['Slice', 'Controller', 'StylesManager'] // Preservar clases principales
|
|
28
|
+
},
|
|
29
|
+
output: {
|
|
30
|
+
comments: false,
|
|
31
|
+
beautify: false
|
|
32
|
+
},
|
|
33
|
+
toplevel: true
|
|
34
|
+
},
|
|
35
|
+
css: {
|
|
36
|
+
level: 2, // Optimización agresiva
|
|
37
|
+
returnPromise: false
|
|
38
|
+
},
|
|
39
|
+
html: {
|
|
40
|
+
collapseWhitespace: true,
|
|
41
|
+
removeComments: true,
|
|
42
|
+
removeRedundantAttributes: true,
|
|
43
|
+
removeEmptyAttributes: true,
|
|
44
|
+
minifyCSS: true,
|
|
45
|
+
minifyJS: true,
|
|
46
|
+
useShortDoctype: true,
|
|
47
|
+
removeAttributeQuotes: true,
|
|
48
|
+
removeOptionalTags: true
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Minifica un archivo JavaScript
|
|
54
|
+
*/
|
|
55
|
+
async function minifyJavaScript(content, filename) {
|
|
56
|
+
try {
|
|
57
|
+
const options = getMinificationOptions().js;
|
|
58
|
+
const result = await terserMinify(content, options);
|
|
59
|
+
|
|
60
|
+
if (result.error) {
|
|
61
|
+
throw result.error;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
65
|
+
const minifiedSize = Buffer.byteLength(result.code, 'utf8');
|
|
66
|
+
const savings = ((originalSize - minifiedSize) / originalSize * 100).toFixed(1);
|
|
67
|
+
|
|
68
|
+
Print.minificationResult(filename, originalSize, minifiedSize, savings);
|
|
69
|
+
|
|
70
|
+
return result.code;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
Print.error(`Error minifying ${filename}: ${error.message}`);
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Minifica un archivo CSS
|
|
79
|
+
*/
|
|
80
|
+
async function minifyCSS(content, filename) {
|
|
81
|
+
try {
|
|
82
|
+
const cleanCSS = new CleanCSS(getMinificationOptions().css);
|
|
83
|
+
const result = cleanCSS.minify(content);
|
|
84
|
+
|
|
85
|
+
if (result.errors && result.errors.length > 0) {
|
|
86
|
+
throw new Error(result.errors.join(', '));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
90
|
+
const minifiedSize = Buffer.byteLength(result.styles, 'utf8');
|
|
91
|
+
const savings = ((originalSize - minifiedSize) / originalSize * 100).toFixed(1);
|
|
92
|
+
|
|
93
|
+
Print.minificationResult(filename, originalSize, minifiedSize, savings);
|
|
94
|
+
|
|
95
|
+
return result.styles;
|
|
96
|
+
} catch (error) {
|
|
97
|
+
Print.error(`Error minifying ${filename}: ${error.message}`);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Minifica un archivo HTML
|
|
104
|
+
*/
|
|
105
|
+
async function minifyHTML(content, filename) {
|
|
106
|
+
try {
|
|
107
|
+
const result = await htmlMinifier.minify(content, getMinificationOptions().html);
|
|
108
|
+
|
|
109
|
+
const originalSize = Buffer.byteLength(content, 'utf8');
|
|
110
|
+
const minifiedSize = Buffer.byteLength(result, 'utf8');
|
|
111
|
+
const savings = ((originalSize - minifiedSize) / originalSize * 100).toFixed(1);
|
|
112
|
+
|
|
113
|
+
Print.minificationResult(filename, originalSize, minifiedSize, savings);
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
} catch (error) {
|
|
117
|
+
Print.error(`Error minifying ${filename}: ${error.message}`);
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Procesa un archivo según su extensión
|
|
124
|
+
*/
|
|
125
|
+
async function processFile(srcPath, destPath, relativePath) {
|
|
126
|
+
try {
|
|
127
|
+
const content = await fs.readFile(srcPath, 'utf8');
|
|
128
|
+
const ext = path.extname(srcPath).toLowerCase();
|
|
129
|
+
let processedContent = content;
|
|
130
|
+
|
|
131
|
+
switch (ext) {
|
|
132
|
+
case '.js':
|
|
133
|
+
processedContent = await minifyJavaScript(content, relativePath);
|
|
134
|
+
break;
|
|
135
|
+
case '.css':
|
|
136
|
+
processedContent = await minifyCSS(content, relativePath);
|
|
137
|
+
break;
|
|
138
|
+
case '.html':
|
|
139
|
+
processedContent = await minifyHTML(content, relativePath);
|
|
140
|
+
break;
|
|
141
|
+
default:
|
|
142
|
+
// Para otros archivos (JSON, etc.), solo copiar
|
|
143
|
+
await fs.copy(srcPath, destPath);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await fs.writeFile(destPath, processedContent, 'utf8');
|
|
148
|
+
|
|
149
|
+
} catch (error) {
|
|
150
|
+
Print.error(`Error processing ${relativePath}: ${error.message}`);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Copia y procesa recursivamente todos los archivos de src a dist
|
|
157
|
+
*/
|
|
158
|
+
async function processDirectory(srcDir, distDir, baseSrcDir) {
|
|
159
|
+
const items = await fs.readdir(srcDir);
|
|
160
|
+
|
|
161
|
+
for (const item of items) {
|
|
162
|
+
const srcPath = path.join(srcDir, item);
|
|
163
|
+
const destPath = path.join(distDir, item);
|
|
164
|
+
const relativePath = path.relative(baseSrcDir, srcPath);
|
|
165
|
+
|
|
166
|
+
const stat = await fs.stat(srcPath);
|
|
167
|
+
|
|
168
|
+
if (stat.isDirectory()) {
|
|
169
|
+
await fs.ensureDir(destPath);
|
|
170
|
+
await processDirectory(srcPath, destPath, baseSrcDir);
|
|
171
|
+
} else {
|
|
172
|
+
const ext = path.extname(srcPath).toLowerCase();
|
|
173
|
+
|
|
174
|
+
// Procesar archivos que pueden ser minificados
|
|
175
|
+
if (['.js', '.css', '.html'].includes(ext)) {
|
|
176
|
+
await processFile(srcPath, destPath, relativePath);
|
|
177
|
+
} else {
|
|
178
|
+
// Copiar otros archivos sin modificar
|
|
179
|
+
await fs.copy(srcPath, destPath);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Crea un bundle optimizado del archivo principal Slice.js
|
|
187
|
+
*/
|
|
188
|
+
async function createOptimizedBundle() {
|
|
189
|
+
try {
|
|
190
|
+
Print.info('Creating optimized Slice.js bundle...');
|
|
191
|
+
|
|
192
|
+
const slicePath = path.join(__dirname, '../../../../src/Slice/Slice.js');
|
|
193
|
+
const distSlicePath = path.join(__dirname, '../../../../dist/Slice/Slice.js');
|
|
194
|
+
|
|
195
|
+
if (!await fs.pathExists(slicePath)) {
|
|
196
|
+
Print.warning('Slice.js main file not found, skipping bundle optimization');
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const content = await fs.readFile(slicePath, 'utf8');
|
|
201
|
+
const minifiedContent = await minifyJavaScript(content, 'Slice/Slice.js');
|
|
202
|
+
|
|
203
|
+
await fs.ensureDir(path.dirname(distSlicePath));
|
|
204
|
+
await fs.writeFile(distSlicePath, minifiedContent, 'utf8');
|
|
205
|
+
|
|
206
|
+
Print.success('Optimized Slice.js bundle created');
|
|
207
|
+
|
|
208
|
+
} catch (error) {
|
|
209
|
+
Print.error(`Error creating optimized bundle: ${error.message}`);
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Copia sliceConfig.json sin modificaciones
|
|
216
|
+
*/
|
|
217
|
+
async function copySliceConfig() {
|
|
218
|
+
try {
|
|
219
|
+
const srcConfigPath = path.join(__dirname, '../../../../src/sliceConfig.json');
|
|
220
|
+
const distConfigPath = path.join(__dirname, '../../../../dist/sliceConfig.json');
|
|
221
|
+
|
|
222
|
+
if (await fs.pathExists(srcConfigPath)) {
|
|
223
|
+
await fs.copy(srcConfigPath, distConfigPath);
|
|
224
|
+
Print.success('sliceConfig.json copied to dist');
|
|
225
|
+
} else {
|
|
226
|
+
Print.warning('sliceConfig.json not found in src, skipping copy');
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
Print.error(`Error copying sliceConfig.json: ${error.message}`);
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Genera estadísticas del build
|
|
236
|
+
*/
|
|
237
|
+
async function generateBuildStats(srcDir, distDir) {
|
|
238
|
+
try {
|
|
239
|
+
Print.info('Generating build statistics...');
|
|
240
|
+
|
|
241
|
+
const calculateDirSize = async (dir) => {
|
|
242
|
+
let totalSize = 0;
|
|
243
|
+
const files = await fs.readdir(dir, { withFileTypes: true });
|
|
244
|
+
|
|
245
|
+
for (const file of files) {
|
|
246
|
+
const filePath = path.join(dir, file.name);
|
|
247
|
+
if (file.isDirectory()) {
|
|
248
|
+
totalSize += await calculateDirSize(filePath);
|
|
249
|
+
} else {
|
|
250
|
+
const stats = await fs.stat(filePath);
|
|
251
|
+
totalSize += stats.size;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return totalSize;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const srcSize = await calculateDirSize(srcDir);
|
|
258
|
+
const distSize = await calculateDirSize(distDir);
|
|
259
|
+
const savings = ((srcSize - distSize) / srcSize * 100).toFixed(1);
|
|
260
|
+
|
|
261
|
+
Print.newLine();
|
|
262
|
+
Print.title('📊 Build Statistics');
|
|
263
|
+
console.log(`📁 Source size: ${(srcSize / 1024).toFixed(1)} KB`);
|
|
264
|
+
console.log(`📦 Production size: ${(distSize / 1024).toFixed(1)} KB`);
|
|
265
|
+
console.log(`💾 Size reduction: ${savings}% saved`);
|
|
266
|
+
|
|
267
|
+
} catch (error) {
|
|
268
|
+
Print.warning(`Could not generate build statistics: ${error.message}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Función principal de build para producción
|
|
274
|
+
*/
|
|
275
|
+
export default async function buildProduction(options = {}) {
|
|
276
|
+
const startTime = Date.now();
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
Print.title('🚀 Building Slice.js project for production...');
|
|
280
|
+
Print.newLine();
|
|
281
|
+
|
|
282
|
+
// Verificar que existe src
|
|
283
|
+
const srcDir = path.join(__dirname, '../../../../src');
|
|
284
|
+
const distDir = path.join(__dirname, '../../../../dist');
|
|
285
|
+
|
|
286
|
+
if (!await fs.pathExists(srcDir)) {
|
|
287
|
+
throw new Error('src directory not found. Run "slice init" first.');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 1. Limpiar directorio dist
|
|
291
|
+
if (await fs.pathExists(distDir)) {
|
|
292
|
+
if (!options.skipClean) {
|
|
293
|
+
Print.info('Cleaning previous build...');
|
|
294
|
+
await fs.remove(distDir);
|
|
295
|
+
Print.success('Previous build cleaned');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
await fs.ensureDir(distDir);
|
|
300
|
+
|
|
301
|
+
// 2. Copiar sliceConfig.json sin modificaciones
|
|
302
|
+
await copySliceConfig();
|
|
303
|
+
|
|
304
|
+
// 3. Procesar todos los archivos de src
|
|
305
|
+
Print.info('Processing and minifying source files...');
|
|
306
|
+
await processDirectory(srcDir, distDir, srcDir);
|
|
307
|
+
Print.success('All source files processed and optimized');
|
|
308
|
+
|
|
309
|
+
// 4. Crear bundle optimizado del archivo principal
|
|
310
|
+
await createOptimizedBundle();
|
|
311
|
+
|
|
312
|
+
// 5. Generar estadísticas
|
|
313
|
+
await generateBuildStats(srcDir, distDir);
|
|
314
|
+
|
|
315
|
+
// 6. Tiempo total
|
|
316
|
+
const buildTime = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
317
|
+
|
|
318
|
+
Print.newLine();
|
|
319
|
+
Print.success(`✨ Production build completed in ${buildTime}s`);
|
|
320
|
+
Print.info('Your optimized project is ready in the /dist directory');
|
|
321
|
+
Print.newLine();
|
|
322
|
+
Print.info('Next steps:');
|
|
323
|
+
console.log(' • The same /api folder serves both development and production');
|
|
324
|
+
console.log(' • Update your Express server to serve from /dist instead of /src');
|
|
325
|
+
console.log(' • Deploy both /api and /dist directories to your hosting provider');
|
|
326
|
+
console.log(' • Use "slice build --serve" to preview the production build locally');
|
|
327
|
+
|
|
328
|
+
return true;
|
|
329
|
+
|
|
330
|
+
} catch (error) {
|
|
331
|
+
Print.error(`Build failed: ${error.message}`);
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Servidor de desarrollo para testing del build de producción
|
|
338
|
+
* Usa Express como el servidor principal pero sirviendo desde /dist
|
|
339
|
+
*/
|
|
340
|
+
export async function serveProductionBuild(port = 3001) {
|
|
341
|
+
try {
|
|
342
|
+
const distDir = path.join(__dirname, '../../../../dist');
|
|
343
|
+
|
|
344
|
+
if (!await fs.pathExists(distDir)) {
|
|
345
|
+
throw new Error('No production build found. Run "slice build" first.');
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
Print.info(`Starting production build server on port ${port}...`);
|
|
349
|
+
|
|
350
|
+
// Implementar servidor estático simple que simula el comportamiento de la API
|
|
351
|
+
const express = await import('express');
|
|
352
|
+
const app = express.default();
|
|
353
|
+
|
|
354
|
+
// Servir archivos estáticos desde dist (equivalente a lo que hace la API con src)
|
|
355
|
+
app.use(express.default.static(distDir));
|
|
356
|
+
|
|
357
|
+
// SPA fallback - servir index.html para rutas no encontradas
|
|
358
|
+
app.get('*', (req, res) => {
|
|
359
|
+
const indexPath = path.join(distDir, 'index.html');
|
|
360
|
+
if (fs.existsSync(indexPath)) {
|
|
361
|
+
res.sendFile(indexPath);
|
|
362
|
+
} else {
|
|
363
|
+
res.status(404).send('Production build not found');
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
app.listen(port, () => {
|
|
368
|
+
Print.success(`Production build server running at http://localhost:${port}`);
|
|
369
|
+
Print.info('Press Ctrl+C to stop the server');
|
|
370
|
+
Print.info('This server simulates production environment using /dist files');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
} catch (error) {
|
|
374
|
+
Print.error(`Error starting production server: ${error.message}`);
|
|
375
|
+
throw error;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Comando build con opciones
|
|
381
|
+
*/
|
|
382
|
+
export async function buildCommand(options = {}) {
|
|
383
|
+
// Verificar dependencias necesarias
|
|
384
|
+
if (!await checkBuildDependencies()) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (options.serve) {
|
|
389
|
+
// Solo servir build existente
|
|
390
|
+
await serveProductionBuild(options.port);
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (options.analyze) {
|
|
395
|
+
// Analizar build sin construir
|
|
396
|
+
await analyzeBuild();
|
|
397
|
+
return true;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Build completo
|
|
401
|
+
const success = await buildProduction(options);
|
|
402
|
+
|
|
403
|
+
if (success && options.preview) {
|
|
404
|
+
Print.newLine();
|
|
405
|
+
Print.info('Starting preview server...');
|
|
406
|
+
setTimeout(() => {
|
|
407
|
+
serveProductionBuild(options.port);
|
|
408
|
+
}, 1000);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return success;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Verifica que las dependencias de build estén instaladas
|
|
416
|
+
*/
|
|
417
|
+
async function checkBuildDependencies() {
|
|
418
|
+
try {
|
|
419
|
+
Print.info('Checking build dependencies...');
|
|
420
|
+
|
|
421
|
+
const packageJsonPath = path.join(__dirname, '../../../../package.json');
|
|
422
|
+
|
|
423
|
+
if (!await fs.pathExists(packageJsonPath)) {
|
|
424
|
+
throw new Error('package.json not found');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
428
|
+
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
|
|
429
|
+
|
|
430
|
+
const requiredDeps = ['terser', 'clean-css', 'html-minifier-terser'];
|
|
431
|
+
const missing = requiredDeps.filter(dep => !deps[dep]);
|
|
432
|
+
|
|
433
|
+
if (missing.length > 0) {
|
|
434
|
+
Print.error('Missing build dependencies:');
|
|
435
|
+
missing.forEach(dep => console.log(` • ${dep}`));
|
|
436
|
+
Print.newLine();
|
|
437
|
+
Print.info('Install missing dependencies:');
|
|
438
|
+
console.log(`npm install --save-dev ${missing.join(' ')}`);
|
|
439
|
+
return false;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
Print.success('All build dependencies are installed');
|
|
443
|
+
return true;
|
|
444
|
+
|
|
445
|
+
} catch (error) {
|
|
446
|
+
Print.error(`Error checking dependencies: ${error.message}`);
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Analiza el tamaño y composición del build
|
|
453
|
+
*/
|
|
454
|
+
async function analyzeBuild() {
|
|
455
|
+
try {
|
|
456
|
+
const distDir = path.join(__dirname, '../../../../dist');
|
|
457
|
+
|
|
458
|
+
if (!await fs.pathExists(distDir)) {
|
|
459
|
+
throw new Error('No production build found. Run "slice build" first.');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
Print.title('📊 Build Analysis');
|
|
463
|
+
Print.newLine();
|
|
464
|
+
|
|
465
|
+
const analyzeDirectory = async (dir, prefix = '') => {
|
|
466
|
+
const items = await fs.readdir(dir);
|
|
467
|
+
let totalSize = 0;
|
|
468
|
+
|
|
469
|
+
for (const item of items) {
|
|
470
|
+
const itemPath = path.join(dir, item);
|
|
471
|
+
const stat = await fs.stat(itemPath);
|
|
472
|
+
|
|
473
|
+
if (stat.isDirectory()) {
|
|
474
|
+
const dirSize = await analyzeDirectory(itemPath, `${prefix}${item}/`);
|
|
475
|
+
totalSize += dirSize;
|
|
476
|
+
} else {
|
|
477
|
+
const size = (stat.size / 1024).toFixed(1);
|
|
478
|
+
console.log(`📄 ${prefix}${item}: ${size} KB`);
|
|
479
|
+
totalSize += stat.size;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
return totalSize;
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const totalSize = await analyzeDirectory(distDir);
|
|
487
|
+
Print.newLine();
|
|
488
|
+
Print.info(`Total build size: ${(totalSize / 1024).toFixed(1)} KB`);
|
|
489
|
+
|
|
490
|
+
} catch (error) {
|
|
491
|
+
Print.error(`Error analyzing build: ${error.message}`);
|
|
492
|
+
}
|
|
493
|
+
}
|
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 };
|
package/package.json
CHANGED
package/post.js
CHANGED
|
@@ -24,7 +24,9 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
24
24
|
|
|
25
25
|
// Main project commands
|
|
26
26
|
projectPackageJson.scripts['slice:init'] = 'node node_modules/slicejs-cli/client.js init';
|
|
27
|
-
projectPackageJson.scripts['slice:
|
|
27
|
+
projectPackageJson.scripts['slice:dev'] = 'node node_modules/slicejs-cli/client.js dev';
|
|
28
|
+
projectPackageJson.scripts['slice:start'] = 'node node_modules/slicejs-cli/client.js start';
|
|
29
|
+
projectPackageJson.scripts['slice:build'] = 'node node_modules/slicejs-cli/client.js build';
|
|
28
30
|
projectPackageJson.scripts['slice:version'] = 'node node_modules/slicejs-cli/client.js version';
|
|
29
31
|
projectPackageJson.scripts['slice:update'] = 'node node_modules/slicejs-cli/client.js update';
|
|
30
32
|
|
|
@@ -32,7 +34,7 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
32
34
|
projectPackageJson.scripts['slice:create'] = 'node node_modules/slicejs-cli/client.js component create';
|
|
33
35
|
projectPackageJson.scripts['slice:list'] = 'node node_modules/slicejs-cli/client.js component list';
|
|
34
36
|
projectPackageJson.scripts['slice:delete'] = 'node node_modules/slicejs-cli/client.js component delete';
|
|
35
|
-
|
|
37
|
+
|
|
36
38
|
// Main repository commands (most used shortcuts)
|
|
37
39
|
projectPackageJson.scripts['slice:get'] = 'node node_modules/slicejs-cli/client.js get';
|
|
38
40
|
projectPackageJson.scripts['slice:browse'] = 'node node_modules/slicejs-cli/client.js browse';
|
|
@@ -43,9 +45,14 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
43
45
|
projectPackageJson.scripts['slice:registry-list'] = 'node node_modules/slicejs-cli/client.js registry list';
|
|
44
46
|
projectPackageJson.scripts['slice:registry-sync'] = 'node node_modules/slicejs-cli/client.js registry sync';
|
|
45
47
|
|
|
48
|
+
// Build-related commands
|
|
49
|
+
projectPackageJson.scripts['slice:build-serve'] = 'node node_modules/slicejs-cli/client.js build --serve';
|
|
50
|
+
projectPackageJson.scripts['slice:build-preview'] = 'node node_modules/slicejs-cli/client.js build --preview';
|
|
51
|
+
projectPackageJson.scripts['slice:build-analyze'] = 'node node_modules/slicejs-cli/client.js build --analyze';
|
|
52
|
+
|
|
46
53
|
// Legacy/compatibility commands
|
|
47
54
|
projectPackageJson.scripts['run'] = 'node api/index.js';
|
|
48
|
-
projectPackageJson.scripts['development'] = 'node
|
|
55
|
+
projectPackageJson.scripts['development'] = 'node node_modules/slicejs-cli/client.js dev';
|
|
49
56
|
|
|
50
57
|
// Module configuration
|
|
51
58
|
projectPackageJson.type = 'module';
|
|
@@ -58,21 +65,32 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
58
65
|
})
|
|
59
66
|
.then(() => {
|
|
60
67
|
console.log('✅ SliceJS CLI commands added to package.json');
|
|
61
|
-
console.log('\n🚀 Main commands:');
|
|
68
|
+
console.log('\n🚀 Main workflow commands:');
|
|
62
69
|
console.log(' npm run slice:init - Initialize Slice.js project');
|
|
70
|
+
console.log(' npm run slice:dev - Start development server (serves from /src)');
|
|
71
|
+
console.log(' npm run slice:build - Build for production (creates /dist)');
|
|
72
|
+
console.log(' npm run slice:start - Start production server (serves from /dist)');
|
|
73
|
+
console.log('\n📦 Component management:');
|
|
63
74
|
console.log(' npm run slice:get Button - Get components from official repository');
|
|
64
75
|
console.log(' npm run slice:browse - View all available components');
|
|
65
76
|
console.log(' npm run slice:sync - Update local components to latest versions');
|
|
66
|
-
console.log(' npm run slice:start - Start development server');
|
|
67
77
|
console.log('\n⚙️ Local component management:');
|
|
68
78
|
console.log(' npm run slice:create - Create local component');
|
|
69
79
|
console.log(' npm run slice:list - List local components');
|
|
70
80
|
console.log(' npm run slice:delete - Delete local component');
|
|
71
|
-
console.log('\n🔧
|
|
81
|
+
console.log('\n🔧 Build utilities:');
|
|
82
|
+
console.log(' npm run slice:build-serve - Build and serve immediately');
|
|
83
|
+
console.log(' npm run slice:build-preview- Build and preview');
|
|
84
|
+
console.log(' npm run slice:build-analyze- Analyze build size');
|
|
85
|
+
console.log('\n🔧 Other utilities:');
|
|
72
86
|
console.log(' npm run slice:version - View version information');
|
|
73
87
|
console.log(' npm run slice:update - Check for available updates');
|
|
74
|
-
console.log('\n🎯
|
|
75
|
-
console.log('
|
|
88
|
+
console.log('\n🎯 Development workflow:');
|
|
89
|
+
console.log(' 1. npm run slice:init - Initialize project');
|
|
90
|
+
console.log(' 2. npm run slice:dev - Develop with hot reload');
|
|
91
|
+
console.log(' 3. npm run slice:build - Build for production');
|
|
92
|
+
console.log(' 4. npm run slice:start - Test production build');
|
|
93
|
+
console.log('\n💡 Tip: Use "slice:sync" to keep your components updated');
|
|
76
94
|
})
|
|
77
95
|
.catch(err => {
|
|
78
96
|
if (err.code === 'ENOENT') {
|
|
@@ -83,9 +101,11 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
83
101
|
description: 'Slice.js project',
|
|
84
102
|
main: 'api/index.js',
|
|
85
103
|
scripts: {
|
|
86
|
-
// Main commands
|
|
104
|
+
// Main workflow commands
|
|
87
105
|
'slice:init': 'node node_modules/slicejs-cli/client.js init',
|
|
88
|
-
'slice:
|
|
106
|
+
'slice:dev': 'node node_modules/slicejs-cli/client.js dev',
|
|
107
|
+
'slice:start': 'node node_modules/slicejs-cli/client.js start',
|
|
108
|
+
'slice:build': 'node node_modules/slicejs-cli/client.js build',
|
|
89
109
|
'slice:version': 'node node_modules/slicejs-cli/client.js version',
|
|
90
110
|
'slice:update': 'node node_modules/slicejs-cli/client.js update',
|
|
91
111
|
|
|
@@ -104,8 +124,14 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
104
124
|
'slice:registry-list': 'node node_modules/slicejs-cli/client.js registry list',
|
|
105
125
|
'slice:registry-sync': 'node node_modules/slicejs-cli/client.js registry sync',
|
|
106
126
|
|
|
127
|
+
// Build utilities
|
|
128
|
+
'slice:build-serve': 'node node_modules/slicejs-cli/client.js build --serve',
|
|
129
|
+
'slice:build-preview': 'node node_modules/slicejs-cli/client.js build --preview',
|
|
130
|
+
'slice:build-analyze': 'node node_modules/slicejs-cli/client.js build --analyze',
|
|
131
|
+
|
|
107
132
|
// Legacy
|
|
108
|
-
'run': 'node api/index.js'
|
|
133
|
+
'run': 'node api/index.js',
|
|
134
|
+
'development': 'node node_modules/slicejs-cli/client.js dev'
|
|
109
135
|
},
|
|
110
136
|
keywords: ['slicejs', 'web-framework', 'components'],
|
|
111
137
|
author: '',
|
|
@@ -125,21 +151,32 @@ fs.promises.access(projectPackageJsonPath, fs.constants.F_OK)
|
|
|
125
151
|
})
|
|
126
152
|
.then(() => {
|
|
127
153
|
console.log('✅ Created package.json with SliceJS CLI commands.');
|
|
128
|
-
console.log('\n🚀 Main commands:');
|
|
154
|
+
console.log('\n🚀 Main workflow commands:');
|
|
129
155
|
console.log(' npm run slice:init - Initialize Slice.js project');
|
|
156
|
+
console.log(' npm run slice:dev - Start development server (serves from /src)');
|
|
157
|
+
console.log(' npm run slice:build - Build for production (creates /dist)');
|
|
158
|
+
console.log(' npm run slice:start - Start production server (serves from /dist)');
|
|
159
|
+
console.log('\n📦 Component management:');
|
|
130
160
|
console.log(' npm run slice:get Button - Get components from official repository');
|
|
131
161
|
console.log(' npm run slice:browse - View all available components');
|
|
132
162
|
console.log(' npm run slice:sync - Update local components to latest versions');
|
|
133
|
-
console.log(' npm run slice:start - Start development server');
|
|
134
163
|
console.log('\n⚙️ Local component management:');
|
|
135
164
|
console.log(' npm run slice:create - Create local component');
|
|
136
165
|
console.log(' npm run slice:list - List local components');
|
|
137
166
|
console.log(' npm run slice:delete - Delete local component');
|
|
138
|
-
console.log('\n🔧
|
|
167
|
+
console.log('\n🔧 Build utilities:');
|
|
168
|
+
console.log(' npm run slice:build-serve - Build and serve immediately');
|
|
169
|
+
console.log(' npm run slice:build-preview- Build and preview');
|
|
170
|
+
console.log(' npm run slice:build-analyze- Analyze build size');
|
|
171
|
+
console.log('\n🔧 Other utilities:');
|
|
139
172
|
console.log(' npm run slice:version - View version information');
|
|
140
173
|
console.log(' npm run slice:update - Check for available updates');
|
|
141
|
-
console.log('\n🎯
|
|
142
|
-
console.log('
|
|
174
|
+
console.log('\n🎯 Development workflow:');
|
|
175
|
+
console.log(' 1. npm run slice:init - Initialize project');
|
|
176
|
+
console.log(' 2. npm run slice:dev - Develop with hot reload');
|
|
177
|
+
console.log(' 3. npm run slice:build - Build for production');
|
|
178
|
+
console.log(' 4. npm run slice:start - Test production build');
|
|
179
|
+
console.log('\n💡 Tip: Use "slice:sync" to keep your components updated');
|
|
143
180
|
})
|
|
144
181
|
.catch(err => {
|
|
145
182
|
console.error('Error creating package.json:', err);
|