create-patties 0.0.8 → 0.0.10
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 +1 -1
- package/bin/create-patties.ts +2 -0
- package/package.json +6 -1
- package/src/index.ts +69 -4
- package/templates/_claude/.claude/rules/islands.md +24 -10
- package/templates/_claude/.claude/rules/optional-ai.md +3 -2
- package/templates/_claude/.claude/skills/patties-cli/SKILL.md +9 -10
- package/templates/_claude/CLAUDE.md +10 -3
- package/templates/_codex/.codex/rules/islands.md +24 -10
- package/templates/_codex/.codex/rules/patties-cli.md +10 -15
- package/templates/_codex/AGENTS.md +5 -7
- package/templates/default/README-template.md +12 -23
- package/templates/default/app/routes/index.tsx +9 -1
- package/templates/default/app/server.ts +6 -3
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ bunx create-patties@latest my-app
|
|
|
9
9
|
|
|
10
10
|
Runs an interactive prompt (project name, agent overlay, demo template,
|
|
11
11
|
runtime target, deploy target), then scaffolds, installs, and gets you
|
|
12
|
-
to `
|
|
12
|
+
to `bunx patties dev` in one shot.
|
|
13
13
|
|
|
14
14
|
## Options
|
|
15
15
|
|
package/bin/create-patties.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-patties",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Scaffolder for new Patties projects.",
|
|
6
6
|
"bin": {
|
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
"README.md",
|
|
17
17
|
"CHANGELOG.md"
|
|
18
18
|
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"test": "bun test",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"lint": "biome check ."
|
|
23
|
+
},
|
|
19
24
|
"engines": {
|
|
20
25
|
"bun": ">=1.0.0"
|
|
21
26
|
}
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,32 @@ import {
|
|
|
12
12
|
} from "./prompts.ts";
|
|
13
13
|
import { renderTemplatesInTree } from "./readme.ts";
|
|
14
14
|
|
|
15
|
+
// Step logger — visible progress so the user can see what scaffolding does.
|
|
16
|
+
// Honors NO_COLOR; degrades gracefully on non-TTY streams.
|
|
17
|
+
function colorOn(): boolean {
|
|
18
|
+
if (process.env.NO_COLOR) return false;
|
|
19
|
+
return Boolean((process.stdout as NodeJS.WriteStream).isTTY);
|
|
20
|
+
}
|
|
21
|
+
const COL = colorOn();
|
|
22
|
+
const c = {
|
|
23
|
+
dim: (s: string) => (COL ? `\x1b[2m${s}\x1b[0m` : s),
|
|
24
|
+
green: (s: string) => (COL ? `\x1b[32m${s}\x1b[0m` : s),
|
|
25
|
+
cyan: (s: string) => (COL ? `\x1b[36m${s}\x1b[0m` : s),
|
|
26
|
+
bold: (s: string) => (COL ? `\x1b[1m${s}\x1b[0m` : s),
|
|
27
|
+
};
|
|
28
|
+
function step(msg: string): void {
|
|
29
|
+
process.stdout.write(` ${c.green("✓")} ${msg}\n`);
|
|
30
|
+
}
|
|
31
|
+
function pending(msg: string): void {
|
|
32
|
+
process.stdout.write(` ${c.dim("…")} ${msg}\n`);
|
|
33
|
+
}
|
|
34
|
+
function header(msg: string): void {
|
|
35
|
+
process.stdout.write(`\n${c.bold(c.cyan("▲"))} ${msg}\n\n`);
|
|
36
|
+
}
|
|
37
|
+
function fmtMs(ms: number): string {
|
|
38
|
+
return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
interface Args {
|
|
16
42
|
name?: string;
|
|
17
43
|
template: AgentTemplate;
|
|
@@ -86,22 +112,33 @@ export async function run(argv: string[]): Promise<number> {
|
|
|
86
112
|
|
|
87
113
|
probeTools();
|
|
88
114
|
|
|
115
|
+
header(`create-patties — scaffolding ${c.bold(args.name)}`);
|
|
116
|
+
|
|
89
117
|
await Bun.$`mkdir -p ${targetDir}`.quiet();
|
|
118
|
+
step(`created directory ${c.dim(targetDir)}`);
|
|
119
|
+
|
|
90
120
|
await Bun.$`cp -R ${baseDir}/. ${targetDir}`.quiet();
|
|
121
|
+
step(`copied base template ${c.dim("(default)")}`);
|
|
91
122
|
|
|
92
123
|
await renameTemplateFiles(targetDir);
|
|
93
124
|
await writePackageJson(targetDir, args.name);
|
|
125
|
+
step(`wrote package.json ${c.dim(`(name: ${args.name})`)}`);
|
|
94
126
|
await patchPattiesConfig(targetDir, args);
|
|
127
|
+
step(`patched patties.config.ts ${c.dim(`(target: ${args.target})`)}`);
|
|
95
128
|
|
|
96
129
|
if (args.template !== "none") {
|
|
97
130
|
const overlay = resolve(TEMPLATES_ROOT, `_${args.template}`);
|
|
98
131
|
if (existsSync(overlay)) {
|
|
99
132
|
await Bun.$`cp -R ${overlay}/. ${targetDir}`.quiet();
|
|
133
|
+
step(`applied agent overlay ${c.dim(`(${args.template})`)}`);
|
|
100
134
|
}
|
|
101
135
|
}
|
|
102
136
|
|
|
103
137
|
if (args.scaffold === "blank") {
|
|
104
138
|
await applyBlankScaffold(targetDir);
|
|
139
|
+
step("applied blank scaffold (no demo)");
|
|
140
|
+
} else {
|
|
141
|
+
step("included interactive todo demo");
|
|
105
142
|
}
|
|
106
143
|
|
|
107
144
|
await renderTemplatesInTree(targetDir, {
|
|
@@ -111,9 +148,28 @@ export async function run(argv: string[]): Promise<number> {
|
|
|
111
148
|
deploy: args.deploy,
|
|
112
149
|
scaffold: args.scaffold,
|
|
113
150
|
});
|
|
151
|
+
const manifestName =
|
|
152
|
+
args.template === "codex"
|
|
153
|
+
? "AGENTS.md"
|
|
154
|
+
: args.template === "claude"
|
|
155
|
+
? "CLAUDE.md"
|
|
156
|
+
: "manifest";
|
|
157
|
+
step(`rendered template variables (README, ${manifestName}, …)`);
|
|
114
158
|
|
|
115
159
|
if (args.install) {
|
|
116
|
-
|
|
160
|
+
pending("installing dependencies (bun install)…");
|
|
161
|
+
const t0 = performance.now();
|
|
162
|
+
const proc = await Bun.$`bun install`.cwd(targetDir).quiet().nothrow();
|
|
163
|
+
const dt = performance.now() - t0;
|
|
164
|
+
if (proc.exitCode === 0) {
|
|
165
|
+
step(`installed dependencies ${c.dim(`(${fmtMs(dt)})`)}`);
|
|
166
|
+
} else {
|
|
167
|
+
step(
|
|
168
|
+
`bun install exited with code ${proc.exitCode} — you may need to retry it manually`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
step(`skipped ${c.dim("`bun install`")} (--no-install)`);
|
|
117
173
|
}
|
|
118
174
|
|
|
119
175
|
let gitSkippedReason: string | undefined;
|
|
@@ -125,14 +181,15 @@ export async function run(argv: string[]): Promise<number> {
|
|
|
125
181
|
.cwd(targetDir)
|
|
126
182
|
.quiet()
|
|
127
183
|
.nothrow();
|
|
184
|
+
step("initialized git and committed");
|
|
128
185
|
} else {
|
|
129
186
|
gitSkippedReason = "git-missing";
|
|
130
187
|
}
|
|
131
188
|
}
|
|
132
189
|
|
|
133
190
|
const nextSteps = args.git
|
|
134
|
-
? `\n cd ${args.name}\n
|
|
135
|
-
: `\n cd ${args.name}\n
|
|
191
|
+
? `\n cd ${args.name}\n bunx patties dev\n`
|
|
192
|
+
: `\n cd ${args.name}\n bunx patties dev\n\n # when you're ready to track this in git:\n git init && git add -A && git commit -m "initial commit"\n`;
|
|
136
193
|
process.stdout.write(`\n✓ created ${args.name}\n${nextSteps}`);
|
|
137
194
|
if (gitSkippedReason === "git-missing") {
|
|
138
195
|
stderr("create-patties: `git` not found — skipping `git init`.");
|
|
@@ -294,10 +351,18 @@ async function patchPattiesConfig(dir: string, args: Args): Promise<void> {
|
|
|
294
351
|
const path = `${dir}/patties.config.ts`;
|
|
295
352
|
if (!(await Bun.file(path).exists())) return;
|
|
296
353
|
const current = await Bun.file(path).text();
|
|
297
|
-
|
|
354
|
+
let next = current.replace(
|
|
298
355
|
/target:\s*"(bun|edge)"/,
|
|
299
356
|
`target: "${args.target}"`,
|
|
300
357
|
);
|
|
358
|
+
// Point the agent manifest at the file the chosen agent already reads.
|
|
359
|
+
// Default (claude / none) leaves the framework default ("CLAUDE.md").
|
|
360
|
+
if (args.template === "codex" && !/agentsMd:/.test(next)) {
|
|
361
|
+
next = next.replace(
|
|
362
|
+
/target:\s*"(bun|edge)",?\n/,
|
|
363
|
+
(m) => `${m.replace(/,?\n$/, ",\n")}\tagentsMd: { path: "AGENTS.md" },\n`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
301
366
|
if (next !== current) await Bun.write(path, next);
|
|
302
367
|
}
|
|
303
368
|
|
|
@@ -7,25 +7,39 @@ parts of a page that need browser-side interactivity (`useState`,
|
|
|
7
7
|
- **Where they live:** `app/islands/*.tsx`. Files outside this
|
|
8
8
|
directory are server-only and never ship to the browser.
|
|
9
9
|
- **How to mark one:** start the file with `"use client";` and
|
|
10
|
-
default-export a React component. The framework
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
default-export a React component. The framework auto-registers it,
|
|
11
|
+
bundles its client code, and serves the chunk under
|
|
12
|
+
`/_patties/client/*` (in dev via `setupDevClient`; in production via
|
|
13
|
+
`patties build`).
|
|
14
|
+
- **How to use one:** import the component from a route file and
|
|
15
|
+
wrap each use-site in `<Island name="…">…</Island>` from
|
|
16
|
+
`patties/render`. The wrapper emits the `data-island` marker + props
|
|
17
|
+
sibling that the client runtime scans for — without it the markup
|
|
18
|
+
SSRs fine but never hydrates.
|
|
19
|
+
|
|
14
20
|
```tsx
|
|
21
|
+
import { Island } from "patties/render";
|
|
15
22
|
import TodoApp from "../islands/TodoApp.tsx";
|
|
16
23
|
|
|
17
24
|
export default function Index(): JSX.Element {
|
|
18
|
-
return
|
|
25
|
+
return (
|
|
26
|
+
<main>
|
|
27
|
+
<Island name="TodoApp">
|
|
28
|
+
<TodoApp />
|
|
29
|
+
</Island>
|
|
30
|
+
</main>
|
|
31
|
+
);
|
|
19
32
|
}
|
|
20
33
|
```
|
|
21
|
-
- **Today's gap (`bun dev`):** dev mode SSRs the island but does not
|
|
22
|
-
yet ship the client bundle. Click handlers are dead under `bun
|
|
23
|
-
dev`. To exercise interactivity right now, run `bun run build &&
|
|
24
|
-
bun start`. Full dev-mode hydration lands with framework spec 18.
|
|
25
34
|
- **Naming:** the island's auto-registered name is the file path
|
|
26
35
|
relative to `app/islands/` with slashes replaced by dashes and the
|
|
27
36
|
extension stripped (e.g. `app/islands/forms/Signup.tsx` →
|
|
28
|
-
`forms-Signup`).
|
|
37
|
+
`forms-Signup`). Pass the same string as `<Island name="…">`. Default
|
|
38
|
+
exports are the only export consulted.
|
|
39
|
+
- **Dev mode hydrates.** `patties dev` builds islands in
|
|
40
|
+
`--mode development` (no minify), serves them, and the client runtime
|
|
41
|
+
hydrates each `[data-island]` marker. HMR reloads the page on island
|
|
42
|
+
changes.
|
|
29
43
|
- **Don't put non-island helpers in `app/islands/`.** Every `.tsx`
|
|
30
44
|
in there is treated as a hydratable component. Put shared
|
|
31
45
|
presentational components in `app/components/` or co-locate them
|
|
@@ -11,5 +11,6 @@ must still install and run without `@anthropic-ai/sdk` present.
|
|
|
11
11
|
- Do not move `@anthropic-ai/sdk` into `dependencies` unless your app
|
|
12
12
|
genuinely requires it at boot. Even then, prefer keeping it as a
|
|
13
13
|
peer + `bun add @anthropic-ai/sdk` step.
|
|
14
|
-
- The auto-generated `
|
|
15
|
-
|
|
14
|
+
- The auto-generated manifest (default `CLAUDE.md`; configurable via
|
|
15
|
+
`config.agentsMd.path`) references AI types, but does not require a
|
|
16
|
+
live SDK at import time.
|
|
@@ -15,15 +15,14 @@ ad-hoc Bun invocations.
|
|
|
15
15
|
### `patties dev` — start the dev server
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
|
|
18
|
+
bunx patties dev # the canonical invocation (bun dev runs the same thing via the package.json script)
|
|
19
19
|
patties dev --port 4000
|
|
20
20
|
patties dev --host 0.0.0.0
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
Starts the dev server with HMR over WebSocket. Reads `patties.config.ts`
|
|
24
|
-
for `target`, `port`, env, and secrets.
|
|
25
|
-
|
|
26
|
-
right now, build and start instead.
|
|
24
|
+
for `target`, `port`, env, and secrets. Islands are bundled and hydrated
|
|
25
|
+
in dev — the same code path users see in production, minus minification.
|
|
27
26
|
|
|
28
27
|
### `patties build` — produce a production bundle
|
|
29
28
|
|
|
@@ -35,8 +34,9 @@ patties build --compile # bun target only; single-binary
|
|
|
35
34
|
```
|
|
36
35
|
|
|
37
36
|
Outputs to `.patties/` by default. The server entry is at
|
|
38
|
-
`.patties/server/server-entry.js`. Build also regenerates
|
|
39
|
-
|
|
37
|
+
`.patties/server/server-entry.js`. Build also regenerates the agent
|
|
38
|
+
manifest section (default `CLAUDE.md`; configurable via
|
|
39
|
+
`config.agentsMd.path`).
|
|
40
40
|
|
|
41
41
|
### `patties start` — run the production bundle
|
|
42
42
|
|
|
@@ -45,8 +45,7 @@ bun start # equivalent to `patties start`
|
|
|
45
45
|
```
|
|
46
46
|
|
|
47
47
|
Runs the last `patties build` output. If no build exists, prints an
|
|
48
|
-
error directing you to `patties build` first.
|
|
49
|
-
build`) to verify island hydration today, since dev mode only SSRs.
|
|
48
|
+
error directing you to `patties build` first.
|
|
50
49
|
|
|
51
50
|
### `patties deploy` — build then dispatch to a deploy plugin
|
|
52
51
|
|
|
@@ -90,10 +89,10 @@ the dev server reads `config.secrets`.
|
|
|
90
89
|
**First-run sanity check after scaffolding:**
|
|
91
90
|
```sh
|
|
92
91
|
bun install
|
|
93
|
-
|
|
92
|
+
bunx patties dev # opens http://localhost:3000 — SSR + island hydration
|
|
94
93
|
```
|
|
95
94
|
|
|
96
|
-
**Verify
|
|
95
|
+
**Verify the production bundle:**
|
|
97
96
|
```sh
|
|
98
97
|
bun run build
|
|
99
98
|
bun start
|
|
@@ -16,6 +16,13 @@ The CLI surface (`patties dev/build/start/deploy/secret`) lives in the
|
|
|
16
16
|
`patties-cli` skill at `.claude/skills/patties-cli/SKILL.md` — it loads
|
|
17
17
|
automatically when CLI work is in scope.
|
|
18
18
|
|
|
19
|
-
The live inventory of routes, islands, agents, tools, and jobs is
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
The live inventory of routes, islands, agents, tools, and jobs is spliced
|
|
20
|
+
into this file between `<!-- patties:manifest-start -->` and
|
|
21
|
+
`<!-- patties:manifest-end -->` — regenerated on every `patties dev/build`
|
|
22
|
+
while everything else here is preserved. Trust that section for inventory
|
|
23
|
+
questions; trust the rules files for conventions. Change the target file
|
|
24
|
+
with `config.agentsMd.path` in `patties.config.ts` if you'd rather the
|
|
25
|
+
manifest live elsewhere.
|
|
26
|
+
|
|
27
|
+
<!-- patties:manifest-start -->
|
|
28
|
+
<!-- patties:manifest-end -->
|
|
@@ -7,25 +7,39 @@ parts of a page that need browser-side interactivity (`useState`,
|
|
|
7
7
|
- **Where they live:** `app/islands/*.tsx`. Files outside this
|
|
8
8
|
directory are server-only and never ship to the browser.
|
|
9
9
|
- **How to mark one:** start the file with `"use client";` and
|
|
10
|
-
default-export a React component. The framework
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
default-export a React component. The framework auto-registers it,
|
|
11
|
+
bundles its client code, and serves the chunk under
|
|
12
|
+
`/_patties/client/*` (in dev via `setupDevClient`; in production via
|
|
13
|
+
`patties build`).
|
|
14
|
+
- **How to use one:** import the component from a route file and
|
|
15
|
+
wrap each use-site in `<Island name="…">…</Island>` from
|
|
16
|
+
`patties/render`. The wrapper emits the `data-island` marker + props
|
|
17
|
+
sibling that the client runtime scans for — without it the markup
|
|
18
|
+
SSRs fine but never hydrates.
|
|
19
|
+
|
|
14
20
|
```tsx
|
|
21
|
+
import { Island } from "patties/render";
|
|
15
22
|
import TodoApp from "../islands/TodoApp.tsx";
|
|
16
23
|
|
|
17
24
|
export default function Index(): JSX.Element {
|
|
18
|
-
return
|
|
25
|
+
return (
|
|
26
|
+
<main>
|
|
27
|
+
<Island name="TodoApp">
|
|
28
|
+
<TodoApp />
|
|
29
|
+
</Island>
|
|
30
|
+
</main>
|
|
31
|
+
);
|
|
19
32
|
}
|
|
20
33
|
```
|
|
21
|
-
- **Today's gap (`bun dev`):** dev mode SSRs the island but does not
|
|
22
|
-
yet ship the client bundle. Click handlers are dead under `bun
|
|
23
|
-
dev`. To exercise interactivity right now, run `bun run build &&
|
|
24
|
-
bun start`. Full dev-mode hydration lands with framework spec 18.
|
|
25
34
|
- **Naming:** the island's auto-registered name is the file path
|
|
26
35
|
relative to `app/islands/` with slashes replaced by dashes and the
|
|
27
36
|
extension stripped (e.g. `app/islands/forms/Signup.tsx` →
|
|
28
|
-
`forms-Signup`).
|
|
37
|
+
`forms-Signup`). Pass the same string as `<Island name="…">`. Default
|
|
38
|
+
exports are the only export consulted.
|
|
39
|
+
- **Dev mode hydrates.** `patties dev` builds islands in
|
|
40
|
+
`--mode development` (no minify), serves them, and the client runtime
|
|
41
|
+
hydrates each `[data-island]` marker. HMR reloads the page on island
|
|
42
|
+
changes.
|
|
29
43
|
- **Don't put non-island helpers in `app/islands/`.** Every `.tsx`
|
|
30
44
|
in there is treated as a hydratable component. Put shared
|
|
31
45
|
presentational components in `app/components/` or co-locate them
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: patties-cli
|
|
3
|
-
description: Use when running, building, deploying, or managing secrets for a Patties app. Covers `patties dev`, `patties build`, `patties start`, `patties deploy`, and `patties secret`. Trigger phrases include "start the dev server", "build the app", "deploy", "set a secret", "run patties".
|
|
4
|
-
---
|
|
5
|
-
|
|
6
1
|
# patties CLI
|
|
7
2
|
|
|
8
3
|
`patties` is the project's CLI. It's installed via the `patties` dependency
|
|
@@ -15,15 +10,14 @@ ad-hoc Bun invocations.
|
|
|
15
10
|
### `patties dev` — start the dev server
|
|
16
11
|
|
|
17
12
|
```sh
|
|
18
|
-
|
|
13
|
+
bunx patties dev # the canonical invocation (bun dev runs the same thing via the package.json script)
|
|
19
14
|
patties dev --port 4000
|
|
20
15
|
patties dev --host 0.0.0.0
|
|
21
16
|
```
|
|
22
17
|
|
|
23
18
|
Starts the dev server with HMR over WebSocket. Reads `patties.config.ts`
|
|
24
|
-
for `target`, `port`, env, and secrets.
|
|
25
|
-
|
|
26
|
-
right now, build and start instead.
|
|
19
|
+
for `target`, `port`, env, and secrets. Islands are bundled and hydrated
|
|
20
|
+
in dev — the same code path users see in production, minus minification.
|
|
27
21
|
|
|
28
22
|
### `patties build` — produce a production bundle
|
|
29
23
|
|
|
@@ -35,8 +29,10 @@ patties build --compile # bun target only; single-binary
|
|
|
35
29
|
```
|
|
36
30
|
|
|
37
31
|
Outputs to `.patties/` by default. The server entry is at
|
|
38
|
-
`.patties/server/server-entry.js`. Build also regenerates
|
|
39
|
-
|
|
32
|
+
`.patties/server/server-entry.js`. Build also regenerates the agent
|
|
33
|
+
manifest section. For Codex projects, set `config.agentsMd.path =
|
|
34
|
+
"AGENTS.md"` in `patties.config.ts` so the manifest lands in the file
|
|
35
|
+
Codex reads from.
|
|
40
36
|
|
|
41
37
|
### `patties start` — run the production bundle
|
|
42
38
|
|
|
@@ -45,8 +41,7 @@ bun start # equivalent to `patties start`
|
|
|
45
41
|
```
|
|
46
42
|
|
|
47
43
|
Runs the last `patties build` output. If no build exists, prints an
|
|
48
|
-
error directing you to `patties build` first.
|
|
49
|
-
build`) to verify island hydration today, since dev mode only SSRs.
|
|
44
|
+
error directing you to `patties build` first.
|
|
50
45
|
|
|
51
46
|
### `patties deploy` — build then dispatch to a deploy plugin
|
|
52
47
|
|
|
@@ -90,10 +85,10 @@ the dev server reads `config.secrets`.
|
|
|
90
85
|
**First-run sanity check after scaffolding:**
|
|
91
86
|
```sh
|
|
92
87
|
bun install
|
|
93
|
-
|
|
88
|
+
bunx patties dev # opens http://localhost:3000 — SSR + island hydration
|
|
94
89
|
```
|
|
95
90
|
|
|
96
|
-
**Verify
|
|
91
|
+
**Verify the production bundle:**
|
|
97
92
|
```sh
|
|
98
93
|
bun run build
|
|
99
94
|
bun start
|
|
@@ -18,11 +18,9 @@ Built with Patties — a Bun-native full-stack meta-framework.
|
|
|
18
18
|
|
|
19
19
|
## Live inventory
|
|
20
20
|
|
|
21
|
-
The
|
|
22
|
-
contents of `app/`.
|
|
23
|
-
|
|
24
|
-
everything below it is owned by the generator.
|
|
21
|
+
The section between the markers below is regenerated by `patties dev`
|
|
22
|
+
and `patties build` from the actual contents of `app/`. Everything
|
|
23
|
+
outside the markers is preserved across regenerations.
|
|
25
24
|
|
|
26
|
-
<!-- patties:
|
|
27
|
-
<!--
|
|
28
|
-
<!-- /patties:generated -->
|
|
25
|
+
<!-- patties:manifest-start -->
|
|
26
|
+
<!-- patties:manifest-end -->
|
|
@@ -17,27 +17,18 @@ An interactive todo demo:
|
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
bun install # if you used --no-install
|
|
20
|
-
|
|
20
|
+
bunx patties dev # → http://localhost:3000
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
> hydrate and the demo actually work, build and serve:
|
|
26
|
-
>
|
|
27
|
-
> ```sh
|
|
28
|
-
> bun run build
|
|
29
|
-
> bun start
|
|
30
|
-
> ```
|
|
31
|
-
>
|
|
32
|
-
> Full dev-mode hydration is tracked under framework spec 18 and will land
|
|
33
|
-
> in a future Patties release — at that point `bun dev` will be enough.
|
|
23
|
+
Dev mode SSRs the page and hydrates the island, so the todo buttons work
|
|
24
|
+
immediately. HMR reloads the browser when you edit a route or island.
|
|
34
25
|
|
|
35
26
|
## Try editing
|
|
36
27
|
|
|
37
28
|
1. Open `app/routes/index.tsx`, change the heading text, save. The browser
|
|
38
29
|
reloads (HMR).
|
|
39
30
|
2. Open `app/islands/TodoApp.tsx`, change the initial todo list or input
|
|
40
|
-
placeholder, save,
|
|
31
|
+
placeholder, save, and try it — the bundle rebuilds on the next request.
|
|
41
32
|
|
|
42
33
|
## Remove the demo when you're ready
|
|
43
34
|
|
|
@@ -73,18 +64,15 @@ A minimal hello-world Patties app:
|
|
|
73
64
|
|
|
74
65
|
```sh
|
|
75
66
|
bun install # if you used --no-install
|
|
76
|
-
|
|
67
|
+
bunx patties dev # → http://localhost:3000
|
|
77
68
|
```
|
|
78
69
|
|
|
79
70
|
## Add your first interactive feature
|
|
80
71
|
|
|
81
72
|
Create `app/islands/` and drop in a component that uses `useState` or
|
|
82
|
-
`useEffect`. Import it from a route file under `app/routes
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
> Note: full dev-mode island hydration is tracked under framework spec 18
|
|
86
|
-
> and lands in a future Patties release. Until then, build + start to see
|
|
87
|
-
> islands react: `bun run build && bun start`.
|
|
73
|
+
`useEffect`. Import it from a route file under `app/routes/` and wrap the
|
|
74
|
+
use site in `<Island name="MyIsland">…</Island>` (from `patties/render`)
|
|
75
|
+
so the SSR markers are emitted and the client runtime hydrates it.
|
|
88
76
|
<!-- /if -->
|
|
89
77
|
|
|
90
78
|
## Project layout
|
|
@@ -156,9 +144,10 @@ Launch a session with `claude` in this directory.
|
|
|
156
144
|
## Codex is set up
|
|
157
145
|
|
|
158
146
|
`AGENTS.md` at the project root describes the framework conventions for
|
|
159
|
-
Codex CLI and other AGENTS.md-aware tools. Patties regenerates
|
|
160
|
-
|
|
161
|
-
`<!-- patties:
|
|
147
|
+
Codex CLI and other AGENTS.md-aware tools. Patties regenerates the
|
|
148
|
+
inventory section between `<!-- patties:manifest-start -->` and
|
|
149
|
+
`<!-- patties:manifest-end -->` on every `patties dev/build` — everything
|
|
150
|
+
outside those markers is yours to edit.
|
|
162
151
|
|
|
163
152
|
Launch a session with `codex` in this directory.
|
|
164
153
|
<!-- /if -->
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
import { Island } from "patties/render";
|
|
1
2
|
import TodoApp from "../islands/TodoApp.tsx";
|
|
2
3
|
|
|
4
|
+
export const meta = {
|
|
5
|
+
title: "Welcome to Patties",
|
|
6
|
+
description: "A Bun-native full-stack meta-framework.",
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
export default function Index(): JSX.Element {
|
|
4
10
|
return (
|
|
5
11
|
<main>
|
|
@@ -8,7 +14,9 @@ export default function Index(): JSX.Element {
|
|
|
8
14
|
This page is server-rendered. The list below is a client island —{" "}
|
|
9
15
|
<code>app/islands/TodoApp.tsx</code> — hydrated in the browser.
|
|
10
16
|
</p>
|
|
11
|
-
<TodoApp
|
|
17
|
+
<Island name="TodoApp">
|
|
18
|
+
<TodoApp />
|
|
19
|
+
</Island>
|
|
12
20
|
<hr />
|
|
13
21
|
<p>
|
|
14
22
|
<small>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type DevServer, setupDevClient } from "patties/dev";
|
|
2
2
|
import { createRenderer } from "patties/render";
|
|
3
3
|
import { createRouter } from "patties/router";
|
|
4
4
|
import { startServer } from "patties/server";
|
|
@@ -11,7 +11,10 @@ interface StartOpts {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export default async function start(opts: StartOpts): Promise<void> {
|
|
14
|
-
|
|
14
|
+
// Bundle islands and serve their chunks under `/_patties/client/*`.
|
|
15
|
+
// `manifest` tells the renderer which <script> to inject so islands hydrate.
|
|
16
|
+
const devClient = await setupDevClient({ appDir: opts.appDir });
|
|
17
|
+
const renderer = createRenderer({ manifest: devClient.manifest, dev: true });
|
|
15
18
|
const router = await createRouter({ appDir: opts.appDir, renderer });
|
|
16
19
|
|
|
17
20
|
startServer({
|
|
@@ -19,7 +22,7 @@ export default async function start(opts: StartOpts): Promise<void> {
|
|
|
19
22
|
hostname: opts.host,
|
|
20
23
|
dev: true,
|
|
21
24
|
devServer: opts.devServer,
|
|
22
|
-
routes: router.routes,
|
|
25
|
+
routes: { ...devClient.routes, ...router.routes },
|
|
23
26
|
fallback: router.fallback,
|
|
24
27
|
});
|
|
25
28
|
}
|