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 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 `bun dev` in one shot.
12
+ to `bunx patties dev` in one shot.
13
13
 
14
14
  ## Options
15
15
 
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env bun
2
+ export {};
3
+
2
4
  if (typeof Bun === "undefined") {
3
5
  console.error(
4
6
  "create-patties requires Bun. Install Bun first: https://bun.sh",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-patties",
3
- "version": "0.0.8",
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
- await Bun.$`bun install`.cwd(targetDir).quiet().nothrow();
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 bun dev\n`
135
- : `\n cd ${args.name}\n bun dev\n\n # when you're ready to track this in git:\n git init && git add -A && git commit -m "initial commit"\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
- const next = current.replace(
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 will register it,
11
- bundle the client code, and inject the hydration script when the
12
- component is rendered from a route.
13
- - **How to use one:** import the component from a route file:
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 <main><TodoApp /></main>;
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`). Default exports are the only export consulted.
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 `AGENTS.md` references AI types, but does not
15
- require a live SDK at import time.
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
- bun dev # equivalent to `patties dev`
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. SSR-only today full island
25
- hydration in dev lands with framework spec 18. To exercise interactivity
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 `AGENTS.md` at
39
- the project root.
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. Use this (after `bun run
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
- bun dev # opens http://localhost:3000
92
+ bunx patties dev # opens http://localhost:3000 — SSR + island hydration
94
93
  ```
95
94
 
96
- **Verify an island actually hydrates today (workaround until spec 18):**
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 in
20
- `AGENTS.md` (regenerated by `patties build`). Trust `AGENTS.md` for
21
- inventory questions; trust the rules files for conventions.
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 will register it,
11
- bundle the client code, and inject the hydration script when the
12
- component is rendered from a route.
13
- - **How to use one:** import the component from a route file:
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 <main><TodoApp /></main>;
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`). Default exports are the only export consulted.
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
- bun dev # equivalent to `patties dev`
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. SSR-only today full island
25
- hydration in dev lands with framework spec 18. To exercise interactivity
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 `AGENTS.md` at
39
- the project root.
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. Use this (after `bun run
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
- bun dev # opens http://localhost:3000
88
+ bunx patties dev # opens http://localhost:3000 — SSR + island hydration
94
89
  ```
95
90
 
96
- **Verify an island actually hydrates today (workaround until spec 18):**
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 block below is regenerated by `patties build` from the actual
22
- contents of `app/`. Hand-edited content above the
23
- `<!-- patties:generated -->` marker is preserved across regenerations;
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:generated -->
27
- <!-- The block below is rewritten by `patties build`. Do not edit by hand. -->
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
- bun dev # → http://localhost:3000
20
+ bunx patties dev # → http://localhost:3000
21
21
  ```
22
22
 
23
- > **Heads up dev mode currently only does SSR.** The todo list will render
24
- > but the buttons won't react to clicks under `bun dev`. To see the island
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, rebuild with `bun run build && bun start`, and try it.
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
- bun dev # → http://localhost:3000
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/`. Islands hydrate
83
- on the client; everything else runs server-only.
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 this file on
160
- `patties build` while preserving sections you mark with
161
- `<!-- patties:user --> ... <!-- /patties:user -->`.
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 { DevServer } from "patties/dev";
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
- const renderer = createRenderer({ dev: true });
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
  }