create-blokd 0.1.0-beta.2 → 0.1.0-beta.9
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 +34 -10
- package/bin/create-blokd.js +5 -2
- package/package.json +1 -1
- package/templates/dashboard/index.html +11 -0
- package/templates/dashboard/package.json +21 -0
- package/templates/dashboard/scripts/dev.mjs +105 -0
- package/templates/dashboard/src/blokd-env.d.ts +4 -0
- package/templates/dashboard/src/entry-client.ts +3 -0
- package/templates/dashboard/src/resumables/counter.ts +10 -0
- package/templates/dashboard/src/resumables/demo.ts +9 -0
- package/templates/dashboard/src/resumables/status.ts +10 -0
- package/templates/dashboard/src/routes/_404.tsx +11 -0
- package/templates/dashboard/src/routes/_error.tsx +17 -0
- package/templates/dashboard/src/routes/_layout.tsx +36 -0
- package/templates/dashboard/src/routes/about.tsx +23 -0
- package/templates/dashboard/src/routes/contact.tsx +58 -0
- package/templates/dashboard/src/routes/index.tsx +46 -0
- package/templates/dashboard/src/routes/reports.tsx +37 -0
- package/templates/dashboard/src/server.ts +19 -0
- package/templates/dashboard/tsconfig.json +17 -0
- package/templates/dashboard/vite.config.ts +11 -0
- package/templates/forms/index.html +11 -0
- package/templates/forms/package.json +21 -0
- package/templates/forms/scripts/dev.mjs +105 -0
- package/templates/forms/src/blokd-env.d.ts +4 -0
- package/templates/forms/src/entry-client.ts +3 -0
- package/templates/forms/src/resumables/counter.ts +10 -0
- package/templates/forms/src/resumables/demo.ts +9 -0
- package/templates/forms/src/routes/_404.tsx +11 -0
- package/templates/forms/src/routes/_error.tsx +17 -0
- package/templates/forms/src/routes/_layout.tsx +36 -0
- package/templates/forms/src/routes/about.tsx +23 -0
- package/templates/forms/src/routes/contact.tsx +58 -0
- package/templates/forms/src/routes/index.tsx +28 -0
- package/templates/forms/src/routes/newsletter.tsx +50 -0
- package/templates/forms/src/server.ts +19 -0
- package/templates/forms/tsconfig.json +17 -0
- package/templates/forms/vite.config.ts +11 -0
- package/templates/marketing/index.html +11 -0
- package/templates/marketing/package.json +21 -0
- package/templates/marketing/scripts/dev.mjs +105 -0
- package/templates/marketing/src/blokd-env.d.ts +4 -0
- package/templates/marketing/src/entry-client.ts +3 -0
- package/templates/marketing/src/resumables/counter.ts +10 -0
- package/templates/marketing/src/resumables/demo.ts +9 -0
- package/templates/marketing/src/routes/_404.tsx +11 -0
- package/templates/marketing/src/routes/_error.tsx +17 -0
- package/templates/marketing/src/routes/_layout.tsx +38 -0
- package/templates/marketing/src/routes/about.tsx +23 -0
- package/templates/marketing/src/routes/contact.tsx +19 -0
- package/templates/marketing/src/routes/index.tsx +24 -0
- package/templates/marketing/src/routes/pricing.tsx +24 -0
- package/templates/marketing/src/server.ts +19 -0
- package/templates/marketing/tsconfig.json +17 -0
- package/templates/marketing/vite.config.ts +11 -0
- package/templates/minimal/package.json +3 -3
- package/templates/minimal/scripts/dev.mjs +105 -0
- package/templates/minimal/src/resumables/counter.ts +10 -0
- package/templates/minimal/src/resumables/demo.ts +9 -4
- package/templates/minimal/src/routes/_layout.tsx +3 -1
- package/templates/minimal/src/routes/about.tsx +7 -1
- package/templates/minimal/src/routes/contact.tsx +58 -0
- package/templates/minimal/src/routes/index.tsx +14 -11
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export const meta = () => ({
|
|
2
|
+
title: "Contact | Blokd App",
|
|
3
|
+
description: "Contact information for a marketing site."
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
export const runtime = "none";
|
|
7
|
+
|
|
8
|
+
export const budget = {
|
|
9
|
+
client: "0kb"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function Contact() {
|
|
13
|
+
return (
|
|
14
|
+
<section>
|
|
15
|
+
<h1>Contact</h1>
|
|
16
|
+
<p>Email hello@example.com to start a project.</p>
|
|
17
|
+
</section>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const meta = () => ({
|
|
2
|
+
title: "Blokd Marketing",
|
|
3
|
+
description: "A static marketing site built with Blokd."
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
export const runtime = "none";
|
|
7
|
+
|
|
8
|
+
export const budget = {
|
|
9
|
+
client: "0kb"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function Home() {
|
|
13
|
+
return (
|
|
14
|
+
<section>
|
|
15
|
+
<h1>Blokd Marketing</h1>
|
|
16
|
+
|
|
17
|
+
<p>
|
|
18
|
+
A server-rendered marketing starter with no client framework runtime.
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<p><a href="/pricing">View pricing</a></p>
|
|
22
|
+
</section>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const meta = () => ({
|
|
2
|
+
title: "Pricing | Blokd App",
|
|
3
|
+
description: "Static pricing page."
|
|
4
|
+
});
|
|
5
|
+
|
|
6
|
+
export const runtime = "none";
|
|
7
|
+
|
|
8
|
+
export const budget = {
|
|
9
|
+
client: "0kb"
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default function Pricing() {
|
|
13
|
+
return (
|
|
14
|
+
<section>
|
|
15
|
+
<h1>Pricing</h1>
|
|
16
|
+
<p>Simple static pricing tiers with no client JavaScript.</p>
|
|
17
|
+
<ul>
|
|
18
|
+
<li>Starter: $99</li>
|
|
19
|
+
<li>Growth: $299</li>
|
|
20
|
+
<li>Scale: custom</li>
|
|
21
|
+
</ul>
|
|
22
|
+
</section>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { createPages } from "blokd/hono";
|
|
3
|
+
import routes from "virtual:blokd/routes";
|
|
4
|
+
|
|
5
|
+
const app = new Hono();
|
|
6
|
+
|
|
7
|
+
app.get("/api/health", c => {
|
|
8
|
+
return c.json({ ok: true });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
app.route(
|
|
12
|
+
"/",
|
|
13
|
+
createPages({
|
|
14
|
+
routes,
|
|
15
|
+
entryClient: "/src/entry-client.ts"
|
|
16
|
+
})
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
export default app;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"jsxImportSource": "blokd",
|
|
8
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
9
|
+
"types": ["node"],
|
|
10
|
+
"strict": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"allowSyntheticDefaultImports": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"noEmit": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src", "vite.config.ts"]
|
|
17
|
+
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.0.0",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"dev": "
|
|
8
|
+
"dev": "node scripts/dev.mjs",
|
|
9
9
|
"build": "vite build",
|
|
10
10
|
"typecheck": "tsc --noEmit"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"blokd": "0.
|
|
13
|
+
"blokd": "0.2.0-beta.0",
|
|
14
14
|
"hono": ">=4.5 <5"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
@@ -18,4 +18,4 @@
|
|
|
18
18
|
"typescript": ">=5.5 <6",
|
|
19
19
|
"vite": ">=6 <9"
|
|
20
20
|
}
|
|
21
|
-
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
import { createServer as createViteServer } from "vite";
|
|
6
|
+
|
|
7
|
+
const host = process.env.HOST ?? "0.0.0.0";
|
|
8
|
+
const port = Number(process.env.PORT ?? 5173);
|
|
9
|
+
|
|
10
|
+
let handleRequest = (_req, res) => {
|
|
11
|
+
res.statusCode = 503;
|
|
12
|
+
res.end("Dev server is starting");
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const httpServer = createServer((req, res) => {
|
|
16
|
+
handleRequest(req, res);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const vite = await createViteServer({
|
|
20
|
+
appType: "custom",
|
|
21
|
+
server: {
|
|
22
|
+
middlewareMode: true,
|
|
23
|
+
hmr: {
|
|
24
|
+
server: httpServer
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
handleRequest = async (req, res) => {
|
|
30
|
+
vite.middlewares(req, res, async () => {
|
|
31
|
+
try {
|
|
32
|
+
const mod = await vite.ssrLoadModule("/src/server.ts");
|
|
33
|
+
const app = mod.default;
|
|
34
|
+
|
|
35
|
+
if (!app || typeof app.fetch !== "function") {
|
|
36
|
+
throw new Error("src/server.ts must default-export a Hono app.");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const request = toWebRequest(req);
|
|
40
|
+
const response = await app.fetch(request);
|
|
41
|
+
|
|
42
|
+
await writeWebResponse(res, response);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
vite.ssrFixStacktrace(error);
|
|
45
|
+
|
|
46
|
+
res.statusCode = 500;
|
|
47
|
+
res.setHeader("content-type", "text/plain; charset=utf-8");
|
|
48
|
+
res.end(error instanceof Error ? error.stack ?? error.message : String(error));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
httpServer.listen(port, host, () => {
|
|
54
|
+
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
55
|
+
|
|
56
|
+
console.log("");
|
|
57
|
+
console.log(` Blokd dev server`);
|
|
58
|
+
console.log(` Local: http://${displayHost}:${port}/`);
|
|
59
|
+
console.log("");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
function toWebRequest(req) {
|
|
63
|
+
const protocol = req.headers["x-forwarded-proto"] ?? "http";
|
|
64
|
+
const hostHeader = req.headers.host ?? `${host}:${port}`;
|
|
65
|
+
const url = `${protocol}://${hostHeader}${req.url ?? "/"}`;
|
|
66
|
+
|
|
67
|
+
const headers = new Headers();
|
|
68
|
+
|
|
69
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
70
|
+
if (Array.isArray(value)) {
|
|
71
|
+
for (const item of value) headers.append(key, item);
|
|
72
|
+
} else if (value !== undefined) {
|
|
73
|
+
headers.set(key, value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const init = {
|
|
78
|
+
method: req.method ?? "GET",
|
|
79
|
+
headers
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
if (init.method !== "GET" && init.method !== "HEAD") {
|
|
83
|
+
init.body = Readable.toWeb(req);
|
|
84
|
+
init.duplex = "half";
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Request(url, init);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function writeWebResponse(res, response) {
|
|
91
|
+
res.statusCode = response.status;
|
|
92
|
+
res.statusMessage = response.statusText;
|
|
93
|
+
|
|
94
|
+
response.headers.forEach((value, key) => {
|
|
95
|
+
res.setHeader(key, value);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
if (!response.body) {
|
|
99
|
+
res.end();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const stream = Readable.fromWeb(response.body);
|
|
104
|
+
stream.pipe(res);
|
|
105
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { defineAction } from "blokd/resume";
|
|
2
|
+
|
|
3
|
+
type MessageState = {
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const show = defineAction<MessageState>(({ state, el }) => {
|
|
8
|
+
el.text(state.text);
|
|
9
|
+
});
|
|
@@ -24,6 +24,8 @@ export default function Layout(props: LayoutProps) {
|
|
|
24
24
|
<a href="/">Home</a>
|
|
25
25
|
{" · "}
|
|
26
26
|
<a href="/about">About</a>
|
|
27
|
+
{" · "}
|
|
28
|
+
<a href="/contact">Contact</a>
|
|
27
29
|
</nav>
|
|
28
30
|
</header>
|
|
29
31
|
|
|
@@ -31,4 +33,4 @@ export default function Layout(props: LayoutProps) {
|
|
|
31
33
|
</body>
|
|
32
34
|
</html>
|
|
33
35
|
);
|
|
34
|
-
}
|
|
36
|
+
}
|
|
@@ -3,6 +3,12 @@ export const meta = () => ({
|
|
|
3
3
|
description: "About this minimal Blokd application."
|
|
4
4
|
});
|
|
5
5
|
|
|
6
|
+
export const runtime = "none";
|
|
7
|
+
|
|
8
|
+
export const budget = {
|
|
9
|
+
client: "0kb"
|
|
10
|
+
};
|
|
11
|
+
|
|
6
12
|
export default function About() {
|
|
7
13
|
return (
|
|
8
14
|
<section>
|
|
@@ -14,4 +20,4 @@ export default function About() {
|
|
|
14
20
|
</p>
|
|
15
21
|
</section>
|
|
16
22
|
);
|
|
17
|
-
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
type ContactData = {
|
|
2
|
+
ok?: boolean;
|
|
3
|
+
email?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
type ContactProps = {
|
|
8
|
+
data?: ContactData;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const meta = () => ({
|
|
12
|
+
title: "Contact | Blokd App",
|
|
13
|
+
description: "A native form route in a minimal Blokd application."
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const runtime = "none";
|
|
17
|
+
|
|
18
|
+
export const budget = {
|
|
19
|
+
client: "0kb"
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export async function action({ request }: { request: Request }) {
|
|
23
|
+
const form = await request.formData();
|
|
24
|
+
const email = String(form.get("email") ?? "").trim();
|
|
25
|
+
|
|
26
|
+
if (!email.includes("@")) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
error: "Enter a valid email."
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
email
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default function Contact(props: ContactProps) {
|
|
40
|
+
return (
|
|
41
|
+
<section>
|
|
42
|
+
<h1>Contact</h1>
|
|
43
|
+
|
|
44
|
+
<p>This form submits with native browser POST and no client JavaScript.</p>
|
|
45
|
+
|
|
46
|
+
{props.data?.error ? <p role="alert">{props.data.error}</p> : null}
|
|
47
|
+
{props.data?.ok ? <p>{`Subscribed ${props.data.email}`}</p> : null}
|
|
48
|
+
|
|
49
|
+
<form method="post">
|
|
50
|
+
<label>
|
|
51
|
+
Email
|
|
52
|
+
<input name="email" type="email" required />
|
|
53
|
+
</label>
|
|
54
|
+
<button>Subscribe</button>
|
|
55
|
+
</form>
|
|
56
|
+
</section>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Island, on } from "blokd";
|
|
2
2
|
|
|
3
3
|
export const meta = () => ({
|
|
4
4
|
title: "Blokd App",
|
|
@@ -6,29 +6,32 @@ export const meta = () => ({
|
|
|
6
6
|
});
|
|
7
7
|
|
|
8
8
|
export default function Home() {
|
|
9
|
-
const [count, setCount] = signal(0);
|
|
10
|
-
|
|
11
9
|
return (
|
|
12
10
|
<section>
|
|
13
11
|
<h1>Blokd App</h1>
|
|
14
12
|
|
|
15
13
|
<p>
|
|
16
|
-
This page demonstrates
|
|
14
|
+
This page demonstrates resumable islands. Blokd does not hydrate the
|
|
15
|
+
whole component tree; client behavior is attached explicitly.
|
|
17
16
|
</p>
|
|
18
17
|
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
<Island name="counter" state={{ count: 0 }}>
|
|
19
|
+
<button
|
|
20
|
+
type="button"
|
|
21
|
+
onClick={on("/src/resumables/counter.ts#increment")}
|
|
22
|
+
>
|
|
23
|
+
Count: 0
|
|
24
|
+
</button>
|
|
25
|
+
</Island>
|
|
22
26
|
|
|
23
|
-
<Island name="demo-island" state={{
|
|
27
|
+
<Island name="demo-island" state={{ text: "Hello from Blokd" }}>
|
|
24
28
|
<button
|
|
25
29
|
type="button"
|
|
26
|
-
|
|
27
|
-
onClick={resumable("/src/resumables/demo.ts#sayHello")}
|
|
30
|
+
onClick={on("/src/resumables/demo.ts#show")}
|
|
28
31
|
>
|
|
29
32
|
Run resumable handler
|
|
30
33
|
</button>
|
|
31
34
|
</Island>
|
|
32
35
|
</section>
|
|
33
36
|
);
|
|
34
|
-
}
|
|
37
|
+
}
|