mgpanel-cli 1.0.1 → 1.0.3
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/README.md +5 -1
- package/bin/mgpanel.js +604 -20
- package/package.json +1 -1
package/README.md
CHANGED
package/bin/mgpanel.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const http = require("http");
|
|
3
4
|
const fs = require("fs");
|
|
4
5
|
const path = require("path");
|
|
5
6
|
|
|
@@ -9,11 +10,24 @@ MGPanel CLI
|
|
|
9
10
|
|
|
10
11
|
Uso:
|
|
11
12
|
mgpanel init <nombre-proyecto>
|
|
13
|
+
|
|
14
|
+
mgpanel make page <nombre-pagina>
|
|
12
15
|
mgpanel make module <nombre-modulo>
|
|
13
16
|
|
|
17
|
+
mgpanel add module <nombre-modulo> --to <nombre-pagina>
|
|
18
|
+
|
|
19
|
+
mgpanel dev [puerto]
|
|
20
|
+
|
|
14
21
|
Ejemplos:
|
|
15
22
|
mgpanel init miweb
|
|
16
|
-
|
|
23
|
+
cd miweb
|
|
24
|
+
|
|
25
|
+
mgpanel make page home
|
|
26
|
+
mgpanel make module header
|
|
27
|
+
mgpanel add module header --to home
|
|
28
|
+
|
|
29
|
+
mgpanel dev
|
|
30
|
+
mgpanel dev 8080
|
|
17
31
|
`);
|
|
18
32
|
}
|
|
19
33
|
|
|
@@ -27,6 +41,19 @@ function writeFileIfNotExists(filePath, content) {
|
|
|
27
41
|
return true;
|
|
28
42
|
}
|
|
29
43
|
|
|
44
|
+
function readJSON(filePath) {
|
|
45
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
46
|
+
return JSON.parse(raw);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function writeJSON(filePath, obj) {
|
|
50
|
+
fs.writeFileSync(filePath, JSON.stringify(obj, null, 2), "utf8");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isValidSlug(name) {
|
|
54
|
+
return /^[a-z0-9-]+$/i.test(name);
|
|
55
|
+
}
|
|
56
|
+
|
|
30
57
|
/**
|
|
31
58
|
* mgpanel init <projectName>
|
|
32
59
|
*/
|
|
@@ -40,27 +67,460 @@ function cmdInit(projectName = "mgpanel-project") {
|
|
|
40
67
|
|
|
41
68
|
console.log("🚀 Creando proyecto MGPanel:", projectName);
|
|
42
69
|
|
|
70
|
+
// base
|
|
43
71
|
ensureDir(projectPath);
|
|
72
|
+
ensureDir(path.join(projectPath, "pages"));
|
|
44
73
|
ensureDir(path.join(projectPath, "modules"));
|
|
74
|
+
ensureDir(path.join(projectPath, "dev"));
|
|
45
75
|
ensureDir(path.join(projectPath, "assets", "css"));
|
|
46
76
|
ensureDir(path.join(projectPath, "assets", "js"));
|
|
47
77
|
|
|
78
|
+
// Global CSS/JS
|
|
79
|
+
const globalCSS = `/* MGPanel Global Styles */
|
|
80
|
+
:root { color-scheme: light; }
|
|
81
|
+
body { margin: 0; font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; }
|
|
82
|
+
`;
|
|
83
|
+
fs.writeFileSync(path.join(projectPath, "assets", "css", "style.css"), globalCSS, "utf8");
|
|
84
|
+
|
|
85
|
+
const globalJS = `// MGPanel Global JS
|
|
86
|
+
console.log("[MGPanel] Global JS loaded");
|
|
87
|
+
`;
|
|
88
|
+
fs.writeFileSync(path.join(projectPath, "assets", "js", "app.js"), globalJS, "utf8");
|
|
89
|
+
|
|
90
|
+
// index.html solo preview local
|
|
48
91
|
const indexHTML = `<!doctype html>
|
|
49
92
|
<html lang="es">
|
|
50
93
|
<head>
|
|
51
94
|
<meta charset="utf-8" />
|
|
52
95
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
53
|
-
<title>MGPanel</title>
|
|
96
|
+
<title>MGPanel (Local Preview)</title>
|
|
97
|
+
|
|
98
|
+
<link rel="stylesheet" href="assets/css/style.css" />
|
|
99
|
+
<script src="assets/js/app.js" defer></script>
|
|
54
100
|
</head>
|
|
55
101
|
<body>
|
|
56
|
-
<
|
|
102
|
+
<div id="app"></div>
|
|
103
|
+
<script src="dev/preview.js"></script>
|
|
57
104
|
</body>
|
|
58
105
|
</html>
|
|
59
106
|
`;
|
|
60
|
-
|
|
61
107
|
fs.writeFileSync(path.join(projectPath, "index.html"), indexHTML, "utf8");
|
|
62
108
|
|
|
109
|
+
// dev/preview.js: carga una page leyendo page.json y componiendo módulos
|
|
110
|
+
const previewJS = `async function loadModule(name) {
|
|
111
|
+
const base = \`modules/\${name}/\${name}\`;
|
|
112
|
+
|
|
113
|
+
// HTML
|
|
114
|
+
const html = await fetch(\`\${base}.html\`).then(r => r.text());
|
|
115
|
+
|
|
116
|
+
// CSS
|
|
117
|
+
const cssHref = \`\${base}.css\`;
|
|
118
|
+
if (!document.querySelector(\`link[data-mgpanel-css="\${cssHref}"]\`)) {
|
|
119
|
+
const link = document.createElement("link");
|
|
120
|
+
link.rel = "stylesheet";
|
|
121
|
+
link.href = cssHref;
|
|
122
|
+
link.setAttribute("data-mgpanel-css", cssHref);
|
|
123
|
+
document.head.appendChild(link);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// JS
|
|
127
|
+
const jsSrc = \`\${base}.js\`;
|
|
128
|
+
if (!document.querySelector(\`script[data-mgpanel-js="\${jsSrc}"]\`)) {
|
|
129
|
+
const script = document.createElement("script");
|
|
130
|
+
script.src = jsSrc;
|
|
131
|
+
script.defer = true;
|
|
132
|
+
script.setAttribute("data-mgpanel-js", jsSrc);
|
|
133
|
+
document.body.appendChild(script);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return html;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async function renderPage(pageName) {
|
|
140
|
+
const configPath = \`pages/\${pageName}/page.json\`;
|
|
141
|
+
const page = await fetch(configPath).then(r => r.json());
|
|
142
|
+
|
|
143
|
+
// Title
|
|
144
|
+
if (page.title) document.title = page.title;
|
|
145
|
+
|
|
146
|
+
// Meta description
|
|
147
|
+
if (page.description) {
|
|
148
|
+
let meta = document.querySelector('meta[name="description"]');
|
|
149
|
+
if (!meta) {
|
|
150
|
+
meta = document.createElement("meta");
|
|
151
|
+
meta.setAttribute("name", "description");
|
|
152
|
+
document.head.appendChild(meta);
|
|
153
|
+
}
|
|
154
|
+
meta.setAttribute("content", page.description);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const app = document.getElementById("app");
|
|
158
|
+
app.innerHTML = "";
|
|
159
|
+
|
|
160
|
+
for (const item of (page.modules || [])) {
|
|
161
|
+
const html = await loadModule(item.name);
|
|
162
|
+
const wrapper = document.createElement("div");
|
|
163
|
+
wrapper.setAttribute("data-mgpanel-module", item.name);
|
|
164
|
+
wrapper.innerHTML = html;
|
|
165
|
+
app.appendChild(wrapper);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const pageFromPath = () => {
|
|
170
|
+
const p = window.location.pathname.replace(/^\\/+/g, "").replace(/\\/+$/g, "");
|
|
171
|
+
return p === "" ? "home" : p;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
renderPage(pageFromPath()).catch(err => {
|
|
175
|
+
console.error("[MGPanel Preview] Error:", err);
|
|
176
|
+
document.getElementById("app").innerHTML =
|
|
177
|
+
"<h2>Error cargando la página. ¿Existe pages/<page>/page.json?</h2>";
|
|
178
|
+
});
|
|
179
|
+
`;
|
|
180
|
+
fs.writeFileSync(path.join(projectPath, "dev", "preview.js"), previewJS, "utf8");
|
|
181
|
+
|
|
182
|
+
// Crear page home por defecto
|
|
183
|
+
ensureDir(path.join(projectPath, "pages", "home"));
|
|
184
|
+
const homePage = {
|
|
185
|
+
route: "/",
|
|
186
|
+
title: "Home",
|
|
187
|
+
description: "Página principal creada con MGPanel.",
|
|
188
|
+
modules: []
|
|
189
|
+
};
|
|
190
|
+
fs.writeFileSync(
|
|
191
|
+
path.join(projectPath, "pages", "home", "page.json"),
|
|
192
|
+
JSON.stringify(homePage, null, 2),
|
|
193
|
+
"utf8"
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// dev/config.md: Documentación para IDEs (Cursor, etc.)
|
|
197
|
+
const configMD = `# MGPanel - Configuración de Desarrollo
|
|
198
|
+
|
|
199
|
+
Esta documentación está diseñada para ayudar a IDEs como Cursor y otros editores a entender el contexto de desarrollo con MGPanel.
|
|
200
|
+
|
|
201
|
+
## ¿Qué es MGPanel?
|
|
202
|
+
|
|
203
|
+
MGPanel es un sistema de gestión de contenido (CMS) basado en Node.js y MongoDB que permite crear sitios web usando HTML, CSS y JavaScript puro, sin frameworks complejos. Los proyectos MGPanel utilizan una arquitectura modular donde las páginas se componen de módulos reutilizables.
|
|
204
|
+
|
|
205
|
+
## Estructura del Proyecto
|
|
206
|
+
|
|
207
|
+
\`\`\`
|
|
208
|
+
proyecto/
|
|
209
|
+
├── pages/ # Páginas del sitio
|
|
210
|
+
│ └── <nombre-pagina>/
|
|
211
|
+
│ └── page.json # Configuración de la página (ruta, título, módulos)
|
|
212
|
+
├── modules/ # Módulos reutilizables
|
|
213
|
+
│ └── <nombre-modulo>/
|
|
214
|
+
│ ├── <nombre-modulo>.html
|
|
215
|
+
│ ├── <nombre-modulo>.css
|
|
216
|
+
│ └── <nombre-modulo>.js
|
|
217
|
+
├── assets/ # Archivos estáticos globales
|
|
218
|
+
│ ├── css/
|
|
219
|
+
│ │ └── style.css
|
|
220
|
+
│ └── js/
|
|
221
|
+
│ └── app.js
|
|
222
|
+
├── dev/ # Utilidades de desarrollo
|
|
223
|
+
│ ├── config.md # Este archivo
|
|
224
|
+
│ └── preview.js # Script para preview local
|
|
225
|
+
└── index.html # Punto de entrada para preview local
|
|
226
|
+
\`\`\`
|
|
227
|
+
|
|
228
|
+
## Comandos Disponibles
|
|
229
|
+
|
|
230
|
+
### Crear una Página
|
|
231
|
+
|
|
232
|
+
\`\`\`bash
|
|
233
|
+
mgpanel make page <nombre-pagina>
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
**Ejemplo:**
|
|
237
|
+
\`\`\`bash
|
|
238
|
+
mgpanel make page about
|
|
239
|
+
mgpanel make page contacto
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
Esto crea:
|
|
243
|
+
- \`pages/<nombre-pagina>/page.json\` - Archivo de configuración de la página
|
|
244
|
+
|
|
245
|
+
**Estructura de page.json:**
|
|
246
|
+
\`\`\`json
|
|
247
|
+
{
|
|
248
|
+
"route": "/about",
|
|
249
|
+
"title": "About",
|
|
250
|
+
"description": "Descripción de la página about.",
|
|
251
|
+
"modules": []
|
|
252
|
+
}
|
|
253
|
+
\`\`\`
|
|
254
|
+
|
|
255
|
+
### Crear un Módulo
|
|
256
|
+
|
|
257
|
+
\`\`\`bash
|
|
258
|
+
mgpanel make module <nombre-modulo>
|
|
259
|
+
\`\`\`
|
|
260
|
+
|
|
261
|
+
**Ejemplo:**
|
|
262
|
+
\`\`\`bash
|
|
263
|
+
mgpanel make module header
|
|
264
|
+
mgpanel make module hero-banner
|
|
265
|
+
mgpanel make module footer
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
Esto crea en \`modules/<nombre-modulo>/\`:
|
|
269
|
+
- \`<nombre-modulo>.html\` - Estructura HTML del módulo
|
|
270
|
+
- \`<nombre-modulo>.css\` - Estilos CSS del módulo
|
|
271
|
+
- \`<nombre-modulo>.js\` - Lógica JavaScript del módulo
|
|
272
|
+
|
|
273
|
+
### Asociar un Módulo a una Página
|
|
274
|
+
|
|
275
|
+
\`\`\`bash
|
|
276
|
+
mgpanel add module <nombre-modulo> --to <nombre-pagina>
|
|
277
|
+
\`\`\`
|
|
278
|
+
|
|
279
|
+
**Ejemplo:**
|
|
280
|
+
\`\`\`bash
|
|
281
|
+
mgpanel add module header --to home
|
|
282
|
+
mgpanel add module hero-banner --to home
|
|
283
|
+
mgpanel add module footer --to home
|
|
284
|
+
\`\`\`
|
|
285
|
+
|
|
286
|
+
Esto actualiza el archivo \`pages/<nombre-pagina>/page.json\` añadiendo el módulo al array \`modules\`:
|
|
287
|
+
|
|
288
|
+
\`\`\`json
|
|
289
|
+
{
|
|
290
|
+
"route": "/",
|
|
291
|
+
"title": "Home",
|
|
292
|
+
"description": "Página principal.",
|
|
293
|
+
"modules": [
|
|
294
|
+
{ "name": "header" },
|
|
295
|
+
{ "name": "hero-banner" },
|
|
296
|
+
{ "name": "footer" }
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
\`\`\`
|
|
300
|
+
|
|
301
|
+
**Importante:** Los módulos se renderizan en el orden en que aparecen en el array \`modules\`.
|
|
302
|
+
|
|
303
|
+
### Servidor de Desarrollo
|
|
304
|
+
|
|
305
|
+
\`\`\`bash
|
|
306
|
+
mgpanel dev [puerto]
|
|
307
|
+
\`\`\`
|
|
308
|
+
|
|
309
|
+
Inicia un servidor HTTP local para previsualizar el proyecto.
|
|
310
|
+
|
|
311
|
+
- **Puerto por defecto:** 3000
|
|
312
|
+
- **Acceso:** http://localhost:3000 (o el puerto especificado)
|
|
313
|
+
|
|
314
|
+
El servidor:
|
|
315
|
+
- Sirve archivos estáticos (HTML, CSS, JS, imágenes)
|
|
316
|
+
- Implementa routing SPA: rutas sin extensión devuelven \`index.html\`
|
|
317
|
+
- Soporta hot-reload manual (refrescar el navegador)
|
|
318
|
+
|
|
319
|
+
**Ejemplos:**
|
|
320
|
+
\`\`\`bash
|
|
321
|
+
mgpanel dev # Puerto 3000
|
|
322
|
+
mgpanel dev 8080 # Puerto 8080
|
|
323
|
+
\`\`\`
|
|
324
|
+
|
|
325
|
+
## Flujo de Trabajo Recomendado
|
|
326
|
+
|
|
327
|
+
1. **Crear un módulo:**
|
|
328
|
+
\`\`\`bash
|
|
329
|
+
mgpanel make module header
|
|
330
|
+
\`\`\`
|
|
331
|
+
|
|
332
|
+
2. **Editar el módulo:**
|
|
333
|
+
- Edita \`modules/header/header.html\`
|
|
334
|
+
- Edita \`modules/header/header.css\`
|
|
335
|
+
- Edita \`modules/header/header.js\`
|
|
336
|
+
|
|
337
|
+
3. **Crear/editar una página:**
|
|
338
|
+
\`\`\`bash
|
|
339
|
+
mgpanel make page home
|
|
340
|
+
\`\`\`
|
|
341
|
+
|
|
342
|
+
4. **Asociar módulos a la página:**
|
|
343
|
+
\`\`\`bash
|
|
344
|
+
mgpanel add module header --to home
|
|
345
|
+
mgpanel add module hero --to home
|
|
346
|
+
mgpanel add module footer --to home
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
5. **Iniciar servidor de desarrollo:**
|
|
350
|
+
\`\`\`bash
|
|
351
|
+
mgpanel dev
|
|
352
|
+
\`\`\`
|
|
353
|
+
|
|
354
|
+
6. **Acceder a la página:**
|
|
355
|
+
- http://localhost:3000 (página home)
|
|
356
|
+
- http://localhost:3000/about (si existe la página about)
|
|
357
|
+
|
|
358
|
+
## Convenciones y Reglas
|
|
359
|
+
|
|
360
|
+
### Nombres de Páginas y Módulos
|
|
361
|
+
|
|
362
|
+
- Usar solo letras minúsculas, números y guiones
|
|
363
|
+
- No usar espacios ni caracteres especiales
|
|
364
|
+
- Ejemplos válidos: \`home\`, \`about-us\`, \`contacto\`, \`header\`, \`hero-banner\`
|
|
365
|
+
- Ejemplos inválidos: \`About Us\`, \`contact_page\`, \`mi@modulo\`
|
|
366
|
+
|
|
367
|
+
### Estructura de Módulos
|
|
368
|
+
|
|
369
|
+
Cada módulo debe tener exactamente 3 archivos con el mismo nombre base:
|
|
370
|
+
|
|
371
|
+
\`\`\`
|
|
372
|
+
modules/<nombre-modulo>/
|
|
373
|
+
├── <nombre-modulo>.html
|
|
374
|
+
├── <nombre-modulo>.css
|
|
375
|
+
└── <nombre-modulo>.js
|
|
376
|
+
\`\`\`
|
|
377
|
+
|
|
378
|
+
### CSS y JavaScript Globales
|
|
379
|
+
|
|
380
|
+
- **Global CSS:** \`assets/css/style.css\` - Se carga en todas las páginas
|
|
381
|
+
- **Global JS:** \`assets/js/app.js\` - Se carga en todas las páginas
|
|
382
|
+
- **Módulo CSS:** Se carga automáticamente cuando el módulo es usado
|
|
383
|
+
- **Módulo JS:** Se carga automáticamente cuando el módulo es usado
|
|
384
|
+
|
|
385
|
+
### Preview Local
|
|
386
|
+
|
|
387
|
+
El archivo \`index.html\` y \`dev/preview.js\` permiten previsualizar las páginas localmente:
|
|
388
|
+
|
|
389
|
+
- Lee \`pages/<nombre>/page.json\`
|
|
390
|
+
- Carga los módulos especificados en el orden definido
|
|
391
|
+
- Inyecta CSS y JS de cada módulo automáticamente
|
|
392
|
+
- Soporta routing basado en la URL
|
|
393
|
+
|
|
394
|
+
## Integración con MGPanel CMS
|
|
395
|
+
|
|
396
|
+
Cuando despliegues el proyecto en MGPanel:
|
|
397
|
+
|
|
398
|
+
1. Las páginas se sincronizan con el CMS
|
|
399
|
+
2. Los módulos se pueden reutilizar entre páginas
|
|
400
|
+
3. El contenido puede ser editado desde el panel de administración
|
|
401
|
+
4. La estructura de archivos se mantiene compatible
|
|
402
|
+
|
|
403
|
+
## Consejos para IDEs
|
|
404
|
+
|
|
405
|
+
- Los archivos \`page.json\` definen la configuración de cada página
|
|
406
|
+
- Los módulos son componentes independientes y reutilizables
|
|
407
|
+
- El orden de los módulos en \`page.json\` determina el orden de renderizado
|
|
408
|
+
- Usa autocompletado para referencias entre módulos y páginas
|
|
409
|
+
- Valida los nombres de módulos antes de asociarlos a páginas
|
|
410
|
+
`;
|
|
411
|
+
|
|
412
|
+
fs.writeFileSync(path.join(projectPath, "dev", "config.md"), configMD, "utf8");
|
|
413
|
+
|
|
414
|
+
// README.md en la raíz del proyecto
|
|
415
|
+
const readmeMD = `# Proyecto MGPanel
|
|
416
|
+
|
|
417
|
+
Este proyecto ha sido creado con **MGPanel CLI**, un sistema para desarrollar sitios web usando HTML, CSS y JavaScript puro, organizados de forma modular.
|
|
418
|
+
|
|
419
|
+
## 📚 Documentación de Desarrollo
|
|
420
|
+
|
|
421
|
+
Para entender completamente cómo trabajar con este proyecto, consulta la documentación completa en:
|
|
422
|
+
|
|
423
|
+
**\`dev/config.md\`**
|
|
424
|
+
|
|
425
|
+
Este archivo contiene información detallada sobre:
|
|
426
|
+
- Comandos para crear páginas y módulos
|
|
427
|
+
- Cómo asociar módulos a páginas
|
|
428
|
+
- Estructura del proyecto
|
|
429
|
+
- Flujo de trabajo recomendado
|
|
430
|
+
- Convenciones y mejores prácticas
|
|
431
|
+
|
|
432
|
+
## 🚀 Inicio Rápido
|
|
433
|
+
|
|
434
|
+
1. **Crear un módulo:**
|
|
435
|
+
\`\`\`bash
|
|
436
|
+
mgpanel make module nombre-modulo
|
|
437
|
+
\`\`\`
|
|
438
|
+
|
|
439
|
+
2. **Crear una página:**
|
|
440
|
+
\`\`\`bash
|
|
441
|
+
mgpanel make page nombre-pagina
|
|
442
|
+
\`\`\`
|
|
443
|
+
|
|
444
|
+
3. **Asociar módulo a página:**
|
|
445
|
+
\`\`\`bash
|
|
446
|
+
mgpanel add module nombre-modulo --to nombre-pagina
|
|
447
|
+
\`\`\`
|
|
448
|
+
|
|
449
|
+
4. **Iniciar servidor de desarrollo:**
|
|
450
|
+
\`\`\`bash
|
|
451
|
+
mgpanel dev
|
|
452
|
+
\`\`\`
|
|
453
|
+
|
|
454
|
+
## 📁 Estructura del Proyecto
|
|
455
|
+
|
|
456
|
+
\`\`\`
|
|
457
|
+
.
|
|
458
|
+
├── pages/ # Páginas del sitio
|
|
459
|
+
│ └── <nombre>/page.json
|
|
460
|
+
├── modules/ # Módulos reutilizables
|
|
461
|
+
│ └── <nombre>/
|
|
462
|
+
│ ├── <nombre>.html
|
|
463
|
+
│ ├── <nombre>.css
|
|
464
|
+
│ └── <nombre>.js
|
|
465
|
+
├── assets/ # Recursos globales
|
|
466
|
+
│ ├── css/style.css
|
|
467
|
+
│ └── js/app.js
|
|
468
|
+
├── dev/ # Desarrollo
|
|
469
|
+
│ ├── config.md # 📖 Documentación completa para IDEs
|
|
470
|
+
│ └── preview.js
|
|
471
|
+
└── index.html # Preview local
|
|
472
|
+
\`\`\`
|
|
473
|
+
|
|
474
|
+
## 🔗 Más Información
|
|
475
|
+
|
|
476
|
+
- **Documentación de desarrollo:** Ver \`dev/config.md\`
|
|
477
|
+
- **MGPanel CLI:** https://github.com/miguayaba/mgpanel-cli
|
|
478
|
+
`;
|
|
479
|
+
|
|
480
|
+
fs.writeFileSync(path.join(projectPath, "README.md"), readmeMD, "utf8");
|
|
481
|
+
|
|
63
482
|
console.log("✅ Proyecto creado correctamente");
|
|
483
|
+
console.log("👉 Ejecuta: cd " + projectName + " && mgpanel dev");
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* mgpanel make page <pageName>
|
|
488
|
+
*/
|
|
489
|
+
function cmdMakePage(pageName) {
|
|
490
|
+
if (!pageName) {
|
|
491
|
+
console.log("❌ Falta el nombre de la página");
|
|
492
|
+
console.log("👉 Usa: mgpanel make page <nombre-pagina>");
|
|
493
|
+
process.exit(1);
|
|
494
|
+
}
|
|
495
|
+
if (!isValidSlug(pageName)) {
|
|
496
|
+
console.log("❌ Nombre de página inválido. Usa letras/números/guiones. Ej: home, about-us");
|
|
497
|
+
process.exit(1);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const pagesDir = path.join(process.cwd(), "pages");
|
|
501
|
+
const pageDir = path.join(pagesDir, pageName);
|
|
502
|
+
ensureDir(pageDir);
|
|
503
|
+
|
|
504
|
+
const pageJsonPath = path.join(pageDir, "page.json");
|
|
505
|
+
const created = writeFileIfNotExists(
|
|
506
|
+
pageJsonPath,
|
|
507
|
+
JSON.stringify(
|
|
508
|
+
{
|
|
509
|
+
route: `/${pageName === "home" ? "" : pageName}`.replace(/\/$/, "/"),
|
|
510
|
+
title: pageName.charAt(0).toUpperCase() + pageName.slice(1),
|
|
511
|
+
description: `Descripción de la página ${pageName}.`,
|
|
512
|
+
modules: []
|
|
513
|
+
},
|
|
514
|
+
null,
|
|
515
|
+
2
|
|
516
|
+
)
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
if (created) {
|
|
520
|
+
console.log(`📄 Página "${pageName}" creada en: pages/${pageName}/page.json`);
|
|
521
|
+
} else {
|
|
522
|
+
console.log(`↩️ La página "${pageName}" ya existe: pages/${pageName}/page.json`);
|
|
523
|
+
}
|
|
64
524
|
}
|
|
65
525
|
|
|
66
526
|
/**
|
|
@@ -72,18 +532,13 @@ function cmdMakeModule(moduleName) {
|
|
|
72
532
|
console.log("👉 Usa: mgpanel make module <nombre-modulo>");
|
|
73
533
|
process.exit(1);
|
|
74
534
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const valid = /^[a-z0-9-]+$/i.test(moduleName);
|
|
78
|
-
if (!valid) {
|
|
79
|
-
console.log("❌ Nombre de módulo inválido. Usa letras/números/guiones, ej: home, about-us");
|
|
535
|
+
if (!isValidSlug(moduleName)) {
|
|
536
|
+
console.log("❌ Nombre de módulo inválido. Usa letras/números/guiones. Ej: header, hero-banner");
|
|
80
537
|
process.exit(1);
|
|
81
538
|
}
|
|
82
539
|
|
|
83
|
-
const
|
|
84
|
-
const modulesDir = path.join(cwd, "modules");
|
|
540
|
+
const modulesDir = path.join(process.cwd(), "modules");
|
|
85
541
|
const moduleDir = path.join(modulesDir, moduleName);
|
|
86
|
-
|
|
87
542
|
ensureDir(moduleDir);
|
|
88
543
|
|
|
89
544
|
const htmlPath = path.join(moduleDir, `${moduleName}.html`);
|
|
@@ -119,23 +574,152 @@ console.log("[MGPanel] Module loaded:", "${moduleName}");
|
|
|
119
574
|
if (skipped.length) console.log("↩️ Ya existían:", skipped.join(", "));
|
|
120
575
|
}
|
|
121
576
|
|
|
122
|
-
|
|
577
|
+
/**
|
|
578
|
+
* mgpanel add module <moduleName> --to <pageName>
|
|
579
|
+
*/
|
|
580
|
+
function cmdAddModule(moduleName, pageName) {
|
|
581
|
+
if (!moduleName || !pageName) {
|
|
582
|
+
console.log("❌ Uso inválido");
|
|
583
|
+
console.log("👉 Usa: mgpanel add module <modulo> --to <pagina>");
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
123
586
|
|
|
124
|
-
const
|
|
587
|
+
const moduleDir = path.join(process.cwd(), "modules", moduleName);
|
|
588
|
+
if (!fs.existsSync(moduleDir)) {
|
|
589
|
+
console.log(`❌ El módulo "${moduleName}" no existe en modules/${moduleName}/`);
|
|
590
|
+
console.log(`👉 Crea el módulo primero: mgpanel make module ${moduleName}`);
|
|
591
|
+
process.exit(1);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const pageJsonPath = path.join(process.cwd(), "pages", pageName, "page.json");
|
|
595
|
+
if (!fs.existsSync(pageJsonPath)) {
|
|
596
|
+
console.log(`❌ La página "${pageName}" no existe (pages/${pageName}/page.json)`);
|
|
597
|
+
console.log(`👉 Crea la página primero: mgpanel make page ${pageName}`);
|
|
598
|
+
process.exit(1);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const page = readJSON(pageJsonPath);
|
|
602
|
+
page.modules = Array.isArray(page.modules) ? page.modules : [];
|
|
603
|
+
|
|
604
|
+
const already = page.modules.some(m => m && m.name === moduleName);
|
|
605
|
+
if (already) {
|
|
606
|
+
console.log(`↩️ El módulo "${moduleName}" ya está agregado a la página "${pageName}".`);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
page.modules.push({ name: moduleName });
|
|
611
|
+
writeJSON(pageJsonPath, page);
|
|
612
|
+
|
|
613
|
+
console.log(`✅ Módulo "${moduleName}" agregado a pages/${pageName}/page.json`);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function cmdDev(port = 3000) {
|
|
617
|
+
const root = process.cwd();
|
|
618
|
+
|
|
619
|
+
const server = http.createServer((req, res) => {
|
|
620
|
+
// 1) Normalizar URL (sin querystring)
|
|
621
|
+
let urlPath = req.url || "/";
|
|
622
|
+
urlPath = decodeURIComponent(urlPath.split("?")[0]);
|
|
623
|
+
|
|
624
|
+
// 2) Seguridad básica: bloquear path traversal
|
|
625
|
+
if (urlPath.includes("..")) {
|
|
626
|
+
res.writeHead(400, { "Content-Type": "text/plain" });
|
|
627
|
+
res.end("400 Bad Request");
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// 3) Root -> index.html
|
|
632
|
+
if (urlPath === "/") urlPath = "/index.html";
|
|
633
|
+
|
|
634
|
+
// 4) SPA fallback: si NO hay extensión (ej: /nosotros), devolver index.html
|
|
635
|
+
const hasExtension = path.extname(urlPath) !== "";
|
|
636
|
+
if (!hasExtension) {
|
|
637
|
+
urlPath = "/index.html";
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const fullPath = path.join(root, urlPath);
|
|
641
|
+
|
|
642
|
+
fs.readFile(fullPath, (err, content) => {
|
|
643
|
+
if (err) {
|
|
644
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
645
|
+
res.end("404 Not Found");
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const ext = path.extname(fullPath).toLowerCase();
|
|
650
|
+
const types = {
|
|
651
|
+
".html": "text/html; charset=utf-8",
|
|
652
|
+
".js": "application/javascript; charset=utf-8",
|
|
653
|
+
".css": "text/css; charset=utf-8",
|
|
654
|
+
".json": "application/json; charset=utf-8",
|
|
655
|
+
".png": "image/png",
|
|
656
|
+
".jpg": "image/jpeg",
|
|
657
|
+
".jpeg": "image/jpeg",
|
|
658
|
+
".svg": "image/svg+xml"
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
res.writeHead(200, {
|
|
662
|
+
"Content-Type": types[ext] || "application/octet-stream",
|
|
663
|
+
"Cache-Control": "no-store"
|
|
664
|
+
});
|
|
665
|
+
res.end(content);
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
server.listen(port, () => {
|
|
670
|
+
console.log(`🚀 MGPanel Dev Server`);
|
|
671
|
+
console.log(`👉 http://localhost:${port}`);
|
|
672
|
+
console.log(`🛑 Ctrl + C para detener`);
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
// ------------------ Router CLI ------------------
|
|
125
677
|
|
|
678
|
+
const args = process.argv.slice(2);
|
|
126
679
|
if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
|
|
127
680
|
printHelp();
|
|
128
681
|
process.exit(0);
|
|
129
682
|
}
|
|
130
683
|
|
|
131
|
-
const [cmd1, cmd2, cmd3] = args;
|
|
684
|
+
const [cmd1, cmd2, cmd3, cmd4, cmd5] = args;
|
|
132
685
|
|
|
133
686
|
if (cmd1 === "init") {
|
|
134
687
|
cmdInit(cmd2);
|
|
135
|
-
|
|
688
|
+
process.exit(0);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if (cmd1 === "make" && cmd2 === "page") {
|
|
692
|
+
cmdMakePage(cmd3);
|
|
693
|
+
process.exit(0);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (cmd1 === "make" && cmd2 === "module") {
|
|
136
697
|
cmdMakeModule(cmd3);
|
|
137
|
-
|
|
138
|
-
console.log("❌ Comando no reconocido.");
|
|
139
|
-
printHelp();
|
|
140
|
-
process.exit(1);
|
|
698
|
+
process.exit(0);
|
|
141
699
|
}
|
|
700
|
+
|
|
701
|
+
if (cmd1 === "add" && cmd2 === "module") {
|
|
702
|
+
const moduleName = cmd3;
|
|
703
|
+
const flag = cmd4;
|
|
704
|
+
const pageName = cmd5;
|
|
705
|
+
|
|
706
|
+
if (flag !== "--to") {
|
|
707
|
+
console.log("❌ Falta --to <pagina>");
|
|
708
|
+
console.log("👉 Usa: mgpanel add module <modulo> --to <pagina>");
|
|
709
|
+
process.exit(1);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
cmdAddModule(moduleName, pageName);
|
|
713
|
+
process.exit(0);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
if (cmd1 === "dev") {
|
|
717
|
+
const port = cmd2 ? Number(cmd2) : 3000;
|
|
718
|
+
cmdDev(port);
|
|
719
|
+
// NO process.exit aquí, el server debe quedarse vivo
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
console.log("❌ Comando no reconocido.");
|
|
724
|
+
printHelp();
|
|
725
|
+
process.exit(1);
|