create-fuzor 0.4.0 → 0.5.3
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/create.d.ts +20 -0
- package/dist/create.js +109 -0
- package/dist/create.js.map +1 -0
- package/index.mjs +1 -1
- package/package.json +18 -14
- package/templates/basic/README.md +26 -0
- package/templates/basic/_gitignore +3 -0
- package/templates/basic/client/context.d.ts +3 -0
- package/templates/basic/client/counter.ts +3 -0
- package/templates/basic/components/app-badge.html +1 -0
- package/templates/basic/index.html +12 -0
- package/templates/basic/package.json +31 -0
- package/templates/basic/pages/about/index.html +5 -0
- package/templates/basic/pages/docs/index.md +25 -0
- package/templates/basic/pages/index.html +9 -0
- package/templates/basic/pages/layout.html +8 -0
- package/templates/basic/pnpm-workspace.yaml +5 -0
- package/templates/basic/server.mjs +14 -0
- package/templates/basic/style.css +1 -0
- package/templates/basic/tsconfig.json +22 -0
- package/templates/basic/vite.config.ts +4 -0
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function createApp(directory: string | undefined, options?: {
|
|
2
|
+
yes?: boolean;
|
|
3
|
+
install?: boolean;
|
|
4
|
+
}): Promise<string>;
|
|
5
|
+
export declare const createCommand: import("citty").CommandDef<{
|
|
6
|
+
readonly directory: {
|
|
7
|
+
readonly type: "positional";
|
|
8
|
+
readonly description: "Directory for the new app";
|
|
9
|
+
readonly required: false;
|
|
10
|
+
};
|
|
11
|
+
readonly yes: {
|
|
12
|
+
readonly type: "boolean";
|
|
13
|
+
readonly description: "Use defaults without prompting";
|
|
14
|
+
};
|
|
15
|
+
readonly install: {
|
|
16
|
+
readonly type: "boolean";
|
|
17
|
+
readonly description: "Install dependencies with pnpm";
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
export declare function runCreateCli(): void;
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { cp, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { defineCommand, runMain } from "citty";
|
|
6
|
+
import * as prompts from "@clack/prompts";
|
|
7
|
+
import colors from "picocolors";
|
|
8
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
9
|
+
const templateRoot = path.join(packageRoot, "templates/basic");
|
|
10
|
+
async function available(directory) {
|
|
11
|
+
try {
|
|
12
|
+
return (await readdir(directory)).length === 0;
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (error.code === "ENOENT")
|
|
16
|
+
return true;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function installDependencies(directory) {
|
|
21
|
+
await new Promise((resolve, reject) => {
|
|
22
|
+
const child = spawn(process.platform === "win32" ? "pnpm.cmd" : "pnpm", ["install"], { cwd: directory, stdio: "inherit" });
|
|
23
|
+
child.once("error", reject);
|
|
24
|
+
child.once("exit", (code) => code === 0 ? resolve() : reject(new Error(`pnpm install exited with ${code}`)));
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function createApp(directory, options = {}) {
|
|
28
|
+
prompts.intro(colors.cyan(colors.bold("◉ Create Fuzor")));
|
|
29
|
+
let selected = directory;
|
|
30
|
+
if (!selected) {
|
|
31
|
+
if (options.yes)
|
|
32
|
+
selected = "fuzor-app";
|
|
33
|
+
else if (!process.stdin.isTTY)
|
|
34
|
+
throw new Error("Pass a directory or --yes in a non-interactive terminal.");
|
|
35
|
+
else {
|
|
36
|
+
const answer = await prompts.text({ message: "Where should we create your app?", placeholder: "my-fuzor-app", defaultValue: "my-fuzor-app" });
|
|
37
|
+
if (typeof answer !== "string") {
|
|
38
|
+
prompts.cancel("Creation cancelled.");
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
selected = answer.trim();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!selected)
|
|
45
|
+
throw new Error("App directory cannot be empty.");
|
|
46
|
+
const destination = path.resolve(selected);
|
|
47
|
+
if (!(await available(destination)))
|
|
48
|
+
throw new Error(`${destination} is not empty. Choose another directory.`);
|
|
49
|
+
const name = path.basename(destination).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "fuzor-app";
|
|
50
|
+
const creatorPackage = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8"));
|
|
51
|
+
const dependency = `^${creatorPackage.version}`;
|
|
52
|
+
const spin = process.stdout.isTTY ? prompts.spinner() : undefined;
|
|
53
|
+
if (spin)
|
|
54
|
+
spin.start("Creating your app");
|
|
55
|
+
else
|
|
56
|
+
console.log("Creating your app...");
|
|
57
|
+
try {
|
|
58
|
+
await mkdir(destination, { recursive: true });
|
|
59
|
+
await cp(templateRoot, destination, { recursive: true });
|
|
60
|
+
await rename(path.join(destination, "_gitignore"), path.join(destination, ".gitignore"));
|
|
61
|
+
const packageFile = path.join(destination, "package.json");
|
|
62
|
+
const contents = (await readFile(packageFile, "utf8"))
|
|
63
|
+
.replaceAll("__APP_NAME__", name)
|
|
64
|
+
.replaceAll("__FUZOR_VERSION__", dependency);
|
|
65
|
+
await writeFile(packageFile, contents);
|
|
66
|
+
const readmeFile = path.join(destination, "README.md");
|
|
67
|
+
await writeFile(readmeFile, (await readFile(readmeFile, "utf8")).replaceAll("__APP_NAME__", name));
|
|
68
|
+
if (spin)
|
|
69
|
+
spin.stop(`Created ${colors.cyan(name)}`);
|
|
70
|
+
else
|
|
71
|
+
console.log(`Created ${name}`);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (spin)
|
|
75
|
+
spin.stop("Could not create app");
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
let installed = false;
|
|
79
|
+
if (options.install) {
|
|
80
|
+
prompts.log.info("Installing dependencies with pnpm");
|
|
81
|
+
await installDependencies(destination);
|
|
82
|
+
installed = true;
|
|
83
|
+
}
|
|
84
|
+
else if (!options.yes && process.stdin.isTTY) {
|
|
85
|
+
const answer = await prompts.confirm({ message: "Install dependencies now?", initialValue: true });
|
|
86
|
+
if (prompts.isCancel(answer))
|
|
87
|
+
prompts.cancel("Dependency installation skipped.");
|
|
88
|
+
else if (answer) {
|
|
89
|
+
await installDependencies(destination);
|
|
90
|
+
installed = true;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
prompts.note(`cd ${path.relative(process.cwd(), destination) || "."}\n${installed ? "" : "pnpm install\n"}pnpm dev`, "Next steps");
|
|
94
|
+
prompts.outro(colors.green("Ready to build with Fuzor"));
|
|
95
|
+
return destination;
|
|
96
|
+
}
|
|
97
|
+
export const createCommand = defineCommand({
|
|
98
|
+
meta: { name: "create", description: "Scaffold a new Fuzor app" },
|
|
99
|
+
args: {
|
|
100
|
+
directory: { type: "positional", description: "Directory for the new app", required: false },
|
|
101
|
+
yes: { type: "boolean", description: "Use defaults without prompting" },
|
|
102
|
+
install: { type: "boolean", description: "Install dependencies with pnpm" }
|
|
103
|
+
},
|
|
104
|
+
async run({ args }) { await createApp(args.directory, { yes: args.yes ?? false, install: args.install ?? false }); }
|
|
105
|
+
});
|
|
106
|
+
export function runCreateCli() {
|
|
107
|
+
void runMain({ ...createCommand, meta: { name: "create-fuzor", description: "Scaffold a new Fuzor app" } });
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"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"}
|
package/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-fuzor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Create a Fuzor app from the official starter",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://fuzor.dev",
|
|
8
|
-
"repository": {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
"repository": { "type": "git", "url": "git+https://github.com/ivanjeremic/fuzor.git", "directory": "packages/create-fuzor" },
|
|
9
|
+
"bin": { "create-fuzor": "./index.mjs" },
|
|
10
|
+
"files": ["index.mjs", "dist", "templates/basic"],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "node scripts/clean-dist.mjs && tsc -p tsconfig.json",
|
|
13
|
+
"prepack": "pnpm build",
|
|
14
|
+
"typecheck": "tsc --noEmit"
|
|
12
15
|
},
|
|
13
|
-
"bin": {
|
|
14
|
-
"create-fuzor": "./index.mjs"
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"index.mjs"
|
|
18
|
-
],
|
|
19
16
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
"@clack/prompts": "^1.8.0",
|
|
18
|
+
"citty": "^0.2.2",
|
|
19
|
+
"picocolors": "^1.1.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"@types/node": "^25.9.1",
|
|
23
|
+
"typescript": "^6.0.3"
|
|
24
|
+
},
|
|
25
|
+
"engines": { "node": "^22.15.0 || >=24.0.0" }
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# __APP_NAME__
|
|
2
|
+
|
|
3
|
+
A [Fuzor](https://fuzor.dev) app. Every page in `pages/` is compiled to finished HTML at build time; choose SPA or MPA navigation and static or server delivery when you build.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm dev # SPA development server
|
|
7
|
+
pnpm dev:mpa # MPA development server
|
|
8
|
+
pnpm dev:electron # prompts to install Electron, then starts Vite HMR + Electron host
|
|
9
|
+
pnpm build:spa # .fuzor/web/dist-spa/: one document, lazy native behavior chunks
|
|
10
|
+
pnpm build:spa --inline # .fuzor/web/dist-spa/index.html with everything inlined
|
|
11
|
+
pnpm build:mpa # .fuzor/web/dist-mpa/: one HTML file per page
|
|
12
|
+
pnpm build:electron # .fuzor/dist-desktop/: inline SPA + Electron host/preload
|
|
13
|
+
pnpm build:mpa:server # .fuzor/dist-backend/: the same pages plus a Node server
|
|
14
|
+
pnpm preview:mpa # serve a finished build locally
|
|
15
|
+
pnpm start:mpa:server # run the server build with server.mjs
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
| Path | Purpose |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| `pages/` | Routes: `index.html` or `index.md` per folder, plus `layout.html` |
|
|
21
|
+
| `components/` | Build-time HTML components such as `<app-badge>` |
|
|
22
|
+
| `client/` | JS/TS modules referenced by HTML module scripts |
|
|
23
|
+
| `index.html` | The document shell with the Fuzor outlet |
|
|
24
|
+
| `server.mjs` | A Node server for server builds |
|
|
25
|
+
|
|
26
|
+
The counter uses native `fuzor.state` and a lazy named `fuzor.action`; its initial value is prerendered. Adding an `api/` folder, a `server.ts` next to a page, or an HTML server-script request loader/action selects backend output automatically. Build-only server props remain static. See the [documentation](https://fuzor.dev/docs/).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<span class="badge">Your Fuzor app is ready</span>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Fuzor app</title>
|
|
7
|
+
<link rel="stylesheet" href="/style.css" />
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<!--fuzor:outlet-start--><!--fuzor:outlet-end-->
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__APP_NAME__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"packageManager": "pnpm@10.33.0",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "fuzor dev",
|
|
8
|
+
"dev:spa": "fuzor dev --target spa",
|
|
9
|
+
"dev:mpa": "fuzor dev --target mpa",
|
|
10
|
+
"dev:spa:server": "fuzor dev --target spa --server",
|
|
11
|
+
"dev:mpa:server": "fuzor dev --target mpa --server",
|
|
12
|
+
"dev:electron": "fuzor dev --target electron",
|
|
13
|
+
"build": "fuzor build",
|
|
14
|
+
"build:spa": "fuzor build --target spa",
|
|
15
|
+
"build:mpa": "fuzor build --target mpa",
|
|
16
|
+
"build:spa:server": "fuzor build --target spa --server",
|
|
17
|
+
"build:mpa:server": "fuzor build --target mpa --server",
|
|
18
|
+
"build:electron": "fuzor build --target electron",
|
|
19
|
+
"preview": "fuzor preview --target mpa",
|
|
20
|
+
"preview:spa": "fuzor preview --target spa",
|
|
21
|
+
"preview:mpa": "fuzor preview --target mpa",
|
|
22
|
+
"preview:spa:server": "fuzor preview --target spa --server",
|
|
23
|
+
"preview:mpa:server": "fuzor preview --target mpa --server",
|
|
24
|
+
"start:spa:server": "node server.mjs spa",
|
|
25
|
+
"start:mpa:server": "node server.mjs mpa",
|
|
26
|
+
"typecheck": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": { "fuzor": "__FUZOR_VERSION__" },
|
|
29
|
+
"devDependencies": { "typescript": "^6.0.3", "vite": "^6.4.3", "@types/node": "^25.9.1" },
|
|
30
|
+
"engines": { "node": "^22.15.0 || >=24.0.0" }
|
|
31
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Markdown page
|
|
3
|
+
description: A Markdown route with a component and a native behavior.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Your Markdown route
|
|
7
|
+
|
|
8
|
+
This page is Markdown. Fuzor compiles it to HTML before the browser requests it.
|
|
9
|
+
|
|
10
|
+
- `pages/` defines routes.
|
|
11
|
+
- `pages/layout.html` wraps them.
|
|
12
|
+
- `behaviors/` holds native DOM behavior.
|
|
13
|
+
|
|
14
|
+
Components and native behaviors work in Markdown when they are on their own line:
|
|
15
|
+
|
|
16
|
+
<app-badge></app-badge>
|
|
17
|
+
|
|
18
|
+
<section><button type="button" class="counter">Count: 0</button><script type="module" src="/client/counter.ts"></script></section>
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
// behaviors/counter.ts runs only where a native behavior uses it.
|
|
22
|
+
export function mount(host: HTMLElement) {}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
[Return home](/)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<section class="hero">
|
|
2
|
+
<p class="eyebrow">HTML first · SPA or MPA · Static or server</p>
|
|
3
|
+
<app-badge></app-badge>
|
|
4
|
+
<h1>Build once. Choose how to ship.</h1>
|
|
5
|
+
<p>Edit <code>pages/index.html</code> and build the same app as SPA or MPA, with static or server delivery.</p>
|
|
6
|
+
<section><script type="module" src="/client/counter.ts"></script><button type="button" class="counter" data-fz-click="increment">Count: <fz-state name="count"></fz-state></button></section>
|
|
7
|
+
<p class="hint">The initial count is prerendered. Its native action loads on interaction.</p>
|
|
8
|
+
<fuzor-link href="/docs/" class="link">Explore the Markdown page →</fuzor-link>
|
|
9
|
+
</section>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<div class="shell">
|
|
2
|
+
<header class="header">
|
|
3
|
+
<a href="/" class="brand">◉ Fuzor</a>
|
|
4
|
+
<nav><fuzor-link href="/">Home</fuzor-link><fuzor-link href="/about/">About</fuzor-link><fuzor-link href="/docs/">Docs</fuzor-link></nav>
|
|
5
|
+
</header>
|
|
6
|
+
<main><slot></slot></main>
|
|
7
|
+
<footer>One app. SPA or MPA. Static or server.</footer>
|
|
8
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { createNodeServer, handleShutdownSignals } from "fuzor/server/node";
|
|
4
|
+
|
|
5
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
const target = process.argv[2] ?? "mpa";
|
|
7
|
+
if (!["spa", "mpa"].includes(target)) throw new Error(`Unknown server target: ${target}. Choose spa or mpa.`);
|
|
8
|
+
const directory = `.fuzor/web/dist-${target}`;
|
|
9
|
+
const { handler, manifest } = await import("./.fuzor/dist-backend/entry.mjs");
|
|
10
|
+
const host = process.env.HOST ?? "127.0.0.1";
|
|
11
|
+
const port = Number(process.env.PORT ?? 4173);
|
|
12
|
+
const server = createNodeServer({ handler, base: manifest.base, assetsDir: path.join(here, directory) });
|
|
13
|
+
handleShutdownSignals(server);
|
|
14
|
+
server.listen(port, host, () => console.log(`Fuzor ${target.toUpperCase()} server listening on http://${host}:${port}`));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:#e9efff;background:#0d1220;font-synthesis:none}*{box-sizing:border-box}body{margin:0}.shell{min-height:100vh;display:flex;flex-direction:column}.header{display:flex;align-items:center;justify-content:space-between;padding:1.5rem max(5vw,1.5rem);border-bottom:1px solid #263044}.brand{color:#8bd8c7;font-weight:800;text-decoration:none;letter-spacing:-.04em}nav{display:flex;gap:1.25rem}nav a,.link{color:#b7c5e1;text-decoration:none}nav a:hover,.link:hover{color:#8bd8c7}main{width:min(880px,90vw);margin:0 auto;flex:1;padding:7vw 0}.hero{max-width:680px}.eyebrow{text-transform:uppercase;letter-spacing:.18em;color:#8bd8c7;font-size:.72rem;font-weight:700}.badge{display:inline-block;padding:.35rem .65rem;border:1px solid #316456;border-radius:999px;color:#8bd8c7;font-size:.78rem}h1{font-size:clamp(2.6rem,7vw,5rem);line-height:1.05;letter-spacing:-.065em;margin:.5rem 0 1.5rem}p,li{line-height:1.7;color:#bac6dc}code{color:#8bd8c7}.counter{background:#8bd8c7;color:#10221f;border:0;border-radius:999px;padding:.85rem 1.25rem;font:inherit;font-weight:700;cursor:pointer}.hint{font-size:.86rem}.link{display:inline-block;margin-top:1.5rem}footer{padding:1.5rem max(5vw,1.5rem);border-top:1px solid #263044;color:#71819e;font-size:.8rem}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2022",
|
|
8
|
+
"DOM"
|
|
9
|
+
],
|
|
10
|
+
"types": [
|
|
11
|
+
"vite/client"
|
|
12
|
+
],
|
|
13
|
+
"strict": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"noEmit": true
|
|
16
|
+
},
|
|
17
|
+
"include": [
|
|
18
|
+
"vite.config.ts",
|
|
19
|
+
".fuzor/*.d.ts",
|
|
20
|
+
"client/**/*.ts"
|
|
21
|
+
]
|
|
22
|
+
}
|