create-bakery 2.0.0-alpha.9 → 2.0.0-rc.1
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 +7 -7
- package/package.json +2 -2
- package/src/index.ts +22 -12
- package/src/prompt.ts +2 -2
- package/src/template.ts +71 -28
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ bun run dev
|
|
|
25
25
|
| `-h`, `--help` | Usage |
|
|
26
26
|
|
|
27
27
|
Use `.` as the directory to scaffold in place. The positional argument is a
|
|
28
|
-
*path*, so its basename becomes the package name
|
|
28
|
+
*path*, so its basename becomes the package name: scoped names come from
|
|
29
29
|
`--name`.
|
|
30
30
|
|
|
31
31
|
It refuses to scaffold into a directory that already has files in it, since that
|
|
@@ -34,16 +34,16 @@ is not undoable. A bare `.git` directory is ignored, so
|
|
|
34
34
|
|
|
35
35
|
## Notes
|
|
36
36
|
|
|
37
|
-
- **Bun only
|
|
37
|
+
- **Bun only**: the generated app depends on Bun APIs throughout.
|
|
38
38
|
- This package has **no dependencies**, not even on Bakery. It writes files.
|
|
39
39
|
|
|
40
40
|
## License
|
|
41
41
|
|
|
42
|
-
MIT with the Commons Clause v1.0
|
|
42
|
+
MIT with the Commons Clause v1.0. See [LICENSE](./LICENSE).
|
|
43
43
|
|
|
44
|
-
**Not an OSI-approved
|
|
45
|
-
the software
|
|
44
|
+
**Not an OSI-approved license.** The Commons Clause removes the right to *sell*
|
|
45
|
+
the software: meaning to charge for a product or service whose value derives
|
|
46
46
|
substantially from it, hosting and support included. Everything else the MIT
|
|
47
|
-
|
|
48
|
-
If your
|
|
47
|
+
license grants is unchanged: use it, modify it, ship it inside your own product.
|
|
48
|
+
If your organization only permits OSI-approved dependencies, this will not pass
|
|
49
49
|
that check.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-bakery",
|
|
3
|
-
"version": "2.0.0-
|
|
3
|
+
"version": "2.0.0-rc.1",
|
|
4
4
|
"description": "Scaffold a Bakery app: bun create bakery <directory>",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bakery",
|
|
@@ -39,6 +39,6 @@
|
|
|
39
39
|
"!src/tests"
|
|
40
40
|
],
|
|
41
41
|
"engines": {
|
|
42
|
-
"bun": ">=1.
|
|
42
|
+
"bun": ">=1.4.0"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/src/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
dependencyRange,
|
|
8
8
|
isValidAppName,
|
|
9
9
|
PLUGIN_IDS,
|
|
10
|
+
PLUGINS_NEEDING_ORM,
|
|
10
11
|
type PluginId,
|
|
11
12
|
type TemplateFile,
|
|
12
13
|
templateFiles,
|
|
@@ -22,7 +23,7 @@ import {
|
|
|
22
23
|
*
|
|
23
24
|
* Deliberately dependency-free. `bun create` downloads this package on its own,
|
|
24
25
|
* so anything it depends on is a download the user waits through before seeing
|
|
25
|
-
* a single file
|
|
26
|
+
* a single file, and the framework it scaffolds is the last thing it should
|
|
26
27
|
* drag along.
|
|
27
28
|
*/
|
|
28
29
|
|
|
@@ -60,7 +61,7 @@ type Options = {
|
|
|
60
61
|
dir: string
|
|
61
62
|
name: string
|
|
62
63
|
install: boolean
|
|
63
|
-
/** `null` means "not specified"
|
|
64
|
+
/** `null` means "not specified". Ask, or fall back to the default. */
|
|
64
65
|
orm: boolean | null
|
|
65
66
|
plugins: PluginId[] | null
|
|
66
67
|
yes: boolean
|
|
@@ -70,14 +71,14 @@ type Options = {
|
|
|
70
71
|
* Parse one `--plugins` value into the ids it names.
|
|
71
72
|
*
|
|
72
73
|
* Split out of `parseArgs` because it is the only flag that validates rather
|
|
73
|
-
* than assigns, and inlining it put the loop over the complexity limit
|
|
74
|
+
* than assigns, and inlining it put the loop over the complexity limit, which
|
|
74
75
|
* is the rule doing its job: a `for` over argv should read as a dispatch table.
|
|
75
76
|
*/
|
|
76
77
|
function parsePlugins(
|
|
77
78
|
value: string,
|
|
78
79
|
): { ok: true; plugins: PluginId[] } | { ok: false; message: string } {
|
|
79
80
|
// `none` rather than an empty string, so "I want no plugins" is something you
|
|
80
|
-
// can state
|
|
81
|
+
// can state: an empty `--plugins=` reads like a mistake and is treated as one
|
|
81
82
|
// by the caller, which rejects an empty value before reaching here.
|
|
82
83
|
if (value === 'none') return { ok: true, plugins: [] }
|
|
83
84
|
|
|
@@ -108,7 +109,7 @@ function parsePlugins(
|
|
|
108
109
|
* user typed, and answering it with a stack trace teaches nothing. Throwing is
|
|
109
110
|
* reserved for a failure of the scaffolding itself.
|
|
110
111
|
*/
|
|
111
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: argv dispatcher
|
|
112
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: argv dispatcher, one branch per flag
|
|
112
113
|
export function parseArgs(
|
|
113
114
|
argv: string[],
|
|
114
115
|
): { ok: true; options: Options } | { ok: false; message: string } {
|
|
@@ -173,7 +174,7 @@ export function parseArgs(
|
|
|
173
174
|
if (dir === null) return { ok: false, message: HELP }
|
|
174
175
|
|
|
175
176
|
// `.` is the documented way to scaffold in place, and `basename(resolve('.'))`
|
|
176
|
-
// is the containing folder's name
|
|
177
|
+
// is the containing folder's name, which is the name the user means.
|
|
177
178
|
const resolved = resolve(dir)
|
|
178
179
|
const appName = name ?? basename(resolved)
|
|
179
180
|
|
|
@@ -197,8 +198,7 @@ export function parseArgs(
|
|
|
197
198
|
* Fill in whatever the flags left unspecified.
|
|
198
199
|
*
|
|
199
200
|
* Asks only when there is a terminal on both ends and `--yes` was not passed.
|
|
200
|
-
* A pipe, a CI runner or a `--yes` takes the defaults
|
|
201
|
-
* which is what `bun create bakery my-app` has always produced, so adding the
|
|
201
|
+
* A pipe, a CI runner or a `--yes` takes the defaults (ORM in, no plugins), * which is what `bun create bakery my-app` has always produced, so adding the
|
|
202
202
|
* prompts changed no existing invocation.
|
|
203
203
|
*
|
|
204
204
|
* Returns `null` when the user cancels, which is a distinct outcome from
|
|
@@ -227,12 +227,22 @@ export async function resolveChoices(
|
|
|
227
227
|
{ id: 'vue', label: 'vue', hint: 'single-file components' },
|
|
228
228
|
{ id: 'analytics', label: 'analytics', hint: 'request metrics' },
|
|
229
229
|
{ id: 'dashboard', label: 'dashboard', hint: 'admin console' },
|
|
230
|
+
{ id: 'db-explorer', label: 'db-explorer', hint: 'browse and edit rows' },
|
|
230
231
|
])
|
|
231
232
|
if (chosen === null) return null
|
|
232
233
|
plugins = PLUGIN_IDS.filter(id => chosen.includes(id))
|
|
233
234
|
}
|
|
234
235
|
}
|
|
235
236
|
|
|
237
|
+
// The explorer browses and edits whatever the ORM is connected to, so
|
|
238
|
+
// scaffolding it without `orm/` produces an app whose headline feature has
|
|
239
|
+
// nothing to show. Turned on rather than refused: the two are asked for
|
|
240
|
+
// separately, and a generated app that boots is better than a prompt that
|
|
241
|
+
// argues with the answer it was just given.
|
|
242
|
+
if (!orm && plugins.some(id => PLUGINS_NEEDING_ORM.includes(id))) {
|
|
243
|
+
orm = true
|
|
244
|
+
}
|
|
245
|
+
|
|
236
246
|
return { orm, plugins }
|
|
237
247
|
}
|
|
238
248
|
|
|
@@ -241,7 +251,7 @@ export async function resolveChoices(
|
|
|
241
251
|
* overwritten.
|
|
242
252
|
*
|
|
243
253
|
* Scaffolding is the one operation where "the directory already had something
|
|
244
|
-
* in it" is almost always a mistake, and it is not undoable
|
|
254
|
+
* in it" is almost always a mistake, and it is not undoable, so this refuses
|
|
245
255
|
* rather than merges or prompts. `.git` and the editor droppings people
|
|
246
256
|
* routinely create a directory with are ignored, because refusing on those
|
|
247
257
|
* makes `git init && bun create bakery .` fail for no reason.
|
|
@@ -254,7 +264,7 @@ export async function isScaffoldable(dir: string): Promise<boolean> {
|
|
|
254
264
|
entries = await readdir(dir)
|
|
255
265
|
} catch {
|
|
256
266
|
// Does not exist, which is the common case and the good one. A permission
|
|
257
|
-
// error also lands here and is caught properly by the write that follows
|
|
267
|
+
// error also lands here and is caught properly by the write that follows:
|
|
258
268
|
// reporting it as "not empty" would be a worse message than the real one.
|
|
259
269
|
return true
|
|
260
270
|
}
|
|
@@ -305,7 +315,7 @@ async function main(): Promise<number> {
|
|
|
305
315
|
if (!(await isScaffoldable(dir))) {
|
|
306
316
|
console.log(
|
|
307
317
|
`${dir} already has files in it. Bakery will not scaffold over an ` +
|
|
308
|
-
'existing directory
|
|
318
|
+
'existing directory. Pick an empty one, or empty this one first.',
|
|
309
319
|
)
|
|
310
320
|
return 1
|
|
311
321
|
}
|
|
@@ -340,7 +350,7 @@ async function main(): Promise<number> {
|
|
|
340
350
|
const code = await proc.exited
|
|
341
351
|
if (code !== 0) {
|
|
342
352
|
console.log(
|
|
343
|
-
'\nbun install failed. The app is written
|
|
353
|
+
'\nbun install failed. The app is written. Run it again in ' +
|
|
344
354
|
`${dir} once the problem is fixed.`,
|
|
345
355
|
)
|
|
346
356
|
return code
|
package/src/prompt.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The interactive half of `bun create bakery`, written from scratch.
|
|
3
3
|
*
|
|
4
4
|
* Every prompt library worth using is a dependency, and this package declares
|
|
5
|
-
* **none** on purpose
|
|
5
|
+
* **none** on purpose: `bun create` downloads it standalone, so anything it
|
|
6
6
|
* pulls in is a download the user waits through before seeing a single file.
|
|
7
7
|
* Two prompts is less code than justifying the exception.
|
|
8
8
|
*
|
|
@@ -185,7 +185,7 @@ export async function confirm(
|
|
|
185
185
|
return null
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
/** Returns the chosen ids, or `null` if the user
|
|
188
|
+
/** Returns the chosen ids, or `null` if the user canceled. */
|
|
189
189
|
export async function multiselect(
|
|
190
190
|
question: string,
|
|
191
191
|
choices: Choice[],
|
package/src/template.ts
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
* checkable: **every `@bakery-framework/*` specifier below resolves through an
|
|
7
7
|
* *enumerated* export, never the `"./*"` wildcard.** That wildcard is a
|
|
8
8
|
* deprecation ramp with one release to live (MONOREPO.md), so a template that
|
|
9
|
-
* leaned on it would generate apps that break on its removal
|
|
9
|
+
* leaned on it would generate apps that break on its removal, and it would
|
|
10
10
|
* break them silently, because the wildcard resolves fine today.
|
|
11
11
|
*
|
|
12
12
|
* Derived from `apps/starter`, which is the honest reference: written against
|
|
13
13
|
* public entry points only and booted in CI. The differences are the ones that
|
|
14
|
-
* have to differ
|
|
14
|
+
* have to differ: real dependency ranges instead of `workspace:*`, the
|
|
15
15
|
* `bakery` bin instead of a relative path into the repo, and a `.gitignore`.
|
|
16
16
|
*/
|
|
17
17
|
|
|
@@ -22,7 +22,12 @@ export type TemplateFile = {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/** The plugins `--plugins` accepts, in the order they are registered. */
|
|
25
|
-
export const PLUGIN_IDS = [
|
|
25
|
+
export const PLUGIN_IDS = [
|
|
26
|
+
'vue',
|
|
27
|
+
'analytics',
|
|
28
|
+
'dashboard',
|
|
29
|
+
'db-explorer',
|
|
30
|
+
] as const
|
|
26
31
|
export type PluginId = (typeof PLUGIN_IDS)[number]
|
|
27
32
|
|
|
28
33
|
export type TemplateOptions = {
|
|
@@ -38,7 +43,7 @@ export type TemplateOptions = {
|
|
|
38
43
|
* `@vue/compiler-sfc` as **peer** dependencies, so scaffolding the plugin
|
|
39
44
|
* without them produces an app that installs cleanly and then fails the first
|
|
40
45
|
* time it compiles an SFC. They go into the generated `dependencies`, because a
|
|
41
|
-
* scaffolded app is an application
|
|
46
|
+
* scaffolded app is an application: it should pin what it needs rather than
|
|
42
47
|
* inherit an unmet peer warning.
|
|
43
48
|
*/
|
|
44
49
|
const PLUGINS: Record<
|
|
@@ -65,8 +70,32 @@ const PLUGINS: Record<
|
|
|
65
70
|
// because it is a local demo; a generated app is not.
|
|
66
71
|
call: 'dashboardPlugin()',
|
|
67
72
|
},
|
|
73
|
+
'db-explorer': {
|
|
74
|
+
pkg: '@bakery-framework/plugin-db-explorer',
|
|
75
|
+
import: 'dbExplorerPlugin',
|
|
76
|
+
// Same rule as the dashboard and for a stronger reason: this one edits
|
|
77
|
+
// rows. With no `users` and no `authorize` the explorer allows loopback in
|
|
78
|
+
// development and denies in production, so a generated app is never born
|
|
79
|
+
// able to write to its database from a browser on the internet.
|
|
80
|
+
//
|
|
81
|
+
// It does not share the dashboard's door. Access here is a *level* per
|
|
82
|
+
// caller rather than a yes, which is why configuring one grants nothing in
|
|
83
|
+
// the other. See `docs/plugins/db-explorer.md`.
|
|
84
|
+
call: 'dbExplorerPlugin()',
|
|
85
|
+
},
|
|
68
86
|
}
|
|
69
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Plugins that cannot work without the ORM.
|
|
90
|
+
*
|
|
91
|
+
* `db-explorer` browses and edits whatever the ORM is connected to, so
|
|
92
|
+
* scaffolding it without `orm/` produces an app whose headline feature has
|
|
93
|
+
* nothing to show. The scaffolder turns the ORM on rather than refusing: the
|
|
94
|
+
* two are asked for separately and a generated app that boots is better than
|
|
95
|
+
* a prompt that argues.
|
|
96
|
+
*/
|
|
97
|
+
export const PLUGINS_NEEDING_ORM: readonly PluginId[] = ['db-explorer']
|
|
98
|
+
|
|
70
99
|
/**
|
|
71
100
|
* The version range the generated `package.json` asks for.
|
|
72
101
|
*
|
|
@@ -82,14 +111,14 @@ export function dependencyRange(ownVersion: string): string {
|
|
|
82
111
|
/**
|
|
83
112
|
* A valid npm package name, scoped or not.
|
|
84
113
|
*
|
|
85
|
-
* Stricter than npm on the parts that are worth being strict about
|
|
86
|
-
* uppercase, no leading dot or dash
|
|
114
|
+
* Stricter than npm on the parts that are worth being strict about (no
|
|
115
|
+
* uppercase, no leading dot or dash), because the generated `name` field is
|
|
87
116
|
* the only place this lands, and a name npm would reject surfaces as a
|
|
88
117
|
* confusing `bun install` failure several steps after the mistake.
|
|
89
118
|
*
|
|
90
119
|
* Scopes are accepted, and are reachable only through `--name`. The positional
|
|
91
120
|
* argument is a *directory path*, so `bun create bakery @co/app` means a nested
|
|
92
|
-
* directory whose basename is `app
|
|
121
|
+
* directory whose basename is `app`: that is what a path argument means, and
|
|
93
122
|
* quietly treating it as a scoped package name instead would be a guess.
|
|
94
123
|
*/
|
|
95
124
|
export function isValidAppName(name: string): boolean {
|
|
@@ -116,7 +145,7 @@ export default defineConfig({
|
|
|
116
145
|
|
|
117
146
|
const calls = plugins.map(id => ` ${PLUGINS[id].call},\n`).join('')
|
|
118
147
|
const dashboardNote = plugins.includes('dashboard')
|
|
119
|
-
? ` // The dashboard authenticates nobody itself
|
|
148
|
+
? ` // The dashboard authenticates nobody itself. The app does, because it is\n` +
|
|
120
149
|
` // the thing that knows who its users are. With no \`authorize\`, access is\n` +
|
|
121
150
|
` // loopback-only in development and denied in production:\n` +
|
|
122
151
|
` //\n` +
|
|
@@ -142,7 +171,7 @@ const INDEX_PAGE = `export default function Home() {
|
|
|
142
171
|
</head>
|
|
143
172
|
<body>
|
|
144
173
|
<h1>{{name}}</h1>
|
|
145
|
-
<p>Edit <code>src/index.tsx</code> and save
|
|
174
|
+
<p>Edit <code>src/index.tsx</code> and save, the page reloads itself.</p>
|
|
146
175
|
<p id="count">loading…</p>
|
|
147
176
|
<script src="/script.js" type="module"></script>
|
|
148
177
|
</body>
|
|
@@ -162,12 +191,12 @@ import DB from '@bakery-framework/orm'
|
|
|
162
191
|
//
|
|
163
192
|
// export default defineRoute({ body: mySchema }, async (req, body) => …)
|
|
164
193
|
//
|
|
165
|
-
// The body option takes a Standard Schema (zod, valibot, arktype
|
|
194
|
+
// The body option takes a Standard Schema (zod, valibot, arktype, Bakery
|
|
166
195
|
// bundles none of them), or a plain function returning the parsed value or
|
|
167
196
|
// throwing. A rejection answers 400 through the same JSON envelope as
|
|
168
197
|
// everything else.
|
|
169
198
|
// posts.authorId is a foreign key into users, so a post cannot be inserted
|
|
170
|
-
// before its author exists
|
|
199
|
+
// before its author exists: the database refuses a dangling id, which is the
|
|
171
200
|
// entire point of the constraint. A real app takes the author from the session;
|
|
172
201
|
// this creates one on demand so the route works on a freshly synced database.
|
|
173
202
|
async function authorId(): Promise<number> {
|
|
@@ -203,14 +232,14 @@ export default defineRoute<{ title: string; slug: string; body: string }>(
|
|
|
203
232
|
/**
|
|
204
233
|
* The same route without a database.
|
|
205
234
|
*
|
|
206
|
-
* Kept to the same shape
|
|
207
|
-
* `data` as an array
|
|
235
|
+
* Kept to the same shape (`defineRoute`, a typed body, one JSON envelope, and
|
|
236
|
+
* `data` as an array), so the client script below is identical either way and
|
|
208
237
|
* the two templates do not drift into demonstrating different things.
|
|
209
238
|
*/
|
|
210
239
|
const API_ROUTE_NO_ORM = `import { defineRoute, response } from '@bakery-framework/core'
|
|
211
240
|
|
|
212
241
|
// In memory, and therefore per process: a cluster (\`--threads N\`) gives each
|
|
213
|
-
// worker its own copy. That is the point at which you want the ORM
|
|
242
|
+
// worker its own copy. That is the point at which you want the ORM: scaffold
|
|
214
243
|
// with it, or add @bakery-framework/orm later.
|
|
215
244
|
const posts: { title: string }[] = []
|
|
216
245
|
|
|
@@ -266,7 +295,7 @@ export const postsByAuthor = Field.Index(posts.authorId)
|
|
|
266
295
|
/**
|
|
267
296
|
* The `declare module` block is the whole point of this file: it is what makes
|
|
268
297
|
* the ORM typed. Without it everything still runs and typechecks, the columns
|
|
269
|
-
* are just permissive `any
|
|
298
|
+
* are just permissive `any`, which is a quiet enough failure that it is worth
|
|
270
299
|
* generating rather than documenting.
|
|
271
300
|
*/
|
|
272
301
|
|
|
@@ -330,7 +359,7 @@ function gitignore(orm: boolean): string {
|
|
|
330
359
|
|
|
331
360
|
return `node_modules
|
|
332
361
|
|
|
333
|
-
# Disposable
|
|
362
|
+
# Disposable: the framework deletes it wholesale on every version bump and
|
|
334
363
|
# dev<->prod switch.
|
|
335
364
|
.cache
|
|
336
365
|
${data}`
|
|
@@ -343,7 +372,7 @@ function readme(name: string, orm: boolean, plugins: PluginId[]): string {
|
|
|
343
372
|
|
|
344
373
|
const ormRows = orm
|
|
345
374
|
? '| `orm/tables.ts` | Table definitions. Run `bun run db:sync` after editing. |\n' +
|
|
346
|
-
'| `orm/views.ts` | View definitions
|
|
375
|
+
'| `orm/views.ts` | View definitions: stored SELECTs, read-only. |\n' +
|
|
347
376
|
"| `orm/index.ts` | Registers the schema with the ORM's types. Without its `declare module` block the ORM still works, untyped. |\n"
|
|
348
377
|
: ''
|
|
349
378
|
|
|
@@ -354,6 +383,11 @@ function readme(name: string, orm: boolean, plugins: PluginId[]): string {
|
|
|
354
383
|
? '\n\nThe dashboard is loopback-only in development and denied in production ' +
|
|
355
384
|
'until you give it an `authorize` predicate.'
|
|
356
385
|
: '') +
|
|
386
|
+
(plugins.includes('db-explorer')
|
|
387
|
+
? '\n\nThe database explorer is served at `/_db` and follows the same rule, ' +
|
|
388
|
+
'with its own access model: a level per caller rather than a yes. ' +
|
|
389
|
+
'Configuring the dashboard grants nothing there.'
|
|
390
|
+
: '') +
|
|
357
391
|
(plugins.includes('vue')
|
|
358
392
|
? '\n\n`vue` and `@vue/compiler-sfc` are direct dependencies rather than ' +
|
|
359
393
|
'unmet peers, so `.vue` pages compile straight after install.'
|
|
@@ -364,7 +398,7 @@ function readme(name: string, orm: boolean, plugins: PluginId[]): string {
|
|
|
364
398
|
const noOrm = orm
|
|
365
399
|
? ''
|
|
366
400
|
: '\nScaffolded without the ORM. `src/api/notes.ts` keeps its posts in memory, ' +
|
|
367
|
-
'which is per process
|
|
401
|
+
'which is per process. Add `@bakery-framework/orm` when you want them to outlive a ' +
|
|
368
402
|
'restart or survive `--threads`.\n'
|
|
369
403
|
|
|
370
404
|
return `# ${name}
|
|
@@ -382,7 +416,7 @@ ${noOrm}
|
|
|
382
416
|
|
|
383
417
|
| Path | What it is |
|
|
384
418
|
| --- | --- |
|
|
385
|
-
| \`src/\` | Served. Every file is a route
|
|
419
|
+
| \`src/\` | Served. Every file is a route, \`src/index.tsx\` is \`/\`, \`src/api/notes.ts\` is \`/api/notes\`. |
|
|
386
420
|
${ormRows}| \`server.config.ts\` | Port, root directory, plugins. |
|
|
387
421
|
${pluginSection}
|
|
388
422
|
\`bun run start\` serves in production mode; add \`--threads N\` to fork a cluster.
|
|
@@ -394,7 +428,7 @@ ${pluginSection}
|
|
|
394
428
|
* every page in the generated app.
|
|
395
429
|
*
|
|
396
430
|
* `@bakery-framework/core/tsconfig.server.json` already sets them, and `tsc` picks them up
|
|
397
|
-
* from there
|
|
431
|
+
* from there, but **Bun's runtime does not follow `extends` into a package
|
|
398
432
|
* specifier**, only a relative path. So at runtime the app is transpiled with
|
|
399
433
|
* Bun's default automatic JSX runtime instead of Bakery's classic
|
|
400
434
|
* `createElement`, and every `.tsx` route fails with `Cannot find module
|
|
@@ -414,7 +448,7 @@ function tsconfig(orm: boolean): string {
|
|
|
414
448
|
.join(',\n')
|
|
415
449
|
|
|
416
450
|
return `{
|
|
417
|
-
"$comment": "The three jsx* options are also set by @bakery-framework/core/tsconfig.server.json, and tsc reads them from there
|
|
451
|
+
"$comment": "The three jsx* options are also set by @bakery-framework/core/tsconfig.server.json, and tsc reads them from there, but Bun's runtime does not follow 'extends' into a package specifier, only a relative path. Without them here, every .tsx page fails at runtime with \\"Cannot find module 'react/jsx-dev-runtime'\\" while typecheck stays clean. Keep them.",
|
|
418
452
|
"extends": "@bakery-framework/core/tsconfig.server.json",
|
|
419
453
|
"compilerOptions": {
|
|
420
454
|
"jsx": "react",
|
|
@@ -431,7 +465,7 @@ ${include}
|
|
|
431
465
|
/**
|
|
432
466
|
* Build the file list for an app named `name`.
|
|
433
467
|
*
|
|
434
|
-
* `range` is threaded in rather than read from disk so this stays pure
|
|
468
|
+
* `range` is threaded in rather than read from disk so this stays pure: the
|
|
435
469
|
* caller resolves it from the running package's own version.
|
|
436
470
|
*/
|
|
437
471
|
export function templateFiles(
|
|
@@ -460,7 +494,16 @@ export function templateFiles(
|
|
|
460
494
|
dev: 'bakery --dev',
|
|
461
495
|
start: 'bakery',
|
|
462
496
|
typecheck: 'tsc --noEmit',
|
|
463
|
-
...(orm
|
|
497
|
+
...(orm
|
|
498
|
+
? {
|
|
499
|
+
'db:sync': 'bun run scripts/db-sync.ts',
|
|
500
|
+
// The codegen entry point: write the schema *from* the database,
|
|
501
|
+
// touching no tables. `--migrate` is the sync engine's adoption
|
|
502
|
+
// flag; naming it here is what makes generation discoverable as a
|
|
503
|
+
// command rather than folklore about a flag.
|
|
504
|
+
'db:codegen': 'bun run scripts/db-sync.ts --migrate',
|
|
505
|
+
}
|
|
506
|
+
: {}),
|
|
464
507
|
},
|
|
465
508
|
// Sorted, because the key order here is otherwise "whichever plugin was
|
|
466
509
|
// listed first", and a generated file that differs run to run is a diff
|
|
@@ -470,21 +513,21 @@ export function templateFiles(
|
|
|
470
513
|
),
|
|
471
514
|
// `bun-types` is not optional, and its absence is invisible until you run
|
|
472
515
|
// tsc. `@bakery-framework/core/tsconfig.server.json` sets
|
|
473
|
-
// `"types": ["bun-types"]
|
|
474
|
-
// error in browser code
|
|
516
|
+
// `"types": ["bun-types"]`. It has to, that is what makes `Bun.*` a type
|
|
517
|
+
// error in browser code, but core declares no dependencies at all, so
|
|
475
518
|
// nothing installs the package the setting names. In this repo it resolves
|
|
476
519
|
// from a *root* devDependency, which is why the gap was invisible from
|
|
477
520
|
// inside the workspace: a generated app typechecked here and died with
|
|
478
521
|
// `TS2688: Cannot find type definition file for 'bun-types'` anywhere else.
|
|
479
522
|
//
|
|
480
|
-
// An app owning its own toolchain is the right shape regardless
|
|
523
|
+
// An app owning its own toolchain is the right shape regardless: the
|
|
481
524
|
// alternative is core taking a dependency, and having none is a property
|
|
482
525
|
// worth more than this convenience.
|
|
483
526
|
devDependencies: {
|
|
484
|
-
'bun-types': '^1.
|
|
527
|
+
'bun-types': '^1.4.0',
|
|
485
528
|
// **`^5`, not `^7`, and the reason is `vue-tsc`.** TypeScript 7 is the
|
|
486
529
|
// native port and does not ship `typescript/lib/tsc`, which `vue-tsc`
|
|
487
|
-
// requires to patch
|
|
530
|
+
// requires to patch, so a Vue app that installs TS 7 gets
|
|
488
531
|
// `Cannot find module 'typescript/lib/tsc'` the first time it typechecks
|
|
489
532
|
// an SFC. Measured on a real app: 7.0.2 crashes, 5.9.3 runs.
|
|
490
533
|
//
|