create-hum 0.0.2 → 0.0.4
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 +4 -50
- package/dist/index.js.map +1 -1
- package/dist/scaffold.d.ts +10 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +56 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +1 -1
- package/templates/basic/_package.json +2 -2
- package/templates/basic/routes/_app.tsx +1 -1
- package/templates/basic/routes/index.tsx +2 -2
- package/templates/basic/routes/partials.tsx +2 -2
package/dist/index.js
CHANGED
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
};
|
|
2
|
+
import { mkdir } from "node:fs/promises";
|
|
3
|
+
import { basename, resolve } from "node:path";
|
|
4
|
+
import { isEmptyDir, scaffold } from "./scaffold.js";
|
|
16
5
|
const USAGE = `create-hum <project-directory>
|
|
17
6
|
|
|
18
7
|
Scaffolds a new Hum project into <project-directory>.
|
|
@@ -20,41 +9,6 @@ Scaffolds a new Hum project into <project-directory>.
|
|
|
20
9
|
pnpm dlx create-hum my-app
|
|
21
10
|
npm create hum@latest my-app
|
|
22
11
|
`;
|
|
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
12
|
const arg = process.argv[2];
|
|
59
13
|
if (arg === undefined || arg === "--help" || arg === "-h") {
|
|
60
14
|
console.log(USAGE);
|
|
@@ -67,7 +21,7 @@ if (!await isEmptyDir(target)) {
|
|
|
67
21
|
}
|
|
68
22
|
const name = basename(target);
|
|
69
23
|
await mkdir(target, { recursive: true });
|
|
70
|
-
await
|
|
24
|
+
await scaffold(target, name);
|
|
71
25
|
console.log(`\nCreated "${name}" in ${target}\n`);
|
|
72
26
|
console.log("Next steps:");
|
|
73
27
|
console.log(` cd ${arg}`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,KAAK,GAAG;;;;;;CAMb,CAAC;AAEF,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;IAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;AAE3C,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;IAC9B,OAAO,CAAC,KAAK,CAAC,gBAAgB,GAAG,oCAAoC,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC9B,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACzC,MAAM,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAE7B,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,QAAQ,MAAM,IAAI,CAAC,CAAC;AAClD,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;AAC3B,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC;AAC3B,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AAC7B,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const TEMPLATE_DIR: string;
|
|
2
|
+
export declare function isEmptyDir(dir: string): Promise<boolean>;
|
|
3
|
+
/**
|
|
4
|
+
* Copies every template file into `target`, renaming the files that cannot
|
|
5
|
+
* live under their final name in this package and substituting the project
|
|
6
|
+
* name for the placeholder. `NAME_PLACEHOLDER` matches nothing in the files
|
|
7
|
+
* that do not use it, so every file can be treated the same way.
|
|
8
|
+
*/
|
|
9
|
+
export declare function scaffold(target: string, name: string, templateDir?: string): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,YAAY,QAExB,CAAC;AA4BF,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAG9D;AAED;;;;;GAKG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,MAAqB,GACjC,OAAO,CAAC,IAAI,CAAC,CAgBf"}
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
export const TEMPLATE_DIR = fileURLToPath(new URL("../templates/basic", import.meta.url));
|
|
6
|
+
/**
|
|
7
|
+
* Template files whose name is only valid once they land in the new project
|
|
8
|
+
* (a `_package.json` inside this package would confuse tooling).
|
|
9
|
+
*/
|
|
10
|
+
const RENAMES = {
|
|
11
|
+
"_gitignore": ".gitignore",
|
|
12
|
+
"_package.json": "package.json",
|
|
13
|
+
"_README.md": "README.md",
|
|
14
|
+
};
|
|
15
|
+
/** Project name placeholder, written as `{{name}}` or `{{ name }}`. */
|
|
16
|
+
const NAME_PLACEHOLDER = /\{\{\s*name\s*\}\}/g;
|
|
17
|
+
async function walk(dir) {
|
|
18
|
+
const out = [];
|
|
19
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
20
|
+
const full = join(dir, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
out.push(...await walk(full));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
out.push(full);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export async function isEmptyDir(dir) {
|
|
31
|
+
if (!existsSync(dir))
|
|
32
|
+
return true;
|
|
33
|
+
return (await readdir(dir)).length === 0;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Copies every template file into `target`, renaming the files that cannot
|
|
37
|
+
* live under their final name in this package and substituting the project
|
|
38
|
+
* name for the placeholder. `NAME_PLACEHOLDER` matches nothing in the files
|
|
39
|
+
* that do not use it, so every file can be treated the same way.
|
|
40
|
+
*/
|
|
41
|
+
export async function scaffold(target, name, templateDir = TEMPLATE_DIR) {
|
|
42
|
+
for (const file of await walk(templateDir)) {
|
|
43
|
+
const rel = file
|
|
44
|
+
.slice(templateDir.length)
|
|
45
|
+
.replace(/\\/g, "/")
|
|
46
|
+
.replace(/^\//, "");
|
|
47
|
+
const parts = rel.split("/");
|
|
48
|
+
const fileName = parts[parts.length - 1];
|
|
49
|
+
parts[parts.length - 1] = RENAMES[fileName] ?? fileName;
|
|
50
|
+
const dest = join(target, ...parts);
|
|
51
|
+
await mkdir(join(dest, ".."), { recursive: true });
|
|
52
|
+
const raw = await readFile(file, "utf8");
|
|
53
|
+
await writeFile(dest, raw.replaceAll(NAME_PLACEHOLDER, name), "utf8");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,YAAY,GAAG,aAAa,CACvC,IAAI,GAAG,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAC/C,CAAC;AAEF;;;GAGG;AACH,MAAM,OAAO,GAA2B;IACtC,YAAY,EAAE,YAAY;IAC1B,eAAe,EAAE,cAAc;IAC/B,YAAY,EAAE,WAAW;CAC1B,CAAC;AAEF,uEAAuE;AACvE,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AAE/C,KAAK,UAAU,IAAI,CAAC,GAAW;IAC7B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,GAAG,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAW;IAC1C,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAClC,OAAO,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAc,EACd,IAAY,EACZ,WAAW,GAAW,YAAY;IAElC,KAAK,MAAM,IAAI,IAAI,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI;aACb,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;aACzB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACzC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC;QAExD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;QACpC,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnD,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,CAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@hono/node-server": "^2.1.4",
|
|
13
|
-
"@humjs/core": "^0.0
|
|
14
|
-
"@humjs/vite-plugin": "^0.0.
|
|
13
|
+
"@humjs/core": "^0.1.0",
|
|
14
|
+
"@humjs/vite-plugin": "^0.0.3",
|
|
15
15
|
"@preact/signals": "^2.11.3",
|
|
16
16
|
"hono": "^4.13.13",
|
|
17
17
|
"preact": "^11.0.1"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { define } from "@humjs/core";
|
|
2
2
|
import Counter from "../islands/Counter.js";
|
|
3
3
|
|
|
4
|
-
export const head = define.head(<title>{{
|
|
4
|
+
export const head = define.head(<title>{{name}}</title>);
|
|
5
5
|
|
|
6
6
|
export default function Home() {
|
|
7
7
|
return (
|
|
8
8
|
<section class="mx-auto max-w-3xl space-y-6 p-8">
|
|
9
|
-
<h1 class="text-3xl font-bold">{{
|
|
9
|
+
<h1 class="text-3xl font-bold">{{name}}</h1>
|
|
10
10
|
{/* Only this island ships JavaScript; the rest stays static HTML. */}
|
|
11
11
|
<Counter start={0} />
|
|
12
12
|
</section>
|
|
@@ -12,7 +12,7 @@ export const head = define.head(({ url }) => (
|
|
|
12
12
|
));
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* `
|
|
15
|
+
* `data-hum-client-nav` on the wrapper makes every link/submit inside it a client side
|
|
16
16
|
* navigation: the browser does not do a full reload, only the `<Partial>` regions
|
|
17
17
|
* of the target page are fetched and swapped in. The target page reuses the same
|
|
18
18
|
* partial `name`, so the region updates without touching the rest of the layout.
|
|
@@ -20,7 +20,7 @@ export const head = define.head(({ url }) => (
|
|
|
20
20
|
export default function PartialsPage(props: RouteProps) {
|
|
21
21
|
const topic = props.url.searchParams.get("topic") ?? "";
|
|
22
22
|
return (
|
|
23
|
-
<div
|
|
23
|
+
<div data-hum-client-nav>
|
|
24
24
|
<p>Client side navigation swaps only the partial region below.</p>
|
|
25
25
|
<nav>
|
|
26
26
|
<a href="/partials">Home</a>
|