@weft-js/create 0.1.1 → 0.1.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.
Files changed (127) hide show
  1. package/README.md +393 -19
  2. package/bin/create-weft.mjs +62 -14
  3. package/package.json +26 -16
  4. package/scripts/tarballs.mjs +56 -6
  5. package/src/data.mjs +90 -18
  6. package/src/index.mjs +210 -51
  7. package/src/stack/pages.mjs +151 -0
  8. package/src/stack/schema.mjs +195 -0
  9. package/src/stack.mjs +102 -313
  10. package/src/templates.mjs +174 -0
  11. package/src/wizard.mjs +65 -16
  12. package/templates/admin/files/drizzle/.schema-hash +1 -0
  13. package/templates/admin/files/drizzle/0000_admin.sql +136 -0
  14. package/templates/admin/files/drizzle/meta/0000_snapshot.json +771 -0
  15. package/templates/admin/files/drizzle/meta/_journal.json +13 -0
  16. package/templates/admin/files/public/favicon.svg +1 -0
  17. package/templates/admin/files/src/auth.server.ts +24 -0
  18. package/templates/admin/files/src/components/AuthPage.tsx +33 -0
  19. package/templates/admin/files/src/components/Failed.tsx +19 -0
  20. package/templates/admin/files/src/components/Logo.tsx +16 -0
  21. package/templates/admin/files/src/components/OrderDetail.tsx +93 -0
  22. package/templates/admin/files/src/components/OrdersFilters.tsx +44 -0
  23. package/templates/admin/files/src/components/OrdersTable.tsx +144 -0
  24. package/templates/admin/files/src/components/Overview.tsx +113 -0
  25. package/templates/admin/files/src/components/StatusBadge.tsx +12 -0
  26. package/templates/admin/files/src/db/schema.ts +68 -0
  27. package/templates/admin/files/src/lib/orders.ts +60 -0
  28. package/templates/admin/files/src/routes/404.tsx +20 -0
  29. package/templates/admin/files/src/routes/_error.tsx +20 -0
  30. package/templates/admin/files/src/routes/_layout.tsx +6 -0
  31. package/templates/admin/files/src/routes/admin/_layout.tsx +89 -0
  32. package/templates/admin/files/src/routes/admin/index.tsx +45 -0
  33. package/templates/admin/files/src/routes/admin/nested/_layout.tsx +35 -0
  34. package/templates/admin/files/src/routes/admin/nested/index.tsx +77 -0
  35. package/templates/admin/files/src/routes/admin/orders/[id].tsx +24 -0
  36. package/templates/admin/files/src/routes/admin/orders/index.tsx +28 -0
  37. package/templates/admin/files/src/routes/admin/per-row.tsx +81 -0
  38. package/templates/admin/files/src/routes/admin/slow.tsx +74 -0
  39. package/templates/admin/files/src/routes/index.tsx +13 -0
  40. package/templates/admin/files/src/routes/login.tsx +53 -0
  41. package/templates/admin/files/src/routes/signup.tsx +60 -0
  42. package/templates/admin/files/src/server/orders.ts +193 -0
  43. package/templates/admin/files/src/server/reports.ts +92 -0
  44. package/templates/admin/files/src/server/session.ts +42 -0
  45. package/templates/admin/files/src/theme.css +43 -0
  46. package/templates/admin/files/weft.config.ts +31 -0
  47. package/templates/admin/template.json +22 -0
  48. package/templates/catalog/files/drizzle/.schema-hash +1 -0
  49. package/templates/catalog/files/drizzle/0000_catalog.sql +47 -0
  50. package/templates/catalog/files/drizzle/meta/0000_snapshot.json +263 -0
  51. package/templates/catalog/files/drizzle/meta/_journal.json +13 -0
  52. package/templates/catalog/files/public/favicon.svg +1 -0
  53. package/templates/catalog/files/seed.sql +13 -0
  54. package/templates/catalog/files/src/components/AddToCart.tsx +22 -0
  55. package/templates/catalog/files/src/components/Aisles.tsx +31 -0
  56. package/templates/catalog/files/src/components/CartLines.tsx +96 -0
  57. package/templates/catalog/files/src/components/CartLink.tsx +29 -0
  58. package/templates/catalog/files/src/components/Failed.tsx +19 -0
  59. package/templates/catalog/files/src/components/Featured.tsx +19 -0
  60. package/templates/catalog/files/src/components/Filters.tsx +40 -0
  61. package/templates/catalog/files/src/components/Logo.tsx +16 -0
  62. package/templates/catalog/files/src/components/ProductArt.tsx +42 -0
  63. package/templates/catalog/files/src/components/ProductCard.tsx +42 -0
  64. package/templates/catalog/files/src/components/ProductList.tsx +65 -0
  65. package/templates/catalog/files/src/components/Related.tsx +19 -0
  66. package/templates/catalog/files/src/components/SiteFooter.tsx +9 -0
  67. package/templates/catalog/files/src/components/SiteHeader.tsx +16 -0
  68. package/templates/catalog/files/src/db/schema.ts +53 -0
  69. package/templates/catalog/files/src/lib/catalog.ts +35 -0
  70. package/templates/catalog/files/src/routes/404.tsx +18 -0
  71. package/templates/catalog/files/src/routes/_error.tsx +18 -0
  72. package/templates/catalog/files/src/routes/_layout.tsx +14 -0
  73. package/templates/catalog/files/src/routes/c/[slug].tsx +53 -0
  74. package/templates/catalog/files/src/routes/cart.tsx +29 -0
  75. package/templates/catalog/files/src/routes/index.tsx +84 -0
  76. package/templates/catalog/files/src/routes/p/[slug].tsx +67 -0
  77. package/templates/catalog/files/src/server/cart.ts +125 -0
  78. package/templates/catalog/files/src/server/catalog.ts +107 -0
  79. package/templates/catalog/files/src/theme.css +159 -0
  80. package/templates/catalog/files/weft.config.ts +30 -0
  81. package/templates/catalog/template.json +20 -0
  82. package/templates/landing/files/.weft/plugins.lock.json +42 -0
  83. package/templates/landing/files/assets/fonts/LICENSE-figtree.txt +93 -0
  84. package/templates/landing/files/assets/fonts/figtree.woff2 +0 -0
  85. package/templates/landing/files/assets/hero.webp +0 -0
  86. package/templates/landing/files/drizzle/.schema-hash +1 -0
  87. package/templates/landing/files/drizzle/0000_landing.sql +6 -0
  88. package/templates/landing/files/drizzle/meta/0000_snapshot.json +60 -0
  89. package/templates/landing/files/drizzle/meta/_journal.json +13 -0
  90. package/templates/landing/files/public/favicon.svg +1 -0
  91. package/templates/landing/files/src/components/SiteFooter.tsx +24 -0
  92. package/templates/landing/files/src/components/SiteHeader.tsx +23 -0
  93. package/templates/landing/files/src/components/WaitlistForm.tsx +36 -0
  94. package/templates/landing/files/src/db/schema.ts +7 -0
  95. package/templates/landing/files/src/routes/404.tsx +24 -0
  96. package/templates/landing/files/src/routes/_error.tsx +24 -0
  97. package/templates/landing/files/src/routes/_layout.tsx +14 -0
  98. package/templates/landing/files/src/routes/index.tsx +130 -0
  99. package/templates/landing/files/src/routes/joined.tsx +30 -0
  100. package/templates/landing/files/src/routes/pricing.tsx +131 -0
  101. package/templates/landing/files/src/routes/privacy.tsx +56 -0
  102. package/templates/landing/files/src/server/waitlist.ts +13 -0
  103. package/templates/landing/files/src/theme.css +108 -0
  104. package/templates/landing/files/weft.config.ts +44 -0
  105. package/templates/landing/template.json +17 -0
  106. package/templates/todo/files/drizzle/.schema-hash +1 -0
  107. package/templates/todo/files/drizzle/0000_todo.sql +82 -0
  108. package/templates/todo/files/drizzle/meta/0000_snapshot.json +462 -0
  109. package/templates/todo/files/drizzle/meta/_journal.json +13 -0
  110. package/templates/todo/files/public/favicon.svg +1 -0
  111. package/templates/todo/files/src/components/AuthPage.tsx +33 -0
  112. package/templates/todo/files/src/components/Logo.tsx +14 -0
  113. package/templates/todo/files/src/components/NewTodo.tsx +27 -0
  114. package/templates/todo/files/src/components/TodoList.tsx +101 -0
  115. package/templates/todo/files/src/db/schema.ts +18 -0
  116. package/templates/todo/files/src/lib/views.ts +13 -0
  117. package/templates/todo/files/src/routes/404.tsx +20 -0
  118. package/templates/todo/files/src/routes/_error.tsx +20 -0
  119. package/templates/todo/files/src/routes/_layout.tsx +6 -0
  120. package/templates/todo/files/src/routes/index.tsx +13 -0
  121. package/templates/todo/files/src/routes/login.tsx +53 -0
  122. package/templates/todo/files/src/routes/signup.tsx +57 -0
  123. package/templates/todo/files/src/routes/todos.tsx +87 -0
  124. package/templates/todo/files/src/server/todos.ts +98 -0
  125. package/templates/todo/files/src/theme.css +71 -0
  126. package/templates/todo/files/weft.config.ts +30 -0
  127. package/templates/todo/template.json +18 -0
