create-hum 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/package.json +22 -3
- package/templates/basic/_README.md +62 -0
- package/templates/basic/_gitignore +5 -0
- package/templates/basic/_package.json +25 -0
- package/templates/basic/client/main.ts +3 -0
- package/templates/basic/client/style.css +1 -0
- package/templates/basic/client/vite-env.d.ts +1 -0
- package/templates/basic/islands/Counter.tsx +14 -0
- package/templates/basic/routes/_404.tsx +3 -0
- package/templates/basic/routes/_app.tsx +22 -0
- package/templates/basic/routes/blog/[id].tsx +19 -0
- package/templates/basic/routes/index.tsx +14 -0
- package/templates/basic/routes/partials.tsx +36 -0
- package/templates/basic/routes/seo.tsx +14 -0
- package/templates/basic/server.ts +16 -0
- package/templates/basic/tsconfig.json +16 -0
- package/templates/basic/vite.config.ts +7 -0
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
4
|
+
import { basename, join, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
const TEMPLATE_DIR = fileURLToPath(new URL("../templates/basic", import.meta.url));
|
|
7
|
+
/**
|
|
8
|
+
* Template files whose name is only valid once they land in the new project
|
|
9
|
+
* (a `_package.json` inside this package would confuse tooling).
|
|
10
|
+
*/
|
|
11
|
+
const RENAMES = {
|
|
12
|
+
"_gitignore": ".gitignore",
|
|
13
|
+
"_package.json": "package.json",
|
|
14
|
+
"_README.md": "README.md",
|
|
15
|
+
};
|
|
16
|
+
const USAGE = `create-hum <project-directory>
|
|
17
|
+
|
|
18
|
+
Scaffolds a new Hum project into <project-directory>.
|
|
19
|
+
|
|
20
|
+
pnpm dlx create-hum my-app
|
|
21
|
+
npm create hum@latest my-app
|
|
22
|
+
`;
|
|
23
|
+
async function walk(dir) {
|
|
24
|
+
const out = [];
|
|
25
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
26
|
+
const full = join(dir, entry.name);
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
out.push(...await walk(full));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
out.push(full);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
async function copyTemplate(target, name) {
|
|
37
|
+
for (const file of await walk(TEMPLATE_DIR)) {
|
|
38
|
+
const rel = file
|
|
39
|
+
.slice(TEMPLATE_DIR.length)
|
|
40
|
+
.replace(/\\/g, "/")
|
|
41
|
+
.replace(/^\//, "");
|
|
42
|
+
const parts = rel.split("/");
|
|
43
|
+
const fileName = parts[parts.length - 1];
|
|
44
|
+
parts[parts.length - 1] = RENAMES[fileName] ?? fileName;
|
|
45
|
+
const dest = join(target, ...parts);
|
|
46
|
+
await mkdir(join(dest, ".."), { recursive: true });
|
|
47
|
+
// `replaceAll` is a no-op for the files that do not use the placeholder,
|
|
48
|
+
// so every file can be treated the same way.
|
|
49
|
+
const raw = await readFile(file, "utf8");
|
|
50
|
+
await writeFile(dest, raw.replaceAll("{{name}}", name), "utf8");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function isEmptyDir(dir) {
|
|
54
|
+
if (!existsSync(dir))
|
|
55
|
+
return true;
|
|
56
|
+
return (await readdir(dir)).length === 0;
|
|
57
|
+
}
|
|
58
|
+
const arg = process.argv[2];
|
|
59
|
+
if (arg === undefined || arg === "--help" || arg === "-h") {
|
|
60
|
+
console.log(USAGE);
|
|
61
|
+
process.exit(arg === undefined ? 1 : 0);
|
|
62
|
+
}
|
|
63
|
+
const target = resolve(process.cwd(), arg);
|
|
64
|
+
if (!await isEmptyDir(target)) {
|
|
65
|
+
console.error(`create-hum: "${arg}" already exists and is not empty.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
const name = basename(target);
|
|
69
|
+
await mkdir(target, { recursive: true });
|
|
70
|
+
await copyTemplate(target, name);
|
|
71
|
+
console.log(`\nCreated "${name}" in ${target}\n`);
|
|
72
|
+
console.log("Next steps:");
|
|
73
|
+
console.log(` cd ${arg}`);
|
|
74
|
+
console.log(" npm install");
|
|
75
|
+
console.log(" npm run dev\n");
|
|
76
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-hum",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Scaffolds a new Hum project",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/oaly2000/hum.git"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"bin": {
|
|
12
|
+
"create-hum": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"templates"
|
|
17
|
+
],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@types/node": "^26.6.5"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -p tsconfig.json",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
}
|
|
6
25
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
A [Hum](https://github.com/) app: static HTML first, islands for interaction.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install
|
|
7
|
+
npm run dev # Custom Hono server with Vite SSR + HMR middleware
|
|
8
|
+
npm run build # client + server bundles into _hum/
|
|
9
|
+
npm start # run the built server
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
The production server bundle includes its npm dependencies, so the runtime
|
|
13
|
+
deployment does not need the project's `node_modules`. The generated `_hum/`
|
|
14
|
+
directory can move or even be renamed — it locates itself next to the server
|
|
15
|
+
bundle — as long as its internal structure is kept and `server/server.js` is the
|
|
16
|
+
entry point. Client assets stay reachable under `/_hum/`, the URL recorded in
|
|
17
|
+
the build manifest. Node.js 20 or newer is still required.
|
|
18
|
+
|
|
19
|
+
## Hono server
|
|
20
|
+
|
|
21
|
+
`server.ts` exports a Hono app. Configure middleware and API routes with the
|
|
22
|
+
normal Hono API; Hum mounts its file-based routes directly onto that app and
|
|
23
|
+
handles unmatched requests with its `_404` page.
|
|
24
|
+
Hum page errors use `_error`; API errors keep using the app's Hono `onError`.
|
|
25
|
+
Vite owns the development listener, preserving SSR and HMR. In production the
|
|
26
|
+
generated entry starts the exported app. The example includes a `/api/health`
|
|
27
|
+
route.
|
|
28
|
+
|
|
29
|
+
## Layout
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
routes/
|
|
33
|
+
_app.tsx document shell (html/head/body)
|
|
34
|
+
_404.tsx not found page
|
|
35
|
+
index.tsx /
|
|
36
|
+
seo.tsx /seo
|
|
37
|
+
blog/[id].tsx /blog/:id
|
|
38
|
+
islands/
|
|
39
|
+
Counter.tsx interactive component
|
|
40
|
+
client/
|
|
41
|
+
main.ts client entry; imports global CSS
|
|
42
|
+
style.css Tailwind CSS entry point
|
|
43
|
+
server.ts Hono app with middleware and API routes
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Tailwind CSS
|
|
47
|
+
|
|
48
|
+
The basic template includes Tailwind CSS v4. Global styles are imported from
|
|
49
|
+
`client/main.ts`; add or edit utility classes in your routes and islands.
|
|
50
|
+
|
|
51
|
+
## Head
|
|
52
|
+
|
|
53
|
+
Route files export `head` — either a JSX fragment or a function of the route
|
|
54
|
+
props. `_app.tsx` provides the defaults, pages override them by tag.
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
export const head = <title>Home</title>;
|
|
58
|
+
|
|
59
|
+
export function head({ params }: RouteProps) {
|
|
60
|
+
return <title>Blog {params.id}</title>;
|
|
61
|
+
}
|
|
62
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{name}}",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"start": "node _hum/server/server.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@hono/node-server": "^2.1.4",
|
|
13
|
+
"@humjs/core": "^0.0.1",
|
|
14
|
+
"@humjs/vite-plugin": "^0.0.1",
|
|
15
|
+
"@preact/signals": "^2.11.3",
|
|
16
|
+
"hono": "^4.13.13",
|
|
17
|
+
"preact": "^11.0.1"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
21
|
+
"@types/node": "^26.6.5",
|
|
22
|
+
"tailwindcss": "^4.3.3",
|
|
23
|
+
"vite": "^8.3.4"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/// <reference types="vite/client" />
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useSignal } from "@preact/signals";
|
|
2
|
+
|
|
3
|
+
/** Interactive island: everything around it stays static HTML. */
|
|
4
|
+
export default function Counter({ start = 0 }: { start?: number }) {
|
|
5
|
+
const count = useSignal(start);
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<div class="counter">
|
|
9
|
+
<button type="button" onClick={() => count.value--}>-</button>
|
|
10
|
+
<output>{count.value}</output>
|
|
11
|
+
<button type="button" onClick={() => count.value++}>+</button>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ComponentChildren } from "preact";
|
|
2
|
+
import { define } from "@humjs/core";
|
|
3
|
+
|
|
4
|
+
/** Application wide head tags. Pages override them by tag. */
|
|
5
|
+
export const head = define.head(
|
|
6
|
+
<>
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
8
|
+
<title>{{ name }}</title>
|
|
9
|
+
</>,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
/** Document shell. Hum injects the merged head tags into `<head>`. */
|
|
13
|
+
export default function App({ children }: { children?: ComponentChildren }) {
|
|
14
|
+
return (
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<head>
|
|
17
|
+
<meta charSet="utf-8" />
|
|
18
|
+
</head>
|
|
19
|
+
<body>{children}</body>
|
|
20
|
+
</html>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { define, type Handlers, page, type RouteProps } from "@humjs/core";
|
|
2
|
+
|
|
3
|
+
interface BlogPageData {
|
|
4
|
+
post: { id: string; title: string };
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const head = define.head(({ params }) => <title>Blog {params.id}
|
|
8
|
+
</title>);
|
|
9
|
+
|
|
10
|
+
export const handlers: Handlers = {
|
|
11
|
+
GET: (c) => {
|
|
12
|
+
const id = c.req.param("id");
|
|
13
|
+
return page({ post: { id, title: `Post ${id}` } });
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default define.page<BlogPageData>(({ data }) => (
|
|
18
|
+
<h1>Blog post {data.post.title}</h1>
|
|
19
|
+
));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { define } from "@humjs/core";
|
|
2
|
+
import Counter from "../islands/Counter.js";
|
|
3
|
+
|
|
4
|
+
export const head = define.head(<title>{{ name }}</title>);
|
|
5
|
+
|
|
6
|
+
export default function Home() {
|
|
7
|
+
return (
|
|
8
|
+
<section class="mx-auto max-w-3xl space-y-6 p-8">
|
|
9
|
+
<h1 class="text-3xl font-bold">{{ name }}</h1>
|
|
10
|
+
{/* Only this island ships JavaScript; the rest stays static HTML. */}
|
|
11
|
+
<Counter start={0} />
|
|
12
|
+
</section>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { define, Partial, type RouteProps } from "@humjs/core";
|
|
2
|
+
|
|
3
|
+
const TOPICS: Record<string, string> = {
|
|
4
|
+
"": "Pick a topic from the list.",
|
|
5
|
+
a: "You are reading topic A.",
|
|
6
|
+
b: "You are reading topic B.",
|
|
7
|
+
c: "You are reading topic C.",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const head = define.head(({ url }) => (
|
|
11
|
+
<title>Partials — {url.searchParams.get("topic") || "home"}</title>
|
|
12
|
+
));
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* `f-client-nav` on the wrapper makes every link/submit inside it a client side
|
|
16
|
+
* navigation: the browser does not do a full reload, only the `<Partial>` regions
|
|
17
|
+
* of the target page are fetched and swapped in. The target page reuses the same
|
|
18
|
+
* partial `name`, so the region updates without touching the rest of the layout.
|
|
19
|
+
*/
|
|
20
|
+
export default function PartialsPage(props: RouteProps) {
|
|
21
|
+
const topic = props.url.searchParams.get("topic") ?? "";
|
|
22
|
+
return (
|
|
23
|
+
<div f-client-nav>
|
|
24
|
+
<p>Client side navigation swaps only the partial region below.</p>
|
|
25
|
+
<nav>
|
|
26
|
+
<a href="/partials">Home</a>
|
|
27
|
+
<a href="/partials?topic=a">A</a>
|
|
28
|
+
<a href="/partials?topic=b">B</a>
|
|
29
|
+
<a href="/partials?topic=c">C</a>
|
|
30
|
+
</nav>
|
|
31
|
+
<Partial name="content">
|
|
32
|
+
<p>{TOPICS[topic] ?? "Unknown topic."}</p>
|
|
33
|
+
</Partial>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { define } from "@humjs/core";
|
|
2
|
+
|
|
3
|
+
/** `head` as a plain JSX fragment: no props needed. */
|
|
4
|
+
export const head = define.head(
|
|
5
|
+
<>
|
|
6
|
+
<title>SEO</title>
|
|
7
|
+
<meta name="description" content="head exported as a JSX fragment" />
|
|
8
|
+
<link rel="canonical" href="/seo" />
|
|
9
|
+
</>,
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export default function Seo() {
|
|
13
|
+
return <h1>SEO</h1>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
|
|
3
|
+
const app = new Hono();
|
|
4
|
+
|
|
5
|
+
configure(app);
|
|
6
|
+
|
|
7
|
+
export default app;
|
|
8
|
+
|
|
9
|
+
function configure(app: Hono) {
|
|
10
|
+
app.use("*", async (c, next) => {
|
|
11
|
+
c.header("x-powered-by", "Hum + Hono");
|
|
12
|
+
await next();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
app.get("/api/health", (c) => c.json({ status: "ok" }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"lib": ["ES2022", "DOM"],
|
|
5
|
+
"module": "NodeNext",
|
|
6
|
+
"moduleResolution": "NodeNext",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"jsxImportSource": "preact",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"types": ["node"]
|
|
14
|
+
},
|
|
15
|
+
"include": ["**/*.ts", "**/*.tsx"]
|
|
16
|
+
}
|
package/README.md
DELETED