what-framework-cli 0.12.4 → 0.13.1
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/package.json +2 -2
- package/src/cli.js +27 -40
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "what-framework-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "What Framework CLI - Dev server, build, and deployment tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"author": "ZVN DEV (https://zvndev.com)",
|
|
23
23
|
"license": "MIT",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"what-framework": "^0.
|
|
25
|
+
"what-framework": "^0.13.1"
|
|
26
26
|
},
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
package/src/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Commands: dev, build, preview, generate
|
|
5
5
|
|
|
6
6
|
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync, statSync, copyFileSync, realpathSync } from 'fs';
|
|
7
|
-
import { join, resolve, relative, extname,
|
|
7
|
+
import { join, resolve, relative, extname, normalize, sep } from 'path';
|
|
8
8
|
import { createServer } from 'http';
|
|
9
9
|
import { spawn } from 'child_process';
|
|
10
10
|
import { createRequire } from 'module';
|
|
@@ -76,7 +76,7 @@ function isAllowedOrigin(origin, allowedHosts) {
|
|
|
76
76
|
class SimpleWebSocketServer {
|
|
77
77
|
constructor({ server, allowedHosts = new Set() }) {
|
|
78
78
|
this.clients = new Set();
|
|
79
|
-
server.on('upgrade', (req, socket,
|
|
79
|
+
server.on('upgrade', (req, socket, _head) => {
|
|
80
80
|
if (req.headers.upgrade?.toLowerCase() !== 'websocket') return;
|
|
81
81
|
|
|
82
82
|
const key = req.headers['sec-websocket-key'];
|
|
@@ -112,6 +112,10 @@ class SimpleWebSocketServer {
|
|
|
112
112
|
class SimpleWebSocket {
|
|
113
113
|
constructor(socket) {
|
|
114
114
|
this.socket = socket;
|
|
115
|
+
/** @type {(() => void) | null} Assigned by the server that owns this client. */
|
|
116
|
+
this.onclose = null;
|
|
117
|
+
/** @type {((event: { data: string }) => void) | null} */
|
|
118
|
+
this.onmessage = null;
|
|
115
119
|
this.socket.on('close', () => this.onclose?.());
|
|
116
120
|
this.socket.on('error', () => this.onclose?.());
|
|
117
121
|
this.socket.on('data', (data) => this._handleData(data));
|
|
@@ -153,7 +157,7 @@ class SimpleWebSocket {
|
|
|
153
157
|
if (opcode === 0x01) { // Text frame
|
|
154
158
|
this.onmessage?.({ data: payload.toString('utf8') });
|
|
155
159
|
}
|
|
156
|
-
} catch
|
|
160
|
+
} catch {}
|
|
157
161
|
}
|
|
158
162
|
|
|
159
163
|
send(data) {
|
|
@@ -179,7 +183,7 @@ class SimpleWebSocket {
|
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
this.socket.write(Buffer.concat([header, payload]));
|
|
182
|
-
} catch
|
|
186
|
+
} catch {}
|
|
183
187
|
}
|
|
184
188
|
|
|
185
189
|
close() {
|
|
@@ -187,7 +191,7 @@ class SimpleWebSocket {
|
|
|
187
191
|
const closeFrame = Buffer.from([0x88, 0x00]);
|
|
188
192
|
this.socket.write(closeFrame);
|
|
189
193
|
this.socket.end();
|
|
190
|
-
} catch
|
|
194
|
+
} catch {}
|
|
191
195
|
}
|
|
192
196
|
}
|
|
193
197
|
|
|
@@ -261,7 +265,7 @@ async function dev() {
|
|
|
261
265
|
const runtimeDirs = requireRuntimeDirs('what dev');
|
|
262
266
|
|
|
263
267
|
const server = createServer(async (req, res) => {
|
|
264
|
-
const url = new URL(req.url, `http://${host}:${port}`);
|
|
268
|
+
const url = new URL(req.url || '/', `http://${host}:${port}`);
|
|
265
269
|
let pathname = url.pathname;
|
|
266
270
|
|
|
267
271
|
// Handle server actions
|
|
@@ -285,7 +289,9 @@ async function dev() {
|
|
|
285
289
|
req.on('end', async () => {
|
|
286
290
|
if (tooLarge) return;
|
|
287
291
|
try {
|
|
288
|
-
|
|
292
|
+
// Parsed for validation only: a malformed body must reach the catch
|
|
293
|
+
// below rather than be answered with the dev placeholder.
|
|
294
|
+
JSON.parse(body);
|
|
289
295
|
// In production, this would call the registered action
|
|
290
296
|
// For dev, we'll return a placeholder response
|
|
291
297
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
@@ -370,8 +376,8 @@ async function dev() {
|
|
|
370
376
|
const noMcp = args.includes('--no-mcp');
|
|
371
377
|
if (!noMcp) {
|
|
372
378
|
try {
|
|
373
|
-
|
|
374
|
-
//
|
|
379
|
+
// Presence is checked by looking for the installed binary, not by
|
|
380
|
+
// resolving the specifier.
|
|
375
381
|
const mcpBin = join(cwd, 'node_modules', '.bin', 'what-devtools-mcp');
|
|
376
382
|
const { existsSync: mcpExists } = await import('node:fs');
|
|
377
383
|
if (mcpExists(mcpBin)) {
|
|
@@ -426,7 +432,7 @@ async function dev() {
|
|
|
426
432
|
for (const client of wsClients) {
|
|
427
433
|
try {
|
|
428
434
|
client.send(message);
|
|
429
|
-
} catch
|
|
435
|
+
} catch {
|
|
430
436
|
wsClients.delete(client);
|
|
431
437
|
}
|
|
432
438
|
}
|
|
@@ -574,7 +580,7 @@ async function preview() {
|
|
|
574
580
|
}
|
|
575
581
|
|
|
576
582
|
const server = createServer((req, res) => {
|
|
577
|
-
let pathname = new URL(req.url, `http://localhost:${port}`).pathname;
|
|
583
|
+
let pathname = new URL(req.url || '/', `http://localhost:${port}`).pathname;
|
|
578
584
|
if (pathname === '/') pathname = '/index.html';
|
|
579
585
|
|
|
580
586
|
// Security: Prevent path traversal
|
|
@@ -638,7 +644,10 @@ async function generate() {
|
|
|
638
644
|
try {
|
|
639
645
|
const mod = await import(pathToFileURL(page).href);
|
|
640
646
|
if (typeof (mod.default || mod) !== 'function') {
|
|
641
|
-
|
|
647
|
+
// ERROR_CODES.PAGE_NO_DEFAULT_EXPORT
|
|
648
|
+
throw Object.assign(new Error('no default-exported component'), {
|
|
649
|
+
code: 'ERR_PAGE_NO_DEFAULT_EXPORT',
|
|
650
|
+
});
|
|
642
651
|
}
|
|
643
652
|
const { body, head } = await renderPage(mod, { params: {}, query: {}, path: route });
|
|
644
653
|
html = staticDocument(body, head);
|
|
@@ -794,38 +803,16 @@ async function loadConfigAsync() {
|
|
|
794
803
|
const mod = await import(fileUrl.href);
|
|
795
804
|
_configCache = mod.default || mod;
|
|
796
805
|
return _configCache;
|
|
797
|
-
} catch
|
|
806
|
+
} catch { /* use defaults */ }
|
|
798
807
|
}
|
|
799
808
|
_configCache = { mode: 'hybrid', pagesDir: 'src/pages', outDir: 'dist' };
|
|
800
809
|
return _configCache;
|
|
801
810
|
}
|
|
802
811
|
|
|
803
|
-
//
|
|
804
|
-
//
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
if (_configCache) return _configCache;
|
|
808
|
-
// Fallback: try JSON.parse for simple object configs (no code execution)
|
|
809
|
-
const configPath = join(cwd, 'what.config.js');
|
|
810
|
-
if (existsSync(configPath)) {
|
|
811
|
-
try {
|
|
812
|
-
const src = readFileSync(configPath, 'utf-8');
|
|
813
|
-
const match = src.match(/export default\s*(\{[\s\S]*?\})/);
|
|
814
|
-
if (match) {
|
|
815
|
-
// Only parse if the content is valid JSON (safe subset)
|
|
816
|
-
// Convert JS object literal to JSON: add quotes to keys
|
|
817
|
-
const jsonLike = match[1]
|
|
818
|
-
.replace(/\/\/[^\n]*/g, '') // strip line comments
|
|
819
|
-
.replace(/\/\*[\s\S]*?\*\//g, '') // strip block comments
|
|
820
|
-
.replace(/,\s*([}\]])/g, '$1') // strip trailing commas
|
|
821
|
-
.replace(/(['"])?(\w+)(['"])?\s*:/g, '"$2":') // quote keys
|
|
822
|
-
.replace(/:\s*'([^']*)'/g, ': "$1"'); // single quotes to double
|
|
823
|
-
return JSON.parse(jsonLike);
|
|
824
|
-
}
|
|
825
|
-
} catch (e) { /* use defaults */ }
|
|
826
|
-
}
|
|
827
|
-
return { mode: 'hybrid', pagesDir: 'src/pages', outDir: 'dist' };
|
|
828
|
-
}
|
|
812
|
+
// A synchronous `loadConfig` lived here, commented as a backward-compatible
|
|
813
|
+
// wrapper. It was never exported and nothing called it; every call site uses
|
|
814
|
+
// loadConfigAsync(). Its JS-object-literal-to-JSON regex chain was also a
|
|
815
|
+
// second, divergent config parser that no test covered.
|
|
829
816
|
|
|
830
817
|
function collectFiles(dir) {
|
|
831
818
|
const files = [];
|
|
@@ -1202,7 +1189,7 @@ function watchFiles(dir, onChange) {
|
|
|
1202
1189
|
}
|
|
1203
1190
|
files.set(f, mtime);
|
|
1204
1191
|
}
|
|
1205
|
-
} catch
|
|
1192
|
+
} catch {
|
|
1206
1193
|
// File was deleted during scan
|
|
1207
1194
|
}
|
|
1208
1195
|
}
|