mgpanel-cli 1.0.0 → 1.0.2

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.
Files changed (3) hide show
  1. package/README.md +42 -0
  2. package/bin/mgpanel.js +419 -27
  3. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # MGPanel CLI 🚀
2
+
3
+ MGPanel CLI es la herramienta oficial para crear y gestionar proyectos **MGPanel** desde tu computadora, usando **HTML, CSS y JavaScript puro**, sin frameworks, sin JSX y sin configuraciones complejas.
4
+
5
+ Diseñado para desarrolladores que quieren **control total del código**, con una estructura clara y preparada para sincronizarse con MGPanel (Node.js + MongoDB).
6
+
7
+ ---
8
+
9
+ ## ✨ Filosofía
10
+
11
+ MGPanel no es otro framework.
12
+
13
+ Es una forma moderna de trabajar con:
14
+ - **HTML**
15
+ - **CSS**
16
+ - **JavaScript**
17
+
18
+ Organizados por módulos, con tooling profesional y libertad total.
19
+
20
+ > Escribes código como siempre.
21
+ > MGPanel se encarga de la estructura y el futuro despliegue.
22
+
23
+ ---
24
+
25
+ ## 📦 Requisitos
26
+
27
+ Antes de empezar, necesitas tener instalado:
28
+
29
+ - **Node.js** (incluye npm y npx)
30
+
31
+ Verifica con:
32
+
33
+ ```bash
34
+ node -v
35
+ npm -v
36
+ ```
37
+
38
+ ## Comandos disponibles
39
+
40
+ Crear un módulo:
41
+
42
+ mgpanel make module home
package/bin/mgpanel.js CHANGED
@@ -1,47 +1,439 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
3
+ const http = require("http");
4
+ const fs = require("fs");
5
+ const path = require("path");
5
6
 
6
- const command = process.argv[2];
7
- const projectName = process.argv[3] || 'mgpanel-project';
7
+ function printHelp() {
8
+ console.log(`
9
+ MGPanel CLI
8
10
 
9
- if (command !== 'init') {
10
- console.log('❌ Comando no reconocido');
11
- console.log('👉 Usa: mgpanel init nombre-proyecto');
12
- process.exit(1);
11
+ Uso:
12
+ mgpanel init <nombre-proyecto>
13
+
14
+ mgpanel make page <nombre-pagina>
15
+ mgpanel make module <nombre-modulo>
16
+
17
+ mgpanel add module <nombre-modulo> --to <nombre-pagina>
18
+
19
+ mgpanel dev [puerto]
20
+
21
+ Ejemplos:
22
+ mgpanel init miweb
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
31
+ `);
13
32
  }
14
33
 
15
- const projectPath = path.join(process.cwd(), projectName);
34
+ function ensureDir(dirPath) {
35
+ fs.mkdirSync(dirPath, { recursive: true });
36
+ }
37
+
38
+ function writeFileIfNotExists(filePath, content) {
39
+ if (fs.existsSync(filePath)) return false;
40
+ fs.writeFileSync(filePath, content, "utf8");
41
+ return true;
42
+ }
16
43
 
17
- if (fs.existsSync(projectPath)) {
18
- console.log('❌ La carpeta ya existe');
19
- process.exit(1);
44
+ function readJSON(filePath) {
45
+ const raw = fs.readFileSync(filePath, "utf8");
46
+ return JSON.parse(raw);
20
47
  }
21
48
 
22
- console.log('🚀 Creando proyecto MGPanel:', projectName);
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
+ }
23
56
 
