@beignet/cli 0.0.9 → 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/CHANGELOG.md +7 -0
- package/README.md +35 -19
- package/dist/choices.d.ts +32 -3
- package/dist/choices.d.ts.map +1 -1
- package/dist/choices.js +54 -3
- package/dist/choices.js.map +1 -1
- package/dist/create-prompts.d.ts +5 -4
- package/dist/create-prompts.d.ts.map +1 -1
- package/dist/create-prompts.js +22 -4
- package/dist/create-prompts.js.map +1 -1
- package/dist/create.d.ts +7 -1
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +13 -3
- package/dist/create.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -6
- package/dist/index.js.map +1 -1
- package/dist/make.d.ts +21 -1
- package/dist/make.d.ts.map +1 -1
- package/dist/make.js +210 -37
- package/dist/make.js.map +1 -1
- package/dist/templates/base.d.ts.map +1 -1
- package/dist/templates/base.js +79 -9
- package/dist/templates/base.js.map +1 -1
- package/dist/templates/db/index.d.ts +21 -0
- package/dist/templates/db/index.d.ts.map +1 -0
- package/dist/templates/db/index.js +14 -0
- package/dist/templates/db/index.js.map +1 -0
- package/dist/templates/db/mysql.d.ts +4 -0
- package/dist/templates/db/mysql.d.ts.map +1 -0
- package/dist/templates/db/mysql.js +972 -0
- package/dist/templates/db/mysql.js.map +1 -0
- package/dist/templates/db/postgres.d.ts +4 -0
- package/dist/templates/db/postgres.d.ts.map +1 -0
- package/dist/templates/db/postgres.js +863 -0
- package/dist/templates/db/postgres.js.map +1 -0
- package/dist/templates/db/sqlite.d.ts +3 -0
- package/dist/templates/db/sqlite.d.ts.map +1 -0
- package/dist/templates/db/sqlite.js +878 -0
- package/dist/templates/db/sqlite.js.map +1 -0
- package/dist/templates/index.d.ts +5 -5
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +21 -20
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/server.d.ts +3 -3
- package/dist/templates/server.d.ts.map +1 -1
- package/dist/templates/server.js +149 -97
- package/dist/templates/server.js.map +1 -1
- package/dist/templates/shared.d.ts +34 -1
- package/dist/templates/shared.d.ts.map +1 -1
- package/dist/templates/shared.js +45 -0
- package/dist/templates/shared.js.map +1 -1
- package/package.json +2 -2
- package/src/choices.ts +70 -3
- package/src/create-prompts.ts +25 -3
- package/src/create.ts +24 -2
- package/src/index.ts +29 -5
- package/src/make.ts +265 -34
- package/src/templates/base.ts +95 -9
- package/src/templates/db/index.ts +34 -0
- package/src/templates/db/mysql.ts +976 -0
- package/src/templates/db/postgres.ts +867 -0
- package/src/templates/{db.ts → db/sqlite.ts} +31 -152
- package/src/templates/index.ts +24 -19
- package/src/templates/server.ts +161 -97
- package/src/templates/shared.ts +90 -1
package/src/choices.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Choice lists
|
|
3
|
-
* heavier scaffold/generator modules. Keeping them
|
|
4
|
-
* flag enums
|
|
2
|
+
* Choice lists, string-literal types, and small pure helpers shared by the
|
|
3
|
+
* CLI entrypoint and the heavier scaffold/generator modules. Keeping them
|
|
4
|
+
* here lets the bin define flag enums and next steps without loading
|
|
5
|
+
* templates or generators at startup.
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -19,6 +20,10 @@ export type TemplateName = "next";
|
|
|
19
20
|
* integrations that add an external service remain selectable.
|
|
20
21
|
*/
|
|
21
22
|
export type IntegrationName = "inngest" | "resend" | "upstash-rate-limit";
|
|
23
|
+
/**
|
|
24
|
+
* Databases supported by the starter's Drizzle persistence layer.
|
|
25
|
+
*/
|
|
26
|
+
export type DatabaseName = "sqlite" | "postgres" | "mysql";
|
|
22
27
|
|
|
23
28
|
/**
|
|
24
29
|
* Supported scaffold template choices.
|
|
@@ -46,6 +51,68 @@ export const integrationChoices = [
|
|
|
46
51
|
"upstash-rate-limit",
|
|
47
52
|
] as const satisfies readonly IntegrationName[];
|
|
48
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Supported scaffold database choices.
|
|
56
|
+
*/
|
|
57
|
+
export const databaseChoices = [
|
|
58
|
+
"sqlite",
|
|
59
|
+
"postgres",
|
|
60
|
+
"mysql",
|
|
61
|
+
] as const satisfies readonly DatabaseName[];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Local database name derived from the app name.
|
|
65
|
+
*
|
|
66
|
+
* Postgres and MySQL database identifiers are stricter than project names,
|
|
67
|
+
* so dashes and dots become underscores. Used by `.env.example`, the
|
|
68
|
+
* generated drizzle config, and the create command's next steps, which must
|
|
69
|
+
* all point at the same local database.
|
|
70
|
+
*/
|
|
71
|
+
export function databaseLocalName(appName: string): string {
|
|
72
|
+
return appName.replaceAll(/[^a-z0-9_]/g, "_");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Docker one-liner that starts a local database matching the generated
|
|
77
|
+
* `.env.example` connection URL. Returns undefined for SQLite, which needs
|
|
78
|
+
* no server.
|
|
79
|
+
*/
|
|
80
|
+
export function databaseStartCommand(
|
|
81
|
+
database: DatabaseName,
|
|
82
|
+
appName: string,
|
|
83
|
+
): string | undefined {
|
|
84
|
+
const databaseName = databaseLocalName(appName);
|
|
85
|
+
|
|
86
|
+
switch (database) {
|
|
87
|
+
case "postgres":
|
|
88
|
+
return `docker run --rm -d -e POSTGRES_USER=beignet -e POSTGRES_PASSWORD=beignet -e POSTGRES_DB=${databaseName} -p 5432:5432 postgres:17-alpine`;
|
|
89
|
+
case "mysql":
|
|
90
|
+
return `docker run --rm -d -e MYSQL_ROOT_PASSWORD=beignet -e MYSQL_DATABASE=${databaseName} -p 3306:3306 mysql:8.4`;
|
|
91
|
+
default:
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Local development connection URL matching `databaseStartCommand`.
|
|
98
|
+
* Used as the `.env.example` value and the generated env defaults.
|
|
99
|
+
*/
|
|
100
|
+
export function databaseLocalUrl(
|
|
101
|
+
database: DatabaseName,
|
|
102
|
+
appName: string,
|
|
103
|
+
): string {
|
|
104
|
+
const databaseName = databaseLocalName(appName);
|
|
105
|
+
|
|
106
|
+
switch (database) {
|
|
107
|
+
case "postgres":
|
|
108
|
+
return `postgres://beignet:beignet@127.0.0.1:5432/${databaseName}`;
|
|
109
|
+
case "mysql":
|
|
110
|
+
return `mysql://root:beignet@127.0.0.1:3306/${databaseName}`;
|
|
111
|
+
default:
|
|
112
|
+
return "file:local.db";
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
49
116
|
/**
|
|
50
117
|
* Shells supported by `beignet completion install` and `uninstall`.
|
|
51
118
|
*/
|
package/src/create-prompts.ts
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import {
|
|
10
|
+
type DatabaseName,
|
|
11
|
+
databaseChoices,
|
|
10
12
|
type IntegrationName,
|
|
11
13
|
integrationChoices,
|
|
12
14
|
type PackageManager,
|
|
@@ -18,6 +20,7 @@ import {
|
|
|
18
20
|
export type CreateSelections = {
|
|
19
21
|
directory?: string;
|
|
20
22
|
api?: boolean;
|
|
23
|
+
db?: DatabaseName;
|
|
21
24
|
integrations?: readonly IntegrationName[];
|
|
22
25
|
packageManager?: PackageManager;
|
|
23
26
|
};
|
|
@@ -35,9 +38,9 @@ type CreatePromptTty = {
|
|
|
35
38
|
* Decide whether `beignet create` should run the interactive prompt flow.
|
|
36
39
|
*
|
|
37
40
|
* Prompts run only on a full TTY, only when `--yes` is absent, and only when
|
|
38
|
-
* no selection flag was passed. Any explicit api, integration, or
|
|
39
|
-
* manager selection means the invocation is scripted, so the
|
|
40
|
-
* path stays byte-identical for agents and CI.
|
|
41
|
+
* no selection flag was passed. Any explicit api, database, integration, or
|
|
42
|
+
* package manager selection means the invocation is scripted, so the
|
|
43
|
+
* non-interactive path stays byte-identical for agents and CI.
|
|
41
44
|
*/
|
|
42
45
|
export function shouldPromptInteractively(
|
|
43
46
|
flags: CreatePromptFlags,
|
|
@@ -48,6 +51,7 @@ export function shouldPromptInteractively(
|
|
|
48
51
|
|
|
49
52
|
const selectionFlagPassed =
|
|
50
53
|
flags.api !== undefined ||
|
|
54
|
+
flags.db !== undefined ||
|
|
51
55
|
(flags.integrations?.length ?? 0) > 0 ||
|
|
52
56
|
flags.packageManager !== undefined;
|
|
53
57
|
|
|
@@ -60,6 +64,12 @@ const integrationHints: Record<IntegrationName, string> = {
|
|
|
60
64
|
"upstash-rate-limit": "Upstash-backed rate limiting",
|
|
61
65
|
};
|
|
62
66
|
|
|
67
|
+
const databaseHints: Record<DatabaseName, string> = {
|
|
68
|
+
sqlite: "libSQL/Turso, zero setup — default",
|
|
69
|
+
postgres: "node-postgres, requires a Postgres 14+ server",
|
|
70
|
+
mysql: "mysql2, requires MySQL 8.0+",
|
|
71
|
+
};
|
|
72
|
+
|
|
63
73
|
/**
|
|
64
74
|
* Run the interactive `beignet create` prompt flow.
|
|
65
75
|
*
|
|
@@ -92,6 +102,17 @@ export async function promptCreateSelections(
|
|
|
92
102
|
});
|
|
93
103
|
if (prompts.isCancel(api)) return undefined;
|
|
94
104
|
|
|
105
|
+
const db = await prompts.select<DatabaseName>({
|
|
106
|
+
message: "Which database?",
|
|
107
|
+
options: databaseChoices.map((database) => ({
|
|
108
|
+
value: database,
|
|
109
|
+
label: database,
|
|
110
|
+
hint: databaseHints[database],
|
|
111
|
+
})),
|
|
112
|
+
initialValue: initial.db ?? "sqlite",
|
|
113
|
+
});
|
|
114
|
+
if (prompts.isCancel(db)) return undefined;
|
|
115
|
+
|
|
95
116
|
const integrations = await prompts.multiselect<IntegrationName>({
|
|
96
117
|
message: "Add provider integrations? (press enter to skip)",
|
|
97
118
|
options: integrationChoices.map((integration) => ({
|
|
@@ -108,6 +129,7 @@ export async function promptCreateSelections(
|
|
|
108
129
|
return {
|
|
109
130
|
directory,
|
|
110
131
|
api,
|
|
132
|
+
db,
|
|
111
133
|
integrations,
|
|
112
134
|
packageManager: initial.packageManager,
|
|
113
135
|
};
|
package/src/create.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdir, readdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {
|
|
4
|
+
type DatabaseName,
|
|
5
|
+
databaseChoices,
|
|
4
6
|
getTemplateFiles,
|
|
5
7
|
type IntegrationName,
|
|
6
8
|
integrationChoices,
|
|
@@ -22,6 +24,11 @@ export type CreateOptions = {
|
|
|
22
24
|
api?: boolean;
|
|
23
25
|
packageManager?: PackageManager;
|
|
24
26
|
integrations?: IntegrationName[];
|
|
27
|
+
/**
|
|
28
|
+
* Database backing the starter's Drizzle persistence layer.
|
|
29
|
+
* Defaults to "sqlite".
|
|
30
|
+
*/
|
|
31
|
+
db?: DatabaseName;
|
|
25
32
|
force?: boolean;
|
|
26
33
|
dryRun?: boolean;
|
|
27
34
|
beignetVersion?: string;
|
|
@@ -37,6 +44,7 @@ export type CreateResult = {
|
|
|
37
44
|
dryRun: boolean;
|
|
38
45
|
files: string[];
|
|
39
46
|
packageManager: PackageManager;
|
|
47
|
+
database: DatabaseName;
|
|
40
48
|
};
|
|
41
49
|
|
|
42
50
|
/**
|
|
@@ -52,6 +60,7 @@ export async function createProject(
|
|
|
52
60
|
const template = options.template ?? "next";
|
|
53
61
|
const api = Boolean(options.api);
|
|
54
62
|
const integrations = resolveIntegrations(options.integrations ?? []);
|
|
63
|
+
const database = options.db ?? "sqlite";
|
|
55
64
|
|
|
56
65
|
if (template !== "next") {
|
|
57
66
|
throw new Error(
|
|
@@ -59,6 +68,12 @@ export async function createProject(
|
|
|
59
68
|
);
|
|
60
69
|
}
|
|
61
70
|
|
|
71
|
+
if (!databaseChoices.includes(database)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Unknown database "${database}". Available databases: ${databaseChoices.join(", ")}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
62
77
|
const dryRun = Boolean(options.dryRun);
|
|
63
78
|
|
|
64
79
|
await assertCanWrite(targetDir, Boolean(options.force));
|
|
@@ -69,6 +84,7 @@ export async function createProject(
|
|
|
69
84
|
beignetVersion: options.beignetVersion ?? getDefaultBeignetVersion(),
|
|
70
85
|
api,
|
|
71
86
|
integrations,
|
|
87
|
+
database,
|
|
72
88
|
});
|
|
73
89
|
|
|
74
90
|
if (!dryRun) {
|
|
@@ -88,6 +104,7 @@ export async function createProject(
|
|
|
88
104
|
dryRun,
|
|
89
105
|
files: files.map((file) => file.path),
|
|
90
106
|
packageManager,
|
|
107
|
+
database,
|
|
91
108
|
};
|
|
92
109
|
}
|
|
93
110
|
|
|
@@ -101,9 +118,14 @@ function resolveIntegrations(
|
|
|
101
118
|
explicitIntegrations: IntegrationName[],
|
|
102
119
|
): IntegrationName[] {
|
|
103
120
|
for (const integration of explicitIntegrations) {
|
|
104
|
-
|
|
121
|
+
const integrationName: string = integration;
|
|
122
|
+
if (baseProviderNames.has(integrationName)) {
|
|
123
|
+
const databaseHint =
|
|
124
|
+
integrationName === "drizzle-sqlite"
|
|
125
|
+
? " Choose the starter database with --db sqlite|postgres|mysql instead."
|
|
126
|
+
: "";
|
|
105
127
|
throw new Error(
|
|
106
|
-
`${integration} is part of every Beignet starter and no longer an --integrations choice
|
|
128
|
+
`${integration} is part of every Beignet starter and no longer an --integrations choice.${databaseHint}`,
|
|
107
129
|
);
|
|
108
130
|
}
|
|
109
131
|
}
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
type CompletionShell,
|
|
18
18
|
completionShellChoices,
|
|
19
|
+
type DatabaseName,
|
|
20
|
+
databaseChoices,
|
|
21
|
+
databaseStartCommand,
|
|
19
22
|
type IntegrationName,
|
|
20
23
|
integrationChoices,
|
|
21
24
|
type MakeFeatureAddon,
|
|
@@ -44,6 +47,7 @@ const outputFormatChoices = [
|
|
|
44
47
|
type CreateFlags = {
|
|
45
48
|
template: TemplateName;
|
|
46
49
|
api?: boolean;
|
|
50
|
+
db?: DatabaseName;
|
|
47
51
|
packageManager?: PackageManager;
|
|
48
52
|
integrations?: readonly IntegrationName[];
|
|
49
53
|
yes?: boolean;
|
|
@@ -282,10 +286,11 @@ const createCommand = buildCommand<
|
|
|
282
286
|
docs: {
|
|
283
287
|
brief: "Create a new Beignet app.",
|
|
284
288
|
fullDescription: `Scaffolds a full-stack Beignet starter: typed contracts, Better Auth,
|
|
285
|
-
Drizzle
|
|
286
|
-
|
|
287
|
-
|
|
289
|
+
Drizzle persistence with checked-in migrations, pino logging, devtools,
|
|
290
|
+
and a shadcn UI shell. Pass --api for an API-only scaffold without the
|
|
291
|
+
UI shell, and --db to choose the database.
|
|
288
292
|
|
|
293
|
+
Available databases: ${databaseChoices.join(", ")}
|
|
289
294
|
Available integrations: ${integrationChoices.join(", ")}
|
|
290
295
|
|
|
291
296
|
Running create on an interactive terminal without selection flags opens a
|
|
@@ -305,6 +310,12 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
305
310
|
withNegated: false,
|
|
306
311
|
brief: "Scaffold an API-only app without the UI shell.",
|
|
307
312
|
},
|
|
313
|
+
db: {
|
|
314
|
+
kind: "enum",
|
|
315
|
+
values: databaseChoices,
|
|
316
|
+
optional: true,
|
|
317
|
+
brief: "Database for Drizzle persistence. Default: sqlite.",
|
|
318
|
+
},
|
|
308
319
|
packageManager: {
|
|
309
320
|
kind: "enum",
|
|
310
321
|
values: packageManagerChoices,
|
|
@@ -354,6 +365,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
354
365
|
let selections: CreateSelections = {
|
|
355
366
|
directory,
|
|
356
367
|
api: flags.api,
|
|
368
|
+
db: flags.db,
|
|
357
369
|
integrations: flags.integrations,
|
|
358
370
|
packageManager: flags.packageManager,
|
|
359
371
|
};
|
|
@@ -382,6 +394,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
382
394
|
}
|
|
383
395
|
|
|
384
396
|
const api = selections.api ?? false;
|
|
397
|
+
const database = selections.db ?? "sqlite";
|
|
385
398
|
const integrations = uniqueValues([...(selections.integrations ?? [])]);
|
|
386
399
|
const result = await createProject({
|
|
387
400
|
name: selections.directory,
|
|
@@ -389,6 +402,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
389
402
|
api,
|
|
390
403
|
packageManager: selections.packageManager,
|
|
391
404
|
integrations,
|
|
405
|
+
db: database,
|
|
392
406
|
force: Boolean(flags.force),
|
|
393
407
|
dryRun: Boolean(flags.dryRun),
|
|
394
408
|
});
|
|
@@ -397,7 +411,7 @@ prompt-based setup. Pass --yes or any selection flag to skip the prompts.`,
|
|
|
397
411
|
this,
|
|
398
412
|
flags.json
|
|
399
413
|
? JSON.stringify(result, null, 2)
|
|
400
|
-
: nextSteps(result, { api, integrations }),
|
|
414
|
+
: nextSteps(result, { api, integrations, database }),
|
|
401
415
|
);
|
|
402
416
|
};
|
|
403
417
|
},
|
|
@@ -1238,6 +1252,7 @@ function nextSteps(
|
|
|
1238
1252
|
options: {
|
|
1239
1253
|
api?: boolean;
|
|
1240
1254
|
integrations?: readonly IntegrationName[];
|
|
1255
|
+
database?: DatabaseName;
|
|
1241
1256
|
} = {},
|
|
1242
1257
|
): string {
|
|
1243
1258
|
if (result.dryRun) {
|
|
@@ -1261,6 +1276,15 @@ ${plannedFiles}`;
|
|
|
1261
1276
|
const openStep = options.api
|
|
1262
1277
|
? " open http://localhost:3000 for the API landing page"
|
|
1263
1278
|
: " open http://localhost:3000/sign-up";
|
|
1279
|
+
const startDatabaseCommand = options.database
|
|
1280
|
+
? databaseStartCommand(options.database, result.name)
|
|
1281
|
+
: undefined;
|
|
1282
|
+
const startDatabaseStep = startDatabaseCommand
|
|
1283
|
+
? `Start the database:
|
|
1284
|
+
${startDatabaseCommand}
|
|
1285
|
+
|
|
1286
|
+
`
|
|
1287
|
+
: "";
|
|
1264
1288
|
|
|
1265
1289
|
return `Created ${result.name} at ${result.targetDir}
|
|
1266
1290
|
|
|
@@ -1269,7 +1293,7 @@ Next steps:
|
|
|
1269
1293
|
${pm} install
|
|
1270
1294
|
cp .env.example .env.local
|
|
1271
1295
|
${envStep}
|
|
1272
|
-
Prepare the database:
|
|
1296
|
+
${startDatabaseStep}Prepare the database:
|
|
1273
1297
|
${cli} db migrate
|
|
1274
1298
|
|
|
1275
1299
|
Start the app:
|