@cloudwerk/cli 0.4.0 → 0.6.0
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/dist/index.js +1289 -93
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { program } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/dev.ts
|
|
7
|
-
import * as
|
|
8
|
-
import * as
|
|
7
|
+
import * as path10 from "path";
|
|
8
|
+
import * as fs10 from "fs";
|
|
9
9
|
import * as os from "os";
|
|
10
10
|
import { serve } from "@hono/node-server";
|
|
11
11
|
import {
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
buildRouteManifest,
|
|
15
15
|
resolveLayouts,
|
|
16
16
|
resolveMiddleware,
|
|
17
|
-
resolveRoutesDir,
|
|
17
|
+
resolveRoutesDir as resolveRoutesDir2,
|
|
18
18
|
hasErrors,
|
|
19
19
|
formatErrors,
|
|
20
20
|
formatWarnings
|
|
@@ -112,11 +112,11 @@ function printError(message, suggestion) {
|
|
|
112
112
|
}
|
|
113
113
|
console.log();
|
|
114
114
|
}
|
|
115
|
-
function logRequest(method,
|
|
115
|
+
function logRequest(method, path16, status, duration) {
|
|
116
116
|
const methodColor = getMethodColor(method);
|
|
117
117
|
const statusColor = status >= 400 ? pc.red : status >= 300 ? pc.yellow : pc.green;
|
|
118
118
|
console.log(
|
|
119
|
-
pc.dim("[") + methodColor(method.padEnd(6)) + pc.dim("]") + " " +
|
|
119
|
+
pc.dim("[") + methodColor(method.padEnd(6)) + pc.dim("]") + " " + path16 + " " + statusColor(String(status)) + " " + pc.dim(`${duration}ms`)
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
@@ -237,26 +237,26 @@ var handleParsingNestedValues = (form, key, value) => {
|
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
// ../../node_modules/.pnpm/hono@4.11.5/node_modules/hono/dist/utils/url.js
|
|
240
|
-
var splitPath = (
|
|
241
|
-
const paths =
|
|
240
|
+
var splitPath = (path16) => {
|
|
241
|
+
const paths = path16.split("/");
|
|
242
242
|
if (paths[0] === "") {
|
|
243
243
|
paths.shift();
|
|
244
244
|
}
|
|
245
245
|
return paths;
|
|
246
246
|
};
|
|
247
247
|
var splitRoutingPath = (routePath) => {
|
|
248
|
-
const { groups, path:
|
|
249
|
-
const paths = splitPath(
|
|
248
|
+
const { groups, path: path16 } = extractGroupsFromPath(routePath);
|
|
249
|
+
const paths = splitPath(path16);
|
|
250
250
|
return replaceGroupMarks(paths, groups);
|
|
251
251
|
};
|
|
252
|
-
var extractGroupsFromPath = (
|
|
252
|
+
var extractGroupsFromPath = (path16) => {
|
|
253
253
|
const groups = [];
|
|
254
|
-
|
|
254
|
+
path16 = path16.replace(/\{[^}]+\}/g, (match2, index) => {
|
|
255
255
|
const mark = `@${index}`;
|
|
256
256
|
groups.push([mark, match2]);
|
|
257
257
|
return mark;
|
|
258
258
|
});
|
|
259
|
-
return { groups, path:
|
|
259
|
+
return { groups, path: path16 };
|
|
260
260
|
};
|
|
261
261
|
var replaceGroupMarks = (paths, groups) => {
|
|
262
262
|
for (let i = groups.length - 1; i >= 0; i--) {
|
|
@@ -311,8 +311,8 @@ var getPath = (request) => {
|
|
|
311
311
|
const charCode = url.charCodeAt(i);
|
|
312
312
|
if (charCode === 37) {
|
|
313
313
|
const queryIndex = url.indexOf("?", i);
|
|
314
|
-
const
|
|
315
|
-
return tryDecodeURI(
|
|
314
|
+
const path16 = url.slice(start, queryIndex === -1 ? void 0 : queryIndex);
|
|
315
|
+
return tryDecodeURI(path16.includes("%25") ? path16.replace(/%25/g, "%2525") : path16);
|
|
316
316
|
} else if (charCode === 63) {
|
|
317
317
|
break;
|
|
318
318
|
}
|
|
@@ -329,11 +329,11 @@ var mergePath = (base, sub, ...rest) => {
|
|
|
329
329
|
}
|
|
330
330
|
return `${base?.[0] === "/" ? "" : "/"}${base}${sub === "/" ? "" : `${base?.at(-1) === "/" ? "" : "/"}${sub?.[0] === "/" ? sub.slice(1) : sub}`}`;
|
|
331
331
|
};
|
|
332
|
-
var checkOptionalParameter = (
|
|
333
|
-
if (
|
|
332
|
+
var checkOptionalParameter = (path16) => {
|
|
333
|
+
if (path16.charCodeAt(path16.length - 1) !== 63 || !path16.includes(":")) {
|
|
334
334
|
return null;
|
|
335
335
|
}
|
|
336
|
-
const segments =
|
|
336
|
+
const segments = path16.split("/");
|
|
337
337
|
const results = [];
|
|
338
338
|
let basePath = "";
|
|
339
339
|
segments.forEach((segment) => {
|
|
@@ -474,9 +474,9 @@ var HonoRequest = class {
|
|
|
474
474
|
*/
|
|
475
475
|
path;
|
|
476
476
|
bodyCache = {};
|
|
477
|
-
constructor(request,
|
|
477
|
+
constructor(request, path16 = "/", matchResult = [[]]) {
|
|
478
478
|
this.raw = request;
|
|
479
|
-
this.path =
|
|
479
|
+
this.path = path16;
|
|
480
480
|
this.#matchResult = matchResult;
|
|
481
481
|
this.#validatedData = {};
|
|
482
482
|
}
|
|
@@ -1212,8 +1212,8 @@ var Hono = class _Hono {
|
|
|
1212
1212
|
return this;
|
|
1213
1213
|
};
|
|
1214
1214
|
});
|
|
1215
|
-
this.on = (method,
|
|
1216
|
-
for (const p of [
|
|
1215
|
+
this.on = (method, path16, ...handlers) => {
|
|
1216
|
+
for (const p of [path16].flat()) {
|
|
1217
1217
|
this.#path = p;
|
|
1218
1218
|
for (const m of [method].flat()) {
|
|
1219
1219
|
handlers.map((handler) => {
|
|
@@ -1270,8 +1270,8 @@ var Hono = class _Hono {
|
|
|
1270
1270
|
* app.route("/api", app2) // GET /api/user
|
|
1271
1271
|
* ```
|
|
1272
1272
|
*/
|
|
1273
|
-
route(
|
|
1274
|
-
const subApp = this.basePath(
|
|
1273
|
+
route(path16, app) {
|
|
1274
|
+
const subApp = this.basePath(path16);
|
|
1275
1275
|
app.routes.map((r) => {
|
|
1276
1276
|
let handler;
|
|
1277
1277
|
if (app.errorHandler === errorHandler) {
|
|
@@ -1297,9 +1297,9 @@ var Hono = class _Hono {
|
|
|
1297
1297
|
* const api = new Hono().basePath('/api')
|
|
1298
1298
|
* ```
|
|
1299
1299
|
*/
|
|
1300
|
-
basePath(
|
|
1300
|
+
basePath(path16) {
|
|
1301
1301
|
const subApp = this.#clone();
|
|
1302
|
-
subApp._basePath = mergePath(this._basePath,
|
|
1302
|
+
subApp._basePath = mergePath(this._basePath, path16);
|
|
1303
1303
|
return subApp;
|
|
1304
1304
|
}
|
|
1305
1305
|
/**
|
|
@@ -1373,7 +1373,7 @@ var Hono = class _Hono {
|
|
|
1373
1373
|
* })
|
|
1374
1374
|
* ```
|
|
1375
1375
|
*/
|
|
1376
|
-
mount(
|
|
1376
|
+
mount(path16, applicationHandler, options) {
|
|
1377
1377
|
let replaceRequest;
|
|
1378
1378
|
let optionHandler;
|
|
1379
1379
|
if (options) {
|
|
@@ -1400,7 +1400,7 @@ var Hono = class _Hono {
|
|
|
1400
1400
|
return [c.env, executionContext];
|
|
1401
1401
|
};
|
|
1402
1402
|
replaceRequest ||= (() => {
|
|
1403
|
-
const mergedPath = mergePath(this._basePath,
|
|
1403
|
+
const mergedPath = mergePath(this._basePath, path16);
|
|
1404
1404
|
const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
|
|
1405
1405
|
return (request) => {
|
|
1406
1406
|
const url = new URL(request.url);
|
|
@@ -1415,14 +1415,14 @@ var Hono = class _Hono {
|
|
|
1415
1415
|
}
|
|
1416
1416
|
await next();
|
|
1417
1417
|
};
|
|
1418
|
-
this.#addRoute(METHOD_NAME_ALL, mergePath(
|
|
1418
|
+
this.#addRoute(METHOD_NAME_ALL, mergePath(path16, "*"), handler);
|
|
1419
1419
|
return this;
|
|
1420
1420
|
}
|
|
1421
|
-
#addRoute(method,
|
|
1421
|
+
#addRoute(method, path16, handler) {
|
|
1422
1422
|
method = method.toUpperCase();
|
|
1423
|
-
|
|
1424
|
-
const r = { basePath: this._basePath, path:
|
|
1425
|
-
this.router.add(method,
|
|
1423
|
+
path16 = mergePath(this._basePath, path16);
|
|
1424
|
+
const r = { basePath: this._basePath, path: path16, method, handler };
|
|
1425
|
+
this.router.add(method, path16, [handler, r]);
|
|
1426
1426
|
this.routes.push(r);
|
|
1427
1427
|
}
|
|
1428
1428
|
#handleError(err, c) {
|
|
@@ -1435,10 +1435,10 @@ var Hono = class _Hono {
|
|
|
1435
1435
|
if (method === "HEAD") {
|
|
1436
1436
|
return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
|
|
1437
1437
|
}
|
|
1438
|
-
const
|
|
1439
|
-
const matchResult = this.router.match(method,
|
|
1438
|
+
const path16 = this.getPath(request, { env });
|
|
1439
|
+
const matchResult = this.router.match(method, path16);
|
|
1440
1440
|
const c = new Context(request, {
|
|
1441
|
-
path:
|
|
1441
|
+
path: path16,
|
|
1442
1442
|
matchResult,
|
|
1443
1443
|
env,
|
|
1444
1444
|
executionCtx,
|
|
@@ -1538,7 +1538,7 @@ var Hono = class _Hono {
|
|
|
1538
1538
|
|
|
1539
1539
|
// ../../node_modules/.pnpm/hono@4.11.5/node_modules/hono/dist/router/reg-exp-router/matcher.js
|
|
1540
1540
|
var emptyParam = [];
|
|
1541
|
-
function match(method,
|
|
1541
|
+
function match(method, path16) {
|
|
1542
1542
|
const matchers = this.buildAllMatchers();
|
|
1543
1543
|
const match2 = ((method2, path22) => {
|
|
1544
1544
|
const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];
|
|
@@ -1554,7 +1554,7 @@ function match(method, path10) {
|
|
|
1554
1554
|
return [matcher[1][index], match3];
|
|
1555
1555
|
});
|
|
1556
1556
|
this.match = match2;
|
|
1557
|
-
return match2(method,
|
|
1557
|
+
return match2(method, path16);
|
|
1558
1558
|
}
|
|
1559
1559
|
|
|
1560
1560
|
// ../../node_modules/.pnpm/hono@4.11.5/node_modules/hono/dist/router/reg-exp-router/node.js
|
|
@@ -1669,12 +1669,12 @@ var Node = class _Node {
|
|
|
1669
1669
|
var Trie = class {
|
|
1670
1670
|
#context = { varIndex: 0 };
|
|
1671
1671
|
#root = new Node();
|
|
1672
|
-
insert(
|
|
1672
|
+
insert(path16, index, pathErrorCheckOnly) {
|
|
1673
1673
|
const paramAssoc = [];
|
|
1674
1674
|
const groups = [];
|
|
1675
1675
|
for (let i = 0; ; ) {
|
|
1676
1676
|
let replaced = false;
|
|
1677
|
-
|
|
1677
|
+
path16 = path16.replace(/\{[^}]+\}/g, (m) => {
|
|
1678
1678
|
const mark = `@\\${i}`;
|
|
1679
1679
|
groups[i] = [mark, m];
|
|
1680
1680
|
i++;
|
|
@@ -1685,7 +1685,7 @@ var Trie = class {
|
|
|
1685
1685
|
break;
|
|
1686
1686
|
}
|
|
1687
1687
|
}
|
|
1688
|
-
const tokens =
|
|
1688
|
+
const tokens = path16.match(/(?::[^\/]+)|(?:\/\*$)|./g) || [];
|
|
1689
1689
|
for (let i = groups.length - 1; i >= 0; i--) {
|
|
1690
1690
|
const [mark] = groups[i];
|
|
1691
1691
|
for (let j = tokens.length - 1; j >= 0; j--) {
|
|
@@ -1724,9 +1724,9 @@ var Trie = class {
|
|
|
1724
1724
|
// ../../node_modules/.pnpm/hono@4.11.5/node_modules/hono/dist/router/reg-exp-router/router.js
|
|
1725
1725
|
var nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];
|
|
1726
1726
|
var wildcardRegExpCache = /* @__PURE__ */ Object.create(null);
|
|
1727
|
-
function buildWildcardRegExp(
|
|
1728
|
-
return wildcardRegExpCache[
|
|
1729
|
-
|
|
1727
|
+
function buildWildcardRegExp(path16) {
|
|
1728
|
+
return wildcardRegExpCache[path16] ??= new RegExp(
|
|
1729
|
+
path16 === "*" ? "" : `^${path16.replace(
|
|
1730
1730
|
/\/\*$|([.\\+*[^\]$()])/g,
|
|
1731
1731
|
(_, metaChar) => metaChar ? `\\${metaChar}` : "(?:|/.*)"
|
|
1732
1732
|
)}$`
|
|
@@ -1748,17 +1748,17 @@ function buildMatcherFromPreprocessedRoutes(routes) {
|
|
|
1748
1748
|
);
|
|
1749
1749
|
const staticMap = /* @__PURE__ */ Object.create(null);
|
|
1750
1750
|
for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {
|
|
1751
|
-
const [pathErrorCheckOnly,
|
|
1751
|
+
const [pathErrorCheckOnly, path16, handlers] = routesWithStaticPathFlag[i];
|
|
1752
1752
|
if (pathErrorCheckOnly) {
|
|
1753
|
-
staticMap[
|
|
1753
|
+
staticMap[path16] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];
|
|
1754
1754
|
} else {
|
|
1755
1755
|
j++;
|
|
1756
1756
|
}
|
|
1757
1757
|
let paramAssoc;
|
|
1758
1758
|
try {
|
|
1759
|
-
paramAssoc = trie.insert(
|
|
1759
|
+
paramAssoc = trie.insert(path16, j, pathErrorCheckOnly);
|
|
1760
1760
|
} catch (e) {
|
|
1761
|
-
throw e === PATH_ERROR ? new UnsupportedPathError(
|
|
1761
|
+
throw e === PATH_ERROR ? new UnsupportedPathError(path16) : e;
|
|
1762
1762
|
}
|
|
1763
1763
|
if (pathErrorCheckOnly) {
|
|
1764
1764
|
continue;
|
|
@@ -1792,12 +1792,12 @@ function buildMatcherFromPreprocessedRoutes(routes) {
|
|
|
1792
1792
|
}
|
|
1793
1793
|
return [regexp, handlerMap, staticMap];
|
|
1794
1794
|
}
|
|
1795
|
-
function findMiddleware(middleware,
|
|
1795
|
+
function findMiddleware(middleware, path16) {
|
|
1796
1796
|
if (!middleware) {
|
|
1797
1797
|
return void 0;
|
|
1798
1798
|
}
|
|
1799
1799
|
for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {
|
|
1800
|
-
if (buildWildcardRegExp(k).test(
|
|
1800
|
+
if (buildWildcardRegExp(k).test(path16)) {
|
|
1801
1801
|
return [...middleware[k]];
|
|
1802
1802
|
}
|
|
1803
1803
|
}
|
|
@@ -1811,7 +1811,7 @@ var RegExpRouter = class {
|
|
|
1811
1811
|
this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
|
|
1812
1812
|
this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };
|
|
1813
1813
|
}
|
|
1814
|
-
add(method,
|
|
1814
|
+
add(method, path16, handler) {
|
|
1815
1815
|
const middleware = this.#middleware;
|
|
1816
1816
|
const routes = this.#routes;
|
|
1817
1817
|
if (!middleware || !routes) {
|
|
@@ -1826,18 +1826,18 @@ var RegExpRouter = class {
|
|
|
1826
1826
|
});
|
|
1827
1827
|
});
|
|
1828
1828
|
}
|
|
1829
|
-
if (
|
|
1830
|
-
|
|
1829
|
+
if (path16 === "/*") {
|
|
1830
|
+
path16 = "*";
|
|
1831
1831
|
}
|
|
1832
|
-
const paramCount = (
|
|
1833
|
-
if (/\*$/.test(
|
|
1834
|
-
const re = buildWildcardRegExp(
|
|
1832
|
+
const paramCount = (path16.match(/\/:/g) || []).length;
|
|
1833
|
+
if (/\*$/.test(path16)) {
|
|
1834
|
+
const re = buildWildcardRegExp(path16);
|
|
1835
1835
|
if (method === METHOD_NAME_ALL) {
|
|
1836
1836
|
Object.keys(middleware).forEach((m) => {
|
|
1837
|
-
middleware[m][
|
|
1837
|
+
middleware[m][path16] ||= findMiddleware(middleware[m], path16) || findMiddleware(middleware[METHOD_NAME_ALL], path16) || [];
|
|
1838
1838
|
});
|
|
1839
1839
|
} else {
|
|
1840
|
-
middleware[method][
|
|
1840
|
+
middleware[method][path16] ||= findMiddleware(middleware[method], path16) || findMiddleware(middleware[METHOD_NAME_ALL], path16) || [];
|
|
1841
1841
|
}
|
|
1842
1842
|
Object.keys(middleware).forEach((m) => {
|
|
1843
1843
|
if (method === METHOD_NAME_ALL || method === m) {
|
|
@@ -1855,7 +1855,7 @@ var RegExpRouter = class {
|
|
|
1855
1855
|
});
|
|
1856
1856
|
return;
|
|
1857
1857
|
}
|
|
1858
|
-
const paths = checkOptionalParameter(
|
|
1858
|
+
const paths = checkOptionalParameter(path16) || [path16];
|
|
1859
1859
|
for (let i = 0, len = paths.length; i < len; i++) {
|
|
1860
1860
|
const path22 = paths[i];
|
|
1861
1861
|
Object.keys(routes).forEach((m) => {
|
|
@@ -1882,13 +1882,13 @@ var RegExpRouter = class {
|
|
|
1882
1882
|
const routes = [];
|
|
1883
1883
|
let hasOwnRoute = method === METHOD_NAME_ALL;
|
|
1884
1884
|
[this.#middleware, this.#routes].forEach((r) => {
|
|
1885
|
-
const ownRoute = r[method] ? Object.keys(r[method]).map((
|
|
1885
|
+
const ownRoute = r[method] ? Object.keys(r[method]).map((path16) => [path16, r[method][path16]]) : [];
|
|
1886
1886
|
if (ownRoute.length !== 0) {
|
|
1887
1887
|
hasOwnRoute ||= true;
|
|
1888
1888
|
routes.push(...ownRoute);
|
|
1889
1889
|
} else if (method !== METHOD_NAME_ALL) {
|
|
1890
1890
|
routes.push(
|
|
1891
|
-
...Object.keys(r[METHOD_NAME_ALL]).map((
|
|
1891
|
+
...Object.keys(r[METHOD_NAME_ALL]).map((path16) => [path16, r[METHOD_NAME_ALL][path16]])
|
|
1892
1892
|
);
|
|
1893
1893
|
}
|
|
1894
1894
|
});
|
|
@@ -1908,13 +1908,13 @@ var SmartRouter = class {
|
|
|
1908
1908
|
constructor(init) {
|
|
1909
1909
|
this.#routers = init.routers;
|
|
1910
1910
|
}
|
|
1911
|
-
add(method,
|
|
1911
|
+
add(method, path16, handler) {
|
|
1912
1912
|
if (!this.#routes) {
|
|
1913
1913
|
throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);
|
|
1914
1914
|
}
|
|
1915
|
-
this.#routes.push([method,
|
|
1915
|
+
this.#routes.push([method, path16, handler]);
|
|
1916
1916
|
}
|
|
1917
|
-
match(method,
|
|
1917
|
+
match(method, path16) {
|
|
1918
1918
|
if (!this.#routes) {
|
|
1919
1919
|
throw new Error("Fatal error");
|
|
1920
1920
|
}
|
|
@@ -1929,7 +1929,7 @@ var SmartRouter = class {
|
|
|
1929
1929
|
for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {
|
|
1930
1930
|
router.add(...routes[i2]);
|
|
1931
1931
|
}
|
|
1932
|
-
res = router.match(method,
|
|
1932
|
+
res = router.match(method, path16);
|
|
1933
1933
|
} catch (e) {
|
|
1934
1934
|
if (e instanceof UnsupportedPathError) {
|
|
1935
1935
|
continue;
|
|
@@ -1973,10 +1973,10 @@ var Node2 = class _Node2 {
|
|
|
1973
1973
|
}
|
|
1974
1974
|
this.#patterns = [];
|
|
1975
1975
|
}
|
|
1976
|
-
insert(method,
|
|
1976
|
+
insert(method, path16, handler) {
|
|
1977
1977
|
this.#order = ++this.#order;
|
|
1978
1978
|
let curNode = this;
|
|
1979
|
-
const parts = splitRoutingPath(
|
|
1979
|
+
const parts = splitRoutingPath(path16);
|
|
1980
1980
|
const possibleKeys = [];
|
|
1981
1981
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
1982
1982
|
const p = parts[i];
|
|
@@ -2027,12 +2027,12 @@ var Node2 = class _Node2 {
|
|
|
2027
2027
|
}
|
|
2028
2028
|
return handlerSets;
|
|
2029
2029
|
}
|
|
2030
|
-
search(method,
|
|
2030
|
+
search(method, path16) {
|
|
2031
2031
|
const handlerSets = [];
|
|
2032
2032
|
this.#params = emptyParams;
|
|
2033
2033
|
const curNode = this;
|
|
2034
2034
|
let curNodes = [curNode];
|
|
2035
|
-
const parts = splitPath(
|
|
2035
|
+
const parts = splitPath(path16);
|
|
2036
2036
|
const curNodesQueue = [];
|
|
2037
2037
|
for (let i = 0, len = parts.length; i < len; i++) {
|
|
2038
2038
|
const part = parts[i];
|
|
@@ -2120,18 +2120,18 @@ var TrieRouter = class {
|
|
|
2120
2120
|
constructor() {
|
|
2121
2121
|
this.#node = new Node2();
|
|
2122
2122
|
}
|
|
2123
|
-
add(method,
|
|
2124
|
-
const results = checkOptionalParameter(
|
|
2123
|
+
add(method, path16, handler) {
|
|
2124
|
+
const results = checkOptionalParameter(path16);
|
|
2125
2125
|
if (results) {
|
|
2126
2126
|
for (let i = 0, len = results.length; i < len; i++) {
|
|
2127
2127
|
this.#node.insert(method, results[i], handler);
|
|
2128
2128
|
}
|
|
2129
2129
|
return;
|
|
2130
2130
|
}
|
|
2131
|
-
this.#node.insert(method,
|
|
2131
|
+
this.#node.insert(method, path16, handler);
|
|
2132
2132
|
}
|
|
2133
|
-
match(method,
|
|
2134
|
-
return this.#node.search(method,
|
|
2133
|
+
match(method, path16) {
|
|
2134
|
+
return this.#node.search(method, path16);
|
|
2135
2135
|
}
|
|
2136
2136
|
};
|
|
2137
2137
|
|
|
@@ -2151,7 +2151,7 @@ var Hono2 = class extends Hono {
|
|
|
2151
2151
|
};
|
|
2152
2152
|
|
|
2153
2153
|
// src/server/createApp.ts
|
|
2154
|
-
import { contextMiddleware } from "@cloudwerk/core";
|
|
2154
|
+
import { contextMiddleware, resolveRoutesDir } from "@cloudwerk/core";
|
|
2155
2155
|
import { setActiveRenderer, getAvailableRenderers } from "@cloudwerk/ui";
|
|
2156
2156
|
|
|
2157
2157
|
// src/server/registerRoutes.ts
|
|
@@ -2165,7 +2165,13 @@ import {
|
|
|
2165
2165
|
resolveNotFoundBoundary,
|
|
2166
2166
|
resolveLoadingBoundary
|
|
2167
2167
|
} from "@cloudwerk/core";
|
|
2168
|
-
import {
|
|
2168
|
+
import {
|
|
2169
|
+
render,
|
|
2170
|
+
renderStream,
|
|
2171
|
+
renderToStream,
|
|
2172
|
+
generateHydrationScript,
|
|
2173
|
+
generatePreloadHints
|
|
2174
|
+
} from "@cloudwerk/ui";
|
|
2169
2175
|
|
|
2170
2176
|
// src/server/loadHandler.ts
|
|
2171
2177
|
import * as fs from "fs";
|
|
@@ -2347,7 +2353,7 @@ import * as path3 from "path";
|
|
|
2347
2353
|
import { builtinModules as builtinModules3 } from "module";
|
|
2348
2354
|
import { build as build3 } from "esbuild";
|
|
2349
2355
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
2350
|
-
import { validateRouteConfig as validateRouteConfig2 } from "@cloudwerk/core";
|
|
2356
|
+
import { validateRouteConfig as validateRouteConfig2, hasUseClientDirective, generateComponentId, validateComponentBoundaries, handleBoundaryValidationResult } from "@cloudwerk/core";
|
|
2351
2357
|
var pageModuleCache = /* @__PURE__ */ new Map();
|
|
2352
2358
|
async function loadPageModule(absolutePath, verbose = false) {
|
|
2353
2359
|
try {
|
|
@@ -2387,6 +2393,11 @@ async function loadPageModule(absolutePath, verbose = false) {
|
|
|
2387
2393
|
throw new Error("No output from esbuild");
|
|
2388
2394
|
}
|
|
2389
2395
|
const code = result.outputFiles[0].text;
|
|
2396
|
+
const sourceCode = fs3.readFileSync(absolutePath, "utf-8");
|
|
2397
|
+
const isClientComponent = hasUseClientDirective(sourceCode);
|
|
2398
|
+
const clientComponentId = isClientComponent ? generateComponentId(absolutePath, path3.dirname(absolutePath)) : void 0;
|
|
2399
|
+
const validationResult = validateComponentBoundaries(sourceCode, absolutePath, isClientComponent);
|
|
2400
|
+
handleBoundaryValidationResult(validationResult, absolutePath, { verbose });
|
|
2390
2401
|
const cacheKey = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2391
2402
|
const tempDir = findSafeTempDir3(absolutePath);
|
|
2392
2403
|
const tempFile = path3.join(tempDir, `.cloudwerk-page-${cacheKey}.mjs`);
|
|
@@ -2435,12 +2446,24 @@ async function loadPageModule(absolutePath, verbose = false) {
|
|
|
2435
2446
|
validatedMethods[method] = rawModule[method];
|
|
2436
2447
|
}
|
|
2437
2448
|
}
|
|
2449
|
+
let validatedGenerateStaticParams = void 0;
|
|
2450
|
+
if ("generateStaticParams" in rawModule && rawModule.generateStaticParams !== void 0) {
|
|
2451
|
+
if (typeof rawModule.generateStaticParams !== "function") {
|
|
2452
|
+
throw new Error(
|
|
2453
|
+
`Page generateStaticParams export must be a function, got ${typeof rawModule.generateStaticParams}`
|
|
2454
|
+
);
|
|
2455
|
+
}
|
|
2456
|
+
validatedGenerateStaticParams = rawModule.generateStaticParams;
|
|
2457
|
+
}
|
|
2438
2458
|
const module = {
|
|
2439
2459
|
default: rawModule.default,
|
|
2440
2460
|
config: validatedConfig,
|
|
2441
2461
|
loader: validatedLoader,
|
|
2442
2462
|
action: validatedAction,
|
|
2443
|
-
...validatedMethods
|
|
2463
|
+
...validatedMethods,
|
|
2464
|
+
isClientComponent,
|
|
2465
|
+
clientComponentId,
|
|
2466
|
+
generateStaticParams: validatedGenerateStaticParams
|
|
2444
2467
|
};
|
|
2445
2468
|
pageModuleCache.set(absolutePath, { module, mtime });
|
|
2446
2469
|
return module;
|
|
@@ -2474,6 +2497,7 @@ import * as path4 from "path";
|
|
|
2474
2497
|
import { builtinModules as builtinModules4 } from "module";
|
|
2475
2498
|
import { build as build4 } from "esbuild";
|
|
2476
2499
|
import { pathToFileURL as pathToFileURL4 } from "url";
|
|
2500
|
+
import { hasUseClientDirective as hasUseClientDirective2, generateComponentId as generateComponentId2, validateComponentBoundaries as validateComponentBoundaries2, handleBoundaryValidationResult as handleBoundaryValidationResult2 } from "@cloudwerk/core";
|
|
2477
2501
|
var layoutModuleCache = /* @__PURE__ */ new Map();
|
|
2478
2502
|
async function loadLayoutModule(absolutePath, verbose = false) {
|
|
2479
2503
|
try {
|
|
@@ -2513,6 +2537,11 @@ async function loadLayoutModule(absolutePath, verbose = false) {
|
|
|
2513
2537
|
throw new Error("No output from esbuild");
|
|
2514
2538
|
}
|
|
2515
2539
|
const code = result.outputFiles[0].text;
|
|
2540
|
+
const sourceCode = fs4.readFileSync(absolutePath, "utf-8");
|
|
2541
|
+
const isClientComponent = hasUseClientDirective2(sourceCode);
|
|
2542
|
+
const clientComponentId = isClientComponent ? generateComponentId2(absolutePath, path4.dirname(absolutePath)) : void 0;
|
|
2543
|
+
const validationResult = validateComponentBoundaries2(sourceCode, absolutePath, isClientComponent);
|
|
2544
|
+
handleBoundaryValidationResult2(validationResult, absolutePath, { verbose });
|
|
2516
2545
|
const cacheKey = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2517
2546
|
const tempDir = findSafeTempDir4(absolutePath);
|
|
2518
2547
|
const tempFile = path4.join(tempDir, `.cloudwerk-layout-${cacheKey}.mjs`);
|
|
@@ -2538,7 +2567,9 @@ async function loadLayoutModule(absolutePath, verbose = false) {
|
|
|
2538
2567
|
}
|
|
2539
2568
|
const module = {
|
|
2540
2569
|
default: rawModule.default,
|
|
2541
|
-
loader: validatedLoader
|
|
2570
|
+
loader: validatedLoader,
|
|
2571
|
+
isClientComponent,
|
|
2572
|
+
clientComponentId
|
|
2542
2573
|
};
|
|
2543
2574
|
layoutModuleCache.set(absolutePath, { module, mtime });
|
|
2544
2575
|
return module;
|
|
@@ -2875,6 +2906,63 @@ function parseSearchParams(c) {
|
|
|
2875
2906
|
return result;
|
|
2876
2907
|
}
|
|
2877
2908
|
|
|
2909
|
+
// src/server/hydrationManifest.ts
|
|
2910
|
+
import * as fs8 from "fs";
|
|
2911
|
+
import { createHydrationManifest, addToHydrationManifest, hasUseClientDirective as hasUseClientDirective3, generateComponentId as generateComponentId3 } from "@cloudwerk/core";
|
|
2912
|
+
function createManifestTracker(appDir, basePath = "/__cloudwerk") {
|
|
2913
|
+
return {
|
|
2914
|
+
components: /* @__PURE__ */ new Map(),
|
|
2915
|
+
basePath,
|
|
2916
|
+
appDir
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
function trackIfClientComponent(tracker, filePath) {
|
|
2920
|
+
if (tracker.components.has(filePath)) {
|
|
2921
|
+
return true;
|
|
2922
|
+
}
|
|
2923
|
+
try {
|
|
2924
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
2925
|
+
if (hasUseClientDirective3(content)) {
|
|
2926
|
+
const componentId = generateComponentId3(filePath, tracker.appDir);
|
|
2927
|
+
tracker.components.set(filePath, {
|
|
2928
|
+
filePath,
|
|
2929
|
+
componentId,
|
|
2930
|
+
exportName: "default",
|
|
2931
|
+
bundled: false
|
|
2932
|
+
});
|
|
2933
|
+
return true;
|
|
2934
|
+
}
|
|
2935
|
+
} catch (error) {
|
|
2936
|
+
return false;
|
|
2937
|
+
}
|
|
2938
|
+
return false;
|
|
2939
|
+
}
|
|
2940
|
+
function createRequestScopedManifest(tracker, usedComponentIds) {
|
|
2941
|
+
const manifest = createHydrationManifest(tracker.basePath);
|
|
2942
|
+
for (const component of tracker.components.values()) {
|
|
2943
|
+
if (usedComponentIds.has(component.componentId)) {
|
|
2944
|
+
addToHydrationManifest(
|
|
2945
|
+
manifest,
|
|
2946
|
+
{
|
|
2947
|
+
filePath: component.filePath,
|
|
2948
|
+
componentId: component.componentId,
|
|
2949
|
+
exportName: component.exportName
|
|
2950
|
+
},
|
|
2951
|
+
`${tracker.basePath}/${component.componentId}.js`
|
|
2952
|
+
);
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
return manifest;
|
|
2956
|
+
}
|
|
2957
|
+
function getComponentByIdFromTracker(tracker, componentId) {
|
|
2958
|
+
for (const component of tracker.components.values()) {
|
|
2959
|
+
if (component.componentId === componentId) {
|
|
2960
|
+
return component;
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
return void 0;
|
|
2964
|
+
}
|
|
2965
|
+
|
|
2878
2966
|
// src/server/registerRoutes.ts
|
|
2879
2967
|
var HTTP_METHODS = [
|
|
2880
2968
|
"GET",
|
|
@@ -2885,11 +2973,44 @@ var HTTP_METHODS = [
|
|
|
2885
2973
|
"OPTIONS",
|
|
2886
2974
|
"HEAD"
|
|
2887
2975
|
];
|
|
2976
|
+
async function injectHydrationScripts(response, hydrationCtx) {
|
|
2977
|
+
if (hydrationCtx.usedComponentIds.size === 0) {
|
|
2978
|
+
return response;
|
|
2979
|
+
}
|
|
2980
|
+
const manifest = createRequestScopedManifest(
|
|
2981
|
+
hydrationCtx.tracker,
|
|
2982
|
+
hydrationCtx.usedComponentIds
|
|
2983
|
+
);
|
|
2984
|
+
const preloadHints = generatePreloadHints(manifest);
|
|
2985
|
+
const hydrationScript = generateHydrationScript(manifest);
|
|
2986
|
+
if (!preloadHints && !hydrationScript) {
|
|
2987
|
+
return response;
|
|
2988
|
+
}
|
|
2989
|
+
const html = await response.text();
|
|
2990
|
+
let modifiedHtml = html;
|
|
2991
|
+
if (preloadHints) {
|
|
2992
|
+
const headEndIndex = modifiedHtml.indexOf("</head>");
|
|
2993
|
+
if (headEndIndex !== -1) {
|
|
2994
|
+
modifiedHtml = modifiedHtml.slice(0, headEndIndex) + "\n" + preloadHints + "\n" + modifiedHtml.slice(headEndIndex);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
if (hydrationScript) {
|
|
2998
|
+
const bodyEndIndex = modifiedHtml.lastIndexOf("</body>");
|
|
2999
|
+
if (bodyEndIndex !== -1) {
|
|
3000
|
+
modifiedHtml = modifiedHtml.slice(0, bodyEndIndex) + "\n" + hydrationScript + "\n" + modifiedHtml.slice(bodyEndIndex);
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
3003
|
+
return new Response(modifiedHtml, {
|
|
3004
|
+
status: response.status,
|
|
3005
|
+
statusText: response.statusText,
|
|
3006
|
+
headers: response.headers
|
|
3007
|
+
});
|
|
3008
|
+
}
|
|
2888
3009
|
function isCloudwerkHandler(fn) {
|
|
2889
3010
|
return typeof fn === "function" && fn.length === 2;
|
|
2890
3011
|
}
|
|
2891
3012
|
function createConfigMiddleware(config) {
|
|
2892
|
-
return async (
|
|
3013
|
+
return async (_c, next) => {
|
|
2893
3014
|
setRouteConfig(config);
|
|
2894
3015
|
await next();
|
|
2895
3016
|
};
|
|
@@ -2919,7 +3040,7 @@ async function executeAction(action, args, c) {
|
|
|
2919
3040
|
throw error;
|
|
2920
3041
|
}
|
|
2921
3042
|
}
|
|
2922
|
-
async function registerRoutes(app, manifest, scanResult, logger, verbose = false) {
|
|
3043
|
+
async function registerRoutes(app, manifest, scanResult, logger, verbose = false, hydrationTracker) {
|
|
2923
3044
|
const registeredRoutes = [];
|
|
2924
3045
|
for (const route of manifest.routes) {
|
|
2925
3046
|
if (route.fileType === "page") {
|
|
@@ -2927,10 +3048,26 @@ async function registerRoutes(app, manifest, scanResult, logger, verbose = false
|
|
|
2927
3048
|
logger.debug(`Loading page: ${route.filePath}`);
|
|
2928
3049
|
const pageModule = await loadPageModule(route.absolutePath, verbose);
|
|
2929
3050
|
const PageComponent = pageModule.default;
|
|
3051
|
+
if (hydrationTracker && pageModule.isClientComponent) {
|
|
3052
|
+
trackIfClientComponent(hydrationTracker, route.absolutePath);
|
|
3053
|
+
if (verbose) {
|
|
3054
|
+
logger.debug(`Tracked client component: ${route.filePath}`);
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
2930
3057
|
const layoutModules = await Promise.all(
|
|
2931
3058
|
route.layouts.map((layoutPath) => loadLayoutModule(layoutPath, verbose))
|
|
2932
3059
|
);
|
|
2933
3060
|
const layouts = layoutModules.map((m) => m.default);
|
|
3061
|
+
if (hydrationTracker) {
|
|
3062
|
+
for (let i = 0; i < layoutModules.length; i++) {
|
|
3063
|
+
if (layoutModules[i].isClientComponent) {
|
|
3064
|
+
trackIfClientComponent(hydrationTracker, route.layouts[i]);
|
|
3065
|
+
if (verbose) {
|
|
3066
|
+
logger.debug(`Tracked client layout: ${route.layouts[i]}`);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
2934
3071
|
const loadingPath = resolveLoadingBoundary(route.filePath, scanResult.loading);
|
|
2935
3072
|
let loadingModule = null;
|
|
2936
3073
|
if (loadingPath) {
|
|
@@ -2967,9 +3104,18 @@ async function registerRoutes(app, manifest, scanResult, logger, verbose = false
|
|
|
2967
3104
|
const request = c.req.raw;
|
|
2968
3105
|
const pathname = new URL(request.url).pathname;
|
|
2969
3106
|
const layoutLoaderData = [];
|
|
3107
|
+
const hydrationCtx = hydrationTracker ? { usedComponentIds: /* @__PURE__ */ new Set(), tracker: hydrationTracker } : null;
|
|
3108
|
+
if (hydrationCtx && pageModule.isClientComponent && pageModule.clientComponentId) {
|
|
3109
|
+
hydrationCtx.usedComponentIds.add(pageModule.clientComponentId);
|
|
3110
|
+
}
|
|
3111
|
+
for (const layoutModule of layoutModules) {
|
|
3112
|
+
if (hydrationCtx && layoutModule.isClientComponent && layoutModule.clientComponentId) {
|
|
3113
|
+
hydrationCtx.usedComponentIds.add(layoutModule.clientComponentId);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
2970
3116
|
const streamingDisabled = pageModule.config?.streaming === false;
|
|
2971
3117
|
const useStreaming = loadingModule && !streamingDisabled;
|
|
2972
|
-
if (useStreaming) {
|
|
3118
|
+
if (useStreaming && loadingModule) {
|
|
2973
3119
|
try {
|
|
2974
3120
|
const LoadingComponent = loadingModule.default;
|
|
2975
3121
|
const loadingProps = { params, searchParams, pathname };
|
|
@@ -3057,7 +3203,11 @@ async function registerRoutes(app, manifest, scanResult, logger, verbose = false
|
|
|
3057
3203
|
};
|
|
3058
3204
|
element = await Promise.resolve(Layout(layoutProps));
|
|
3059
3205
|
}
|
|
3060
|
-
|
|
3206
|
+
const response = await renderToStream(element);
|
|
3207
|
+
if (hydrationCtx && hydrationCtx.usedComponentIds.size > 0) {
|
|
3208
|
+
return injectHydrationScripts(response, hydrationCtx);
|
|
3209
|
+
}
|
|
3210
|
+
return response;
|
|
3061
3211
|
} catch (error) {
|
|
3062
3212
|
if (error instanceof NotFoundError) {
|
|
3063
3213
|
const notFoundPath = resolveNotFoundBoundary(route.filePath, scanResult.notFound);
|
|
@@ -3144,6 +3294,15 @@ async function registerRoutes(app, manifest, scanResult, logger, verbose = false
|
|
|
3144
3294
|
const searchParams = parseSearchParams(c);
|
|
3145
3295
|
const request = c.req.raw;
|
|
3146
3296
|
const layoutLoaderData = [];
|
|
3297
|
+
const actionHydrationCtx = hydrationTracker ? { usedComponentIds: /* @__PURE__ */ new Set(), tracker: hydrationTracker } : null;
|
|
3298
|
+
if (actionHydrationCtx && pageModule.isClientComponent && pageModule.clientComponentId) {
|
|
3299
|
+
actionHydrationCtx.usedComponentIds.add(pageModule.clientComponentId);
|
|
3300
|
+
}
|
|
3301
|
+
for (const layoutModule of layoutModules) {
|
|
3302
|
+
if (actionHydrationCtx && layoutModule.isClientComponent && layoutModule.clientComponentId) {
|
|
3303
|
+
actionHydrationCtx.usedComponentIds.add(layoutModule.clientComponentId);
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3147
3306
|
try {
|
|
3148
3307
|
const actionArgs = { params, request, context: c };
|
|
3149
3308
|
const actionResult = await executeAction(actionFn, actionArgs, c);
|
|
@@ -3187,7 +3346,11 @@ async function registerRoutes(app, manifest, scanResult, logger, verbose = false
|
|
|
3187
3346
|
};
|
|
3188
3347
|
element = await Promise.resolve(Layout(layoutProps));
|
|
3189
3348
|
}
|
|
3190
|
-
|
|
3349
|
+
const actionResponse = await renderToStream(element);
|
|
3350
|
+
if (actionHydrationCtx && actionHydrationCtx.usedComponentIds.size > 0) {
|
|
3351
|
+
return injectHydrationScripts(actionResponse, actionHydrationCtx);
|
|
3352
|
+
}
|
|
3353
|
+
return actionResponse;
|
|
3191
3354
|
} catch (error) {
|
|
3192
3355
|
if (error instanceof NotFoundError) {
|
|
3193
3356
|
const notFoundPath = resolveNotFoundBoundary(route.filePath, scanResult.notFound);
|
|
@@ -3340,6 +3503,145 @@ function registerMethod(app, method, pattern, handler) {
|
|
|
3340
3503
|
}
|
|
3341
3504
|
}
|
|
3342
3505
|
|
|
3506
|
+
// src/server/hydrationRoutes.ts
|
|
3507
|
+
import {
|
|
3508
|
+
generateHydrationRuntime as generateHydrationRuntime2,
|
|
3509
|
+
generateReactHydrationRuntime as generateReactHydrationRuntime2
|
|
3510
|
+
} from "@cloudwerk/ui";
|
|
3511
|
+
|
|
3512
|
+
// src/server/clientBundle.ts
|
|
3513
|
+
import * as fs9 from "fs";
|
|
3514
|
+
import * as path9 from "path";
|
|
3515
|
+
import { createRequire } from "module";
|
|
3516
|
+
import { build as build8 } from "esbuild";
|
|
3517
|
+
import { generateComponentId as generateComponentId4, createHydrationManifest as createHydrationManifest2, addToHydrationManifest as addToHydrationManifest2 } from "@cloudwerk/core";
|
|
3518
|
+
import { generateHydrationRuntime, generateReactHydrationRuntime } from "@cloudwerk/ui";
|
|
3519
|
+
var require2 = createRequire(import.meta.url);
|
|
3520
|
+
var honoMainPath = require2.resolve("hono");
|
|
3521
|
+
function findNodeModulesDir(filePath) {
|
|
3522
|
+
let dir = filePath;
|
|
3523
|
+
while (dir !== path9.dirname(dir)) {
|
|
3524
|
+
dir = path9.dirname(dir);
|
|
3525
|
+
if (path9.basename(dir) === "node_modules") {
|
|
3526
|
+
return dir;
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3529
|
+
return path9.join(path9.dirname(filePath), "..", "..");
|
|
3530
|
+
}
|
|
3531
|
+
var nodeModulesPath = findNodeModulesDir(honoMainPath);
|
|
3532
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
3533
|
+
async function generateBundleOnDemand(componentPath, appDir, options = {}) {
|
|
3534
|
+
const { renderer = "hono-jsx" } = options;
|
|
3535
|
+
const cacheKey = `${componentPath}:${renderer}`;
|
|
3536
|
+
const stat = fs9.statSync(componentPath);
|
|
3537
|
+
const cached = bundleCache.get(cacheKey);
|
|
3538
|
+
if (cached && cached.mtime === stat.mtimeMs) {
|
|
3539
|
+
return {
|
|
3540
|
+
content: cached.content,
|
|
3541
|
+
componentId: generateComponentId4(componentPath, appDir)
|
|
3542
|
+
};
|
|
3543
|
+
}
|
|
3544
|
+
const jsxImportSource = renderer === "react" ? "react" : "hono/jsx/dom";
|
|
3545
|
+
const result = await build8({
|
|
3546
|
+
entryPoints: [componentPath],
|
|
3547
|
+
bundle: true,
|
|
3548
|
+
write: false,
|
|
3549
|
+
format: "esm",
|
|
3550
|
+
platform: "browser",
|
|
3551
|
+
target: ["es2020", "chrome80", "firefox80", "safari14"],
|
|
3552
|
+
jsx: "automatic",
|
|
3553
|
+
jsxImportSource,
|
|
3554
|
+
minify: false,
|
|
3555
|
+
sourcemap: "inline",
|
|
3556
|
+
define: {
|
|
3557
|
+
"process.env.NODE_ENV": JSON.stringify("development")
|
|
3558
|
+
},
|
|
3559
|
+
logLevel: "silent",
|
|
3560
|
+
// Resolve hono/react from the package's node_modules
|
|
3561
|
+
nodePaths: [nodeModulesPath]
|
|
3562
|
+
});
|
|
3563
|
+
if (!result.outputFiles || result.outputFiles.length === 0) {
|
|
3564
|
+
throw new Error("No output from esbuild");
|
|
3565
|
+
}
|
|
3566
|
+
const content = result.outputFiles[0].text;
|
|
3567
|
+
const componentId = generateComponentId4(componentPath, appDir);
|
|
3568
|
+
bundleCache.set(cacheKey, {
|
|
3569
|
+
content,
|
|
3570
|
+
mtime: stat.mtimeMs,
|
|
3571
|
+
bundlePath: `/__cloudwerk/${componentId}.js`
|
|
3572
|
+
});
|
|
3573
|
+
return { content, componentId };
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
// src/server/hydrationRoutes.ts
|
|
3577
|
+
var runtimeCache = /* @__PURE__ */ new Map();
|
|
3578
|
+
function registerHydrationRoutes(app, options) {
|
|
3579
|
+
const { tracker, appDir, logger, verbose = false, renderer = "hono-jsx" } = options;
|
|
3580
|
+
app.get("/__cloudwerk/runtime.js", (c) => {
|
|
3581
|
+
if (verbose) {
|
|
3582
|
+
logger.debug("Serving Hono JSX hydration runtime");
|
|
3583
|
+
}
|
|
3584
|
+
let runtime = runtimeCache.get("hono-jsx");
|
|
3585
|
+
if (!runtime) {
|
|
3586
|
+
runtime = generateHydrationRuntime2();
|
|
3587
|
+
runtimeCache.set("hono-jsx", runtime);
|
|
3588
|
+
}
|
|
3589
|
+
return c.text(runtime, 200, {
|
|
3590
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
3591
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
3592
|
+
});
|
|
3593
|
+
});
|
|
3594
|
+
app.get("/__cloudwerk/react-runtime.js", (c) => {
|
|
3595
|
+
if (verbose) {
|
|
3596
|
+
logger.debug("Serving React hydration runtime");
|
|
3597
|
+
}
|
|
3598
|
+
let runtime = runtimeCache.get("react");
|
|
3599
|
+
if (!runtime) {
|
|
3600
|
+
runtime = generateReactHydrationRuntime2();
|
|
3601
|
+
runtimeCache.set("react", runtime);
|
|
3602
|
+
}
|
|
3603
|
+
return c.text(runtime, 200, {
|
|
3604
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
3605
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
3606
|
+
});
|
|
3607
|
+
});
|
|
3608
|
+
app.get("/__cloudwerk/:componentId{.+\\.js$}", async (c) => {
|
|
3609
|
+
const componentIdWithExt = c.req.param("componentId");
|
|
3610
|
+
const componentId = componentIdWithExt.replace(/\.js$/, "");
|
|
3611
|
+
if (verbose) {
|
|
3612
|
+
logger.debug(`Serving component bundle: ${componentId}`);
|
|
3613
|
+
}
|
|
3614
|
+
const component = getComponentByIdFromTracker(tracker, componentId);
|
|
3615
|
+
if (!component) {
|
|
3616
|
+
logger.warn(`[Cloudwerk] Component not found: ${componentId}`);
|
|
3617
|
+
return c.text(`// Component not found: ${componentId}`, 404, {
|
|
3618
|
+
"Content-Type": "application/javascript; charset=utf-8"
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
try {
|
|
3622
|
+
const { content } = await generateBundleOnDemand(
|
|
3623
|
+
component.filePath,
|
|
3624
|
+
appDir,
|
|
3625
|
+
{ renderer }
|
|
3626
|
+
);
|
|
3627
|
+
return c.text(content, 200, {
|
|
3628
|
+
"Content-Type": "application/javascript; charset=utf-8",
|
|
3629
|
+
"Cache-Control": "no-cache"
|
|
3630
|
+
// Dev mode - always check for updates
|
|
3631
|
+
});
|
|
3632
|
+
} catch (error) {
|
|
3633
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3634
|
+
logger.error(`[Cloudwerk] Failed to bundle component ${componentId}: ${message}`);
|
|
3635
|
+
return c.text(`// Bundle error: ${message}`, 500, {
|
|
3636
|
+
"Content-Type": "application/javascript; charset=utf-8"
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
});
|
|
3640
|
+
if (verbose) {
|
|
3641
|
+
logger.info("Registered hydration routes at /__cloudwerk/*");
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3343
3645
|
// src/constants.ts
|
|
3344
3646
|
var DEFAULT_PORT = 3e3;
|
|
3345
3647
|
var DEFAULT_HOST = "localhost";
|
|
@@ -3381,7 +3683,16 @@ async function createApp(manifest, scanResult, config, logger, verbose = false)
|
|
|
3381
3683
|
app.use("*", middleware);
|
|
3382
3684
|
}
|
|
3383
3685
|
}
|
|
3384
|
-
const
|
|
3686
|
+
const appDir = resolveRoutesDir(config, manifest.rootDir);
|
|
3687
|
+
const hydrationTracker = createManifestTracker(appDir);
|
|
3688
|
+
registerHydrationRoutes(app, {
|
|
3689
|
+
tracker: hydrationTracker,
|
|
3690
|
+
appDir,
|
|
3691
|
+
logger,
|
|
3692
|
+
verbose,
|
|
3693
|
+
renderer: rendererName === "react" ? "react" : "hono-jsx"
|
|
3694
|
+
});
|
|
3695
|
+
const routes = await registerRoutes(app, manifest, scanResult, logger, verbose, hydrationTracker);
|
|
3385
3696
|
app.notFound((c) => {
|
|
3386
3697
|
return c.json(
|
|
3387
3698
|
{
|
|
@@ -3407,9 +3718,9 @@ async function createApp(manifest, scanResult, config, logger, verbose = false)
|
|
|
3407
3718
|
}
|
|
3408
3719
|
|
|
3409
3720
|
// src/version.ts
|
|
3410
|
-
import { createRequire } from "module";
|
|
3411
|
-
var
|
|
3412
|
-
var pkg =
|
|
3721
|
+
import { createRequire as createRequire2 } from "module";
|
|
3722
|
+
var require3 = createRequire2(import.meta.url);
|
|
3723
|
+
var pkg = require3("../package.json");
|
|
3413
3724
|
var VERSION = pkg.version;
|
|
3414
3725
|
|
|
3415
3726
|
// src/commands/dev.ts
|
|
@@ -3418,8 +3729,8 @@ async function dev(pathArg, options) {
|
|
|
3418
3729
|
const verbose = options.verbose ?? false;
|
|
3419
3730
|
const logger = createLogger(verbose);
|
|
3420
3731
|
try {
|
|
3421
|
-
const cwd = pathArg ?
|
|
3422
|
-
if (!
|
|
3732
|
+
const cwd = pathArg ? path10.resolve(process.cwd(), pathArg) : process.cwd();
|
|
3733
|
+
if (!fs10.existsSync(cwd)) {
|
|
3423
3734
|
throw new CliError(
|
|
3424
3735
|
`Directory does not exist: ${cwd}`,
|
|
3425
3736
|
"ENOENT",
|
|
@@ -3430,9 +3741,9 @@ async function dev(pathArg, options) {
|
|
|
3430
3741
|
logger.debug(`Loading configuration...`);
|
|
3431
3742
|
const config = await loadConfig(cwd);
|
|
3432
3743
|
logger.debug(`Config loaded: routesDir=${config.routesDir}, extensions=${config.extensions.join(", ")}`);
|
|
3433
|
-
const routesDir =
|
|
3744
|
+
const routesDir = resolveRoutesDir2(config, cwd);
|
|
3434
3745
|
logger.debug(`Routes directory: ${routesDir}`);
|
|
3435
|
-
if (!
|
|
3746
|
+
if (!fs10.existsSync(routesDir)) {
|
|
3436
3747
|
throw new CliError(
|
|
3437
3748
|
`Routes directory does not exist: ${routesDir}`,
|
|
3438
3749
|
"ENOENT",
|
|
@@ -3496,8 +3807,8 @@ async function dev(pathArg, options) {
|
|
|
3496
3807
|
process.exit(1);
|
|
3497
3808
|
}
|
|
3498
3809
|
});
|
|
3499
|
-
await new Promise((
|
|
3500
|
-
server.on("listening",
|
|
3810
|
+
await new Promise((resolve3) => {
|
|
3811
|
+
server.on("listening", resolve3);
|
|
3501
3812
|
});
|
|
3502
3813
|
const startupTime = Date.now() - startTime;
|
|
3503
3814
|
const localUrl = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}/`;
|
|
@@ -3556,7 +3867,892 @@ function setupGracefulShutdown(server, logger) {
|
|
|
3556
3867
|
process.on("SIGTERM", shutdown);
|
|
3557
3868
|
}
|
|
3558
3869
|
|
|
3870
|
+
// src/commands/build.ts
|
|
3871
|
+
import * as path12 from "path";
|
|
3872
|
+
import * as fs12 from "fs";
|
|
3873
|
+
import {
|
|
3874
|
+
loadConfig as loadConfig2,
|
|
3875
|
+
scanRoutes as scanRoutes2,
|
|
3876
|
+
buildRouteManifest as buildRouteManifest2,
|
|
3877
|
+
resolveLayouts as resolveLayouts2,
|
|
3878
|
+
resolveMiddleware as resolveMiddleware2,
|
|
3879
|
+
resolveRoutesDir as resolveRoutesDir3,
|
|
3880
|
+
hasErrors as hasErrors2,
|
|
3881
|
+
formatErrors as formatErrors2,
|
|
3882
|
+
formatWarnings as formatWarnings2
|
|
3883
|
+
} from "@cloudwerk/core";
|
|
3884
|
+
|
|
3885
|
+
// src/server/ssg.ts
|
|
3886
|
+
import * as fs11 from "fs/promises";
|
|
3887
|
+
import * as path11 from "path";
|
|
3888
|
+
async function getStaticRoutesAsync(manifest, logger) {
|
|
3889
|
+
const staticRoutes = [];
|
|
3890
|
+
for (const route of manifest.routes) {
|
|
3891
|
+
if (route.fileType !== "page") {
|
|
3892
|
+
continue;
|
|
3893
|
+
}
|
|
3894
|
+
try {
|
|
3895
|
+
const module = await loadPageModule(route.absolutePath);
|
|
3896
|
+
if (module.config?.rendering === "static") {
|
|
3897
|
+
staticRoutes.push({ route, module });
|
|
3898
|
+
logger?.debug(`Found static route: ${route.urlPattern}`);
|
|
3899
|
+
}
|
|
3900
|
+
} catch (error) {
|
|
3901
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3902
|
+
logger?.warn(`Failed to load page module for ${route.urlPattern}: ${errorMessage}`);
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
return staticRoutes;
|
|
3906
|
+
}
|
|
3907
|
+
function hasDynamicSegments(segments) {
|
|
3908
|
+
return segments.some(
|
|
3909
|
+
(segment) => segment.type === "dynamic" || segment.type === "catchAll" || segment.type === "optionalCatchAll"
|
|
3910
|
+
);
|
|
3911
|
+
}
|
|
3912
|
+
function interpolatePath(urlPattern, params) {
|
|
3913
|
+
let result = urlPattern;
|
|
3914
|
+
for (const [key, value] of Object.entries(params)) {
|
|
3915
|
+
const encodedValue = encodeURIComponent(value);
|
|
3916
|
+
result = result.replace(`:${key}`, encodedValue);
|
|
3917
|
+
result = result.replace(`*${key}`, encodedValue);
|
|
3918
|
+
}
|
|
3919
|
+
return result;
|
|
3920
|
+
}
|
|
3921
|
+
function urlPathToOutputFile(urlPath) {
|
|
3922
|
+
let normalizedPath = urlPath;
|
|
3923
|
+
if (normalizedPath.startsWith("/")) {
|
|
3924
|
+
normalizedPath = normalizedPath.slice(1);
|
|
3925
|
+
}
|
|
3926
|
+
if (normalizedPath === "") {
|
|
3927
|
+
return "index.html";
|
|
3928
|
+
}
|
|
3929
|
+
return path11.join(normalizedPath, "index.html");
|
|
3930
|
+
}
|
|
3931
|
+
async function generateStaticSite(app, manifest, outputDir, logger, verbose = false) {
|
|
3932
|
+
const staticRoutes = await getStaticRoutesAsync(manifest, verbose ? logger : void 0);
|
|
3933
|
+
const results = [];
|
|
3934
|
+
await fs11.mkdir(outputDir, { recursive: true });
|
|
3935
|
+
for (const { route, module } of staticRoutes) {
|
|
3936
|
+
try {
|
|
3937
|
+
const urlPaths = await getUrlPathsForRouteWithModule(route, module, verbose ? logger : void 0);
|
|
3938
|
+
for (const urlPath of urlPaths) {
|
|
3939
|
+
const result = await generateStaticPage(app, urlPath, outputDir, logger, verbose);
|
|
3940
|
+
results.push(result);
|
|
3941
|
+
}
|
|
3942
|
+
} catch (error) {
|
|
3943
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3944
|
+
logger.error(`Failed to generate static pages for ${route.urlPattern}: ${errorMessage}`);
|
|
3945
|
+
results.push({
|
|
3946
|
+
urlPath: route.urlPattern,
|
|
3947
|
+
outputFile: urlPathToOutputFile(route.urlPattern),
|
|
3948
|
+
success: false,
|
|
3949
|
+
error: errorMessage
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
const successCount = results.filter((r) => r.success).length;
|
|
3954
|
+
const failureCount = results.filter((r) => !r.success).length;
|
|
3955
|
+
return {
|
|
3956
|
+
totalPages: results.length,
|
|
3957
|
+
successCount,
|
|
3958
|
+
failureCount,
|
|
3959
|
+
routes: results,
|
|
3960
|
+
outputDir
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
async function getUrlPathsForRouteWithModule(route, module, logger) {
|
|
3964
|
+
if (!hasDynamicSegments(route.segments)) {
|
|
3965
|
+
return [route.urlPattern];
|
|
3966
|
+
}
|
|
3967
|
+
if (!module.generateStaticParams) {
|
|
3968
|
+
logger?.warn(
|
|
3969
|
+
`Static route ${route.urlPattern} has dynamic segments but no generateStaticParams export. Skipping.`
|
|
3970
|
+
);
|
|
3971
|
+
return [];
|
|
3972
|
+
}
|
|
3973
|
+
const paramsList = await module.generateStaticParams({});
|
|
3974
|
+
if (!Array.isArray(paramsList)) {
|
|
3975
|
+
throw new Error(
|
|
3976
|
+
`generateStaticParams must return an array, got ${typeof paramsList}`
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
const urlPaths = paramsList.map((params) => {
|
|
3980
|
+
if (typeof params !== "object" || params === null) {
|
|
3981
|
+
throw new Error(
|
|
3982
|
+
`generateStaticParams must return array of objects, got ${typeof params}`
|
|
3983
|
+
);
|
|
3984
|
+
}
|
|
3985
|
+
return interpolatePath(route.urlPattern, params);
|
|
3986
|
+
});
|
|
3987
|
+
logger?.debug(`Generated ${urlPaths.length} paths for ${route.urlPattern}`);
|
|
3988
|
+
return urlPaths;
|
|
3989
|
+
}
|
|
3990
|
+
async function generateStaticPage(app, urlPath, outputDir, logger, verbose) {
|
|
3991
|
+
const outputFile = urlPathToOutputFile(urlPath);
|
|
3992
|
+
const outputPath = path11.join(outputDir, outputFile);
|
|
3993
|
+
try {
|
|
3994
|
+
const url = `http://localhost${urlPath}`;
|
|
3995
|
+
const request = new Request(url, { method: "GET" });
|
|
3996
|
+
const response = await app.fetch(request);
|
|
3997
|
+
if (!response.ok) {
|
|
3998
|
+
throw new Error(`Request failed with status ${response.status}`);
|
|
3999
|
+
}
|
|
4000
|
+
const html = await response.text();
|
|
4001
|
+
await fs11.mkdir(path11.dirname(outputPath), { recursive: true });
|
|
4002
|
+
await fs11.writeFile(outputPath, html, "utf-8");
|
|
4003
|
+
if (verbose) {
|
|
4004
|
+
logger.debug(`Generated: ${outputFile}`);
|
|
4005
|
+
}
|
|
4006
|
+
return {
|
|
4007
|
+
urlPath,
|
|
4008
|
+
outputFile,
|
|
4009
|
+
success: true
|
|
4010
|
+
};
|
|
4011
|
+
} catch (error) {
|
|
4012
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4013
|
+
logger.error(`Failed to generate ${urlPath}: ${errorMessage}`);
|
|
4014
|
+
return {
|
|
4015
|
+
urlPath,
|
|
4016
|
+
outputFile,
|
|
4017
|
+
success: false,
|
|
4018
|
+
error: errorMessage
|
|
4019
|
+
};
|
|
4020
|
+
}
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
// src/commands/build.ts
|
|
4024
|
+
var DEFAULT_OUTPUT_DIR = "./dist";
|
|
4025
|
+
var STATIC_SUBDIR = "static";
|
|
4026
|
+
async function build9(pathArg, options) {
|
|
4027
|
+
const startTime = Date.now();
|
|
4028
|
+
const verbose = options.verbose ?? false;
|
|
4029
|
+
const logger = createLogger(verbose);
|
|
4030
|
+
try {
|
|
4031
|
+
const cwd = pathArg ? path12.resolve(process.cwd(), pathArg) : process.cwd();
|
|
4032
|
+
if (!fs12.existsSync(cwd)) {
|
|
4033
|
+
throw new CliError(
|
|
4034
|
+
`Directory does not exist: ${cwd}`,
|
|
4035
|
+
"ENOENT",
|
|
4036
|
+
`Make sure the path exists and try again.`
|
|
4037
|
+
);
|
|
4038
|
+
}
|
|
4039
|
+
logger.debug(`Working directory: ${cwd}`);
|
|
4040
|
+
logger.debug(`Loading configuration...`);
|
|
4041
|
+
const config = await loadConfig2(cwd);
|
|
4042
|
+
logger.debug(`Config loaded: routesDir=${config.routesDir}, extensions=${config.extensions.join(", ")}`);
|
|
4043
|
+
const routesDir = resolveRoutesDir3(config, cwd);
|
|
4044
|
+
logger.debug(`Routes directory: ${routesDir}`);
|
|
4045
|
+
if (!fs12.existsSync(routesDir)) {
|
|
4046
|
+
throw new CliError(
|
|
4047
|
+
`Routes directory does not exist: ${routesDir}`,
|
|
4048
|
+
"ENOENT",
|
|
4049
|
+
`Create the "${config.routesDir}" directory or update routesDir in cloudwerk.config.ts`
|
|
4050
|
+
);
|
|
4051
|
+
}
|
|
4052
|
+
logger.debug(`Scanning routes...`);
|
|
4053
|
+
const scanResult = await scanRoutes2(routesDir, config);
|
|
4054
|
+
logger.debug(`Found ${scanResult.routes.length} route files`);
|
|
4055
|
+
logger.debug(`Building route manifest...`);
|
|
4056
|
+
const manifest = buildRouteManifest2(
|
|
4057
|
+
scanResult,
|
|
4058
|
+
routesDir,
|
|
4059
|
+
resolveLayouts2,
|
|
4060
|
+
resolveMiddleware2
|
|
4061
|
+
);
|
|
4062
|
+
if (hasErrors2(manifest)) {
|
|
4063
|
+
const errorMessages = formatErrors2(manifest.errors);
|
|
4064
|
+
logger.error(errorMessages);
|
|
4065
|
+
throw new CliError(
|
|
4066
|
+
"Route validation failed",
|
|
4067
|
+
"VALIDATION_ERROR",
|
|
4068
|
+
"Fix the errors above and try again."
|
|
4069
|
+
);
|
|
4070
|
+
}
|
|
4071
|
+
if (manifest.warnings.length > 0) {
|
|
4072
|
+
const warningMessages = formatWarnings2(manifest.warnings);
|
|
4073
|
+
logger.warn(warningMessages);
|
|
4074
|
+
}
|
|
4075
|
+
if (manifest.routes.length === 0) {
|
|
4076
|
+
logger.warn(`No routes found in ${config.routesDir}`);
|
|
4077
|
+
logger.warn(`Create a route.ts file to get started.`);
|
|
4078
|
+
}
|
|
4079
|
+
const outputBase = options.output ?? DEFAULT_OUTPUT_DIR;
|
|
4080
|
+
const outputDir = path12.isAbsolute(outputBase) ? outputBase : path12.resolve(cwd, outputBase);
|
|
4081
|
+
logger.info(`Building to ${outputDir}...`);
|
|
4082
|
+
if (options.ssg) {
|
|
4083
|
+
logger.debug(`Scanning for static routes...`);
|
|
4084
|
+
const staticRoutes = await getStaticRoutesAsync(manifest, verbose ? logger : void 0);
|
|
4085
|
+
if (staticRoutes.length === 0) {
|
|
4086
|
+
logger.warn(`No static routes found. Add rendering: 'static' to route configs to enable SSG.`);
|
|
4087
|
+
} else {
|
|
4088
|
+
logger.info(`Found ${staticRoutes.length} static route(s)`);
|
|
4089
|
+
logger.debug(`Creating Hono app for SSG...`);
|
|
4090
|
+
const { app } = await createApp(manifest, scanResult, config, logger, verbose);
|
|
4091
|
+
const staticOutputDir = path12.join(outputDir, STATIC_SUBDIR);
|
|
4092
|
+
logger.info(`Generating static pages to ${staticOutputDir}...`);
|
|
4093
|
+
const ssgStartTime = Date.now();
|
|
4094
|
+
const result = await generateStaticSite(app, manifest, staticOutputDir, logger, verbose);
|
|
4095
|
+
const ssgDuration = Date.now() - ssgStartTime;
|
|
4096
|
+
printSSGResults(result, logger, ssgDuration);
|
|
4097
|
+
if (result.failureCount > 0) {
|
|
4098
|
+
throw new CliError(
|
|
4099
|
+
`Failed to generate ${result.failureCount} static page(s)`,
|
|
4100
|
+
"SSG_ERROR",
|
|
4101
|
+
"Check the errors above and fix any issues."
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
}
|
|
4105
|
+
} else {
|
|
4106
|
+
logger.debug(`Scanning for static routes...`);
|
|
4107
|
+
const staticRoutes = await getStaticRoutesAsync(manifest, verbose ? logger : void 0);
|
|
4108
|
+
if (staticRoutes.length > 0) {
|
|
4109
|
+
logger.info(`Found ${staticRoutes.length} static route(s). Use --ssg to generate static pages.`);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
const buildTime = Date.now() - startTime;
|
|
4113
|
+
logger.success(`Build completed in ${buildTime}ms`);
|
|
4114
|
+
} catch (error) {
|
|
4115
|
+
if (error instanceof CliError) {
|
|
4116
|
+
printError(error.message, error.suggestion);
|
|
4117
|
+
process.exit(1);
|
|
4118
|
+
}
|
|
4119
|
+
if (error instanceof Error) {
|
|
4120
|
+
printError(error.message);
|
|
4121
|
+
if (verbose && error.stack) {
|
|
4122
|
+
console.log(error.stack);
|
|
4123
|
+
}
|
|
4124
|
+
process.exit(1);
|
|
4125
|
+
}
|
|
4126
|
+
printError(String(error));
|
|
4127
|
+
process.exit(1);
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
function printSSGResults(result, logger, duration) {
|
|
4131
|
+
console.log();
|
|
4132
|
+
logger.log("Static Site Generation Results:");
|
|
4133
|
+
logger.log(` Total pages: ${result.totalPages}`);
|
|
4134
|
+
logger.log(` Generated: ${result.successCount}`);
|
|
4135
|
+
if (result.failureCount > 0) {
|
|
4136
|
+
logger.log(` Failed: ${result.failureCount}`);
|
|
4137
|
+
}
|
|
4138
|
+
logger.log(` Output: ${result.outputDir}`);
|
|
4139
|
+
logger.log(` Duration: ${duration}ms`);
|
|
4140
|
+
const pagesToShow = result.routes.filter((r) => r.success).slice(0, 10);
|
|
4141
|
+
if (pagesToShow.length > 0) {
|
|
4142
|
+
console.log();
|
|
4143
|
+
logger.log("Generated pages:");
|
|
4144
|
+
for (const page of pagesToShow) {
|
|
4145
|
+
logger.log(` ${page.urlPath} -> ${page.outputFile}`);
|
|
4146
|
+
}
|
|
4147
|
+
if (result.successCount > 10) {
|
|
4148
|
+
logger.log(` ... and ${result.successCount - 10} more`);
|
|
4149
|
+
}
|
|
4150
|
+
}
|
|
4151
|
+
const failedPages = result.routes.filter((r) => !r.success);
|
|
4152
|
+
if (failedPages.length > 0) {
|
|
4153
|
+
console.log();
|
|
4154
|
+
logger.error("Failed pages:");
|
|
4155
|
+
for (const page of failedPages) {
|
|
4156
|
+
logger.error(` ${page.urlPath}: ${page.error}`);
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
4160
|
+
|
|
4161
|
+
// src/commands/config.ts
|
|
4162
|
+
import * as readline from "readline";
|
|
4163
|
+
import pc2 from "picocolors";
|
|
4164
|
+
|
|
4165
|
+
// src/utils/configWriter.ts
|
|
4166
|
+
import * as fs13 from "fs";
|
|
4167
|
+
import * as path13 from "path";
|
|
4168
|
+
import { loadConfig as loadConfig3 } from "@cloudwerk/core";
|
|
4169
|
+
var CONFIG_FILE_NAMES = [
|
|
4170
|
+
"cloudwerk.config.ts",
|
|
4171
|
+
"cloudwerk.config.js",
|
|
4172
|
+
"cloudwerk.config.mjs"
|
|
4173
|
+
];
|
|
4174
|
+
function findConfigFile(cwd) {
|
|
4175
|
+
for (const filename of CONFIG_FILE_NAMES) {
|
|
4176
|
+
const configPath = path13.join(cwd, filename);
|
|
4177
|
+
if (fs13.existsSync(configPath)) {
|
|
4178
|
+
return configPath;
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
return null;
|
|
4182
|
+
}
|
|
4183
|
+
function readCloudwerkConfig(cwd) {
|
|
4184
|
+
const configPath = findConfigFile(cwd);
|
|
4185
|
+
if (!configPath) {
|
|
4186
|
+
return {};
|
|
4187
|
+
}
|
|
4188
|
+
const content = fs13.readFileSync(configPath, "utf-8");
|
|
4189
|
+
return parseConfigContent(content);
|
|
4190
|
+
}
|
|
4191
|
+
function parseConfigContent(content) {
|
|
4192
|
+
const result = {};
|
|
4193
|
+
const rendererMatch = content.match(/renderer\s*:\s*['"`]([^'"`]+)['"`]/i);
|
|
4194
|
+
if (rendererMatch) {
|
|
4195
|
+
const value = rendererMatch[1];
|
|
4196
|
+
if (value === "hono-jsx" || value === "react" || value === "preact") {
|
|
4197
|
+
result.renderer = value;
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
4200
|
+
const routesDirMatch = content.match(/routesDir\s*:\s*['"`]([^'"`]+)['"`]/i);
|
|
4201
|
+
if (routesDirMatch) {
|
|
4202
|
+
result.routesDir = routesDirMatch[1];
|
|
4203
|
+
}
|
|
4204
|
+
return result;
|
|
4205
|
+
}
|
|
4206
|
+
function writeCloudwerkConfig(cwd, updates) {
|
|
4207
|
+
const configPath = findConfigFile(cwd);
|
|
4208
|
+
if (!configPath) {
|
|
4209
|
+
const newConfigPath = path13.join(cwd, "cloudwerk.config.ts");
|
|
4210
|
+
const content2 = generateMinimalConfig(updates);
|
|
4211
|
+
fs13.writeFileSync(newConfigPath, content2, "utf-8");
|
|
4212
|
+
return true;
|
|
4213
|
+
}
|
|
4214
|
+
let content = fs13.readFileSync(configPath, "utf-8");
|
|
4215
|
+
if (updates.renderer !== void 0) {
|
|
4216
|
+
content = updateRenderer(content, updates.renderer);
|
|
4217
|
+
}
|
|
4218
|
+
fs13.writeFileSync(configPath, content, "utf-8");
|
|
4219
|
+
return true;
|
|
4220
|
+
}
|
|
4221
|
+
function updateRenderer(content, renderer) {
|
|
4222
|
+
const rendererPattern = /(renderer\s*:\s*)['"`][^'"`]*['"`]/;
|
|
4223
|
+
if (rendererPattern.test(content)) {
|
|
4224
|
+
return content.replace(rendererPattern, `$1'${renderer}'`);
|
|
4225
|
+
}
|
|
4226
|
+
const uiSectionPattern = /(ui\s*:\s*\{)/;
|
|
4227
|
+
if (uiSectionPattern.test(content)) {
|
|
4228
|
+
return content.replace(uiSectionPattern, `$1
|
|
4229
|
+
renderer: '${renderer}',`);
|
|
4230
|
+
}
|
|
4231
|
+
return addUiSection(content, renderer);
|
|
4232
|
+
}
|
|
4233
|
+
function addUiSection(content, renderer) {
|
|
4234
|
+
if (!content.includes("defineConfig")) {
|
|
4235
|
+
return generateMinimalConfig({ renderer });
|
|
4236
|
+
}
|
|
4237
|
+
const lines = content.split("\n");
|
|
4238
|
+
const result = [];
|
|
4239
|
+
let inserted = false;
|
|
4240
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
4241
|
+
const line = lines[i];
|
|
4242
|
+
const trimmed = line.trim();
|
|
4243
|
+
if (!inserted && (trimmed === "})" || trimmed.startsWith("})"))) {
|
|
4244
|
+
const indent = detectIndentation(lines, i);
|
|
4245
|
+
result.unshift(line);
|
|
4246
|
+
result.unshift(`${indent}},`);
|
|
4247
|
+
result.unshift(`${indent} renderer: '${renderer}',`);
|
|
4248
|
+
result.unshift(`${indent}ui: {`);
|
|
4249
|
+
inserted = true;
|
|
4250
|
+
} else {
|
|
4251
|
+
result.unshift(line);
|
|
4252
|
+
}
|
|
4253
|
+
}
|
|
4254
|
+
if (!inserted) {
|
|
4255
|
+
return generateMinimalConfig({ renderer });
|
|
4256
|
+
}
|
|
4257
|
+
return result.join("\n");
|
|
4258
|
+
}
|
|
4259
|
+
function detectIndentation(lines, closingLineIndex) {
|
|
4260
|
+
for (let i = closingLineIndex - 1; i >= 0; i--) {
|
|
4261
|
+
const line = lines[i];
|
|
4262
|
+
const match2 = line.match(/^(\s+)\S/);
|
|
4263
|
+
if (match2) {
|
|
4264
|
+
return match2[1];
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
return " ";
|
|
4268
|
+
}
|
|
4269
|
+
function generateMinimalConfig(updates) {
|
|
4270
|
+
const lines = [
|
|
4271
|
+
"import { defineConfig } from '@cloudwerk/core'",
|
|
4272
|
+
"",
|
|
4273
|
+
"export default defineConfig({",
|
|
4274
|
+
" routesDir: 'app',"
|
|
4275
|
+
];
|
|
4276
|
+
if (updates.renderer) {
|
|
4277
|
+
lines.push(" ui: {");
|
|
4278
|
+
lines.push(` renderer: '${updates.renderer}',`);
|
|
4279
|
+
lines.push(" },");
|
|
4280
|
+
}
|
|
4281
|
+
lines.push("})");
|
|
4282
|
+
lines.push("");
|
|
4283
|
+
return lines.join("\n");
|
|
4284
|
+
}
|
|
4285
|
+
|
|
4286
|
+
// src/utils/tsconfigWriter.ts
|
|
4287
|
+
import * as fs14 from "fs";
|
|
4288
|
+
import * as path14 from "path";
|
|
4289
|
+
function findTsConfig(cwd) {
|
|
4290
|
+
const tsconfigPath = path14.join(cwd, "tsconfig.json");
|
|
4291
|
+
if (fs14.existsSync(tsconfigPath)) {
|
|
4292
|
+
return tsconfigPath;
|
|
4293
|
+
}
|
|
4294
|
+
return null;
|
|
4295
|
+
}
|
|
4296
|
+
function readTsConfig(cwd) {
|
|
4297
|
+
const tsconfigPath = findTsConfig(cwd);
|
|
4298
|
+
if (!tsconfigPath) {
|
|
4299
|
+
return {};
|
|
4300
|
+
}
|
|
4301
|
+
try {
|
|
4302
|
+
const content = fs14.readFileSync(tsconfigPath, "utf-8");
|
|
4303
|
+
const config = JSON.parse(content);
|
|
4304
|
+
return {
|
|
4305
|
+
jsxImportSource: config.compilerOptions?.jsxImportSource,
|
|
4306
|
+
...config.compilerOptions
|
|
4307
|
+
};
|
|
4308
|
+
} catch {
|
|
4309
|
+
return {};
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
function updateTsConfig(cwd, updates) {
|
|
4313
|
+
const tsconfigPath = findTsConfig(cwd);
|
|
4314
|
+
if (!tsconfigPath) {
|
|
4315
|
+
return false;
|
|
4316
|
+
}
|
|
4317
|
+
try {
|
|
4318
|
+
const content = fs14.readFileSync(tsconfigPath, "utf-8");
|
|
4319
|
+
const config = JSON.parse(content);
|
|
4320
|
+
if (!config.compilerOptions) {
|
|
4321
|
+
config.compilerOptions = {};
|
|
4322
|
+
}
|
|
4323
|
+
if (updates.jsxImportSource !== void 0) {
|
|
4324
|
+
config.compilerOptions.jsxImportSource = updates.jsxImportSource;
|
|
4325
|
+
}
|
|
4326
|
+
fs14.writeFileSync(tsconfigPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
4327
|
+
return true;
|
|
4328
|
+
} catch {
|
|
4329
|
+
return false;
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
|
|
4333
|
+
// src/utils/dependencyManager.ts
|
|
4334
|
+
import * as fs15 from "fs";
|
|
4335
|
+
import * as path15 from "path";
|
|
4336
|
+
import { execFileSync } from "child_process";
|
|
4337
|
+
var REACT_SPECIFIC_LIBRARIES = [
|
|
4338
|
+
"@tanstack/react-query",
|
|
4339
|
+
"framer-motion",
|
|
4340
|
+
"react-hook-form",
|
|
4341
|
+
"react-router",
|
|
4342
|
+
"react-router-dom",
|
|
4343
|
+
"@react-spring/core",
|
|
4344
|
+
"@react-spring/web",
|
|
4345
|
+
"@react-spring/three",
|
|
4346
|
+
"react-redux",
|
|
4347
|
+
"@reduxjs/toolkit",
|
|
4348
|
+
"react-aria",
|
|
4349
|
+
"react-stately",
|
|
4350
|
+
"@react-aria/button",
|
|
4351
|
+
"@react-aria/focus",
|
|
4352
|
+
"@radix-ui/react-dialog",
|
|
4353
|
+
"@radix-ui/react-dropdown-menu",
|
|
4354
|
+
"@radix-ui/react-popover",
|
|
4355
|
+
"@radix-ui/react-tooltip",
|
|
4356
|
+
"@radix-ui/react-accordion",
|
|
4357
|
+
"@headlessui/react",
|
|
4358
|
+
"react-dnd",
|
|
4359
|
+
"react-beautiful-dnd",
|
|
4360
|
+
"react-virtualized",
|
|
4361
|
+
"react-window",
|
|
4362
|
+
"react-select",
|
|
4363
|
+
"react-datepicker",
|
|
4364
|
+
"react-toastify",
|
|
4365
|
+
"react-i18next",
|
|
4366
|
+
"styled-components",
|
|
4367
|
+
"@emotion/react",
|
|
4368
|
+
"@emotion/styled",
|
|
4369
|
+
"jotai",
|
|
4370
|
+
"zustand",
|
|
4371
|
+
"recoil",
|
|
4372
|
+
"use-gesture",
|
|
4373
|
+
"@use-gesture/react"
|
|
4374
|
+
];
|
|
4375
|
+
function detectPackageManager(cwd) {
|
|
4376
|
+
if (fs15.existsSync(path15.join(cwd, "pnpm-lock.yaml"))) {
|
|
4377
|
+
return "pnpm";
|
|
4378
|
+
}
|
|
4379
|
+
if (fs15.existsSync(path15.join(cwd, "yarn.lock"))) {
|
|
4380
|
+
return "yarn";
|
|
4381
|
+
}
|
|
4382
|
+
if (fs15.existsSync(path15.join(cwd, "bun.lockb"))) {
|
|
4383
|
+
return "bun";
|
|
4384
|
+
}
|
|
4385
|
+
if (fs15.existsSync(path15.join(cwd, "package-lock.json"))) {
|
|
4386
|
+
return "npm";
|
|
4387
|
+
}
|
|
4388
|
+
const packageJsonPath = path15.join(cwd, "package.json");
|
|
4389
|
+
if (fs15.existsSync(packageJsonPath)) {
|
|
4390
|
+
try {
|
|
4391
|
+
const packageJson = JSON.parse(fs15.readFileSync(packageJsonPath, "utf-8"));
|
|
4392
|
+
const pm = packageJson.packageManager;
|
|
4393
|
+
if (pm) {
|
|
4394
|
+
if (pm.startsWith("pnpm")) return "pnpm";
|
|
4395
|
+
if (pm.startsWith("yarn")) return "yarn";
|
|
4396
|
+
if (pm.startsWith("bun")) return "bun";
|
|
4397
|
+
if (pm.startsWith("npm")) return "npm";
|
|
4398
|
+
}
|
|
4399
|
+
} catch {
|
|
4400
|
+
}
|
|
4401
|
+
}
|
|
4402
|
+
return "npm";
|
|
4403
|
+
}
|
|
4404
|
+
function getInstalledDependencies(cwd) {
|
|
4405
|
+
const packageJsonPath = path15.join(cwd, "package.json");
|
|
4406
|
+
if (!fs15.existsSync(packageJsonPath)) {
|
|
4407
|
+
return {
|
|
4408
|
+
dependencies: {},
|
|
4409
|
+
devDependencies: {}
|
|
4410
|
+
};
|
|
4411
|
+
}
|
|
4412
|
+
try {
|
|
4413
|
+
const content = fs15.readFileSync(packageJsonPath, "utf-8");
|
|
4414
|
+
const packageJson = JSON.parse(content);
|
|
4415
|
+
return {
|
|
4416
|
+
dependencies: packageJson.dependencies || {},
|
|
4417
|
+
devDependencies: packageJson.devDependencies || {}
|
|
4418
|
+
};
|
|
4419
|
+
} catch {
|
|
4420
|
+
return {
|
|
4421
|
+
dependencies: {},
|
|
4422
|
+
devDependencies: {}
|
|
4423
|
+
};
|
|
4424
|
+
}
|
|
4425
|
+
}
|
|
4426
|
+
function hasReactDependencies(cwd) {
|
|
4427
|
+
const { dependencies, devDependencies } = getInstalledDependencies(cwd);
|
|
4428
|
+
const allDeps = { ...dependencies, ...devDependencies };
|
|
4429
|
+
const reactPackages = ["react", "react-dom", "@types/react", "@types/react-dom"];
|
|
4430
|
+
const installedPackages = reactPackages.filter((pkg2) => pkg2 in allDeps);
|
|
4431
|
+
return {
|
|
4432
|
+
hasReact: "react" in allDeps,
|
|
4433
|
+
hasReactDom: "react-dom" in allDeps,
|
|
4434
|
+
hasTypes: "@types/react" in allDeps || "@types/react-dom" in allDeps,
|
|
4435
|
+
installedPackages
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4438
|
+
function detectIncompatibleReactLibs(cwd) {
|
|
4439
|
+
const { dependencies, devDependencies } = getInstalledDependencies(cwd);
|
|
4440
|
+
const allDeps = { ...dependencies, ...devDependencies };
|
|
4441
|
+
const incompatible = [];
|
|
4442
|
+
for (const lib of REACT_SPECIFIC_LIBRARIES) {
|
|
4443
|
+
if (lib in allDeps) {
|
|
4444
|
+
incompatible.push(lib);
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
for (const pkg2 of Object.keys(allDeps)) {
|
|
4448
|
+
if (pkg2.startsWith("@radix-ui/react-") && !incompatible.includes(pkg2)) {
|
|
4449
|
+
incompatible.push(pkg2);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
return incompatible.sort();
|
|
4453
|
+
}
|
|
4454
|
+
function buildInstallArgs(pm, packages, isDev) {
|
|
4455
|
+
const args = [];
|
|
4456
|
+
switch (pm) {
|
|
4457
|
+
case "pnpm":
|
|
4458
|
+
args.push("add");
|
|
4459
|
+
if (isDev) args.push("-D");
|
|
4460
|
+
args.push(...packages);
|
|
4461
|
+
break;
|
|
4462
|
+
case "yarn":
|
|
4463
|
+
args.push("add");
|
|
4464
|
+
if (isDev) args.push("-D");
|
|
4465
|
+
args.push(...packages);
|
|
4466
|
+
break;
|
|
4467
|
+
case "bun":
|
|
4468
|
+
args.push("add");
|
|
4469
|
+
if (isDev) args.push("-d");
|
|
4470
|
+
args.push(...packages);
|
|
4471
|
+
break;
|
|
4472
|
+
case "npm":
|
|
4473
|
+
default:
|
|
4474
|
+
args.push("install");
|
|
4475
|
+
if (isDev) args.push("--save-dev");
|
|
4476
|
+
args.push(...packages);
|
|
4477
|
+
break;
|
|
4478
|
+
}
|
|
4479
|
+
return args;
|
|
4480
|
+
}
|
|
4481
|
+
function buildRemoveArgs(pm, packages) {
|
|
4482
|
+
const args = [];
|
|
4483
|
+
switch (pm) {
|
|
4484
|
+
case "pnpm":
|
|
4485
|
+
args.push("remove", ...packages);
|
|
4486
|
+
break;
|
|
4487
|
+
case "yarn":
|
|
4488
|
+
args.push("remove", ...packages);
|
|
4489
|
+
break;
|
|
4490
|
+
case "bun":
|
|
4491
|
+
args.push("remove", ...packages);
|
|
4492
|
+
break;
|
|
4493
|
+
case "npm":
|
|
4494
|
+
default:
|
|
4495
|
+
args.push("uninstall", ...packages);
|
|
4496
|
+
break;
|
|
4497
|
+
}
|
|
4498
|
+
return args;
|
|
4499
|
+
}
|
|
4500
|
+
function addDependencies(cwd, packages, isDev = false) {
|
|
4501
|
+
if (packages.length === 0) {
|
|
4502
|
+
return true;
|
|
4503
|
+
}
|
|
4504
|
+
const pm = detectPackageManager(cwd);
|
|
4505
|
+
const args = buildInstallArgs(pm, packages, isDev);
|
|
4506
|
+
try {
|
|
4507
|
+
execFileSync(pm, args, {
|
|
4508
|
+
cwd,
|
|
4509
|
+
stdio: "pipe"
|
|
4510
|
+
});
|
|
4511
|
+
return true;
|
|
4512
|
+
} catch (error) {
|
|
4513
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4514
|
+
console.error(`Failed to run ${pm} ${args.join(" ")}: ${message}`);
|
|
4515
|
+
return false;
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
function removeDependencies(cwd, packages) {
|
|
4519
|
+
if (packages.length === 0) {
|
|
4520
|
+
return true;
|
|
4521
|
+
}
|
|
4522
|
+
const pm = detectPackageManager(cwd);
|
|
4523
|
+
const args = buildRemoveArgs(pm, packages);
|
|
4524
|
+
try {
|
|
4525
|
+
execFileSync(pm, args, {
|
|
4526
|
+
cwd,
|
|
4527
|
+
stdio: "pipe"
|
|
4528
|
+
});
|
|
4529
|
+
return true;
|
|
4530
|
+
} catch (error) {
|
|
4531
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4532
|
+
console.error(`Failed to run ${pm} ${args.join(" ")}: ${message}`);
|
|
4533
|
+
return false;
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
|
|
4537
|
+
// src/commands/config.ts
|
|
4538
|
+
var VALID_RENDERERS = ["react", "hono-jsx", "preact"];
|
|
4539
|
+
function configGet(key) {
|
|
4540
|
+
const cwd = process.cwd();
|
|
4541
|
+
switch (key) {
|
|
4542
|
+
case "renderer": {
|
|
4543
|
+
const config = readCloudwerkConfig(cwd);
|
|
4544
|
+
const tsconfig = readTsConfig(cwd);
|
|
4545
|
+
if (config.renderer) {
|
|
4546
|
+
console.log(config.renderer);
|
|
4547
|
+
} else if (tsconfig.jsxImportSource) {
|
|
4548
|
+
const jsxSource = tsconfig.jsxImportSource;
|
|
4549
|
+
if (jsxSource === "react") {
|
|
4550
|
+
console.log("react");
|
|
4551
|
+
} else if (jsxSource === "hono/jsx") {
|
|
4552
|
+
console.log("hono-jsx");
|
|
4553
|
+
} else if (jsxSource === "preact") {
|
|
4554
|
+
console.log("preact");
|
|
4555
|
+
} else {
|
|
4556
|
+
console.log(pc2.yellow("unknown") + pc2.dim(` (jsxImportSource: ${jsxSource})`));
|
|
4557
|
+
}
|
|
4558
|
+
} else {
|
|
4559
|
+
console.log("hono-jsx" + pc2.dim(" (default)"));
|
|
4560
|
+
}
|
|
4561
|
+
break;
|
|
4562
|
+
}
|
|
4563
|
+
default:
|
|
4564
|
+
console.log(pc2.red(`Error: Unknown config key '${key}'`));
|
|
4565
|
+
console.log();
|
|
4566
|
+
console.log(pc2.dim("Available keys:"));
|
|
4567
|
+
console.log(pc2.dim(" - renderer"));
|
|
4568
|
+
process.exit(1);
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
async function configSet(key, value) {
|
|
4572
|
+
const cwd = process.cwd();
|
|
4573
|
+
switch (key) {
|
|
4574
|
+
case "renderer": {
|
|
4575
|
+
await setRenderer(cwd, value);
|
|
4576
|
+
break;
|
|
4577
|
+
}
|
|
4578
|
+
default:
|
|
4579
|
+
console.log(pc2.red(`Error: Unknown config key '${key}'`));
|
|
4580
|
+
console.log();
|
|
4581
|
+
console.log(pc2.dim("Available keys:"));
|
|
4582
|
+
console.log(pc2.dim(" - renderer"));
|
|
4583
|
+
process.exit(1);
|
|
4584
|
+
}
|
|
4585
|
+
}
|
|
4586
|
+
async function setRenderer(cwd, renderer) {
|
|
4587
|
+
if (!VALID_RENDERERS.includes(renderer)) {
|
|
4588
|
+
console.log(pc2.red(`Error: Invalid renderer '${renderer}'`));
|
|
4589
|
+
console.log();
|
|
4590
|
+
console.log(pc2.dim("Valid renderers:"));
|
|
4591
|
+
console.log(pc2.dim(" - react"));
|
|
4592
|
+
console.log(pc2.dim(" - hono-jsx"));
|
|
4593
|
+
console.log(pc2.dim(" - preact (limited support)"));
|
|
4594
|
+
process.exit(1);
|
|
4595
|
+
}
|
|
4596
|
+
const rendererType = renderer;
|
|
4597
|
+
if (rendererType === "preact") {
|
|
4598
|
+
console.log(pc2.yellow("Warning: Preact renderer is not fully implemented yet."));
|
|
4599
|
+
console.log(pc2.yellow("Only configuration will be updated."));
|
|
4600
|
+
console.log();
|
|
4601
|
+
}
|
|
4602
|
+
const currentConfig = readCloudwerkConfig(cwd);
|
|
4603
|
+
if (currentConfig.renderer === rendererType) {
|
|
4604
|
+
console.log(pc2.yellow(`Renderer is already set to '${rendererType}'`));
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4607
|
+
console.log();
|
|
4608
|
+
if (rendererType === "react") {
|
|
4609
|
+
await switchToReact(cwd);
|
|
4610
|
+
} else if (rendererType === "hono-jsx") {
|
|
4611
|
+
await switchToHonoJsx(cwd);
|
|
4612
|
+
} else if (rendererType === "preact") {
|
|
4613
|
+
await switchToPreact(cwd);
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4616
|
+
async function switchToReact(cwd) {
|
|
4617
|
+
writeCloudwerkConfig(cwd, { renderer: "react" });
|
|
4618
|
+
console.log(pc2.green("\u2713") + " Updated cloudwerk.config.ts");
|
|
4619
|
+
const tsconfigUpdated = updateTsConfig(cwd, { jsxImportSource: "react" });
|
|
4620
|
+
if (tsconfigUpdated) {
|
|
4621
|
+
console.log(pc2.green("\u2713") + " Updated tsconfig.json " + pc2.dim("(jsxImportSource: react)"));
|
|
4622
|
+
} else {
|
|
4623
|
+
console.log(pc2.yellow("\u26A0") + " tsconfig.json not found, skipping");
|
|
4624
|
+
}
|
|
4625
|
+
console.log(pc2.cyan("\u2713") + " Installing dependencies...");
|
|
4626
|
+
const { hasReact, hasReactDom, hasTypes } = hasReactDependencies(cwd);
|
|
4627
|
+
const runtimeDeps = [];
|
|
4628
|
+
if (!hasReact) runtimeDeps.push("react");
|
|
4629
|
+
if (!hasReactDom) runtimeDeps.push("react-dom");
|
|
4630
|
+
if (runtimeDeps.length > 0) {
|
|
4631
|
+
const success = addDependencies(cwd, runtimeDeps, false);
|
|
4632
|
+
if (success) {
|
|
4633
|
+
console.log(pc2.green("\u2713") + " Added " + runtimeDeps.join(", "));
|
|
4634
|
+
} else {
|
|
4635
|
+
console.log(pc2.red("\u2717") + " Failed to install " + runtimeDeps.join(", "));
|
|
4636
|
+
}
|
|
4637
|
+
}
|
|
4638
|
+
if (!hasTypes) {
|
|
4639
|
+
const typesDeps = ["@types/react", "@types/react-dom"];
|
|
4640
|
+
const success = addDependencies(cwd, typesDeps, true);
|
|
4641
|
+
if (success) {
|
|
4642
|
+
console.log(pc2.green("\u2713") + " Added " + typesDeps.join(", ") + pc2.dim(" (dev)"));
|
|
4643
|
+
} else {
|
|
4644
|
+
console.log(pc2.red("\u2717") + " Failed to install type definitions");
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
console.log();
|
|
4648
|
+
console.log(pc2.green(pc2.bold("Renderer switched to React.")));
|
|
4649
|
+
console.log();
|
|
4650
|
+
console.log(pc2.dim("Note: Most Hono JSX code works with React unchanged."));
|
|
4651
|
+
console.log(pc2.dim("See migration guide: https://cloudwerk.dev/docs/migration"));
|
|
4652
|
+
console.log();
|
|
4653
|
+
}
|
|
4654
|
+
async function switchToHonoJsx(cwd) {
|
|
4655
|
+
const incompatibleLibs = detectIncompatibleReactLibs(cwd);
|
|
4656
|
+
if (incompatibleLibs.length > 0) {
|
|
4657
|
+
console.log(pc2.yellow("\u26A0") + " " + pc2.bold("Compatibility warnings:"));
|
|
4658
|
+
console.log();
|
|
4659
|
+
console.log(pc2.dim(" The following dependencies may not work with Hono JSX:"));
|
|
4660
|
+
console.log();
|
|
4661
|
+
for (const lib of incompatibleLibs) {
|
|
4662
|
+
console.log(pc2.yellow(" - ") + lib + pc2.dim(" (uses React internals)"));
|
|
4663
|
+
}
|
|
4664
|
+
console.log();
|
|
4665
|
+
console.log(pc2.dim(" Consider keeping the React renderer or finding alternatives."));
|
|
4666
|
+
console.log();
|
|
4667
|
+
const proceed = await askConfirmation("Continue anyway?", false);
|
|
4668
|
+
if (!proceed) {
|
|
4669
|
+
console.log(pc2.dim("Aborted."));
|
|
4670
|
+
return;
|
|
4671
|
+
}
|
|
4672
|
+
console.log();
|
|
4673
|
+
}
|
|
4674
|
+
writeCloudwerkConfig(cwd, { renderer: "hono-jsx" });
|
|
4675
|
+
console.log(pc2.green("\u2713") + " Updated cloudwerk.config.ts");
|
|
4676
|
+
const tsconfigUpdated = updateTsConfig(cwd, { jsxImportSource: "hono/jsx" });
|
|
4677
|
+
if (tsconfigUpdated) {
|
|
4678
|
+
console.log(pc2.green("\u2713") + " Updated tsconfig.json " + pc2.dim("(jsxImportSource: hono/jsx)"));
|
|
4679
|
+
} else {
|
|
4680
|
+
console.log(pc2.yellow("\u26A0") + " tsconfig.json not found, skipping");
|
|
4681
|
+
}
|
|
4682
|
+
const { installedPackages } = hasReactDependencies(cwd);
|
|
4683
|
+
if (installedPackages.length > 0) {
|
|
4684
|
+
console.log();
|
|
4685
|
+
const removeReact = await askConfirmation("Remove React dependencies?", false);
|
|
4686
|
+
if (removeReact) {
|
|
4687
|
+
const success = removeDependencies(cwd, installedPackages);
|
|
4688
|
+
if (success) {
|
|
4689
|
+
console.log(pc2.green("\u2713") + " Removed " + installedPackages.join(", "));
|
|
4690
|
+
} else {
|
|
4691
|
+
console.log(pc2.red("\u2717") + " Failed to remove React dependencies");
|
|
4692
|
+
}
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
console.log();
|
|
4696
|
+
console.log(pc2.green(pc2.bold("Renderer switched to Hono JSX.")));
|
|
4697
|
+
console.log();
|
|
4698
|
+
console.log(pc2.bold("Benefits:"));
|
|
4699
|
+
console.log(pc2.dim("- Smaller bundle size (~3kb vs ~45kb)"));
|
|
4700
|
+
console.log(pc2.dim("- Workers-optimized"));
|
|
4701
|
+
console.log(pc2.dim("- No external dependencies"));
|
|
4702
|
+
console.log();
|
|
4703
|
+
if (incompatibleLibs.length > 0) {
|
|
4704
|
+
console.log(pc2.bold("Potential issues:"));
|
|
4705
|
+
console.log(pc2.dim("- React-specific libraries won't work"));
|
|
4706
|
+
console.log(pc2.dim("- Some Context API differences"));
|
|
4707
|
+
console.log(pc2.dim("- forwardRef pattern may differ"));
|
|
4708
|
+
console.log();
|
|
4709
|
+
}
|
|
4710
|
+
console.log(pc2.dim("See migration guide: https://cloudwerk.dev/docs/migration"));
|
|
4711
|
+
console.log();
|
|
4712
|
+
}
|
|
4713
|
+
async function switchToPreact(cwd) {
|
|
4714
|
+
writeCloudwerkConfig(cwd, { renderer: "preact" });
|
|
4715
|
+
console.log(pc2.green("\u2713") + " Updated cloudwerk.config.ts");
|
|
4716
|
+
const tsconfigUpdated = updateTsConfig(cwd, { jsxImportSource: "preact" });
|
|
4717
|
+
if (tsconfigUpdated) {
|
|
4718
|
+
console.log(pc2.green("\u2713") + " Updated tsconfig.json " + pc2.dim("(jsxImportSource: preact)"));
|
|
4719
|
+
} else {
|
|
4720
|
+
console.log(pc2.yellow("\u26A0") + " tsconfig.json not found, skipping");
|
|
4721
|
+
}
|
|
4722
|
+
console.log();
|
|
4723
|
+
console.log(pc2.yellow(pc2.bold("Preact configuration applied.")));
|
|
4724
|
+
console.log();
|
|
4725
|
+
console.log(pc2.yellow("Note: Full Preact support is coming in a future release."));
|
|
4726
|
+
console.log(pc2.yellow("You may need to manually install preact:"));
|
|
4727
|
+
console.log(pc2.dim(" pnpm add preact"));
|
|
4728
|
+
console.log();
|
|
4729
|
+
}
|
|
4730
|
+
async function askConfirmation(question, defaultValue) {
|
|
4731
|
+
const rl = readline.createInterface({
|
|
4732
|
+
input: process.stdin,
|
|
4733
|
+
output: process.stdout
|
|
4734
|
+
});
|
|
4735
|
+
const hint = defaultValue ? "Y/n" : "y/N";
|
|
4736
|
+
return new Promise((resolve3) => {
|
|
4737
|
+
rl.question(pc2.cyan("? ") + question + pc2.dim(` (${hint}) `), (answer) => {
|
|
4738
|
+
rl.close();
|
|
4739
|
+
const normalized = answer.trim().toLowerCase();
|
|
4740
|
+
if (normalized === "") {
|
|
4741
|
+
resolve3(defaultValue);
|
|
4742
|
+
} else if (normalized === "y" || normalized === "yes") {
|
|
4743
|
+
resolve3(true);
|
|
4744
|
+
} else {
|
|
4745
|
+
resolve3(false);
|
|
4746
|
+
}
|
|
4747
|
+
});
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
|
|
3559
4751
|
// src/index.ts
|
|
3560
4752
|
program.name("cloudwerk").description("Cloudwerk CLI - Build and deploy full-stack apps to Cloudflare").version(VERSION);
|
|
3561
4753
|
program.command("dev [path]").description("Start development server").option("-p, --port <number>", "Port to listen on", String(DEFAULT_PORT)).option("-H, --host <host>", "Host to bind", DEFAULT_HOST).option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(dev);
|
|
4754
|
+
program.command("build [path]").description("Build project for production").option("-o, --output <dir>", "Output directory", "./dist").option("--ssg", "Generate static pages for routes with rendering: static").option("-c, --config <path>", "Path to config file").option("--verbose", "Enable verbose logging").action(build9);
|
|
4755
|
+
var configCmd = program.command("config").description("Manage Cloudwerk configuration");
|
|
4756
|
+
configCmd.command("get <key>").description("Get a configuration value").action(configGet);
|
|
4757
|
+
configCmd.command("set <key> <value>").description("Set a configuration value").action(configSet);
|
|
3562
4758
|
program.parse();
|