24
- // Crear carpetas
25
- fs.mkdirSync(projectPath);
26
- fs.mkdirSync(path.join(projectPath, 'modules'));
27
- fs.mkdirSync(path.join(projectPath, 'assets'));
28
- fs.mkdirSync(path.join(projectPath, 'assets/css'));
29
- fs.mkdirSync(path.join(projectPath, 'assets/js'));
57
+ /**
58
+ * mgpanel init <projectName>
59
+ */
60
+ function cmdInit(projectName = "mgpanel-project") {
61
+ const projectPath = path.join(process.cwd(), projectName);
30
62
 
31
- // Crear index.html
32
- const indexHTML = `<!DOCTYPE html>
63
+ if (fs.existsSync(projectPath)) {
64
+ console.log("❌ La carpeta ya existe:", projectName);
65
+ process.exit(1);
66
+ }
67
+
68
+ console.log("🚀 Creando proyecto MGPanel:", projectName);
69
+
70
+ // base
71
+ ensureDir(projectPath);
72
+ ensureDir(path.join(projectPath, "pages"));
73
+ ensureDir(path.join(projectPath, "modules"));
74
+ ensureDir(path.join(projectPath, "dev"));
75
+ ensureDir(path.join(projectPath, "assets", "css"));
76
+ ensureDir(path.join(projectPath, "assets", "js"));
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
91
+ const indexHTML = `<!doctype html>
33
92
  <html lang="es">
34
93
  <head>
35
- <meta charset="UTF-8">
36
- <title>MGPanel</title>
94
+ <meta charset="utf-8" />
95
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
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>
37
100
  </head>
38
101
  <body>
39
- <h1>Hola Mundo desde MGPanel 🚀</h1>
102
+ <div id="app"></div>
103
+ <script src="dev/preview.js"></script>
40
104
  </body>
41
105
  </html>
42
106
  `;
107
+ fs.writeFileSync(path.join(projectPath, "index.html"), indexHTML, "utf8");
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/&lt;page&gt;/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
+ console.log("✅ Proyecto creado correctamente");
197
+ console.log("👉 Ejecuta: cd " + projectName + " && mgpanel dev");
198
+ }
199
+
200
+ /**
201
+ * mgpanel make page <pageName>
202
+ */
203
+ function cmdMakePage(pageName) {
204
+ if (!pageName) {
205
+ console.log("❌ Falta el nombre de la página");
206
+ console.log("👉 Usa: mgpanel make page <nombre-pagina>");
207
+ process.exit(1);
208
+ }
209
+ if (!isValidSlug(pageName)) {
210
+ console.log("❌ Nombre de página inválido. Usa letras/números/guiones. Ej: home, about-us");
211
+ process.exit(1);
212
+ }
213
+
214
+ const pagesDir = path.join(process.cwd(), "pages");
215
+ const pageDir = path.join(pagesDir, pageName);
216
+ ensureDir(pageDir);
217
+
218
+ const pageJsonPath = path.join(pageDir, "page.json");
219
+ const created = writeFileIfNotExists(
220
+ pageJsonPath,
221
+ JSON.stringify(
222
+ {
223
+ route: `/${pageName === "home" ? "" : pageName}`.replace(/\/$/, "/"),
224
+ title: pageName.charAt(0).toUpperCase() + pageName.slice(1),
225
+ description: `Descripción de la página ${pageName}.`,
226
+ modules: []
227
+ },
228
+ null,
229
+ 2
230
+ )
231
+ );
232
+
233
+ if (created) {
234
+ console.log(`📄 Página "${pageName}" creada en: pages/${pageName}/page.json`);
235
+ } else {
236
+ console.log(`↩️ La página "${pageName}" ya existe: pages/${pageName}/page.json`);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * mgpanel make module <moduleName>
242
+ */
243
+ function cmdMakeModule(moduleName) {
244
+ if (!moduleName) {
245
+ console.log("❌ Falta el nombre del módulo");
246
+ console.log("👉 Usa: mgpanel make module <nombre-modulo>");
247
+ process.exit(1);
248
+ }
249
+ if (!isValidSlug(moduleName)) {
250
+ console.log("❌ Nombre de módulo inválido. Usa letras/números/guiones. Ej: header, hero-banner");
251
+ process.exit(1);
252
+ }
253
+
254
+ const modulesDir = path.join(process.cwd(), "modules");
255
+ const moduleDir = path.join(modulesDir, moduleName);
256
+ ensureDir(moduleDir);
257
+
258
+ const htmlPath = path.join(moduleDir, `${moduleName}.html`);
259
+ const cssPath = path.join(moduleDir, `${moduleName}.css`);
260
+ const jsPath = path.join(moduleDir, `${moduleName}.js`);
43
261
 
44
- fs.writeFileSync(path.join(projectPath, 'index.html'), indexHTML);
262
+ const htmlContent = `<!-- Module: ${moduleName} -->
263
+ <section class="${moduleName}">
264
+ <h2>${moduleName}</h2>
265
+ <p>Módulo creado con MGPanel ✅</p>
266
+ </section>
267
+ `;
268
+
269
+ const cssContent = `/* Module: ${moduleName} */
270
+ .${moduleName} {
271
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
272
+ }
273
+ `;
274
+
275
+ const jsContent = `// Module: ${moduleName}
276
+ console.log("[MGPanel] Module loaded:", "${moduleName}");
277
+ `;
278
+
279
+ const created = [];
280
+ const skipped = [];
281
+
282
+ (writeFileIfNotExists(htmlPath, htmlContent) ? created : skipped).push(`${moduleName}.html`);
283
+ (writeFileIfNotExists(cssPath, cssContent) ? created : skipped).push(`${moduleName}.css`);
284
+ (writeFileIfNotExists(jsPath, jsContent) ? created : skipped).push(`${moduleName}.js`);
285
+
286
+ console.log(`🧩 Módulo "${moduleName}" listo en: modules/${moduleName}/`);
287
+ if (created.length) console.log("✅ Archivos creados:", created.join(", "));
288
+ if (skipped.length) console.log("↩️ Ya existían:", skipped.join(", "));
289
+ }
290
+
291
+ /**
292
+ * mgpanel add module <moduleName> --to <pageName>
293
+ */
294
+ function cmdAddModule(moduleName, pageName) {
295
+ if (!moduleName || !pageName) {
296
+ console.log("❌ Uso inválido");
297
+ console.log("👉 Usa: mgpanel add module <modulo> --to <pagina>");
298
+ process.exit(1);
299
+ }
300
+
301
+ const moduleDir = path.join(process.cwd(), "modules", moduleName);
302
+ if (!fs.existsSync(moduleDir)) {
303
+ console.log(`❌ El módulo "${moduleName}" no existe en modules/${moduleName}/`);
304
+ console.log(`👉 Crea el módulo primero: mgpanel make module ${moduleName}`);
305
+ process.exit(1);
306
+ }
307
+
308
+ const pageJsonPath = path.join(process.cwd(), "pages", pageName, "page.json");
309
+ if (!fs.existsSync(pageJsonPath)) {
310
+ console.log(`❌ La página "${pageName}" no existe (pages/${pageName}/page.json)`);
311
+ console.log(`👉 Crea la página primero: mgpanel make page ${pageName}`);
312
+ process.exit(1);
313
+ }
314
+
315
+ const page = readJSON(pageJsonPath);
316
+ page.modules = Array.isArray(page.modules) ? page.modules : [];
317
+
318
+ const already = page.modules.some(m => m && m.name === moduleName);
319
+ if (already) {
320
+ console.log(`↩️ El módulo "${moduleName}" ya está agregado a la página "${pageName}".`);
321
+ return;
322
+ }
323
+
324
+ page.modules.push({ name: moduleName });
325
+ writeJSON(pageJsonPath, page);
326
+
327
+ console.log(`✅ Módulo "${moduleName}" agregado a pages/${pageName}/page.json`);
328
+ }
329
+
330
+ function cmdDev(port = 3000) {
331
+ const root = process.cwd();
332
+
333
+ const server = http.createServer((req, res) => {
334
+ // 1) Normalizar URL (sin querystring)
335
+ let urlPath = req.url || "/";
336
+ urlPath = decodeURIComponent(urlPath.split("?")[0]);
337
+
338
+ // 2) Seguridad básica: bloquear path traversal
339
+ if (urlPath.includes("..")) {
340
+ res.writeHead(400, { "Content-Type": "text/plain" });
341
+ res.end("400 Bad Request");
342
+ return;
343
+ }
344
+
345
+ // 3) Root -> index.html
346
+ if (urlPath === "/") urlPath = "/index.html";
347
+
348
+ // 4) SPA fallback: si NO hay extensión (ej: /nosotros), devolver index.html
349
+ const hasExtension = path.extname(urlPath) !== "";
350
+ if (!hasExtension) {
351
+ urlPath = "/index.html";
352
+ }
353
+
354
+ const fullPath = path.join(root, urlPath);
355
+
356
+ fs.readFile(fullPath, (err, content) => {
357
+ if (err) {
358
+ res.writeHead(404, { "Content-Type": "text/plain" });
359
+ res.end("404 Not Found");
360
+ return;
361
+ }
362
+
363
+ const ext = path.extname(fullPath).toLowerCase();
364
+ const types = {
365
+ ".html": "text/html; charset=utf-8",
366
+ ".js": "application/javascript; charset=utf-8",
367
+ ".css": "text/css; charset=utf-8",
368
+ ".json": "application/json; charset=utf-8",
369
+ ".png": "image/png",
370
+ ".jpg": "image/jpeg",
371
+ ".jpeg": "image/jpeg",
372
+ ".svg": "image/svg+xml"
373
+ };
374
+
375
+ res.writeHead(200, {
376
+ "Content-Type": types[ext] || "application/octet-stream",
377
+ "Cache-Control": "no-store"
378
+ });
379
+ res.end(content);
380
+ });
381
+ });
382
+
383
+ server.listen(port, () => {
384
+ console.log(`🚀 MGPanel Dev Server`);
385
+ console.log(`👉 http://localhost:${port}`);
386
+ console.log(`🛑 Ctrl + C para detener`);
387
+ });
388
+ }
389
+
390
+ // ------------------ Router CLI ------------------
391
+
392
+ const args = process.argv.slice(2);
393
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
394
+ printHelp();
395
+ process.exit(0);
396
+ }
397
+
398
+ const [cmd1, cmd2, cmd3, cmd4, cmd5] = args;
399
+
400
+ if (cmd1 === "init") {
401
+ cmdInit(cmd2);
402
+ process.exit(0);
403
+ }
404
+
405
+ if (cmd1 === "make" && cmd2 === "page") {
406
+ cmdMakePage(cmd3);
407
+ process.exit(0);
408
+ }
409
+
410
+ if (cmd1 === "make" && cmd2 === "module") {
411
+ cmdMakeModule(cmd3);
412
+ process.exit(0);
413
+ }
414
+
415
+ if (cmd1 === "add" && cmd2 === "module") {
416
+ const moduleName = cmd3;
417
+ const flag = cmd4;
418
+ const pageName = cmd5;
419
+
420
+ if (flag !== "--to") {
421
+ console.log("❌ Falta --to <pagina>");
422
+ console.log("👉 Usa: mgpanel add module <modulo> --to <pagina>");
423
+ process.exit(1);
424
+ }
425
+
426
+ cmdAddModule(moduleName, pageName);
427
+ process.exit(0);
428
+ }
429
+
430
+ if (cmd1 === "dev") {
431
+ const port = cmd2 ? Number(cmd2) : 3000;
432
+ cmdDev(port);
433
+ // NO process.exit aquí, el server debe quedarse vivo
434
+ return;
435
+ }
45
436
 
46
- console.log('✅ Proyecto creado correctamente');
47
-
437
+ console.log("❌ Comando no reconocido.");
438
+ printHelp();
439
+ process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mgpanel-cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "MGPanel CLI",
5
5
  "bin": {
6
6
  "mgpanel": "./bin/mgpanel.js"