package/README.md CHANGED
@@ -1,28 +1,402 @@
1
- # @weft-js/create
1
+ <p align="center">
2
+ <a href="https://github.com/Ravonus/weft">
3
+ <img src="https://raw.githubusercontent.com/Ravonus/weft/main/site/public/logo.svg" width="88" height="88" alt="Weft">
4
+ </a>
5
+ </p>
2
6
 
3
- > **Early release (0.1.1).** APIs will change between minor versions.
7
+ <h1 align="center">@weft-js/create</h1>
4
8
 
5
- Starts a new weft app.
9
+ <p align="center">
10
+ <strong>Start a Weft app: pages, a server, a database, sign-in and Warp's components, wired up.</strong><br>
11
+ The new app passes <code>weft check</code> as made, and comes with an <code>AGENTS.md</code> for the Weft it installed.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@weft-js/create"><img src="https://img.shields.io/npm/v/@weft-js/create?color=145c3c&label=npm" alt="npm version"></a>
16
+ <a href="https://github.com/Ravonus/weft/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-145c3c" alt="MIT license"></a>
17
+ <a href="https://github.com/Ravonus/weft/actions"><img src="https://img.shields.io/github/actions/workflow/status/Ravonus/weft/quality.yml?branch=main&label=checks" alt="checks"></a>
18
+ <img src="https://img.shields.io/badge/node-%3E%3D26-145c3c" alt="Node 26 or later">
19
+ </p>
20
+
21
+ <p align="center">
22
+ <a href="#quick-start">Quick start</a> ·
23
+ <a href="https://github.com/Ravonus/weft/blob/main/site/content/learn/installation.md">Docs</a> ·
24
+ <a href="https://github.com/Ravonus/weft/tree/main/packages/create/templates">Templates</a> ·
25
+ <a href="https://github.com/Ravonus/weft/blob/main/CHANGELOG.md">Changelog</a> ·
26
+ <a href="https://github.com/Ravonus/weft">GitHub</a>
27
+ </p>
28
+
29
+ > [!IMPORTANT]
30
+ > **Early release (0.1.x).** The scaffolds install and check clean, and flags and generated files can still change
31
+ > between minor versions.
32
+
33
+ `@weft-js/create` is what runs when you type `npm create @weft-js`. It writes a new
34
+ [Weft](https://github.com/Ravonus/weft) app into an empty folder: a home page, an about page, a todo list on server
35
+ functions, sign-in, and the config, types and editor setup around them. Then it installs the dependencies, starts a git
36
+ repository and writes an `AGENTS.md` from the Weft it just installed.
37
+
38
+ In a terminal it asks what the flags don't answer, with the default already picked. Without one (an agent, CI), it never
39
+ prompts.
40
+
41
+ ## Highlights
42
+
43
+ - **One question per choice, or none.** UI kit, database, data layer, sign-in, runtime, config format and package
44
+ manager. A flag answers its question, and `--yes` takes every default.
45
+ - **A full stack by default.** `--yes` gives Warp, SQLite through Drizzle, and Better Auth, with a guarded todo list
46
+ that works before you write a line.
47
+ - **Four UI choices.** Warp's components, Warp's behavior and tokens with your own markup, Tailwind, or plain CSS.
48
+ - **Three databases, three data layers.** SQLite, Postgres or MySQL, through Drizzle, Prisma or plain SQL migrations.
49
+ - **Four starting apps.** `--template todo`, `admin`, `catalog` or `landing`: real apps with their own look and
50
+ their own migrations.
51
+ - **Pinned versions.** Every dependency is pinned to the version the test matrix ran, and Weft's own packages to the
52
+ version of this scaffold.
53
+ - **Agent-ready.** `AGENTS.md`, `weft check --json` and `weft docs` are there from the first run.
54
+
55
+ ## Contents
56
+
57
+ - [Quick start](#quick-start)
58
+ - [Flags](#flags)
59
+ - [The questions](#the-questions)
60
+ - [UI](#ui)
61
+ - [Database and data layer](#database-and-data-layer)
62
+ - [Sign-in](#sign-in)
63
+ - [Templates](#templates)
64
+ - [What gets generated](#what-gets-generated)
65
+ - [Package managers and runtimes](#package-managers-and-runtimes)
66
+ - [Recipes](#recipes)
67
+ - [Using it from code](#using-it-from-code)
68
+ - [Troubleshooting](#troubleshooting)
69
+ - [Related packages](#related-packages)
70
+
71
+ ## Quick start
6
72
 
7
73
  ```bash
8
74
  npm create @weft-js my-app
9
75
  ```
10
76
 
11
- In a terminal it asks what the flags don't answer. `--yes` takes the defaults.
77
+ ```bash
78
+ pnpm create @weft-js my-app
79
+ ```
80
+
81
+ ```bash
82
+ bun create @weft-js my-app
83
+ ```
84
+
85
+ ```bash
86
+ deno run -A npm:@weft-js/create my-app
87
+ ```
88
+
89
+ Then:
90
+
91
+ ```bash
92
+ cd my-app
93
+ npx weft db generate # with a database: the first migration
94
+ npx weft dev
95
+ ```
96
+
97
+ The last thing it prints is the exact commands for your package manager. Open the URL `weft dev` prints, sign up, and
98
+ the todo list at `/todos` is yours.
99
+
100
+ You need Node 26 or later, or Bun or Deno in its place.
101
+
102
+ ## Flags
103
+
104
+ | Flag | Values | Default | What it does |
105
+ |---|---|---|---|
106
+ | `[dir]` | a folder that's missing or empty | asked; `my-weft-app` with `--yes` | Where the app goes |
107
+ | `--template` | `todo`, `admin`, `catalog`, `landing` | none | Starts from a fuller app ([below](#templates)) |
108
+ | `--ui` | `warp`, `headless`, `tailwind`, `none` | `warp` | The UI kit ([below](#ui)) |
109
+ | `--db` | `sqlite`, `postgres`, `mysql`, `none` | `sqlite` | The database |
110
+ | `--layer` | `drizzle`, `prisma`, `native` | `drizzle` | The data layer. `native` is SQL migrations with no ORM. Asked only with a database |
111
+ | `--auth`, `--no-auth` | | on, with a database | Sign-in |
112
+ | `--auth-provider` | `better-auth`, `custom` | `better-auth` | `custom` writes a stub for your own provider |
113
+ | `--runtime` | `node`, `bun`, `deno` | the one running it | What `weft build` targets |
114
+ | `--config` | `ts`, `json`, `yaml` | `ts` | The format of the Weft config |
115
+ | `--pm` | `npm`, `pnpm`, `bun`, `deno`, `yarn` | the one you ran it with | The package manager |
116
+ | `--git`, `--no-git` | | on | Start a git repository |
117
+ | `--install`, `--no-install` | | on; off with no terminal and no `--yes` | Install the dependencies, then write `AGENTS.md` |
118
+ | `--name` | a package name | the folder's name | The `name` in `package.json`, and the app's title |
119
+ | `--tarballs` | a folder with `tarballs.json` | none | Pins Weft's packages to local tarballs, for testing a checkout |
120
+ | `--yes`, `-y` | | | Take the defaults for anything not given |
121
+ | `--help`, `-h` | | | Print the usage |
122
+
123
+ With npm, flags go after `--`:
124
+
125
+ ```bash
126
+ npm create @weft-js my-app -- --ui tailwind --db none
127
+ ```
128
+
129
+ > [!NOTE]
130
+ > With no terminal and no `[dir]`, it prints the usage and exits 1: it can't ask where to put the app. With no terminal
131
+ > and no `--yes`, it writes the app and runs nothing else, so git and the install stay off unless you ask for them.
132
+
133
+ ## The questions
134
+
135
+ In a terminal, each question is a list: the arrow keys move, Enter picks. Enter all the way through is the same app as
136
+ `--yes`.
137
+
138
+ | # | Question | Options | Default | Asked when |
139
+ |---|---|---|---|---|
140
+ | 1 | UI | Warp, Headless, Tailwind, None | Warp | always |
141
+ | 2 | Database | SQLite, Postgres, MySQL, none | SQLite | always |
142
+ | 3 | Data layer | Drizzle, Prisma, Native (no ORM) | Drizzle | there's a database |
143
+ | 4 | Sign-in | Better Auth, Bring your own, None | Better Auth | there's a database |
144
+ | 5 | Runtime | Node, Bun, Deno | the one running it | always |
145
+ | 6 | Config file | TypeScript, JSON, YAML | TypeScript | always |
146
+ | 7 | Package manager | npm, pnpm, Bun, Deno, Yarn | the one you ran it with | always |
147
+ | 8 | Start a git repository | Yes, No | Yes | always |
148
+ | 9 | Install dependencies | Yes, No | Yes | always |
149
+
150
+ A flag skips its question, and a template skips the ones it settles.
151
+
152
+ ## UI
153
+
154
+ | `--ui` | What you get | Dependencies |
155
+ |---|---|---|
156
+ | `warp` | Warp's components (`Card`, `Button`, `Badge`…) and the `warp()` plugin in the config. The look is `warp({ seed, dials, radius, fonts, tokens })`, and `weft check` fails with D010 if a color pair can't pass contrast | `@weft-js/warp` |
157
+ | `headless` | Warp's behavior and tokens, your markup. `src/app.css` styles plain elements over Warp's `--warp-*` tokens, and the layout imports `@weft-js/warp-core/styles/warp.css` | [`@weft-js/warp-core`](https://www.npmjs.com/package/@weft-js/warp-core) |
158
+ | `tailwind` | Tailwind utilities. Writes `vite.config.ts` and `src/app.css`, the same files `weft add tailwind` writes | Tailwind, as dev dependencies |
159
+ | `none` | A small `src/app.css` on system colors, and nothing else | none |
160
+
161
+ ## Database and data layer
162
+
163
+ `--db` picks the engine, and `--layer` how the app talks to it.
164
+
165
+ | `--layer` | Schema | Migrations | `ctx.db` | Packages |
166
+ |---|---|---|---|---|
167
+ | `drizzle` | `src/db/schema.ts` | `drizzle/`, from `weft db generate` | Drizzle, from `@weft-js/start/db/<engine>` | `drizzle-orm`, `drizzle-zod`, `drizzle-kit` |
168
+ | `prisma` | `prisma/schema.prisma` | `weft db generate` | a Prisma client with the engine's adapter | `@prisma/client`, `prisma`, `@weft-js/db` |
169
+ | `native` | SQL | `migrations/0000_todos.sql`; `weft db generate <name>` starts a new one | `@weft-js/db/plugin-native/<engine>` | `@weft-js/db` |
170
+
171
+ | `--db` | `DATABASE_URL` in `.env` | Driver |
172
+ |---|---|---|
173
+ | `sqlite` | `file:.data/app.db` | none |
174
+ | `postgres` | `postgres://localhost:5432/<name>` | `postgres` |
175
+ | `mysql` | `mysql://root@localhost:3306/<name>` | `mysql2` |
176
+
177
+ `src/env.server.ts` checks the environment with `@t3-oss/env-core` and `zod`. Migrations apply on the next request, so
178
+ after `weft db generate` there's nothing else to run. With Prisma, `postinstall` runs `prisma generate`, and its
179
+ client goes to `src/generated/`, which `.gitignore` leaves out.
180
+
181
+ ## Sign-in
182
+
183
+ Sign-in needs a database: `--auth` with `--db none` is refused.
184
+
185
+ | Choice | What it writes |
186
+ |---|---|
187
+ | Better Auth (`--auth`, the default) | `src/auth.server.ts` on Better Auth, with its tables in your schema; `src/server/session.ts`; a sign-in and sign-up page at `/login`; a guard on `/todos`; and a `BETTER_AUTH_SECRET` that's new for every scaffold |
188
+ | Bring your own (`--auth-provider custom`) | `src/auth.server.ts` with `defineAuth` from `@weft-js/start/auth`: a `session` and a `handler` to fill in. The app compiles, with no one signed in, and the todo list is public |
189
+ | None (`--no-auth`) | No sign-in. The todo list is public |
190
+
191
+ Login with Better Auth is an opaque session in the database, in an HTTP-only cookie, never a JWT. `ctx.user` is the
192
+ signed-in user in every server function, and `auth: 'user'` on a `query` or `mutation` requires one.
193
+
194
+ ## Templates
195
+
196
+ A template is a real app laid over the scaffold, in place of the default todo app. Each runs on Postgres, uses Warp and
197
+ Drizzle, has a TypeScript config, and ships its own look, migrations and pages.
198
+
199
+ | Template | What it is | Pages | Sign-in | Look |
200
+ |---|---|---|---|---|
201
+ | `todo` | Sign-in and sign-up, and a todo list | `/`, `/login`, `/signup`, `/todos` | on | quiet, cozy, sharp corners, a serif display face |
202
+ | `admin` | An admin area with an orders table, order pages and nested layouts, behind sign-in | `/`, `/login`, `/signup`, `/admin`, `/admin/orders`, `/admin/orders/[id]`, `/admin/nested`, `/admin/per-row`, `/admin/slow` | on | balanced, compact, soft corners, system fonts |
203
+ | `catalog` | A shop: categories, product pages and a cart, with seed data in `seed.sql` | `/`, `/c/[slug]`, `/p/[slug]`, `/cart` | off | balanced, comfortable, round corners |
204
+ | `landing` | A landing page with pricing, a privacy page and a sign-up list | `/`, `/pricing`, `/privacy`, `/joined` | off | vivid, round corners, the Figtree font |
205
+
206
+ A template settles what it needs, and refuses a flag that conflicts rather than ignoring it:
207
+
208
+ | Setting | Every template |
209
+ |---|---|
210
+ | `--db` | `postgres` |
211
+ | `--ui` | `warp` |
212
+ | `--layer` | `drizzle` |
213
+ | `--config` | `ts` (fonts and media are plugin options in code) |
214
+ | sign-in | as in the table above |
215
+
216
+ `todo` and `admin` also add [`@weft-js/warp-icons`](https://www.npmjs.com/package/@weft-js/warp-icons). `landing` adds `@weft-js/warp-icons`, [`@weft-js/plugin-media`](https://www.npmjs.com/package/@weft-js/plugin-media) and
217
+ [`@weft-js/text`](https://www.npmjs.com/package/@weft-js/text), with its hero image and font in `assets/`.
218
+
219
+ A template's `drizzle/` ships with it, written so it runs on a fresh database and on one that already has these tables.
220
+ Set `DATABASE_URL` in `.env` to an empty Postgres database and start the app: `drizzle/` applies on the first request,
221
+ and there's no `weft db generate` to run. The theme is `src/theme.css` plus `warp({ seed, dials, radius })` in the config.
222
+
223
+ ```bash
224
+ npm create @weft-js shop -- --template catalog
225
+ ```
226
+
227
+ ## What gets generated
228
+
229
+ The default app (`--yes`):
230
+
231
+ | File | What it is |
232
+ |---|---|
233
+ | `package.json` | `dev`, `build`, `check`, `typecheck`, `start` and `db:generate` scripts, each a `weft` command. `@weft-js/cli` and TypeScript 7 as dev dependencies |
234
+ | `weft.config.ts` | `site` (name, title, description), `plugins`, and `runtime` when it isn't Node |
235
+ | `tsconfig.json` | Strict, `jsxImportSource: '@weft-js/core'`, the `#/*` alias for `src/*`, and the `@weft-js/cli/ts-plugin` editor plugin |
236
+ | `.vscode/settings.json` | Points VS Code's TypeScript at that plugin |
237
+ | `.gitignore` | `node_modules/`, `.weft/` (except the plugin lock), `.output/`, `dist/`, and with a database `.env` and `.data/` |
238
+ | `.weft/plugins.lock.json` | The plugins you chose, with the permissions they declare |
239
+ | `AGENTS.md` | A short guide; after the install, `weft agents-md` writes the full one over it |
240
+ | `.env`, `.env.example` | `DATABASE_URL`, and with Better Auth `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` |
241
+ | `src/env.server.ts` | The environment, checked |
242
+ | `src/context.server.ts` | What `ctx` holds in server functions |
243
+ | `src/routes/_layout.tsx` | The shell: links, and sign in or sign out |
244
+ | `src/routes/index.tsx` | The home page: a counter in a card |
245
+ | `src/routes/about.tsx` | The about page |
246
+ | `src/routes/todos/index.tsx`, `_layout.tsx` | The todo list, and the guard that sends a signed-out visitor to sign in |
247
+ | `src/routes/login.tsx` | Sign in and sign up |
248
+ | `src/server/todos.ts` | The todo list's `query` and `mutation` functions |
249
+ | `src/server/session.ts` | `me`, `signUp`, `signIn` and `signOut` |
250
+ | `src/auth.server.ts` | Better Auth |
251
+ | `drizzle.config.ts`, `src/db/schema.ts`, `src/db/index.server.ts` | The database |
252
+
253
+ Other choices change that list:
254
+
255
+ | Choice | Adds | Drops |
256
+ |---|---|---|
257
+ | `--ui headless`, `none` | `src/app.css` | |
258
+ | `--ui tailwind` | `src/app.css`, `vite.config.ts` | |
259
+ | `--db none` | | the database, server and sign-in files, and the layout's Todos link |
260
+ | `--layer prisma` | `prisma/schema.prisma`, `prisma.config.ts`, a `postinstall` script | `drizzle.config.ts`, `src/db/schema.ts` |
261
+ | `--layer native` | `migrations/0000_todos.sql` | `drizzle.config.ts`, `src/db/schema.ts` |
262
+ | `--no-auth` | | the sign-in files, the guard and the auth secret |
263
+ | `--config json`, `yaml` | `weft.config.json` or `.yaml`, with a `$schema` for editor completion | `weft.config.ts` |
264
+ | `--pm pnpm` | `pnpm-workspace.yaml`, which settles the build scripts pnpm would stop at | |
265
+ | `--template <t>` | the template's files, and `src/db/auth.ts` when it has sign-in | the about page and the default todo app |
266
+
267
+ Everything else in `.weft/` is generated by `weft` itself on the first `weft dev` or `weft check`, so a scaffold never
268
+ goes stale against the CLI.
269
+
270
+ `weft check` passes on the new app as made. Its one finding is the warning H001, which asks for `site.url` in the
271
+ config before you deploy.
272
+
273
+ ### Pinned versions
274
+
275
+ | Package | Version |
276
+ |---|---|
277
+ | `@weft-js/*` | the version of this package |
278
+ | `typescript` | 7.0.2 |
279
+ | `drizzle-orm`, `drizzle-kit`, `drizzle-zod` | 0.45.4, 0.31.11, 0.8.3 |
280
+ | `prisma`, `@prisma/client`, `@prisma/adapter-*` | 7.10.1 |
281
+ | `better-auth` | 1.7.14 |
282
+ | `postgres`, `mysql2` | 4.0.0, 3.24.6 |
283
+ | `kysely` (native layer with sign-in) | 0.29.7 |
284
+ | `@t3-oss/env-core`, `zod` | 0.13.11, 4.6.7 |
285
+ | `@types/node` | 26.6.5 |
286
+
287
+ ## Package managers and runtimes
12
288
 
13
- | Flag | Values | Default |
289
+ `--pm` picks the package manager for the install and for the commands it prints at the end. Without it, a run under Node
290
+ uses the one that started it (from `npm_config_user_agent`), and a run under Bun or Deno uses that one.
291
+
292
+ | `--pm` | How to start it | The `weft` command it prints |
14
293
  |---|---|---|
15
- | `--ui` | `warp`, `headless` (warp-core only), `tailwind`, `none` | `warp` |
16
- | `--db` | `sqlite`, `postgres`, `mysql`, `none` | `sqlite` |
17
- | `--layer` | `drizzle`, `prisma`, `native` | `drizzle` |
18
- | `--auth`, `--no-auth` | Sign-in, when there's a database | on |
19
- | `--auth-provider` | `better-auth`, `custom` | `better-auth` |
20
- | `--runtime` | `node`, `bun`, `deno` | the one running it |
21
- | `--config` | `ts`, `json`, `yaml`: the format of the weft config | `ts` |
22
- | `--pm` | `npm`, `pnpm`, `bun`, `deno`, `yarn` | the one you ran it with |
23
- | `--git`, `--no-git`, `--install`, `--no-install` | Start a git repository; install dependencies | on, or off with no terminal and no `--yes` |
24
- | `--name` | The package name | the folder's name |
25
-
26
- With npm, flags go after `--`: `npm create @weft-js my-app -- --ui tailwind --db none`.
27
-
28
- Until weft is on npm, make an app from a checkout instead: [Installation](../../site/content/learn/installation.md).
294
+ | `npm` | `npm create @weft-js my-app` | `npx weft` |
295
+ | `pnpm` | `pnpm create @weft-js my-app` | `pnpm exec weft` |
296
+ | `bun` | `bun create @weft-js my-app` | `bunx --bun weft` |
297
+ | `deno` | `deno run -A npm:@weft-js/create my-app` | `deno run -A npm:@weft-js/cli` |
298
+ | `yarn` | `yarn create @weft-js my-app` | `yarn weft` |
299
+
300
+ `--runtime` is separate: it's what `weft build` targets and `weft start` runs. Without it, the scaffold picks the one
301
+ running it, since a machine may have only that one. Anything but Node is written into the config as `runtime`.
302
+
303
+ ## Recipes
304
+
305
+ A front end with no server code:
306
+
307
+ ```bash
308
+ npm create @weft-js site -- --db none --yes
309
+ ```
310
+
311
+ Postgres with Prisma and your own auth:
312
+
313
+ ```bash
314
+ pnpm create @weft-js app --db postgres --layer prisma \
315
+ --auth-provider custom --yes
316
+ ```
317
+
318
+ Tailwind on MySQL with plain SQL, a YAML config, and no install:
319
+
320
+ ```bash
321
+ bun create @weft-js app --ui tailwind --db mysql \
322
+ --layer native --config yaml --no-install --yes
323
+ ```
324
+
325
+ For an agent or CI: no prompts, and everything run.
326
+
327
+ ```bash
328
+ npm create @weft-js my-app -- --yes
329
+ ```
330
+
331
+ ## Using it from code
332
+
333
+ The package exports what the command is built from. `weft add db` and `weft add auth` use `./stack`.
334
+
335
+ | Import | Exports |
336
+ |---|---|
337
+ | `@weft-js/create` | `scaffold(dir, options)`, `template(options)`, `withTemplate(options)`, and the choice lists `UI`, `DB`, `LAYERS`, `RUNTIMES`, `CONFIGS`, `TEMPLATES` |
338
+ | `@weft-js/create/stack` | `stackDeps`, `stackFiles`, `authFiles`, `VERSIONS`, `VERSION` and the page builders: the database and auth part of a scaffold |
339
+
340
+ ```js
341
+ import { scaffold, template } from '@weft-js/create'
342
+
343
+ // Every file as { path: text }, written nowhere
344
+ const files = template({
345
+ name: 'shop',
346
+ ui: 'warp',
347
+ db: 'sqlite',
348
+ auth: true,
349
+ })
350
+
351
+ // The same, written into an empty folder
352
+ const { files: written } = scaffold('./shop', {
353
+ ui: 'warp',
354
+ db: 'sqlite',
355
+ auth: true,
356
+ })
357
+ ```
358
+
359
+ > [!NOTE]
360
+ > `template()` and `scaffold()` don't apply the wizard's defaults: without `db` there's no database, and without
361
+ > `auth` no sign-in. They don't install or start git either.
362
+
363
+ ## Troubleshooting
364
+
365
+ | Message | Why | Fix |
366
+ |---|---|---|
367
+ | `<dir> is not empty` | It only writes into a missing or empty folder | Pick another name, or empty it |
368
+ | `--auth keeps users in a database` | Sign-in with `--db none` | Add `--db sqlite`, `postgres` or `mysql`, or `--no-auth` |
369
+ | `the <name> template needs --db postgres` (or `--ui warp`, `--layer drizzle`, `--config ts`, sign-in on or off) | A flag conflicts with the template | Drop the flag, or use the value it names |
370
+ | `--ui must be one of …` and the like | An unknown value | Use one from [Flags](#flags) |
371
+ | `The install didn't finish; run … yourself.` | The package manager failed. The app is already written | Run the command it prints. The run exits 1 |
372
+ | `git init didn't finish` | git isn't installed, or failed | The app is fine; run `git init` when you want it |
373
+
374
+ ## Related packages
375
+
376
+ | Package | What it is |
377
+ |---|---|
378
+ | [`@weft-js/cli`](https://www.npmjs.com/package/@weft-js/cli) <sup>[source](https://github.com/Ravonus/weft/tree/main/packages/cli)</sup> | The `weft` command every new app uses |
379
+ | [`@weft-js/core`](https://www.npmjs.com/package/@weft-js/core) <sup>[source](https://github.com/Ravonus/weft/tree/main/packages/core)</sup> | The runtime, the compiler binding, the Vite plugin, SSR and test utilities |
380
+ | [`@weft-js/start`](https://www.npmjs.com/package/@weft-js/start) <sup>[source](https://github.com/Ravonus/weft/tree/main/packages/start)</sup> | File routes, server functions, SSR, the database, auth and live data |
381
+ | [`@weft-js/db`](https://www.npmjs.com/package/@weft-js/db) <sup>[source](https://github.com/Ravonus/weft/tree/main/packages/db)</sup> | The Prisma and native SQL data layers |
382
+ | [`@weft-js/warp`](https://www.npmjs.com/package/@weft-js/warp) <sup>[source](https://github.com/Ravonus/weft/tree/main/packages/warp)</sup> | Warp's components, and the `warp()` theming plugin |
383
+
384
+ [Installation](https://github.com/Ravonus/weft/blob/main/site/content/learn/installation.md) walks through every file in
385
+ a new app, and how to run, check, build and deploy it.
386
+
387
+ ## Contributing
388
+
389
+ [CONTRIBUTING.md](https://github.com/Ravonus/weft/blob/main/CONTRIBUTING.md) has the setup. To work on this package:
390
+
391
+ ```bash
392
+ pnpm --filter @weft-js/create test
393
+ ```
394
+
395
+ A template is a folder in
396
+ [`packages/create/templates`](https://github.com/Ravonus/weft/tree/main/packages/create/templates) with a
397
+ `template.json` (what it `requires`, its `pages`, its `warp()` options and `dependencies`) and a `files/` folder that's
398
+ copied as it is.
399
+
400
+ ## License
401
+
402
+ [MIT](https://github.com/Ravonus/weft/blob/main/LICENSE).
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // create-weft [dir] [--ui warp|headless|tailwind|none] [--db sqlite|postgres|mysql|none] [--layer drizzle|prisma|native] [--[no-]auth] [--auth-provider better-auth|custom] [--runtime node|bun|deno]
2
+ // create-weft [dir] [--template landing|todo|admin|catalog] [--ui warp|headless|tailwind|none] [--db sqlite|postgres|mysql|none] [--layer drizzle|prisma|native] [--[no-]auth] [--auth-provider better-auth|custom] [--runtime node|bun|deno]
3
3
  // [--config ts|json|yaml] [--pm npm|pnpm|bun|deno|yarn] [--[no-]git] [--[no-]install] [--name <name>] [--tarballs <dir>] [--yes]
4
4
  // In a terminal it asks what the flags don't answer; --yes, or no terminal, takes the defaults.
5
5
  import { execFileSync } from 'node:child_process'
@@ -7,16 +7,42 @@ import { readFileSync, writeFileSync } from 'node:fs'
7
7
  import { join, resolve } from 'node:path'
8
8
  import { createInterface } from 'node:readline/promises'
9
9
  import { parseArgs } from 'node:util'
10
- import { CONFIGS, DB, LAYERS, RUNTIMES, scaffold, UI } from '../src/index.mjs'
10
+ import {
11
+ CONFIGS,
12
+ DB,
13
+ LAYERS,
14
+ RUNTIMES,
15
+ scaffold,
16
+ TEMPLATES,
17
+ UI,
18
+ withTemplate,
19
+ } from '../src/index.mjs'
11
20
  import { resolveChoices } from '../src/wizard.mjs'
12
- import { pin } from '../scripts/tarballs.mjs'
21
+ import { pin, pinWorkspace } from '../scripts/tarballs.mjs'
13
22
 
14
- const usage = `Usage: npm create @weft-js -- [dir] [--ui ${UI.join('|')}] [--db ${DB.join('|')}] [--layer ${LAYERS.join('|')}] [--[no-]auth] [--auth-provider better-auth|custom] [--runtime ${RUNTIMES.join('|')}] [--config ${CONFIGS.join('|')}] [--pm <manager>] [--[no-]git] [--[no-]install] [--name <name>] [--tarballs <dir>] [--yes]`
15
- const string = { type: 'string' }, boolean = { type: 'boolean' }
23
+ const usage = `Usage: npm create @weft-js -- [dir] [--template ${TEMPLATES.join('|')}] [--ui ${UI.join('|')}] [--db ${DB.join('|')}] [--layer ${LAYERS.join('|')}] [--[no-]auth] [--auth-provider better-auth|custom] [--runtime ${RUNTIMES.join('|')}] [--config ${CONFIGS.join('|')}] [--pm <manager>] [--[no-]git] [--[no-]install] [--name <name>] [--tarballs <dir>] [--yes]`
24
+ const string = { type: 'string' },
25
+ boolean = { type: 'boolean' }
16
26
  const { values, positionals } = parseArgs({
17
27
  allowPositionals: true,
18
28
  allowNegative: true,
19
- options: { ui: string, db: string, layer: string, auth: boolean, 'auth-provider': string, runtime: string, config: string, pm: string, git: boolean, install: boolean, name: string, tarballs: string, yes: { ...boolean, short: 'y' }, help: { ...boolean, short: 'h' } },
29
+ options: {
30
+ template: string,
31
+ ui: string,
32
+ db: string,
33
+ layer: string,
34
+ auth: boolean,
35
+ 'auth-provider': string,
36
+ runtime: string,
37
+ config: string,
38
+ pm: string,
39
+ git: boolean,
40
+ install: boolean,
41
+ name: string,
42
+ tarballs: string,
43
+ yes: { ...boolean, short: 'y' },
44
+ help: { ...boolean, short: 'h' },
45
+ },
20
46
  })
21
47
  const interactive = process.stdin.isTTY && process.stdout.isTTY
22
48
  if (values.help || positionals.length > 1 || (!positionals.length && !interactive)) {
@@ -33,40 +59,62 @@ async function directory() {
33
59
  return answer.trim() || 'my-weft-app'
34
60
  }
35
61
 
36
- /** Git and the install are conveniences: the app is already written, so a failure here only leaves a step for later. */
62
+ /** The app is already written, so a failure here leaves a step for later; a failed install still fails the command. */
37
63
  function step(what, command, args, cwd) {
38
64
  try {
39
- execFileSync(command, args, { cwd, stdio: ['ignore', 'ignore', 'inherit'], shell: process.platform === 'win32' }) // npm is npm.cmd there
65
+ execFileSync(command, args, {
66
+ cwd,
67
+ stdio: ['ignore', 'ignore', 'inherit'],
68
+ shell: process.platform === 'win32',
69
+ }) // npm is npm.cmd there
40
70
  return true
41
71
  } catch {
42
- console.error(`create-weft: ${what} didn't finish; run \`${[command, ...args].join(' ')}\` yourself.`)
72
+ console.error(
73
+ `create-weft: ${what} didn't finish; run \`${[command, ...args].join(' ')}\` yourself.`,
74
+ )
43
75
  return false
44
76
  }
45
77
  }
46
78
 
47
79
  try {
48
80
  const { yes, help: _help, name, tarballs, 'auth-provider': authProvider, ...flags } = values
49
- if (authProvider && !['better-auth', 'custom'].includes(authProvider)) throw new Error('--auth-provider must be better-auth or custom')
81
+ if (authProvider && !['better-auth', 'custom'].includes(authProvider))
82
+ throw new Error('--auth-provider must be better-auth or custom')
50
83
  if (authProvider) flags.auth = authProvider === 'custom' ? 'custom' : true
51
84
  const where = await directory()
52
85
  const dir = resolve(where)
53
- const choices = await resolveChoices(flags, { interactive, yes, io: { input: process.stdin, output: process.stdout } })
86
+ const choices = await resolveChoices(withTemplate(flags), {
87
+ interactive,
88
+ yes,
89
+ io: { input: process.stdin, output: process.stdout },
90
+ })
54
91
  const { files } = scaffold(dir, { ...choices, ...(name && { name }) })
55
92
  // Before the install: a checkout's packages aren't on npm, so `local.mjs new` needs them wired in from the start.
56
93
  if (tarballs) {
57
94
  const file = join(dir, 'package.json')
58
- writeFileSync(file, JSON.stringify(pin(JSON.parse(readFileSync(file, 'utf8')), JSON.parse(readFileSync(join(tarballs, 'tarballs.json'), 'utf8'))), null, 2))
95
+ const map = JSON.parse(readFileSync(join(tarballs, 'tarballs.json'), 'utf8'))
96
+ writeFileSync(file, JSON.stringify(pin(JSON.parse(readFileSync(file, 'utf8')), map), null, 2))
97
+ pinWorkspace(dir, map)
59
98
  }
60
99
  console.log(`\nCreated ${files.length} files in ${dir}`)
61
100
  if (choices.git) step('git init', 'git', ['init', '--quiet'], dir)
62
101
  const installed = choices.install && step('The install', choices.pm, ['install'], dir)
63
- const weft = { npm: 'npx weft', pnpm: 'pnpm exec weft', bun: 'bunx --bun weft', deno: 'deno run -A npm:@weft-js/cli', yarn: 'yarn weft' }[choices.pm]
102
+ const weft = {
103
+ npm: 'npx weft',
104
+ pnpm: 'pnpm exec weft',
105
+ bun: 'bunx --bun weft',
106
+ deno: 'deno run -A npm:@weft-js/cli',
107
+ yarn: 'yarn weft',
108
+ }[choices.pm]
64
109
  // AGENTS.md comes from the weft just installed, so it always matches the compiler the app runs.
65
110
  if (installed) {
66
111
  const [command, ...args] = weft.split(' ')
67
112
  step('AGENTS.md', command, [...args, 'agents-md'], dir)
68
113
  }
69
- console.log(`\n cd ${where}${installed ? '' : `\n ${choices.pm} install\n ${weft} agents-md`}${choices.db === 'none' ? '' : `\n ${weft} db generate`}\n ${weft} dev\n`)
114
+ console.log(
115
+ `\n cd ${where}${installed ? '' : `\n ${choices.pm} install\n ${weft} agents-md`}${choices.db === 'none' || choices.template ? '' : `\n ${weft} db generate`}\n ${weft} dev\n`,
116
+ )
117
+ if (choices.install && !installed) process.exitCode = 1
70
118
  } catch (e) {
71
119
  console.error(`create-weft: ${e.message}`)
72
120
  process.exit(1)
package/package.json CHANGED
@@ -1,30 +1,40 @@
1
1
  {
2
2
  "name": "@weft-js/create",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Scaffold a weft app: npm create @weft-js",
5
+ "keywords": [
6
+ "weft"
7
+ ],
5
8
  "license": "MIT",
6
- "type": "module",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Ravonus/weft.git",
12
+ "directory": "packages/create"
13
+ },
14
+ "homepage": "https://github.com/Ravonus/weft/tree/main/packages/create#readme",
15
+ "bugs": {
16
+ "url": "https://github.com/Ravonus/weft/issues"
17
+ },
7
18
  "bin": {
8
19
  "create-weft": "./bin/create-weft.mjs"
9
20
  },
21
+ "files": [
22
+ "bin",
23
+ "src",
24
+ "templates",
25
+ "scripts/tarballs.mjs",
26
+ "README.md",
27
+ "LICENSE"
28
+ ],
29
+ "type": "module",
10
30
  "exports": {
11
31
  ".": "./src/index.mjs",
12
32
  "./stack": "./src/stack.mjs"
13
33
  },
14
- "engines": {
15
- "node": ">=26"
16
- },
17
34
  "publishConfig": {
18
35
  "access": "public"
19
36
  },
20
- "keywords": [
21
- "weft"
22
- ],
23
- "files": [
24
- "bin",
25
- "src",
26
- "scripts/tarballs.mjs",
27
- "README.md",
28
- "LICENSE"
29
- ]
30
- }
37
+ "engines": {
38
+ "node": ">=26"
39
+ }
40
+ }