mgpanel-cli 1.0.1 → 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 (2) hide show
  1. package/bin/mgpanel.js +318 -20
  2. package/package.json +1 -1
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
- mgpanel make module home
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,174 @@ 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
- <h1>Hola Mundo desde MGPanel 🚀</h1>
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/&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
+
63
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
+ }
64
238
  }
65
239
 
66
240
  /**
@@ -72,18 +246,13 @@ function cmdMakeModule(moduleName) {
72
246
  console.log("👉 Usa: mgpanel make module <nombre-modulo>");
73
247
  process.exit(1);
74
248
  }
75
-
76
- // Validación básica: slug simple
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");
249
+ if (!isValidSlug(moduleName)) {
250
+ console.log("❌ Nombre de módulo inválido. Usa letras/números/guiones. Ej: header, hero-banner");
80
251
  process.exit(1);
81
252
  }
82
253
 
83
- const cwd = process.cwd();
84
- const modulesDir = path.join(cwd, "modules");
254
+ const modulesDir = path.join(process.cwd(), "modules");
85
255
  const moduleDir = path.join(modulesDir, moduleName);
86
-
87
256
  ensureDir(moduleDir);
88
257
 
89
258
  const htmlPath = path.join(moduleDir, `${moduleName}.html`);
@@ -119,23 +288,152 @@ console.log("[MGPanel] Module loaded:", "${moduleName}");
119
288
  if (skipped.length) console.log("↩️ Ya existían:", skipped.join(", "));
120
289
  }
121
290
 
122
- // ------------------ Router simple ------------------
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
+ }
123
300
 
124
- const args = process.argv.slice(2);
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
+ }
125
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);
126
393
  if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
127
394
  printHelp();
128
395
  process.exit(0);
129
396
  }
130
397
 
131
- const [cmd1, cmd2, cmd3] = args;
398
+ const [cmd1, cmd2, cmd3, cmd4, cmd5] = args;
132
399
 
133
400
  if (cmd1 === "init") {
134
401
  cmdInit(cmd2);
135
- } else if (cmd1 === "make" && cmd2 === "module") {
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") {
136
411
  cmdMakeModule(cmd3);
137
- } else {
138
- console.log("❌ Comando no reconocido.");
139
- printHelp();
140
- process.exit(1);
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);
141
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
+ }
436
+
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.1",
3
+ "version": "1.0.2",
4
4
  "description": "MGPanel CLI",
5
5
  "bin": {
6
6
  "mgpanel": "./bin/mgpanel.js"