kilatjs 0.1.0 → 0.1.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.
- package/README.md +481 -260
- package/dist/adapters/react.d.ts +6 -1
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +1335 -0
- package/dist/core/router.d.ts +10 -7
- package/dist/core/types.d.ts +4 -1
- package/dist/core/vite-router.d.ts +34 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +425 -199
- package/dist/server/dev-server.d.ts +0 -0
- package/dist/server/live-reload.d.ts +27 -0
- package/dist/server/server.d.ts +11 -1
- package/dist/server/vite-dev.d.ts +38 -0
- package/dist/server/vite-server.d.ts +17 -0
- package/package.json +10 -4
package/dist/index.js
CHANGED
|
@@ -2,18 +2,138 @@
|
|
|
2
2
|
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
3
|
import React from "react";
|
|
4
4
|
|
|
5
|
+
// src/server/live-reload.ts
|
|
6
|
+
var clients = new Set;
|
|
7
|
+
var liveReloadServer = null;
|
|
8
|
+
function startLiveReload(port = 35729) {
|
|
9
|
+
if (liveReloadServer)
|
|
10
|
+
return;
|
|
11
|
+
liveReloadServer = Bun.serve({
|
|
12
|
+
port,
|
|
13
|
+
fetch(req, server) {
|
|
14
|
+
if (server.upgrade(req, { data: {} })) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
return new Response("Live Reload Server", { status: 200 });
|
|
18
|
+
},
|
|
19
|
+
websocket: {
|
|
20
|
+
open(ws) {
|
|
21
|
+
clients.add(ws);
|
|
22
|
+
},
|
|
23
|
+
close(ws) {
|
|
24
|
+
clients.delete(ws);
|
|
25
|
+
},
|
|
26
|
+
message() {}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function notifyReload() {
|
|
31
|
+
const message = JSON.stringify({ type: "reload" });
|
|
32
|
+
for (const client of clients) {
|
|
33
|
+
try {
|
|
34
|
+
client.send(message);
|
|
35
|
+
} catch {
|
|
36
|
+
clients.delete(client);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function getLiveReloadScript(port = 35729) {
|
|
41
|
+
return `
|
|
42
|
+
<script>
|
|
43
|
+
(function() {
|
|
44
|
+
const ws = new WebSocket('ws://localhost:${port}');
|
|
45
|
+
ws.onmessage = function(e) {
|
|
46
|
+
const data = JSON.parse(e.data);
|
|
47
|
+
if (data.type === 'reload') {
|
|
48
|
+
console.log('[KilatJS] Reloading...');
|
|
49
|
+
location.reload();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
ws.onclose = function() {
|
|
53
|
+
console.log('[KilatJS] Live reload disconnected. Reconnecting...');
|
|
54
|
+
setTimeout(function() { location.reload(); }, 1000);
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
</script>`;
|
|
58
|
+
}
|
|
59
|
+
async function watchDirectory(dir, onChange) {
|
|
60
|
+
const fileTimestamps = new Map;
|
|
61
|
+
let timeout = null;
|
|
62
|
+
let lastChange = 0;
|
|
63
|
+
const triggerChange = (filename) => {
|
|
64
|
+
const now = Date.now();
|
|
65
|
+
if (now - lastChange < 100)
|
|
66
|
+
return;
|
|
67
|
+
lastChange = now;
|
|
68
|
+
if (timeout)
|
|
69
|
+
clearTimeout(timeout);
|
|
70
|
+
timeout = setTimeout(() => {
|
|
71
|
+
onChange();
|
|
72
|
+
}, 50);
|
|
73
|
+
};
|
|
74
|
+
async function scanFiles() {
|
|
75
|
+
const timestamps = new Map;
|
|
76
|
+
try {
|
|
77
|
+
const glob = new Bun.Glob("**/*.{ts,tsx,js,jsx,css}");
|
|
78
|
+
for await (const file of glob.scan({ cwd: dir, absolute: true })) {
|
|
79
|
+
try {
|
|
80
|
+
const stat = Bun.file(file);
|
|
81
|
+
const lastModified = (await stat.stat())?.mtime?.getTime() || 0;
|
|
82
|
+
timestamps.set(file, lastModified);
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.warn(`⚠️ Could not scan directory ${dir}:`, error);
|
|
87
|
+
}
|
|
88
|
+
return timestamps;
|
|
89
|
+
}
|
|
90
|
+
const initialFiles = await scanFiles();
|
|
91
|
+
for (const [file, time] of initialFiles) {
|
|
92
|
+
fileTimestamps.set(file, time);
|
|
93
|
+
}
|
|
94
|
+
const pollInterval = setInterval(async () => {
|
|
95
|
+
const currentFiles = await scanFiles();
|
|
96
|
+
for (const [file, time] of currentFiles) {
|
|
97
|
+
const previousTime = fileTimestamps.get(file);
|
|
98
|
+
if (previousTime === undefined || previousTime < time) {
|
|
99
|
+
fileTimestamps.set(file, time);
|
|
100
|
+
if (previousTime !== undefined) {
|
|
101
|
+
const relativePath = file.replace(dir + "/", "");
|
|
102
|
+
triggerChange(relativePath);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
for (const file of fileTimestamps.keys()) {
|
|
107
|
+
if (!currentFiles.has(file)) {
|
|
108
|
+
fileTimestamps.delete(file);
|
|
109
|
+
const relativePath = file.replace(dir + "/", "");
|
|
110
|
+
triggerChange(relativePath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}, 500);
|
|
114
|
+
globalThis.__kilatWatchIntervals = globalThis.__kilatWatchIntervals || [];
|
|
115
|
+
globalThis.__kilatWatchIntervals.push(pollInterval);
|
|
116
|
+
}
|
|
117
|
+
function stopLiveReload() {
|
|
118
|
+
if (liveReloadServer) {
|
|
119
|
+
liveReloadServer.stop();
|
|
120
|
+
liveReloadServer = null;
|
|
121
|
+
}
|
|
122
|
+
clients.clear();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/adapters/react.ts
|
|
5
126
|
class ReactAdapter {
|
|
6
127
|
static async renderToString(component, props) {
|
|
7
128
|
try {
|
|
8
129
|
const element = React.createElement(component, props);
|
|
9
|
-
|
|
10
|
-
return html;
|
|
130
|
+
return renderToStaticMarkup(element);
|
|
11
131
|
} catch (error) {
|
|
12
132
|
console.error("Error rendering React component:", error);
|
|
13
133
|
throw error;
|
|
14
134
|
}
|
|
15
135
|
}
|
|
16
|
-
static createDocument(html, meta = {}, config) {
|
|
136
|
+
static createDocument(html, meta = {}, config, options = {}) {
|
|
17
137
|
const title = meta.title || "KilatJS App";
|
|
18
138
|
const description = meta.description || "";
|
|
19
139
|
const robots = meta.robots || "index,follow";
|
|
@@ -70,6 +190,8 @@ class ReactAdapter {
|
|
|
70
190
|
`;
|
|
71
191
|
metaTags += `<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet" />
|
|
72
192
|
`;
|
|
193
|
+
const liveReloadScript = config?.dev ? getLiveReloadScript() : "";
|
|
194
|
+
const clientScriptTag = options.clientScript ? `<script>(${options.clientScript.toString()})()</script>` : "";
|
|
73
195
|
return `<!DOCTYPE html>
|
|
74
196
|
<html lang="en">
|
|
75
197
|
<head>
|
|
@@ -80,34 +202,8 @@ class ReactAdapter {
|
|
|
80
202
|
<div id="root">
|
|
81
203
|
${html}
|
|
82
204
|
</div>
|
|
83
|
-
${
|
|
84
|
-
|
|
85
|
-
let currentServerId = null;
|
|
86
|
-
let isReconnecting = false;
|
|
87
|
-
|
|
88
|
-
function connect() {
|
|
89
|
-
const source = new EventSource('/_kilat/live-reload');
|
|
90
|
-
|
|
91
|
-
source.onmessage = (event) => {
|
|
92
|
-
const newServerId = event.data;
|
|
93
|
-
if (currentServerId === null) {
|
|
94
|
-
currentServerId = newServerId;
|
|
95
|
-
} else if (currentServerId !== newServerId) {
|
|
96
|
-
// Server ID changed, reload!
|
|
97
|
-
location.reload();
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
source.onerror = () => {
|
|
102
|
-
source.close();
|
|
103
|
-
// Try to reconnect in 1s
|
|
104
|
-
setTimeout(connect, 1000);
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
connect();
|
|
109
|
-
})();
|
|
110
|
-
</script>` : ""}
|
|
205
|
+
${liveReloadScript}
|
|
206
|
+
${clientScriptTag}
|
|
111
207
|
</body>
|
|
112
208
|
</html>`;
|
|
113
209
|
}
|
|
@@ -260,6 +356,7 @@ class HTMXAdapter {
|
|
|
260
356
|
<div id="root">
|
|
261
357
|
${html}
|
|
262
358
|
</div>
|
|
359
|
+
${config?.dev ? getLiveReloadScript() : ""}
|
|
263
360
|
</body>
|
|
264
361
|
</html>`;
|
|
265
362
|
}
|
|
@@ -273,23 +370,19 @@ class Router {
|
|
|
273
370
|
routes = new Map;
|
|
274
371
|
config;
|
|
275
372
|
staticPaths = new Map;
|
|
276
|
-
serverId = Date.now().toString();
|
|
277
373
|
fsRouter;
|
|
278
374
|
routeCache = new Map;
|
|
279
375
|
preloadedRoutes = new Map;
|
|
280
376
|
routePatterns = [];
|
|
281
377
|
apiRoutes = new Map;
|
|
282
|
-
staticApiResponses = new Map;
|
|
283
378
|
static NOT_FOUND_RESPONSE = new Response("404 Not Found", { status: 404 });
|
|
284
379
|
static METHOD_NOT_ALLOWED_RESPONSE = new Response(JSON.stringify({ error: "Method Not Allowed" }), { status: 405, headers: { "Content-Type": "application/json" } });
|
|
285
380
|
static INTERNAL_ERROR_RESPONSE = new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500, headers: { "Content-Type": "application/json" } });
|
|
286
381
|
contextPool = [];
|
|
287
|
-
poolIndex = 0;
|
|
288
382
|
constructor(config) {
|
|
289
383
|
this.config = config;
|
|
290
|
-
const routesDir = config.routesDir.startsWith("/") ? config.routesDir : `${process.cwd()}/${config.routesDir}`;
|
|
291
384
|
this.fsRouter = new Bun.FileSystemRouter({
|
|
292
|
-
dir: routesDir,
|
|
385
|
+
dir: config.routesDir,
|
|
293
386
|
style: "nextjs",
|
|
294
387
|
origin: `http://${config.hostname || "localhost"}:${config.port || 3000}`
|
|
295
388
|
});
|
|
@@ -300,33 +393,25 @@ class Router {
|
|
|
300
393
|
getStaticPaths() {
|
|
301
394
|
return this.staticPaths;
|
|
302
395
|
}
|
|
303
|
-
async loadRoutes() {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
await this.preloadAllRoutes();
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
console.log("\uD83D\uDCCB Preloaded routes:");
|
|
314
|
-
for (const [route, exports] of this.preloadedRoutes.entries()) {
|
|
315
|
-
console.log(` ${route} (${route.includes("[") ? "dynamic" : "static"})`);
|
|
316
|
-
}
|
|
317
|
-
console.log("\uD83D\uDCCB Dynamic route patterns:", this.routePatterns.length);
|
|
318
|
-
for (const pattern of this.routePatterns) {
|
|
319
|
-
console.log(` ${pattern.pattern} -> ${pattern.filePath}`);
|
|
320
|
-
}
|
|
396
|
+
async loadRoutes(silent = false) {
|
|
397
|
+
this.fsRouter.reload();
|
|
398
|
+
this.routeCache.clear();
|
|
399
|
+
this.preloadedRoutes.clear();
|
|
400
|
+
this.routePatterns.length = 0;
|
|
401
|
+
this.apiRoutes.clear();
|
|
402
|
+
this.staticHtmlFiles.clear();
|
|
403
|
+
await this.preloadAllRoutes(silent);
|
|
404
|
+
if (!silent) {
|
|
405
|
+
console.log("\uD83D\uDD04 FileSystemRouter initialized with", this.preloadedRoutes.size, "routes");
|
|
321
406
|
}
|
|
322
407
|
}
|
|
323
|
-
async preloadAllRoutes() {
|
|
324
|
-
|
|
325
|
-
await this.scanAndPreloadRoutes(routesDir, "");
|
|
408
|
+
async preloadAllRoutes(silent = false) {
|
|
409
|
+
await this.scanAndPreloadRoutes(this.config.routesDir, "", silent);
|
|
326
410
|
}
|
|
327
|
-
|
|
411
|
+
staticHtmlFiles = new Map;
|
|
412
|
+
async scanAndPreloadRoutes(dir, _basePath, _silent = false) {
|
|
328
413
|
try {
|
|
329
|
-
const proc = Bun.spawn(["find", dir, "-name", "*.ts", "-o", "-name", "*.tsx", "-o", "-name", "*.js", "-o", "-name", "*.jsx"], {
|
|
414
|
+
const proc = Bun.spawn(["find", dir, "-name", "*.ts", "-o", "-name", "*.tsx", "-o", "-name", "*.js", "-o", "-name", "*.jsx", "-o", "-name", "*.html"], {
|
|
330
415
|
stdout: "pipe"
|
|
331
416
|
});
|
|
332
417
|
const output = await new Response(proc.stdout).text();
|
|
@@ -334,13 +419,18 @@ class Router {
|
|
|
334
419
|
`).filter(Boolean);
|
|
335
420
|
for (const filePath of files) {
|
|
336
421
|
const relativePath = filePath.replace(dir, "");
|
|
337
|
-
|
|
422
|
+
const isHtml = filePath.endsWith(".html");
|
|
423
|
+
let routePath = relativePath.replace(/\.(tsx?|jsx?|html)$/, "");
|
|
338
424
|
if (routePath.endsWith("/index")) {
|
|
339
425
|
routePath = routePath.slice(0, -6) || "/";
|
|
340
426
|
}
|
|
341
427
|
if (!routePath.startsWith("/")) {
|
|
342
428
|
routePath = "/" + routePath;
|
|
343
429
|
}
|
|
430
|
+
if (isHtml) {
|
|
431
|
+
this.staticHtmlFiles.set(routePath, filePath);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
344
434
|
const routeType = this.getRouteType(routePath);
|
|
345
435
|
try {
|
|
346
436
|
const routeExports = await import(filePath);
|
|
@@ -367,32 +457,29 @@ class Router {
|
|
|
367
457
|
console.warn("Failed to scan routes:", error);
|
|
368
458
|
}
|
|
369
459
|
}
|
|
460
|
+
getStaticHtmlFile(path) {
|
|
461
|
+
return this.staticHtmlFiles.get(path);
|
|
462
|
+
}
|
|
370
463
|
createRoutePattern(routePath) {
|
|
371
464
|
const paramNames = [];
|
|
372
465
|
let pattern = routePath.replace(/[.*+?^${}|\\]/g, "\\$&");
|
|
373
|
-
pattern = pattern.replace(/\\?\[([^\]]+)\\?\]/g, (
|
|
466
|
+
pattern = pattern.replace(/\\?\[([^\]]+)\\?\]/g, (_match, paramName) => {
|
|
374
467
|
paramNames.push(paramName);
|
|
375
468
|
return "([^/]+)";
|
|
376
469
|
});
|
|
377
470
|
try {
|
|
378
471
|
const regex = new RegExp(`^${pattern}$`);
|
|
379
|
-
|
|
380
|
-
return {
|
|
381
|
-
regex,
|
|
382
|
-
paramNames
|
|
383
|
-
};
|
|
472
|
+
return { regex, paramNames };
|
|
384
473
|
} catch (error) {
|
|
385
474
|
console.warn(`Failed to create pattern for ${routePath}:`, error);
|
|
386
475
|
return null;
|
|
387
476
|
}
|
|
388
477
|
}
|
|
389
478
|
getRouteType(pathname) {
|
|
390
|
-
if (pathname.startsWith("/api"))
|
|
479
|
+
if (pathname.startsWith("/api"))
|
|
391
480
|
return "api";
|
|
392
|
-
|
|
393
|
-
if (pathname.includes("[") || pathname.includes(":")) {
|
|
481
|
+
if (pathname.includes("[") || pathname.includes(":"))
|
|
394
482
|
return "dynamic";
|
|
395
|
-
}
|
|
396
483
|
return "static";
|
|
397
484
|
}
|
|
398
485
|
matchRoute(path) {
|
|
@@ -416,8 +503,7 @@ class Router {
|
|
|
416
503
|
routePattern.paramNames.forEach((name, index) => {
|
|
417
504
|
params[name] = regexMatch[index + 1];
|
|
418
505
|
});
|
|
419
|
-
|
|
420
|
-
let routePath = routePattern.filePath.replace(routesDir, "").replace(/\.(tsx?|jsx?)$/, "");
|
|
506
|
+
let routePath = routePattern.filePath.replace(this.config.routesDir, "").replace(/\.(tsx?|jsx?)$/, "");
|
|
421
507
|
if (!routePath.startsWith("/")) {
|
|
422
508
|
routePath = "/" + routePath;
|
|
423
509
|
}
|
|
@@ -437,21 +523,6 @@ class Router {
|
|
|
437
523
|
this.routeCache.set(path, match);
|
|
438
524
|
return match;
|
|
439
525
|
}
|
|
440
|
-
pathMatchesPattern(pattern, path) {
|
|
441
|
-
const patternParts = pattern.split("/").filter(Boolean);
|
|
442
|
-
const pathParts = path.split("/").filter(Boolean);
|
|
443
|
-
if (patternParts.length !== pathParts.length) {
|
|
444
|
-
return false;
|
|
445
|
-
}
|
|
446
|
-
for (let i = 0;i < patternParts.length; i++) {
|
|
447
|
-
const patternPart = patternParts[i];
|
|
448
|
-
const pathPart = pathParts[i];
|
|
449
|
-
if (!patternPart.startsWith("[") && patternPart !== pathPart) {
|
|
450
|
-
return false;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
return true;
|
|
454
|
-
}
|
|
455
526
|
async handleRequest(request) {
|
|
456
527
|
const url = request.url;
|
|
457
528
|
const pathStart = url.indexOf("/", 8);
|
|
@@ -460,11 +531,10 @@ class Router {
|
|
|
460
531
|
if (path.startsWith("/api/")) {
|
|
461
532
|
return this.handleApiRouteFast(request, path);
|
|
462
533
|
}
|
|
463
|
-
if (path.endsWith(".css") || path === "/favicon.ico"
|
|
534
|
+
if (path.endsWith(".css") || path === "/favicon.ico") {
|
|
464
535
|
const staticResponse = await this.handleStaticFile(path);
|
|
465
|
-
if (staticResponse)
|
|
536
|
+
if (staticResponse)
|
|
466
537
|
return staticResponse;
|
|
467
|
-
}
|
|
468
538
|
}
|
|
469
539
|
if (!this.config.dev) {
|
|
470
540
|
const outDir = this.config.outDir || "./dist";
|
|
@@ -473,11 +543,17 @@ class Router {
|
|
|
473
543
|
if (await file.exists()) {
|
|
474
544
|
return new Response(file);
|
|
475
545
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
546
|
+
}
|
|
547
|
+
const htmlFilePath = this.staticHtmlFiles.get(path);
|
|
548
|
+
if (htmlFilePath) {
|
|
549
|
+
const file = Bun.file(htmlFilePath);
|
|
550
|
+
if (await file.exists()) {
|
|
551
|
+
return new Response(file, {
|
|
552
|
+
headers: {
|
|
553
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
554
|
+
"Cache-Control": this.config.dev ? "no-cache" : "public, max-age=3600"
|
|
555
|
+
}
|
|
556
|
+
});
|
|
481
557
|
}
|
|
482
558
|
}
|
|
483
559
|
const match = this.matchRoute(path);
|
|
@@ -509,7 +585,7 @@ class Router {
|
|
|
509
585
|
return data;
|
|
510
586
|
}
|
|
511
587
|
const meta = exports.meta || {};
|
|
512
|
-
const html = await this.renderPage(exports.default, { data, params, state: context.state }, exports.ui, meta);
|
|
588
|
+
const html = await this.renderPage(exports.default, { data, params, state: context.state }, exports.ui, meta, { clientScript: exports.clientScript });
|
|
513
589
|
const cacheControl = routeType === "dynamic" ? "no-cache" : "public, max-age=3600";
|
|
514
590
|
this.returnContextToPool(context);
|
|
515
591
|
return new Response(html, {
|
|
@@ -520,9 +596,8 @@ class Router {
|
|
|
520
596
|
});
|
|
521
597
|
} catch (error) {
|
|
522
598
|
this.returnContextToPool(context);
|
|
523
|
-
if (error instanceof Response)
|
|
599
|
+
if (error instanceof Response)
|
|
524
600
|
return error;
|
|
525
|
-
}
|
|
526
601
|
console.error("Error rendering page:", error);
|
|
527
602
|
return Router.INTERNAL_ERROR_RESPONSE;
|
|
528
603
|
}
|
|
@@ -539,7 +614,7 @@ class Router {
|
|
|
539
614
|
params[name] = regexMatch[index + 1];
|
|
540
615
|
});
|
|
541
616
|
for (const [preloadedPath, preloadedExports] of this.preloadedRoutes.entries()) {
|
|
542
|
-
if (preloadedPath.includes("[") && preloadedPath.startsWith("/api/")
|
|
617
|
+
if (preloadedPath.includes("[") && preloadedPath.startsWith("/api/")) {
|
|
543
618
|
exports = preloadedExports;
|
|
544
619
|
break;
|
|
545
620
|
}
|
|
@@ -550,25 +625,22 @@ class Router {
|
|
|
550
625
|
}
|
|
551
626
|
}
|
|
552
627
|
}
|
|
553
|
-
if (!exports)
|
|
628
|
+
if (!exports)
|
|
554
629
|
return Router.NOT_FOUND_RESPONSE;
|
|
555
|
-
|
|
556
|
-
const method = request.method;
|
|
557
|
-
const handler = exports[method] || exports.default;
|
|
630
|
+
const handler = exports[request.method] || exports.default;
|
|
558
631
|
if (!handler || typeof handler !== "function") {
|
|
559
632
|
return Router.METHOD_NOT_ALLOWED_RESPONSE;
|
|
560
633
|
}
|
|
561
634
|
try {
|
|
562
|
-
const context = this.getMinimalApiContext(request,
|
|
635
|
+
const context = this.getMinimalApiContext(request, params);
|
|
563
636
|
const response = await handler(context);
|
|
564
|
-
if (response instanceof Response)
|
|
637
|
+
if (response instanceof Response)
|
|
565
638
|
return response;
|
|
566
|
-
}
|
|
567
639
|
return new Response(JSON.stringify(response), {
|
|
568
640
|
headers: { "Content-Type": "application/json" }
|
|
569
641
|
});
|
|
570
642
|
} catch (error) {
|
|
571
|
-
console.error(`API Error [${method} ${path}]:`, error);
|
|
643
|
+
console.error(`API Error [${request.method} ${path}]:`, error);
|
|
572
644
|
return Router.INTERNAL_ERROR_RESPONSE;
|
|
573
645
|
}
|
|
574
646
|
}
|
|
@@ -593,7 +665,7 @@ class Router {
|
|
|
593
665
|
this.contextPool.push(context);
|
|
594
666
|
}
|
|
595
667
|
}
|
|
596
|
-
getMinimalApiContext(request,
|
|
668
|
+
getMinimalApiContext(request, params = {}) {
|
|
597
669
|
return {
|
|
598
670
|
request,
|
|
599
671
|
params,
|
|
@@ -610,62 +682,24 @@ class Router {
|
|
|
610
682
|
return new Response(cssFile, {
|
|
611
683
|
headers: {
|
|
612
684
|
"Content-Type": "text/css",
|
|
613
|
-
"Cache-Control": "public, max-age=3600"
|
|
685
|
+
"Cache-Control": this.config.dev ? "no-cache" : "public, max-age=3600"
|
|
614
686
|
}
|
|
615
687
|
});
|
|
616
688
|
}
|
|
617
|
-
} catch
|
|
689
|
+
} catch {}
|
|
618
690
|
}
|
|
619
691
|
if (path === "/favicon.ico") {
|
|
620
692
|
return new Response(null, { status: 204 });
|
|
621
693
|
}
|
|
622
|
-
if (path === "/_kilat/live-reload") {
|
|
623
|
-
const serverId = this.serverId;
|
|
624
|
-
return new Response(new ReadableStream({
|
|
625
|
-
start(controller) {
|
|
626
|
-
controller.enqueue(`data: ${serverId}
|
|
627
|
-
|
|
628
|
-
`);
|
|
629
|
-
}
|
|
630
|
-
}), {
|
|
631
|
-
headers: {
|
|
632
|
-
"Content-Type": "text/event-stream",
|
|
633
|
-
"Cache-Control": "no-cache",
|
|
634
|
-
Connection: "keep-alive"
|
|
635
|
-
}
|
|
636
|
-
});
|
|
637
|
-
}
|
|
638
694
|
return null;
|
|
639
695
|
}
|
|
640
|
-
|
|
641
|
-
const html = `<!DOCTYPE html>
|
|
642
|
-
<html lang="en">
|
|
643
|
-
<head>
|
|
644
|
-
<title>404 - Page Not Found</title>
|
|
645
|
-
<meta charset="utf-8" />
|
|
646
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
647
|
-
<link rel="stylesheet" href="/styles.css" />
|
|
648
|
-
</head>
|
|
649
|
-
<body>
|
|
650
|
-
<div id="root">
|
|
651
|
-
<div class="container">
|
|
652
|
-
<h1>404 - Page Not Found</h1>
|
|
653
|
-
<p>The page you're looking for doesn't exist.</p>
|
|
654
|
-
<a href="/">Go back home</a>
|
|
655
|
-
</div>
|
|
656
|
-
</div>
|
|
657
|
-
</body>
|
|
658
|
-
</html>`;
|
|
659
|
-
return new Response(html, {
|
|
660
|
-
status: 404,
|
|
661
|
-
headers: { "Content-Type": "text/html; charset=utf-8" }
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
async renderPage(PageComponent, props, uiFramework = "react", meta = {}) {
|
|
696
|
+
async renderPage(PageComponent, props, uiFramework = "react", meta = {}, options = {}) {
|
|
665
697
|
switch (uiFramework) {
|
|
666
698
|
case "react":
|
|
667
699
|
const reactContent = await ReactAdapter.renderToString(PageComponent, props);
|
|
668
|
-
return ReactAdapter.createDocument(reactContent, meta, this.config
|
|
700
|
+
return ReactAdapter.createDocument(reactContent, meta, this.config, {
|
|
701
|
+
clientScript: options.clientScript
|
|
702
|
+
});
|
|
669
703
|
case "htmx":
|
|
670
704
|
const htmxContent = await HTMXAdapter.renderToString(PageComponent, props);
|
|
671
705
|
return HTMXAdapter.createDocument(htmxContent, meta, this.config);
|
|
@@ -678,71 +712,172 @@ class Router {
|
|
|
678
712
|
class KilatServer {
|
|
679
713
|
router;
|
|
680
714
|
config;
|
|
715
|
+
appDir;
|
|
716
|
+
routesDir;
|
|
681
717
|
constructor(config) {
|
|
682
718
|
this.config = config;
|
|
683
|
-
|
|
719
|
+
const { appDir, routesDir } = this.resolvePaths(config);
|
|
720
|
+
this.appDir = appDir;
|
|
721
|
+
this.routesDir = routesDir;
|
|
722
|
+
this.router = new Router({ ...config, routesDir: this.routesDir });
|
|
723
|
+
}
|
|
724
|
+
resolvePaths(config) {
|
|
725
|
+
const cwd = process.cwd();
|
|
726
|
+
const appDir = config.appDir.startsWith("/") ? config.appDir : `${cwd}/${config.appDir}`;
|
|
727
|
+
const routesPath = `${appDir}/routes`;
|
|
728
|
+
const pagesPath = `${appDir}/pages`;
|
|
729
|
+
const checkRoutes = Bun.spawnSync(["test", "-d", routesPath]);
|
|
730
|
+
if (checkRoutes.exitCode === 0) {
|
|
731
|
+
return { appDir, routesDir: routesPath };
|
|
732
|
+
}
|
|
733
|
+
const checkPages = Bun.spawnSync(["test", "-d", pagesPath]);
|
|
734
|
+
if (checkPages.exitCode === 0) {
|
|
735
|
+
return { appDir, routesDir: pagesPath };
|
|
736
|
+
}
|
|
737
|
+
console.warn(`⚠️ No routes/ or pages/ folder found in ${appDir}, defaulting to routes/`);
|
|
738
|
+
return { appDir, routesDir: routesPath };
|
|
684
739
|
}
|
|
685
740
|
async start() {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
741
|
+
const config = this.config;
|
|
742
|
+
const router = this.router;
|
|
743
|
+
if (config.tailwind?.enabled) {
|
|
744
|
+
await this.runTailwind(false, config.dev);
|
|
745
|
+
if (config.dev) {
|
|
746
|
+
this.runTailwind(true, true);
|
|
691
747
|
}
|
|
692
748
|
}
|
|
693
|
-
await
|
|
694
|
-
const
|
|
695
|
-
const
|
|
749
|
+
await router.loadRoutes(config.dev);
|
|
750
|
+
const routes = {};
|
|
751
|
+
const cssPath = config.tailwind?.cssPath || "./styles.css";
|
|
752
|
+
routes["/styles.css"] = async () => {
|
|
753
|
+
const file = Bun.file(cssPath);
|
|
754
|
+
if (await file.exists()) {
|
|
755
|
+
return new Response(file, {
|
|
756
|
+
headers: { "Content-Type": "text/css" }
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
return new Response("", { headers: { "Content-Type": "text/css" } });
|
|
760
|
+
};
|
|
696
761
|
const server = Bun.serve({
|
|
697
762
|
port: config.port || 3000,
|
|
698
763
|
hostname: config.hostname || "localhost",
|
|
764
|
+
development: config.dev ? {
|
|
765
|
+
hmr: true,
|
|
766
|
+
console: true
|
|
767
|
+
} : false,
|
|
768
|
+
routes,
|
|
699
769
|
async fetch(request) {
|
|
700
770
|
return router.handleRequest(request);
|
|
701
771
|
}
|
|
702
772
|
});
|
|
703
|
-
console.log(`
|
|
704
|
-
\uD83D\uDE80 KilatJS Server Running!
|
|
705
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
706
|
-
Local: http://${server.hostname}:${server.port}
|
|
707
|
-
Mode: ${config.dev ? "Development" : "Production"}
|
|
708
|
-
━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
709
|
-
`);
|
|
710
773
|
if (config.dev) {
|
|
711
|
-
console.log(
|
|
712
|
-
|
|
774
|
+
console.log(`
|
|
775
|
+
⚡ KilatJS Dev Server
|
|
776
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
777
|
+
➜ http://${server.hostname}:${server.port}
|
|
778
|
+
➜ HMR + Live Reload enabled
|
|
779
|
+
➜ Edit files and see changes instantly
|
|
780
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
781
|
+
`);
|
|
782
|
+
startLiveReload();
|
|
783
|
+
watchDirectory(this.routesDir, async () => {
|
|
784
|
+
await router.loadRoutes(true);
|
|
785
|
+
notifyReload();
|
|
786
|
+
});
|
|
787
|
+
const componentsDir = `${this.appDir}/components`;
|
|
788
|
+
const checkComponents = Bun.spawnSync(["test", "-d", componentsDir]);
|
|
789
|
+
if (checkComponents.exitCode === 0) {
|
|
790
|
+
watchDirectory(componentsDir, async () => {
|
|
791
|
+
await router.loadRoutes(true);
|
|
792
|
+
notifyReload();
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
console.log(`
|
|
797
|
+
\uD83D\uDE80 KilatJS Production Server
|
|
798
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
799
|
+
➜ http://${server.hostname}:${server.port}
|
|
800
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
801
|
+
`);
|
|
713
802
|
}
|
|
714
803
|
return server;
|
|
715
804
|
}
|
|
716
805
|
async buildStatic() {
|
|
806
|
+
console.log(`
|
|
807
|
+
\uD83D\uDD28 KilatJS Production Build
|
|
808
|
+
`);
|
|
717
809
|
if (this.config.tailwind?.enabled) {
|
|
718
|
-
await this.runTailwind(false);
|
|
810
|
+
await this.runTailwind(false, false);
|
|
719
811
|
}
|
|
720
|
-
await this.router.loadRoutes();
|
|
721
|
-
console.log(`\uD83D\uDD28 Building for hybrid deployment...
|
|
722
|
-
`);
|
|
812
|
+
await this.router.loadRoutes(true);
|
|
723
813
|
await this.ensureDir(this.config.outDir);
|
|
724
|
-
|
|
725
|
-
console.log("─────────────────────────────────────");
|
|
726
|
-
console.log(" Using Bun's built-in FileSystemRouter for dynamic routing");
|
|
727
|
-
console.log(" Static assets will be copied to output directory");
|
|
814
|
+
await this.displayRouteAnalysis();
|
|
728
815
|
await this.copyStaticAssets();
|
|
729
816
|
await this.generateProductionServer();
|
|
730
817
|
console.log(`
|
|
731
|
-
✅
|
|
818
|
+
✅ Build Complete!
|
|
732
819
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
733
|
-
|
|
734
|
-
Output: ${import.meta.dir}/../${this.config.outDir}
|
|
820
|
+
Output: ${this.config.outDir}
|
|
735
821
|
|
|
736
|
-
|
|
822
|
+
Start: bun ${this.config.outDir}/server.js
|
|
737
823
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
824
|
+
`);
|
|
825
|
+
}
|
|
826
|
+
async displayRouteAnalysis() {
|
|
827
|
+
console.log("\uD83D\uDCC4 Routes Analysis:");
|
|
828
|
+
console.log("─────────────────────────────────────────────────────────");
|
|
829
|
+
const routes = [];
|
|
830
|
+
try {
|
|
831
|
+
const proc = Bun.spawn(["find", this.routesDir, "-name", "*.ts", "-o", "-name", "*.tsx", "-o", "-name", "*.js", "-o", "-name", "*.jsx"], {
|
|
832
|
+
stdout: "pipe"
|
|
833
|
+
});
|
|
834
|
+
const output = await new Response(proc.stdout).text();
|
|
835
|
+
const files = output.trim().split(`
|
|
836
|
+
`).filter(Boolean);
|
|
837
|
+
for (const filePath of files) {
|
|
838
|
+
const relativePath = filePath.replace(this.routesDir, "");
|
|
839
|
+
let routePath = relativePath.replace(/\.(tsx?|jsx?)$/, "");
|
|
840
|
+
if (routePath.endsWith("/index")) {
|
|
841
|
+
routePath = routePath.slice(0, -6) || "/";
|
|
842
|
+
}
|
|
843
|
+
if (!routePath.startsWith("/")) {
|
|
844
|
+
routePath = "/" + routePath;
|
|
845
|
+
}
|
|
846
|
+
let type;
|
|
847
|
+
if (routePath.startsWith("/api")) {
|
|
848
|
+
type = "API";
|
|
849
|
+
} else if (routePath.includes("[")) {
|
|
850
|
+
type = "Dynamic SSR";
|
|
851
|
+
} else {
|
|
852
|
+
type = "SSR";
|
|
853
|
+
}
|
|
854
|
+
const shortFile = relativePath.replace(/^\//, "");
|
|
855
|
+
routes.push({ path: routePath, type, file: shortFile });
|
|
856
|
+
}
|
|
857
|
+
} catch (error) {
|
|
858
|
+
console.warn("Failed to analyze routes:", error);
|
|
859
|
+
}
|
|
860
|
+
routes.sort((a, b) => a.path.localeCompare(b.path));
|
|
861
|
+
const maxPathLen = Math.max(...routes.map((r) => r.path.length), 10);
|
|
862
|
+
const maxTypeLen = Math.max(...routes.map((r) => r.type.length), 6);
|
|
863
|
+
for (const route of routes) {
|
|
864
|
+
const typeIcon = route.type === "API" ? "⚡" : route.type === "Dynamic SSR" ? "\uD83D\uDD04" : "\uD83D\uDCC4";
|
|
865
|
+
const paddedPath = route.path.padEnd(maxPathLen);
|
|
866
|
+
const paddedType = route.type.padEnd(maxTypeLen);
|
|
867
|
+
console.log(` ${typeIcon} ${paddedPath} ${paddedType} ${route.file}`);
|
|
868
|
+
}
|
|
869
|
+
console.log("─────────────────────────────────────────────────────────");
|
|
870
|
+
const apiCount = routes.filter((r) => r.type === "API").length;
|
|
871
|
+
const dynamicCount = routes.filter((r) => r.type === "Dynamic SSR").length;
|
|
872
|
+
const ssrCount = routes.filter((r) => r.type === "SSR").length;
|
|
873
|
+
console.log(` Total: ${routes.length} routes (${ssrCount} SSR, ${dynamicCount} Dynamic, ${apiCount} API)
|
|
738
874
|
`);
|
|
739
875
|
}
|
|
740
876
|
async generateProductionServer() {
|
|
741
877
|
console.log(`
|
|
742
878
|
⚙️ Generating production server with FileSystemRouter...`);
|
|
743
|
-
const srcDir = this.config.routesDir.replace("/routes", "");
|
|
744
879
|
const srcOutDir = `${this.config.outDir}/src`;
|
|
745
|
-
await this.copyDir(
|
|
880
|
+
await this.copyDir(this.appDir, srcOutDir);
|
|
746
881
|
console.log(` ✓ src/ (copied with all components and routes)`);
|
|
747
882
|
const serverCode = `#!/usr/bin/env bun
|
|
748
883
|
/**
|
|
@@ -833,7 +968,9 @@ Bun.serve({
|
|
|
833
968
|
return new Response("404 Not Found", { status: 404 });
|
|
834
969
|
}
|
|
835
970
|
|
|
836
|
-
|
|
971
|
+
// Use relative path for cleaner logs
|
|
972
|
+
const relativePath = match.filePath.replace(ROOT + "/", "");
|
|
973
|
+
console.log(\`Route matched: \${path} -> \${relativePath}\`);
|
|
837
974
|
|
|
838
975
|
try {
|
|
839
976
|
// Dynamically import the route
|
|
@@ -903,17 +1040,11 @@ Bun.serve({
|
|
|
903
1040
|
await Bun.write(serverPath, serverCode);
|
|
904
1041
|
console.log(` ✓ server.js (FileSystemRouter-based)`);
|
|
905
1042
|
}
|
|
906
|
-
|
|
907
|
-
if (bytes < 1024)
|
|
908
|
-
return `${bytes} B`;
|
|
909
|
-
if (bytes < 1024 * 1024)
|
|
910
|
-
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
911
|
-
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
912
|
-
}
|
|
913
|
-
async runTailwind(watch) {
|
|
1043
|
+
async runTailwind(watch, silent = false) {
|
|
914
1044
|
const { inputPath, cssPath } = this.config.tailwind || {};
|
|
915
1045
|
if (!inputPath || !cssPath) {
|
|
916
|
-
|
|
1046
|
+
if (!silent)
|
|
1047
|
+
console.warn("⚠️ Tailwind enabled but inputPath or cssPath missing");
|
|
917
1048
|
return;
|
|
918
1049
|
}
|
|
919
1050
|
const resolvedInputPath = inputPath.startsWith("/") ? inputPath : `${process.cwd()}/${inputPath}`;
|
|
@@ -922,18 +1053,21 @@ Bun.serve({
|
|
|
922
1053
|
if (watch) {
|
|
923
1054
|
args.push("--watch");
|
|
924
1055
|
}
|
|
925
|
-
|
|
1056
|
+
if (!silent) {
|
|
1057
|
+
console.log(`\uD83C\uDFA8 ${watch ? "Watching" : "Building"} Tailwind CSS...`);
|
|
1058
|
+
}
|
|
926
1059
|
try {
|
|
927
1060
|
const proc = Bun.spawn(args, {
|
|
928
|
-
stdout: "inherit",
|
|
929
|
-
stderr: "inherit",
|
|
1061
|
+
stdout: silent ? "pipe" : "inherit",
|
|
1062
|
+
stderr: silent ? "pipe" : "inherit",
|
|
930
1063
|
cwd: process.cwd()
|
|
931
1064
|
});
|
|
932
1065
|
if (!watch) {
|
|
933
1066
|
await proc.exited;
|
|
934
1067
|
}
|
|
935
1068
|
} catch (error) {
|
|
936
|
-
|
|
1069
|
+
if (!silent)
|
|
1070
|
+
console.error("Failed to run Tailwind:", error);
|
|
937
1071
|
}
|
|
938
1072
|
}
|
|
939
1073
|
async copyStaticAssets() {
|
|
@@ -979,7 +1113,7 @@ Bun.serve({
|
|
|
979
1113
|
}
|
|
980
1114
|
// src/index.ts
|
|
981
1115
|
var defaultConfig = {
|
|
982
|
-
|
|
1116
|
+
appDir: "./src",
|
|
983
1117
|
outDir: "./dist",
|
|
984
1118
|
port: 3000,
|
|
985
1119
|
hostname: "localhost",
|
|
@@ -1020,6 +1154,93 @@ function defineConfig(config) {
|
|
|
1020
1154
|
function defineMeta(meta) {
|
|
1021
1155
|
return meta;
|
|
1022
1156
|
}
|
|
1157
|
+
async function runCLI(args = process.argv.slice(2)) {
|
|
1158
|
+
const command = args[0];
|
|
1159
|
+
if (!command) {
|
|
1160
|
+
console.error("Please specify a command: dev or build");
|
|
1161
|
+
process.exit(1);
|
|
1162
|
+
}
|
|
1163
|
+
async function loadConfig() {
|
|
1164
|
+
const configPath = `${process.cwd()}/kilat.config.ts`;
|
|
1165
|
+
const file = Bun.file(configPath);
|
|
1166
|
+
if (!await file.exists()) {
|
|
1167
|
+
console.warn("⚠️ No kilat.config.ts found, using defaults");
|
|
1168
|
+
return {};
|
|
1169
|
+
}
|
|
1170
|
+
try {
|
|
1171
|
+
const configModule = await import(configPath);
|
|
1172
|
+
return configModule.default || configModule;
|
|
1173
|
+
} catch (error) {
|
|
1174
|
+
console.error("Failed to load config:", error);
|
|
1175
|
+
return {};
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const config = await loadConfig();
|
|
1179
|
+
switch (command) {
|
|
1180
|
+
case "dev":
|
|
1181
|
+
const isProduction = args.includes("--production");
|
|
1182
|
+
const devServer = createKilat({ ...config, dev: !isProduction });
|
|
1183
|
+
await devServer.start();
|
|
1184
|
+
break;
|
|
1185
|
+
case "build":
|
|
1186
|
+
await buildStatic(config);
|
|
1187
|
+
break;
|
|
1188
|
+
case "serve":
|
|
1189
|
+
const serveOutDir = config.outDir || "./dist";
|
|
1190
|
+
const serverPath = `${process.cwd()}/${serveOutDir}/server.js`;
|
|
1191
|
+
const serverFile = Bun.file(serverPath);
|
|
1192
|
+
if (!await serverFile.exists()) {
|
|
1193
|
+
console.error(`❌ Production server not found at ${serveOutDir}/server.js`);
|
|
1194
|
+
console.error(` Run 'kilat build' first to generate the production build.`);
|
|
1195
|
+
process.exit(1);
|
|
1196
|
+
}
|
|
1197
|
+
const serveProc = Bun.spawn(["bun", serverPath], {
|
|
1198
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
1199
|
+
cwd: process.cwd()
|
|
1200
|
+
});
|
|
1201
|
+
await serveProc.exited;
|
|
1202
|
+
break;
|
|
1203
|
+
case "preview":
|
|
1204
|
+
const outDir = config.outDir || "./dist";
|
|
1205
|
+
const port = config.port || 3000;
|
|
1206
|
+
const hostname = config.hostname || "localhost";
|
|
1207
|
+
const root = `${process.cwd()}/${outDir}`;
|
|
1208
|
+
console.log(`
|
|
1209
|
+
\uD83D\uDD0D Preview server running:`);
|
|
1210
|
+
console.log(` ➜ http://${hostname}:${port}`);
|
|
1211
|
+
console.log(` Serving: ${outDir}
|
|
1212
|
+
`);
|
|
1213
|
+
Bun.serve({
|
|
1214
|
+
port,
|
|
1215
|
+
hostname,
|
|
1216
|
+
async fetch(req) {
|
|
1217
|
+
const url = new URL(req.url);
|
|
1218
|
+
let path = url.pathname;
|
|
1219
|
+
const filePath = `${root}${path}`;
|
|
1220
|
+
let file = Bun.file(filePath);
|
|
1221
|
+
if (await file.exists()) {
|
|
1222
|
+
return new Response(file);
|
|
1223
|
+
}
|
|
1224
|
+
const indexHtml = `${filePath}/index.html`;
|
|
1225
|
+
file = Bun.file(indexHtml);
|
|
1226
|
+
if (await file.exists()) {
|
|
1227
|
+
return new Response(file);
|
|
1228
|
+
}
|
|
1229
|
+
const rootIndex = `${root}/index.html`;
|
|
1230
|
+
file = Bun.file(rootIndex);
|
|
1231
|
+
if (await file.exists()) {
|
|
1232
|
+
return new Response(file);
|
|
1233
|
+
}
|
|
1234
|
+
return new Response("404 Not Found", { status: 404 });
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
break;
|
|
1238
|
+
default:
|
|
1239
|
+
console.error(`Unknown command: ${command}`);
|
|
1240
|
+
console.log("Available commands: dev, build, serve, preview");
|
|
1241
|
+
process.exit(1);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1023
1244
|
var Kilat = {
|
|
1024
1245
|
createKilat,
|
|
1025
1246
|
startDevServer,
|
|
@@ -1027,11 +1248,16 @@ var Kilat = {
|
|
|
1027
1248
|
defineConfig,
|
|
1028
1249
|
defineLoader,
|
|
1029
1250
|
defineMeta,
|
|
1251
|
+
runCLI,
|
|
1030
1252
|
defaultConfig
|
|
1031
1253
|
};
|
|
1032
1254
|
var src_default = Kilat;
|
|
1033
1255
|
export {
|
|
1256
|
+
stopLiveReload,
|
|
1257
|
+
startLiveReload,
|
|
1034
1258
|
startDevServer,
|
|
1259
|
+
runCLI,
|
|
1260
|
+
notifyReload,
|
|
1035
1261
|
defineMeta,
|
|
1036
1262
|
defineLoader,
|
|
1037
1263
|
defineConfig,
|